0
0
Fork 0
mirror of https://github.com/denoland/rusty_v8.git synced 2024-12-28 10:09:25 -05:00
denoland-rusty-v8/tests/test_api.rs

2944 lines
97 KiB
Rust
Raw Normal View History

2020-01-02 13:57:00 -05:00
// Copyright 2019-2020 the Deno authors. All rights reserved. MIT license.
#[macro_use]
extern crate lazy_static;
use std::convert::{Into, TryFrom, TryInto};
use std::ptr::NonNull;
2019-12-26 10:45:55 -05:00
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex;
use rusty_v8 as v8;
// TODO(piscisaureus): Ideally there would be no need to import this trait.
use v8::MapFnTo;
lazy_static! {
static ref INIT_LOCK: Mutex<u32> = Mutex::new(0);
}
#[must_use]
struct SetupGuard {}
impl Drop for SetupGuard {
fn drop(&mut self) {
// TODO shutdown process cleanly.
}
}
fn setup() -> SetupGuard {
let mut g = INIT_LOCK.lock().unwrap();
*g += 1;
if *g == 1 {
v8::V8::initialize_platform(v8::new_default_platform().unwrap());
v8::V8::initialize();
}
SetupGuard {}
}
#[test]
fn handle_scope_nested() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope1 = hs.enter();
{
let mut hs = v8::HandleScope::new(scope1);
let _scope2 = hs.enter();
}
}
}
#[test]
#[allow(clippy::float_cmp)]
fn handle_scope_numbers() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope1 = hs.enter();
let l1 = v8::Integer::new(scope1, -123);
let l2 = v8::Integer::new_from_unsigned(scope1, 456);
{
let mut hs = v8::HandleScope::new(scope1);
let scope2 = hs.enter();
2019-12-20 10:01:45 -05:00
let l3 = v8::Number::new(scope2, 78.9);
assert_eq!(l1.value(), -123);
assert_eq!(l2.value(), 456);
assert_eq!(l3.value(), 78.9);
assert_eq!(v8::Number::value(&l1), -123f64);
assert_eq!(v8::Number::value(&l2), 456f64);
}
}
}
// TODO: the type checker is kumbaya with this but in reality the
// `Local<Integer>` created at the end of the test is created in HandleScope
// `hs2` and not in `hs1` as specified. When this local is accessed, which is
// after `hs2` is destroyed, a crash happens.
#[test]
#[ignore]
fn handle_scope_early_drop() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
let mut hs1 = v8::HandleScope::new(&mut isolate);
let hs1 = hs1.enter();
let local = {
let mut hs2 = v8::HandleScope::new(hs1);
let _hs2 = hs2.enter();
v8::Integer::new(hs1, 123)
};
assert_eq!(local.value(), 123);
}
#[test]
fn global_handles() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
let mut g1 = v8::Global::<v8::String>::new();
let mut g2 = v8::Global::<v8::Integer>::new();
let mut g3 = v8::Global::<v8::Integer>::new();
let mut _g4 = v8::Global::<v8::Integer>::new();
let g5 = v8::Global::<v8::Script>::new();
let mut g6 = v8::Global::<v8::Integer>::new();
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let l1 = v8::String::new(scope, "bla").unwrap();
let l2 = v8::Integer::new(scope, 123);
g1.set(scope, l1);
g2.set(scope, l2);
g3.set(scope, &g2);
_g4 = v8::Global::new_from(scope, l2);
let l6 = v8::Integer::new(scope, 100);
g6.set(scope, l6);
}
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
assert!(!g1.is_empty());
assert_eq!(g1.get(scope).unwrap().to_rust_string_lossy(scope), "bla");
assert!(!g2.is_empty());
assert_eq!(g2.get(scope).unwrap().value(), 123);
assert!(!g3.is_empty());
assert_eq!(g3.get(scope).unwrap().value(), 123);
assert!(!_g4.is_empty());
assert_eq!(_g4.get(scope).unwrap().value(), 123);
assert!(g5.is_empty());
let num = g6.get(scope).unwrap();
g6.reset(scope);
assert_eq!(num.value(), 100);
}
g1.reset(&mut isolate);
assert!(g1.is_empty());
g2.reset(&mut isolate);
assert!(g2.is_empty());
g3.reset(&mut isolate);
assert!(g3.is_empty());
_g4.reset(&mut isolate);
assert!(_g4.is_empty());
assert!(g5.is_empty());
}
#[test]
fn global_handle_drop() {
let _setup_guard = setup();
// Global 'g1' will be dropped _after_ the Isolate has been disposed.
let mut g1 = v8::Global::<v8::String>::new();
let mut isolate = v8::Isolate::new(Default::default());
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let l1 = v8::String::new(scope, "foo").unwrap();
g1.set(scope, l1);
// Global 'g2' will be dropped _before_ the Isolate has been disposed.
let l2 = v8::String::new(scope, "bar").unwrap();
let _g2 = v8::Global::new_from(scope, l2);
}
2019-12-04 02:03:17 -05:00
#[test]
fn test_string() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
2019-12-04 02:03:17 -05:00
let reference = "Hello 🦕 world!";
let local = v8::String::new(scope, reference).unwrap();
2019-12-05 12:28:20 -05:00
assert_eq!(15, local.length());
2019-12-20 10:01:45 -05:00
assert_eq!(17, local.utf8_length(scope));
assert_eq!(reference, local.to_rust_string_lossy(scope));
}
2020-01-22 11:23:42 -05:00
{
let mut hs = v8::HandleScope::new(&mut isolate);
2020-01-22 11:23:42 -05:00
let scope = hs.enter();
let local = v8::String::empty(scope);
2020-01-22 11:23:42 -05:00
assert_eq!(0, local.length());
assert_eq!(0, local.utf8_length(scope));
assert_eq!("", local.to_rust_string_lossy(scope));
}
{
let mut hs = v8::HandleScope::new(&mut isolate);
2020-01-22 11:23:42 -05:00
let scope = hs.enter();
let local =
v8::String::new_from_utf8(scope, b"", v8::NewStringType::Normal).unwrap();
assert_eq!(0, local.length());
assert_eq!(0, local.utf8_length(scope));
assert_eq!("", local.to_rust_string_lossy(scope));
}
2019-12-04 02:03:17 -05:00
}
2019-12-22 09:05:39 -05:00
#[test]
#[allow(clippy::float_cmp)]
fn escapable_handle_scope() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope1 = hs.enter();
// After dropping EscapableHandleScope, we should be able to
2019-12-22 09:05:39 -05:00
// read escaped values.
2019-12-30 09:28:39 -05:00
let number = {
let mut hs = v8::EscapableHandleScope::new(scope1);
let escapable_scope = hs.enter();
2019-12-30 09:28:39 -05:00
let number = v8::Number::new(escapable_scope, 78.9);
2019-12-22 09:05:39 -05:00
escapable_scope.escape(number)
};
assert_eq!(number.value(), 78.9);
let string = {
let mut hs = v8::EscapableHandleScope::new(scope1);
let escapable_scope = hs.enter();
let string = v8::String::new(escapable_scope, "Hello 🦕 world!").unwrap();
escapable_scope.escape(string)
2019-12-22 09:05:39 -05:00
};
assert_eq!("Hello 🦕 world!", string.to_rust_string_lossy(scope1));
let string = {
let mut hs = v8::EscapableHandleScope::new(scope1);
let escapable_scope = hs.enter();
2019-12-22 09:05:39 -05:00
let nested_str_val = {
let mut hs = v8::EscapableHandleScope::new(escapable_scope);
let nested_escapable_scope = hs.enter();
let string =
v8::String::new(nested_escapable_scope, "Hello 🦕 world!").unwrap();
nested_escapable_scope.escape(string)
2019-12-22 09:05:39 -05:00
};
escapable_scope.escape(nested_str_val)
};
assert_eq!("Hello 🦕 world!", string.to_rust_string_lossy(scope1));
}
2019-12-22 09:05:39 -05:00
}
#[test]
fn context_scope() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
assert!(scope.get_current_context().is_none());
assert!(scope.get_entered_or_microtask_context().is_none());
{
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
assert!(scope.get_current_context().unwrap() == context);
assert!(scope.get_entered_or_microtask_context().unwrap() == context);
}
assert!(scope.get_current_context().is_none());
assert!(scope.get_entered_or_microtask_context().is_none());
}
#[test]
fn microtasks() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
isolate.run_microtasks();
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
let function = v8::Function::new(
scope,
context,
|_: v8::FunctionCallbackScope,
_: v8::FunctionCallbackArguments,
_: v8::ReturnValue| {
CALL_COUNT.fetch_add(1, Ordering::SeqCst);
},
)
.unwrap();
scope.isolate().enqueue_microtask(function);
assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 0);
scope.isolate().run_microtasks();
assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 1);
}
}
#[test]
fn get_isolate_from_handle() {
extern "C" {
fn v8__internal__GetIsolateFromHeapObject(
location: *const v8::Data,
) -> *mut v8::Isolate;
}
fn check_handle_helper(
isolate_ptr: NonNull<v8::Isolate>,
expect_some: Option<bool>,
local: v8::Local<v8::Data>,
) {
let maybe_ptr = unsafe { v8__internal__GetIsolateFromHeapObject(&*local) };
let maybe_ptr = NonNull::new(maybe_ptr);
if let Some(ptr) = maybe_ptr {
assert_eq!(ptr, isolate_ptr);
}
if let Some(expected_some) = expect_some {
assert_eq!(maybe_ptr.is_some(), expected_some);
}
};
fn check_handle<'s, S, F, D>(scope: &mut S, expect_some: Option<bool>, f: F)
where
S: v8::ToLocal<'s>,
F: Fn(&mut S) -> D,
D: Into<v8::Local<'s, v8::Data>>,
{
let isolate_ptr = NonNull::from(scope.isolate());
let local = f(scope).into();
// Check that we can get the isolate from a Local.
check_handle_helper(isolate_ptr, expect_some, local);
// Check that we can still get it after converting it to a Global and back.
let global = v8::Global::new_from(scope, local);
let local = global.get(scope).unwrap();
check_handle_helper(isolate_ptr, expect_some, local);
};
fn check_eval<'s, S>(scope: &mut S, expect_some: Option<bool>, code: &str)
where
S: v8::ToLocal<'s>,
{
let context = scope.get_current_context().unwrap();
check_handle(scope, expect_some, |scope| {
eval(scope, context, code).unwrap()
});
}
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let s = cs.enter();
check_handle(s, None, |s| v8::null(s));
check_handle(s, None, |s| v8::undefined(s));
check_handle(s, None, |s| v8::Boolean::new(s, true));
check_handle(s, None, |s| v8::Boolean::new(s, false));
check_handle(s, None, |s| v8::String::new(s, "").unwrap());
check_eval(s, None, "''");
check_handle(s, Some(true), |s| v8::String::new(s, "Words").unwrap());
check_eval(s, Some(true), "'Hello'");
check_eval(s, Some(true), "Symbol()");
check_handle(s, Some(true), |s| v8::Object::new(s));
check_eval(s, Some(true), "this");
check_handle(s, Some(true), |s| s.get_current_context().unwrap());
check_eval(s, Some(true), "({ foo: 'bar' })");
check_eval(s, Some(true), "() => {}");
check_handle(s, Some(true), |s| v8::Number::new(s, 4.2f64));
check_handle(s, Some(true), |s| v8::Number::new(s, -0f64));
check_handle(s, Some(false), |s| v8::Integer::new(s, 0));
check_eval(s, Some(true), "3.3");
check_eval(s, Some(false), "3.3 / 3.3");
}
#[test]
fn array_buffer() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let ab = v8::ArrayBuffer::new(scope, 42);
assert_eq!(42, ab.byte_length());
2019-12-21 11:05:51 -05:00
let bs = v8::ArrayBuffer::new_backing_store(scope, 84);
assert_eq!(84, bs.byte_length());
assert_eq!(false, bs.is_shared());
let data: Box<[u8]> = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9].into_boxed_slice();
let unique_bs = v8::ArrayBuffer::new_backing_store_from_boxed_slice(data);
assert_eq!(10, unique_bs.byte_length());
assert_eq!(false, unique_bs.is_shared());
assert_eq!(unique_bs[0].get(), 0);
assert_eq!(unique_bs[9].get(), 9);
let shared_bs_1 = unique_bs.make_shared();
assert_eq!(10, shared_bs_1.byte_length());
assert_eq!(false, shared_bs_1.is_shared());
assert_eq!(shared_bs_1[0].get(), 0);
assert_eq!(shared_bs_1[9].get(), 9);
let ab = v8::ArrayBuffer::with_backing_store(scope, &shared_bs_1);
let shared_bs_2 = ab.get_backing_store();
assert_eq!(10, shared_bs_2.byte_length());
assert_eq!(shared_bs_2[0].get(), 0);
assert_eq!(shared_bs_2[9].get(), 9);
}
}
2020-02-28 18:40:48 -05:00
#[test]
fn backing_store_segfault() {
let _setup_guard = setup();
let array_buffer_allocator = v8::new_default_allocator().make_shared();
2020-02-28 18:40:48 -05:00
let shared_bs = {
assert_eq!(1, v8::SharedRef::use_count(&array_buffer_allocator));
let params = v8::Isolate::create_params()
.array_buffer_allocator(array_buffer_allocator.clone());
2020-02-28 18:40:48 -05:00
assert_eq!(2, v8::SharedRef::use_count(&array_buffer_allocator));
let mut isolate = v8::Isolate::new(params);
assert_eq!(2, v8::SharedRef::use_count(&array_buffer_allocator));
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let ab = v8::ArrayBuffer::new(scope, 10);
let shared_bs = ab.get_backing_store();
assert_eq!(3, v8::SharedRef::use_count(&array_buffer_allocator));
shared_bs
};
assert_eq!(1, v8::SharedRef::use_count(&shared_bs));
assert_eq!(2, v8::SharedRef::use_count(&array_buffer_allocator));
drop(array_buffer_allocator);
drop(shared_bs); // Error occurred here.
}
#[test]
fn shared_array_buffer_allocator() {
let alloc1 = v8::new_default_allocator().make_shared();
assert_eq!(1, v8::SharedRef::use_count(&alloc1));
let alloc2 = alloc1.clone();
assert_eq!(2, v8::SharedRef::use_count(&alloc1));
assert_eq!(2, v8::SharedRef::use_count(&alloc2));
let mut alloc2 = v8::SharedPtr::from(alloc2);
assert_eq!(2, v8::SharedRef::use_count(&alloc1));
assert_eq!(2, v8::SharedPtr::use_count(&alloc2));
drop(alloc1);
assert_eq!(1, v8::SharedPtr::use_count(&alloc2));
alloc2.take();
assert_eq!(0, v8::SharedPtr::use_count(&alloc2));
}
#[test]
fn array_buffer_with_shared_backing_store() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let ab1 = v8::ArrayBuffer::new(scope, 42);
assert_eq!(42, ab1.byte_length());
let bs1 = ab1.get_backing_store();
assert_eq!(ab1.byte_length(), bs1.byte_length());
assert_eq!(2, v8::SharedRef::use_count(&bs1));
let bs2 = ab1.get_backing_store();
assert_eq!(ab1.byte_length(), bs2.byte_length());
assert_eq!(3, v8::SharedRef::use_count(&bs1));
assert_eq!(3, v8::SharedRef::use_count(&bs2));
let bs3 = ab1.get_backing_store();
assert_eq!(ab1.byte_length(), bs3.byte_length());
assert_eq!(4, v8::SharedRef::use_count(&bs1));
assert_eq!(4, v8::SharedRef::use_count(&bs2));
assert_eq!(4, v8::SharedRef::use_count(&bs3));
drop(bs2);
assert_eq!(3, v8::SharedRef::use_count(&bs1));
assert_eq!(3, v8::SharedRef::use_count(&bs3));
drop(bs1);
assert_eq!(2, v8::SharedRef::use_count(&bs3));
let ab2 = v8::ArrayBuffer::with_backing_store(scope, &bs3);
assert_eq!(ab1.byte_length(), ab2.byte_length());
assert_eq!(3, v8::SharedRef::use_count(&bs3));
let bs4 = ab2.get_backing_store();
assert_eq!(ab1.byte_length(), bs4.byte_length());
assert_eq!(4, v8::SharedRef::use_count(&bs3));
assert_eq!(4, v8::SharedRef::use_count(&bs4));
let bs5 = bs4.clone();
assert_eq!(5, v8::SharedRef::use_count(&bs3));
assert_eq!(5, v8::SharedRef::use_count(&bs4));
assert_eq!(5, v8::SharedRef::use_count(&bs5));
drop(bs3);
assert_eq!(4, v8::SharedRef::use_count(&bs4));
assert_eq!(4, v8::SharedRef::use_count(&bs4));
drop(bs4);
assert_eq!(3, v8::SharedRef::use_count(&bs5));
}
}
2019-12-20 10:01:45 -05:00
fn v8_str<'sc>(
scope: &mut impl v8::ToLocal<'sc>,
2019-12-20 10:01:45 -05:00
s: &str,
) -> v8::Local<'sc, v8::String> {
v8::String::new(scope, s).unwrap()
2019-12-23 18:09:03 -05:00
}
fn eval<'sc>(
scope: &mut impl v8::ToLocal<'sc>,
context: v8::Local<v8::Context>,
2020-02-13 15:03:25 -05:00
code: &str,
) -> Option<v8::Local<'sc, v8::Value>> {
let mut hs = v8::EscapableHandleScope::new(scope);
let scope = hs.enter();
2019-12-23 18:09:03 -05:00
let source = v8_str(scope, code);
let mut script = v8::Script::compile(scope, context, source, None).unwrap();
let r = script.run(scope, context);
r.map(|v| scope.escape(v))
}
2019-12-20 18:28:08 -05:00
#[test]
fn try_catch() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
2019-12-20 18:28:08 -05:00
{
// Error thrown - should be caught.
let mut try_catch = v8::TryCatch::new(scope);
let tc = try_catch.enter();
let result = eval(scope, context, "throw new Error('foo')");
assert!(result.is_none());
assert!(tc.has_caught());
Use correct lifetime for TryCatch::exception()/message() return value (#380) According to v8.h, "the returned handle is valid until this TryCatch block has been destroyed". This is incorrect, as can be demonstrated with the test below. In practice the return value lives no longer and no shorter than the active HandleScope at the time these methods are called. An issue has been opened about this in the V8 bug tracker: https://bugs.chromium.org/p/v8/issues/detail?id=10537. ```rust fn try_catch_bad_lifetimes() { let _setup_guard = setup(); let mut isolate = v8::Isolate::new(Default::default()); let mut hs = v8::HandleScope::new(&mut isolate); let scope = hs.enter(); let context = v8::Context::new(scope); let mut cs = v8::ContextScope::new(scope, context); let scope = cs.enter(); let caught_msg_2 = { let mut try_catch = v8::TryCatch::new(scope); let try_catch = try_catch.enter(); let caught_msg_1 = { let mut hs = v8::HandleScope::new(scope); let scope = hs.enter(); // Throw exception #1. let msg_1 = v8::String::new(scope, "BOOM!").unwrap(); let exc_1 = v8::Exception::type_error(scope, msg_1); scope.isolate().throw_exception(exc_1); // Catch exception #1. let caught_msg_1 = try_catch.message().unwrap(); let caught_str_1 = caught_msg_1.get(scope).to_rust_string_lossy(scope); assert!(caught_str_1.contains("BOOM")); // Move `caught_msg_1` out of the HandleScope it was created in. // The borrow checker allows this because `caught_msg_1`'s // lifetime is contrained to not outlive the TryCatch, but it is // allowed to outlive the HandleScope that was active when the // exception was caught. caught_msg_1 }; // Next line crashes. let caught_str_1 = caught_msg_1.get(scope).to_rust_string_lossy(scope); assert!(caught_str_1.contains("BOOM")); // Throws exception #2. let msg_2 = v8::String::new(scope, "DANG!").unwrap(); let exc_2 = v8::Exception::type_error(scope, msg_2); scope.isolate().throw_exception(exc_2); // Catch exception #2. let caught_msg_2 = try_catch.message().unwrap(); let caught_str_2 = caught_msg_2.get(scope).to_rust_string_lossy(scope); assert!(caught_str_2.contains("DANG")); // Move `caught_msg_2` out of the extent of the TryCatch, but still // within the extent of its HandleScope. This is unnecessarily // rejected at compile time. caught_msg_2 }; let caught_str_2 = caught_msg_2.get(scope).to_rust_string_lossy(scope); assert!(caught_str_2.contains("DANG")); } ```
2020-05-24 14:58:10 -04:00
assert!(tc.exception(scope).is_some());
2019-12-20 18:28:08 -05:00
assert!(tc.stack_trace(scope, context).is_some());
Use correct lifetime for TryCatch::exception()/message() return value (#380) According to v8.h, "the returned handle is valid until this TryCatch block has been destroyed". This is incorrect, as can be demonstrated with the test below. In practice the return value lives no longer and no shorter than the active HandleScope at the time these methods are called. An issue has been opened about this in the V8 bug tracker: https://bugs.chromium.org/p/v8/issues/detail?id=10537. ```rust fn try_catch_bad_lifetimes() { let _setup_guard = setup(); let mut isolate = v8::Isolate::new(Default::default()); let mut hs = v8::HandleScope::new(&mut isolate); let scope = hs.enter(); let context = v8::Context::new(scope); let mut cs = v8::ContextScope::new(scope, context); let scope = cs.enter(); let caught_msg_2 = { let mut try_catch = v8::TryCatch::new(scope); let try_catch = try_catch.enter(); let caught_msg_1 = { let mut hs = v8::HandleScope::new(scope); let scope = hs.enter(); // Throw exception #1. let msg_1 = v8::String::new(scope, "BOOM!").unwrap(); let exc_1 = v8::Exception::type_error(scope, msg_1); scope.isolate().throw_exception(exc_1); // Catch exception #1. let caught_msg_1 = try_catch.message().unwrap(); let caught_str_1 = caught_msg_1.get(scope).to_rust_string_lossy(scope); assert!(caught_str_1.contains("BOOM")); // Move `caught_msg_1` out of the HandleScope it was created in. // The borrow checker allows this because `caught_msg_1`'s // lifetime is contrained to not outlive the TryCatch, but it is // allowed to outlive the HandleScope that was active when the // exception was caught. caught_msg_1 }; // Next line crashes. let caught_str_1 = caught_msg_1.get(scope).to_rust_string_lossy(scope); assert!(caught_str_1.contains("BOOM")); // Throws exception #2. let msg_2 = v8::String::new(scope, "DANG!").unwrap(); let exc_2 = v8::Exception::type_error(scope, msg_2); scope.isolate().throw_exception(exc_2); // Catch exception #2. let caught_msg_2 = try_catch.message().unwrap(); let caught_str_2 = caught_msg_2.get(scope).to_rust_string_lossy(scope); assert!(caught_str_2.contains("DANG")); // Move `caught_msg_2` out of the extent of the TryCatch, but still // within the extent of its HandleScope. This is unnecessarily // rejected at compile time. caught_msg_2 }; let caught_str_2 = caught_msg_2.get(scope).to_rust_string_lossy(scope); assert!(caught_str_2.contains("DANG")); } ```
2020-05-24 14:58:10 -04:00
assert!(tc.message(scope).is_some());
2019-12-20 18:28:08 -05:00
assert_eq!(
Use correct lifetime for TryCatch::exception()/message() return value (#380) According to v8.h, "the returned handle is valid until this TryCatch block has been destroyed". This is incorrect, as can be demonstrated with the test below. In practice the return value lives no longer and no shorter than the active HandleScope at the time these methods are called. An issue has been opened about this in the V8 bug tracker: https://bugs.chromium.org/p/v8/issues/detail?id=10537. ```rust fn try_catch_bad_lifetimes() { let _setup_guard = setup(); let mut isolate = v8::Isolate::new(Default::default()); let mut hs = v8::HandleScope::new(&mut isolate); let scope = hs.enter(); let context = v8::Context::new(scope); let mut cs = v8::ContextScope::new(scope, context); let scope = cs.enter(); let caught_msg_2 = { let mut try_catch = v8::TryCatch::new(scope); let try_catch = try_catch.enter(); let caught_msg_1 = { let mut hs = v8::HandleScope::new(scope); let scope = hs.enter(); // Throw exception #1. let msg_1 = v8::String::new(scope, "BOOM!").unwrap(); let exc_1 = v8::Exception::type_error(scope, msg_1); scope.isolate().throw_exception(exc_1); // Catch exception #1. let caught_msg_1 = try_catch.message().unwrap(); let caught_str_1 = caught_msg_1.get(scope).to_rust_string_lossy(scope); assert!(caught_str_1.contains("BOOM")); // Move `caught_msg_1` out of the HandleScope it was created in. // The borrow checker allows this because `caught_msg_1`'s // lifetime is contrained to not outlive the TryCatch, but it is // allowed to outlive the HandleScope that was active when the // exception was caught. caught_msg_1 }; // Next line crashes. let caught_str_1 = caught_msg_1.get(scope).to_rust_string_lossy(scope); assert!(caught_str_1.contains("BOOM")); // Throws exception #2. let msg_2 = v8::String::new(scope, "DANG!").unwrap(); let exc_2 = v8::Exception::type_error(scope, msg_2); scope.isolate().throw_exception(exc_2); // Catch exception #2. let caught_msg_2 = try_catch.message().unwrap(); let caught_str_2 = caught_msg_2.get(scope).to_rust_string_lossy(scope); assert!(caught_str_2.contains("DANG")); // Move `caught_msg_2` out of the extent of the TryCatch, but still // within the extent of its HandleScope. This is unnecessarily // rejected at compile time. caught_msg_2 }; let caught_str_2 = caught_msg_2.get(scope).to_rust_string_lossy(scope); assert!(caught_str_2.contains("DANG")); } ```
2020-05-24 14:58:10 -04:00
tc.message(scope)
.unwrap()
.get(scope)
.to_rust_string_lossy(scope),
2019-12-20 18:28:08 -05:00
"Uncaught Error: foo"
);
};
{
// No error thrown.
let mut try_catch = v8::TryCatch::new(scope);
let tc = try_catch.enter();
let result = eval(scope, context, "1 + 1");
assert!(result.is_some());
assert!(!tc.has_caught());
Use correct lifetime for TryCatch::exception()/message() return value (#380) According to v8.h, "the returned handle is valid until this TryCatch block has been destroyed". This is incorrect, as can be demonstrated with the test below. In practice the return value lives no longer and no shorter than the active HandleScope at the time these methods are called. An issue has been opened about this in the V8 bug tracker: https://bugs.chromium.org/p/v8/issues/detail?id=10537. ```rust fn try_catch_bad_lifetimes() { let _setup_guard = setup(); let mut isolate = v8::Isolate::new(Default::default()); let mut hs = v8::HandleScope::new(&mut isolate); let scope = hs.enter(); let context = v8::Context::new(scope); let mut cs = v8::ContextScope::new(scope, context); let scope = cs.enter(); let caught_msg_2 = { let mut try_catch = v8::TryCatch::new(scope); let try_catch = try_catch.enter(); let caught_msg_1 = { let mut hs = v8::HandleScope::new(scope); let scope = hs.enter(); // Throw exception #1. let msg_1 = v8::String::new(scope, "BOOM!").unwrap(); let exc_1 = v8::Exception::type_error(scope, msg_1); scope.isolate().throw_exception(exc_1); // Catch exception #1. let caught_msg_1 = try_catch.message().unwrap(); let caught_str_1 = caught_msg_1.get(scope).to_rust_string_lossy(scope); assert!(caught_str_1.contains("BOOM")); // Move `caught_msg_1` out of the HandleScope it was created in. // The borrow checker allows this because `caught_msg_1`'s // lifetime is contrained to not outlive the TryCatch, but it is // allowed to outlive the HandleScope that was active when the // exception was caught. caught_msg_1 }; // Next line crashes. let caught_str_1 = caught_msg_1.get(scope).to_rust_string_lossy(scope); assert!(caught_str_1.contains("BOOM")); // Throws exception #2. let msg_2 = v8::String::new(scope, "DANG!").unwrap(); let exc_2 = v8::Exception::type_error(scope, msg_2); scope.isolate().throw_exception(exc_2); // Catch exception #2. let caught_msg_2 = try_catch.message().unwrap(); let caught_str_2 = caught_msg_2.get(scope).to_rust_string_lossy(scope); assert!(caught_str_2.contains("DANG")); // Move `caught_msg_2` out of the extent of the TryCatch, but still // within the extent of its HandleScope. This is unnecessarily // rejected at compile time. caught_msg_2 }; let caught_str_2 = caught_msg_2.get(scope).to_rust_string_lossy(scope); assert!(caught_str_2.contains("DANG")); } ```
2020-05-24 14:58:10 -04:00
assert!(tc.exception(scope).is_none());
2019-12-20 18:28:08 -05:00
assert!(tc.stack_trace(scope, context).is_none());
Use correct lifetime for TryCatch::exception()/message() return value (#380) According to v8.h, "the returned handle is valid until this TryCatch block has been destroyed". This is incorrect, as can be demonstrated with the test below. In practice the return value lives no longer and no shorter than the active HandleScope at the time these methods are called. An issue has been opened about this in the V8 bug tracker: https://bugs.chromium.org/p/v8/issues/detail?id=10537. ```rust fn try_catch_bad_lifetimes() { let _setup_guard = setup(); let mut isolate = v8::Isolate::new(Default::default()); let mut hs = v8::HandleScope::new(&mut isolate); let scope = hs.enter(); let context = v8::Context::new(scope); let mut cs = v8::ContextScope::new(scope, context); let scope = cs.enter(); let caught_msg_2 = { let mut try_catch = v8::TryCatch::new(scope); let try_catch = try_catch.enter(); let caught_msg_1 = { let mut hs = v8::HandleScope::new(scope); let scope = hs.enter(); // Throw exception #1. let msg_1 = v8::String::new(scope, "BOOM!").unwrap(); let exc_1 = v8::Exception::type_error(scope, msg_1); scope.isolate().throw_exception(exc_1); // Catch exception #1. let caught_msg_1 = try_catch.message().unwrap(); let caught_str_1 = caught_msg_1.get(scope).to_rust_string_lossy(scope); assert!(caught_str_1.contains("BOOM")); // Move `caught_msg_1` out of the HandleScope it was created in. // The borrow checker allows this because `caught_msg_1`'s // lifetime is contrained to not outlive the TryCatch, but it is // allowed to outlive the HandleScope that was active when the // exception was caught. caught_msg_1 }; // Next line crashes. let caught_str_1 = caught_msg_1.get(scope).to_rust_string_lossy(scope); assert!(caught_str_1.contains("BOOM")); // Throws exception #2. let msg_2 = v8::String::new(scope, "DANG!").unwrap(); let exc_2 = v8::Exception::type_error(scope, msg_2); scope.isolate().throw_exception(exc_2); // Catch exception #2. let caught_msg_2 = try_catch.message().unwrap(); let caught_str_2 = caught_msg_2.get(scope).to_rust_string_lossy(scope); assert!(caught_str_2.contains("DANG")); // Move `caught_msg_2` out of the extent of the TryCatch, but still // within the extent of its HandleScope. This is unnecessarily // rejected at compile time. caught_msg_2 }; let caught_str_2 = caught_msg_2.get(scope).to_rust_string_lossy(scope); assert!(caught_str_2.contains("DANG")); } ```
2020-05-24 14:58:10 -04:00
assert!(tc.message(scope).is_none());
2019-12-20 18:28:08 -05:00
assert!(tc.rethrow().is_none());
};
{
// Rethrow and reset.
let mut try_catch_1 = v8::TryCatch::new(scope);
let tc1 = try_catch_1.enter();
{
let mut try_catch_2 = v8::TryCatch::new(scope);
let tc2 = try_catch_2.enter();
eval(scope, context, "throw 'bar'");
assert!(tc2.has_caught());
assert!(tc2.rethrow().is_some());
tc2.reset();
assert!(!tc2.has_caught());
}
assert!(tc1.has_caught());
};
}
2019-12-23 08:16:01 -05:00
}
Use correct lifetime for TryCatch::exception()/message() return value (#380) According to v8.h, "the returned handle is valid until this TryCatch block has been destroyed". This is incorrect, as can be demonstrated with the test below. In practice the return value lives no longer and no shorter than the active HandleScope at the time these methods are called. An issue has been opened about this in the V8 bug tracker: https://bugs.chromium.org/p/v8/issues/detail?id=10537. ```rust fn try_catch_bad_lifetimes() { let _setup_guard = setup(); let mut isolate = v8::Isolate::new(Default::default()); let mut hs = v8::HandleScope::new(&mut isolate); let scope = hs.enter(); let context = v8::Context::new(scope); let mut cs = v8::ContextScope::new(scope, context); let scope = cs.enter(); let caught_msg_2 = { let mut try_catch = v8::TryCatch::new(scope); let try_catch = try_catch.enter(); let caught_msg_1 = { let mut hs = v8::HandleScope::new(scope); let scope = hs.enter(); // Throw exception #1. let msg_1 = v8::String::new(scope, "BOOM!").unwrap(); let exc_1 = v8::Exception::type_error(scope, msg_1); scope.isolate().throw_exception(exc_1); // Catch exception #1. let caught_msg_1 = try_catch.message().unwrap(); let caught_str_1 = caught_msg_1.get(scope).to_rust_string_lossy(scope); assert!(caught_str_1.contains("BOOM")); // Move `caught_msg_1` out of the HandleScope it was created in. // The borrow checker allows this because `caught_msg_1`'s // lifetime is contrained to not outlive the TryCatch, but it is // allowed to outlive the HandleScope that was active when the // exception was caught. caught_msg_1 }; // Next line crashes. let caught_str_1 = caught_msg_1.get(scope).to_rust_string_lossy(scope); assert!(caught_str_1.contains("BOOM")); // Throws exception #2. let msg_2 = v8::String::new(scope, "DANG!").unwrap(); let exc_2 = v8::Exception::type_error(scope, msg_2); scope.isolate().throw_exception(exc_2); // Catch exception #2. let caught_msg_2 = try_catch.message().unwrap(); let caught_str_2 = caught_msg_2.get(scope).to_rust_string_lossy(scope); assert!(caught_str_2.contains("DANG")); // Move `caught_msg_2` out of the extent of the TryCatch, but still // within the extent of its HandleScope. This is unnecessarily // rejected at compile time. caught_msg_2 }; let caught_str_2 = caught_msg_2.get(scope).to_rust_string_lossy(scope); assert!(caught_str_2.contains("DANG")); } ```
2020-05-24 14:58:10 -04:00
#[test]
fn try_catch_caught_lifetime() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let (caught_exc, caught_msg) = {
let mut try_catch = v8::TryCatch::new(scope);
let try_catch = try_catch.enter();
// Throw exception.
let msg = v8::String::new(scope, "DANG!").unwrap();
let exc = v8::Exception::type_error(scope, msg);
scope.isolate().throw_exception(exc);
// Catch exception.
let caught_exc = try_catch.exception(scope).unwrap();
let caught_msg = try_catch.message(scope).unwrap();
// Move `caught_exc` and `caught_msg` out of the extent of the TryCatch,
// but still within the extent of the enclosing HandleScope.
(caught_exc, caught_msg)
};
// This should not crash.
assert!(caught_exc
.to_string(scope)
.unwrap()
.to_rust_string_lossy(scope)
.contains("DANG"));
assert!(caught_msg
.get(scope)
.to_rust_string_lossy(scope)
.contains("DANG"));
}
2019-12-23 08:16:01 -05:00
#[test]
fn throw_exception() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
2019-12-23 08:16:01 -05:00
{
let mut try_catch = v8::TryCatch::new(scope);
let tc = try_catch.enter();
let exception = v8_str(scope, "boom");
scope.isolate().throw_exception(exception.into());
2019-12-23 08:16:01 -05:00
assert!(tc.has_caught());
assert!(tc
Use correct lifetime for TryCatch::exception()/message() return value (#380) According to v8.h, "the returned handle is valid until this TryCatch block has been destroyed". This is incorrect, as can be demonstrated with the test below. In practice the return value lives no longer and no shorter than the active HandleScope at the time these methods are called. An issue has been opened about this in the V8 bug tracker: https://bugs.chromium.org/p/v8/issues/detail?id=10537. ```rust fn try_catch_bad_lifetimes() { let _setup_guard = setup(); let mut isolate = v8::Isolate::new(Default::default()); let mut hs = v8::HandleScope::new(&mut isolate); let scope = hs.enter(); let context = v8::Context::new(scope); let mut cs = v8::ContextScope::new(scope, context); let scope = cs.enter(); let caught_msg_2 = { let mut try_catch = v8::TryCatch::new(scope); let try_catch = try_catch.enter(); let caught_msg_1 = { let mut hs = v8::HandleScope::new(scope); let scope = hs.enter(); // Throw exception #1. let msg_1 = v8::String::new(scope, "BOOM!").unwrap(); let exc_1 = v8::Exception::type_error(scope, msg_1); scope.isolate().throw_exception(exc_1); // Catch exception #1. let caught_msg_1 = try_catch.message().unwrap(); let caught_str_1 = caught_msg_1.get(scope).to_rust_string_lossy(scope); assert!(caught_str_1.contains("BOOM")); // Move `caught_msg_1` out of the HandleScope it was created in. // The borrow checker allows this because `caught_msg_1`'s // lifetime is contrained to not outlive the TryCatch, but it is // allowed to outlive the HandleScope that was active when the // exception was caught. caught_msg_1 }; // Next line crashes. let caught_str_1 = caught_msg_1.get(scope).to_rust_string_lossy(scope); assert!(caught_str_1.contains("BOOM")); // Throws exception #2. let msg_2 = v8::String::new(scope, "DANG!").unwrap(); let exc_2 = v8::Exception::type_error(scope, msg_2); scope.isolate().throw_exception(exc_2); // Catch exception #2. let caught_msg_2 = try_catch.message().unwrap(); let caught_str_2 = caught_msg_2.get(scope).to_rust_string_lossy(scope); assert!(caught_str_2.contains("DANG")); // Move `caught_msg_2` out of the extent of the TryCatch, but still // within the extent of its HandleScope. This is unnecessarily // rejected at compile time. caught_msg_2 }; let caught_str_2 = caught_msg_2.get(scope).to_rust_string_lossy(scope); assert!(caught_str_2.contains("DANG")); } ```
2020-05-24 14:58:10 -04:00
.exception(scope)
2019-12-23 08:16:01 -05:00
.unwrap()
.strict_equals(v8_str(scope, "boom").into()));
};
}
2019-12-20 18:28:08 -05:00
}
#[test]
fn thread_safe_handle_drop_after_isolate() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
let handle = isolate.thread_safe_handle();
// We can call it twice.
let handle_ = isolate.thread_safe_handle();
// Check that handle is Send and Sync.
fn f<S: Send + Sync>(_: S) {}
f(handle_);
// All methods on IsolateHandle should return false after the isolate is
// dropped.
drop(isolate);
assert_eq!(false, handle.terminate_execution());
assert_eq!(false, handle.cancel_terminate_execution());
assert_eq!(false, handle.is_execution_terminating());
static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
extern "C" fn callback(
_isolate: &mut v8::Isolate,
data: *mut std::ffi::c_void,
) {
assert_eq!(data, std::ptr::null_mut());
CALL_COUNT.fetch_add(1, Ordering::SeqCst);
}
assert_eq!(
false,
handle.request_interrupt(callback, std::ptr::null_mut())
);
assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 0);
}
#[test]
fn terminate_execution() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
let (tx, rx) = std::sync::mpsc::channel::<bool>();
let handle = isolate.thread_safe_handle();
let t = std::thread::spawn(move || {
// allow deno to boot and run
std::thread::sleep(std::time::Duration::from_millis(300));
handle.terminate_execution();
// allow shutdown
std::thread::sleep(std::time::Duration::from_millis(200));
// unless reported otherwise the test should fail after this point
tx.send(false).ok();
});
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
// Rn an infinite loop, which should be terminated.
let source = v8_str(scope, "for(;;) {}");
let r = v8::Script::compile(scope, context, source, None);
let mut script = r.unwrap();
let result = script.run(scope, context);
assert!(result.is_none());
// TODO assert_eq!(e.to_string(), "Uncaught Error: execution terminated")
let msg = rx.recv().expect("execution should be terminated");
assert!(!msg);
// Make sure the isolate unusable again.
eval(scope, context, "1+1").expect("execution should be possible again");
t.join().expect("join t");
}
// TODO(ry) This test should use threads
2020-01-15 15:33:47 -05:00
#[test]
fn request_interrupt_small_scripts() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
let handle = isolate.thread_safe_handle();
2020-01-15 15:33:47 -05:00
{
let mut hs = v8::HandleScope::new(&mut isolate);
2020-01-15 15:33:47 -05:00
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
2020-01-15 15:33:47 -05:00
static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
extern "C" fn callback(
_isolate: &mut v8::Isolate,
data: *mut std::ffi::c_void,
) {
assert_eq!(data, std::ptr::null_mut());
CALL_COUNT.fetch_add(1, Ordering::SeqCst);
}
handle.request_interrupt(callback, std::ptr::null_mut());
2020-01-15 15:33:47 -05:00
eval(scope, context, "(function(x){return x;})(1);");
assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 1);
}
}
#[test]
2019-12-24 16:40:41 -05:00
fn add_message_listener() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
isolate.set_capture_stack_trace_for_uncaught_exceptions(true, 32);
static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
extern "C" fn check_message_0(
message: v8::Local<v8::Message>,
_exception: v8::Local<v8::Value>,
) {
let mut sc = v8::CallbackScope::new(message);
let mut sc = v8::HandleScope::new(sc.enter());
let scope = sc.enter();
let context = scope.get_current_context().unwrap();
2019-12-31 09:40:34 -05:00
let message_str = message.get(scope);
assert_eq!(message_str.to_rust_string_lossy(scope), "Uncaught foo");
assert_eq!(Some(1), message.get_line_number(context));
assert!(message.get_script_resource_name(scope).is_some());
assert!(message.get_source_line(scope, context).is_some());
assert_eq!(message.get_start_position(), 0);
assert_eq!(message.get_end_position(), 1);
assert_eq!(message.get_wasm_function_index(), -1);
assert!(message.error_level() >= 0);
assert_eq!(message.get_start_column(), 0);
assert_eq!(message.get_end_column(), 1);
assert!(!message.is_shared_cross_origin());
assert!(!message.is_opaque());
2019-12-31 11:17:26 -05:00
let stack_trace = message.get_stack_trace(scope).unwrap();
assert_eq!(1, stack_trace.get_frame_count());
let frame = stack_trace.get_frame(scope, 0).unwrap();
assert_eq!(1, frame.get_line_number());
assert_eq!(1, frame.get_column());
assert_eq!(3, frame.get_script_id());
assert!(frame.get_script_name(scope).is_none());
assert!(frame.get_script_name_or_source_url(scope).is_none());
assert!(frame.get_function_name(scope).is_none());
assert_eq!(false, frame.is_eval());
assert_eq!(false, frame.is_constructor());
assert_eq!(false, frame.is_wasm());
assert_eq!(true, frame.is_user_javascript());
2019-12-31 09:40:34 -05:00
CALL_COUNT.fetch_add(1, Ordering::SeqCst);
}
isolate.add_message_listener(check_message_0);
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let source = v8::String::new(scope, "throw 'foo'").unwrap();
let mut script = v8::Script::compile(scope, context, source, None).unwrap();
assert!(script.run(scope, context).is_none());
assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 1);
}
}
fn unexpected_module_resolve_callback<'a>(
_context: v8::Local<'a, v8::Context>,
_specifier: v8::Local<'a, v8::String>,
_referrer: v8::Local<'a, v8::Module>,
) -> Option<v8::Local<'a, v8::Module>> {
2019-12-24 16:40:41 -05:00
unreachable!()
}
#[test]
fn set_host_initialize_import_meta_object_callback() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
2019-12-24 16:40:41 -05:00
static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
extern "C" fn callback(
context: v8::Local<v8::Context>,
_module: v8::Local<v8::Module>,
meta: v8::Local<v8::Object>,
2019-12-24 16:40:41 -05:00
) {
CALL_COUNT.fetch_add(1, Ordering::SeqCst);
let mut cbs = v8::CallbackScope::new(context);
let mut hs = v8::HandleScope::new(cbs.enter());
let scope = hs.enter();
let key = v8::String::new(scope, "foo").unwrap();
let value = v8::String::new(scope, "bar").unwrap();
2019-12-30 09:28:39 -05:00
meta.create_data_property(context, key.into(), value.into());
2019-12-24 16:40:41 -05:00
}
isolate.set_host_initialize_import_meta_object_callback(callback);
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let source = mock_source(scope, "google.com", "import.meta;");
2019-12-24 16:40:41 -05:00
let mut module =
v8::script_compiler::compile_module(scope, source).unwrap();
2019-12-24 16:40:41 -05:00
let result =
module.instantiate_module(context, unexpected_module_resolve_callback);
assert!(result.is_some());
let meta = module.evaluate(scope, context).unwrap();
2019-12-24 16:40:41 -05:00
assert!(meta.is_object());
let meta = meta.to_object(scope).unwrap();
let key = v8::String::new(scope, "foo").unwrap();
let expected = v8::String::new(scope, "bar").unwrap();
let actual = meta.get(scope, context, key.into()).unwrap();
2019-12-24 16:40:41 -05:00
assert!(expected.strict_equals(actual));
assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 1);
}
2019-12-24 16:40:41 -05:00
}
2019-12-04 08:12:27 -05:00
#[test]
fn script_compile_and_run() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let source = v8::String::new(scope, "'Hello ' + 13 + 'th planet'").unwrap();
let mut script = v8::Script::compile(scope, context, source, None).unwrap();
source.to_rust_string_lossy(scope);
let result = script.run(scope, context).unwrap();
let result = result.to_string(scope).unwrap();
assert_eq!(result.to_rust_string_lossy(scope), "Hello 13th planet");
}
2019-12-04 08:12:27 -05:00
}
2019-12-18 05:46:36 -05:00
#[test]
fn script_origin() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
2019-12-18 05:46:36 -05:00
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let resource_name = v8::String::new(scope, "foo.js").unwrap();
let resource_line_offset = v8::Integer::new(scope, 4);
let resource_column_offset = v8::Integer::new(scope, 5);
let resource_is_shared_cross_origin = v8::Boolean::new(scope, true);
let script_id = v8::Integer::new(scope, 123);
let source_map_url = v8::String::new(scope, "source_map_url").unwrap();
let resource_is_opaque = v8::Boolean::new(scope, true);
let is_wasm = v8::Boolean::new(scope, false);
let is_module = v8::Boolean::new(scope, false);
2019-12-18 05:46:36 -05:00
let script_origin = v8::ScriptOrigin::new(
resource_name.into(),
resource_line_offset,
resource_column_offset,
resource_is_shared_cross_origin,
script_id,
source_map_url.into(),
resource_is_opaque,
is_wasm,
is_module,
);
let source = v8::String::new(scope, "1+2").unwrap();
2019-12-18 05:46:36 -05:00
let mut script =
v8::Script::compile(scope, context, source, Some(&script_origin))
.unwrap();
source.to_rust_string_lossy(scope);
let _result = script.run(scope, context).unwrap();
}
2019-12-18 05:46:36 -05:00
}
#[test]
fn get_version() {
assert!(v8::V8::get_version().len() > 3);
}
#[test]
fn set_flags_from_command_line() {
let r = v8::V8::set_flags_from_command_line(vec![
"binaryname".to_string(),
"--log-colour".to_string(),
"--should-be-ignored".to_string(),
]);
assert_eq!(
r,
vec!["binaryname".to_string(), "--should-be-ignored".to_string()]
);
}
#[test]
fn inspector_string_view() {
let chars = b"Hello world!";
let view = v8::inspector::StringView::from(&chars[..]);
assert_eq!(chars.len(), view.into_iter().len());
assert_eq!(chars.len(), view.len());
2020-05-05 18:06:35 -04:00
for (c1, c2) in chars.iter().copied().map(u16::from).zip(view) {
assert_eq!(c1, c2);
}
}
#[test]
fn inspector_string_buffer() {
let chars = b"Hello Venus!";
let mut buf = {
let src_view = v8::inspector::StringView::from(&chars[..]);
2020-05-05 18:06:35 -04:00
v8::inspector::StringBuffer::create(src_view)
};
let view = buf.as_mut().unwrap().string();
assert_eq!(chars.len(), view.into_iter().len());
assert_eq!(chars.len(), view.len());
for (c1, c2) in chars.iter().copied().map(u16::from).zip(view) {
assert_eq!(c1, c2);
}
}
#[test]
fn test_primitives() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let null = v8::null(scope);
assert!(!null.is_undefined());
assert!(null.is_null());
assert!(null.is_null_or_undefined());
let undefined = v8::undefined(scope);
assert!(undefined.is_undefined());
assert!(!undefined.is_null());
assert!(undefined.is_null_or_undefined());
2020-01-04 18:08:27 -05:00
let true_ = v8::Boolean::new(scope, true);
assert!(true_.is_true());
assert!(!true_.is_undefined());
assert!(!true_.is_null());
assert!(!true_.is_null_or_undefined());
2020-01-04 18:08:27 -05:00
let false_ = v8::Boolean::new(scope, false);
assert!(false_.is_false());
assert!(!false_.is_undefined());
assert!(!false_.is_null());
assert!(!false_.is_null_or_undefined());
}
}
2019-12-08 20:26:58 -05:00
#[test]
fn exception() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let msg_in = v8::String::new(scope, "This is a test error").unwrap();
let _exception = v8::Exception::error(scope, msg_in);
let _exception = v8::Exception::range_error(scope, msg_in);
let _exception = v8::Exception::reference_error(scope, msg_in);
let _exception = v8::Exception::syntax_error(scope, msg_in);
let exception = v8::Exception::type_error(scope, msg_in);
let actual_msg_out =
v8::Exception::create_message(scope, exception).get(scope);
let expected_msg_out =
v8::String::new(scope, "Uncaught TypeError: This is a test error").unwrap();
assert!(actual_msg_out.strict_equals(expected_msg_out.into()));
assert!(v8::Exception::get_stack_trace(scope, exception).is_none());
2019-12-08 20:26:58 -05:00
}
2019-12-09 17:11:31 -05:00
#[test]
fn create_message_argument_lifetimes() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
{
let create_message = v8::Function::new(
scope,
context,
|scope: v8::FunctionCallbackScope,
args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue| {
let message = v8::Exception::create_message(scope, args.get(0));
let message_str = message.get(scope);
rv.set(message_str.into())
},
)
.unwrap();
let receiver = context.global(scope);
let message_str = v8::String::new(scope, "mishap").unwrap();
let exception = v8::Exception::type_error(scope, message_str);
let actual = create_message
.call(scope, context, receiver.into(), &[exception])
.unwrap();
let expected =
v8::String::new(scope, "Uncaught TypeError: mishap").unwrap();
assert!(actual.strict_equals(expected.into()));
}
}
2019-12-09 17:11:31 -05:00
#[test]
fn json() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let json_string = v8_str(scope, "{\"a\": 1, \"b\": 2}");
let maybe_value = v8::json::parse(scope, context, json_string);
2019-12-09 17:11:31 -05:00
assert!(maybe_value.is_some());
let value = maybe_value.unwrap();
let maybe_stringified = v8::json::stringify(scope, context, value);
2019-12-09 17:11:31 -05:00
assert!(maybe_stringified.is_some());
let stringified = maybe_stringified.unwrap();
let rust_str = stringified.to_rust_string_lossy(scope);
2019-12-09 17:11:31 -05:00
assert_eq!("{\"a\":1,\"b\":2}".to_string(), rust_str);
}
2019-12-09 17:11:31 -05:00
}
2019-12-09 19:14:07 -05:00
#[test]
fn object_template() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let object_templ = v8::ObjectTemplate::new(scope);
let function_templ = v8::FunctionTemplate::new(scope, fortytwo_callback);
let name = v8_str(scope, "f");
let attr = v8::READ_ONLY + v8::DONT_ENUM + v8::DONT_DELETE;
object_templ.set_with_attr(name.into(), function_templ.into(), attr);
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let object = object_templ.new_instance(scope, context).unwrap();
assert!(!object.is_null_or_undefined());
let name = v8_str(scope, "g");
context.global(scope).define_own_property(
context,
name.into(),
object.into(),
v8::DONT_ENUM,
);
let source = r#"
{
const d = Object.getOwnPropertyDescriptor(globalThis, "g");
[d.configurable, d.enumerable, d.writable].toString()
}
"#;
let actual = eval(scope, context, source).unwrap();
let expected = v8_str(scope, "true,false,true");
assert!(expected.strict_equals(actual));
let actual = eval(scope, context, "g.f()").unwrap();
let expected = v8::Integer::new(scope, 42);
assert!(expected.strict_equals(actual));
let source = r#"
{
const d = Object.getOwnPropertyDescriptor(g, "f");
[d.configurable, d.enumerable, d.writable].toString()
}
"#;
let actual = eval(scope, context, source).unwrap();
let expected = v8_str(scope, "false,false,false");
assert!(expected.strict_equals(actual));
}
}
#[test]
fn object_template_from_function_template() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let mut function_templ =
v8::FunctionTemplate::new(scope, fortytwo_callback);
let expected_class_name = v8_str(scope, "fortytwo");
function_templ.set_class_name(expected_class_name);
let object_templ =
v8::ObjectTemplate::new_from_template(scope, function_templ);
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let object = object_templ.new_instance(scope, context).unwrap();
assert!(!object.is_null_or_undefined());
let name = v8_str(scope, "g");
context
.global(scope)
.set(context, name.into(), object.into());
let actual_class_name = eval(scope, context, "g.constructor.name").unwrap();
assert!(expected_class_name.strict_equals(actual_class_name));
}
}
2019-12-09 19:14:07 -05:00
#[test]
fn object() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let null: v8::Local<v8::Value> = v8::null(scope).into();
let n1: v8::Local<v8::Name> = v8::String::new(scope, "a").unwrap().into();
let n2: v8::Local<v8::Name> = v8::String::new(scope, "b").unwrap().into();
2019-12-20 10:01:45 -05:00
let v1: v8::Local<v8::Value> = v8::Number::new(scope, 1.0).into();
let v2: v8::Local<v8::Value> = v8::Number::new(scope, 2.0).into();
let object = v8::Object::with_prototype_and_properties(
scope,
null,
&[n1, n2],
&[v1, v2],
);
2019-12-09 19:14:07 -05:00
assert!(!object.is_null_or_undefined());
let lhs = object.creation_context(scope).global(scope);
let rhs = context.global(scope);
assert!(lhs.strict_equals(rhs.into()));
2019-12-30 12:14:06 -05:00
let object_ = v8::Object::new(scope);
assert!(!object_.is_null_or_undefined());
2020-01-02 13:56:28 -05:00
let id = object_.get_identity_hash();
assert_ne!(id, 0);
}
2019-12-09 19:14:07 -05:00
}
2019-12-10 22:43:22 -05:00
2020-01-02 10:41:40 -05:00
#[test]
fn array() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
2020-01-02 10:41:40 -05:00
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let s1 = v8::String::new(scope, "a").unwrap();
let s2 = v8::String::new(scope, "b").unwrap();
let array = v8::Array::new(scope, 2);
2020-02-14 09:42:54 -05:00
assert_eq!(array.length(), 2);
let lhs = array.creation_context(scope).global(scope);
let rhs = context.global(scope);
assert!(lhs.strict_equals(rhs.into()));
2020-02-14 09:42:54 -05:00
array.set_index(context, 0, s1.into());
array.set_index(context, 1, s2.into());
2020-01-02 10:41:40 -05:00
2020-02-14 09:42:54 -05:00
let maybe_v1 = array.get_index(scope, context, 0);
2020-01-02 10:41:40 -05:00
assert!(maybe_v1.is_some());
assert!(maybe_v1.unwrap().same_value(s1.into()));
2020-02-14 09:42:54 -05:00
let maybe_v2 = array.get_index(scope, context, 1);
assert!(maybe_v2.is_some());
assert!(maybe_v2.unwrap().same_value(s2.into()));
let array = v8::Array::new_with_elements(scope, &[]);
assert_eq!(array.length(), 0);
let array = v8::Array::new_with_elements(scope, &[s1.into(), s2.into()]);
assert_eq!(array.length(), 2);
let maybe_v1 = array.get_index(scope, context, 0);
assert!(maybe_v1.is_some());
assert!(maybe_v1.unwrap().same_value(s1.into()));
let maybe_v2 = array.get_index(scope, context, 1);
2020-01-02 10:41:40 -05:00
assert!(maybe_v2.is_some());
assert!(maybe_v2.unwrap().same_value(s2.into()));
}
}
#[test]
fn create_data_property() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
eval(scope, context, "var a = {};");
let key = v8_str(scope, "a");
let obj = context
.global(scope)
.get(scope, context, key.into())
.unwrap();
assert!(obj.is_object());
let obj = obj.to_object(scope).unwrap();
let key = v8_str(scope, "foo");
let value = v8_str(scope, "bar");
assert!(obj
.create_data_property(context, key.into(), value.into())
.unwrap());
2019-12-30 09:28:39 -05:00
let actual = obj.get(scope, context, key.into()).unwrap();
assert!(value.strict_equals(actual));
2019-12-26 14:38:16 -05:00
let key2 = v8_str(scope, "foo2");
assert!(obj.set(context, key2.into(), value.into()).unwrap());
2019-12-30 09:28:39 -05:00
let actual = obj.get(scope, context, key2.into()).unwrap();
2019-12-26 14:38:16 -05:00
assert!(value.strict_equals(actual));
2020-01-02 12:01:36 -05:00
}
}
#[test]
fn object_set_accessor() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
2020-01-02 12:01:36 -05:00
{
static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
let getter = |scope: v8::PropertyCallbackScope,
key: v8::Local<v8::Name>,
args: v8::PropertyCallbackArguments,
mut rv: v8::ReturnValue| {
let context = scope.get_current_context().unwrap();
let this = args.this();
let expected_key = v8::String::new(scope, "getter_key").unwrap();
assert!(key.strict_equals(expected_key.into()));
let int_key = v8::String::new(scope, "int_key").unwrap();
let int_value = this.get(scope, context, int_key.into()).unwrap();
let int_value = v8::Local::<v8::Integer>::try_from(int_value).unwrap();
assert_eq!(int_value.value(), 42);
let s = v8::String::new(scope, "hello").unwrap();
assert!(rv.get(scope).is_undefined());
rv.set(s.into());
2020-01-02 12:01:36 -05:00
CALL_COUNT.fetch_add(1, Ordering::SeqCst);
};
let mut obj = v8::Object::new(scope);
let getter_key = v8::String::new(scope, "getter_key").unwrap();
obj.set_accessor(context, getter_key.into(), getter);
let int_key = v8::String::new(scope, "int_key").unwrap();
obj.set(context, int_key.into(), v8::Integer::new(scope, 42).into());
let obj_name = v8::String::new(scope, "obj").unwrap();
context
.global(scope)
.set(context, obj_name.into(), obj.into());
let actual = eval(scope, context, "obj.getter_key").unwrap();
let expected = v8::String::new(scope, "hello").unwrap();
2020-01-02 12:01:36 -05:00
assert!(actual.strict_equals(expected.into()));
2020-01-02 12:01:36 -05:00
assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 1);
}
}
2019-12-13 20:18:30 -05:00
#[test]
fn promise_resolved() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
2019-12-20 10:01:45 -05:00
let maybe_resolver = v8::PromiseResolver::new(scope, context);
2019-12-13 20:18:30 -05:00
assert!(maybe_resolver.is_some());
let resolver = maybe_resolver.unwrap();
let promise = resolver.get_promise(scope);
2019-12-13 20:18:30 -05:00
assert!(!promise.has_handler());
assert_eq!(promise.state(), v8::PromiseState::Pending);
2019-12-30 09:28:39 -05:00
let value = v8::String::new(scope, "test").unwrap();
resolver.resolve(context, value.into());
2019-12-13 20:18:30 -05:00
assert_eq!(promise.state(), v8::PromiseState::Fulfilled);
2019-12-20 10:01:45 -05:00
let result = promise.result(scope);
let result_str = result.to_string(scope).unwrap();
2019-12-20 10:01:45 -05:00
assert_eq!(result_str.to_rust_string_lossy(scope), "test".to_string());
2019-12-13 20:18:30 -05:00
// Resolve again with different value, since promise is already in `Fulfilled` state
// it should be ignored.
2019-12-30 09:28:39 -05:00
let value = v8::String::new(scope, "test2").unwrap();
resolver.resolve(context, value.into());
2019-12-20 10:01:45 -05:00
let result = promise.result(scope);
let result_str = result.to_string(scope).unwrap();
2019-12-20 10:01:45 -05:00
assert_eq!(result_str.to_rust_string_lossy(scope), "test".to_string());
}
2019-12-13 20:18:30 -05:00
}
#[test]
fn promise_rejected() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
2019-12-20 10:01:45 -05:00
let maybe_resolver = v8::PromiseResolver::new(scope, context);
2019-12-13 20:18:30 -05:00
assert!(maybe_resolver.is_some());
let resolver = maybe_resolver.unwrap();
let promise = resolver.get_promise(scope);
2019-12-13 20:18:30 -05:00
assert!(!promise.has_handler());
assert_eq!(promise.state(), v8::PromiseState::Pending);
2019-12-30 09:28:39 -05:00
let value = v8::String::new(scope, "test").unwrap();
let rejected = resolver.reject(context, value.into());
assert!(rejected.unwrap());
2019-12-13 20:18:30 -05:00
assert_eq!(promise.state(), v8::PromiseState::Rejected);
2019-12-20 10:01:45 -05:00
let result = promise.result(scope);
let result_str = result.to_string(scope).unwrap();
2019-12-20 10:01:45 -05:00
assert_eq!(result_str.to_rust_string_lossy(scope), "test".to_string());
2019-12-13 20:18:30 -05:00
// Reject again with different value, since promise is already in `Rejected` state
// it should be ignored.
2019-12-30 09:28:39 -05:00
let value = v8::String::new(scope, "test2").unwrap();
resolver.reject(context, value.into());
2019-12-20 10:01:45 -05:00
let result = promise.result(scope);
let result_str = result.to_string(scope).unwrap();
2019-12-20 10:01:45 -05:00
assert_eq!(result_str.to_rust_string_lossy(scope), "test".to_string());
}
2019-12-13 20:18:30 -05:00
}
2020-04-01 17:47:19 -04:00
#[test]
fn proxy() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
2020-04-01 17:47:19 -04:00
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let target = v8::Object::new(scope);
let handler = v8::Object::new(scope);
let maybe_proxy = v8::Proxy::new(scope, context, target, handler);
assert!(maybe_proxy.is_some());
let mut proxy = maybe_proxy.unwrap();
assert!(target == proxy.get_target(scope));
assert!(handler == proxy.get_handler(scope));
assert!(!proxy.is_revoked());
proxy.revoke();
assert!(proxy.is_revoked());
}
}
2019-12-13 20:18:30 -05:00
fn fn_callback(
scope: v8::FunctionCallbackScope,
args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
assert_eq!(args.length(), 0);
let s = v8::String::new(scope, "Hello callback!").unwrap();
assert!(rv.get(scope).is_undefined());
rv.set(s.into());
}
fn fn_callback2(
scope: v8::FunctionCallbackScope,
args: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
assert_eq!(args.length(), 2);
let arg1_val = v8::String::new(scope, "arg1").unwrap();
let arg1 = args.get(0);
assert!(arg1.is_string());
assert!(arg1.strict_equals(arg1_val.into()));
let arg2_val = v8::Integer::new(scope, 2);
let arg2 = args.get(1);
assert!(arg2.is_number());
assert!(arg2.strict_equals(arg2_val.into()));
let s = v8::String::new(scope, "Hello callback!").unwrap();
assert!(rv.get(scope).is_undefined());
rv.set(s.into());
}
fn fortytwo_callback(
scope: v8::FunctionCallbackScope,
_: v8::FunctionCallbackArguments,
mut rv: v8::ReturnValue,
) {
rv.set(v8::Integer::new(scope, 42).into());
}
fn data_is_true_callback(
_scope: v8::FunctionCallbackScope,
args: v8::FunctionCallbackArguments,
_rv: v8::ReturnValue,
) {
let data = args.data();
assert!(data.is_some());
let data = data.unwrap();
assert!(data.is_true());
}
2019-12-10 22:43:22 -05:00
#[test]
fn function() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
2019-12-26 10:45:55 -05:00
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let global = context.global(scope);
let recv: v8::Local<v8::Value> = global.into();
2019-12-10 22:43:22 -05:00
// create function using template
2019-12-20 10:01:45 -05:00
let mut fn_template = v8::FunctionTemplate::new(scope, fn_callback);
let function = fn_template
2019-12-20 10:01:45 -05:00
.get_function(scope, context)
2019-12-10 22:43:22 -05:00
.expect("Unable to create function");
let lhs = function.creation_context(scope).global(scope);
let rhs = context.global(scope);
assert!(lhs.strict_equals(rhs.into()));
function
.call(scope, context, recv, &[])
.expect("Function call failed");
2019-12-10 22:43:22 -05:00
// create function without a template
let function = v8::Function::new(scope, context, fn_callback2)
2019-12-19 08:13:33 -05:00
.expect("Unable to create function");
let arg1 = v8::String::new(scope, "arg1").unwrap();
let arg2 = v8::Integer::new(scope, 2);
let value = function
.call(scope, context, recv, &[arg1.into(), arg2.into()])
.unwrap();
let value_str = value.to_string(scope).unwrap();
2019-12-20 10:01:45 -05:00
let rust_str = value_str.to_rust_string_lossy(scope);
2019-12-10 22:43:22 -05:00
assert_eq!(rust_str, "Hello callback!".to_string());
// create a function with associated data
let true_data = v8::Boolean::new(scope, true);
let function = v8::Function::new_with_data(
scope,
context,
true_data.into(),
data_is_true_callback,
)
.expect("Unable to create function with data");
function
.call(scope, context, recv, &[])
.expect("Function call failed");
}
2019-12-10 22:43:22 -05:00
}
2019-12-19 08:13:33 -05:00
extern "C" fn promise_reject_callback(msg: v8::PromiseRejectMessage) {
let mut scope = v8::CallbackScope::new(&msg);
let scope = scope.enter();
2019-12-19 08:13:33 -05:00
let event = msg.get_event();
assert_eq!(event, v8::PromiseRejectEvent::PromiseRejectWithNoHandler);
let promise = msg.get_promise();
2019-12-19 08:13:33 -05:00
assert_eq!(promise.state(), v8::PromiseState::Rejected);
let value = msg.get_value();
{
let mut hs = v8::HandleScope::new(scope);
let scope = hs.enter();
let value_str = value.to_string(scope).unwrap();
2019-12-20 10:01:45 -05:00
let rust_str = value_str.to_rust_string_lossy(scope);
2019-12-19 08:13:33 -05:00
assert_eq!(rust_str, "promise rejected".to_string());
}
2019-12-19 08:13:33 -05:00
}
#[test]
fn set_promise_reject_callback() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
2019-12-19 08:13:33 -05:00
isolate.set_promise_reject_callback(promise_reject_callback);
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let resolver = v8::PromiseResolver::new(scope, context).unwrap();
2019-12-30 09:28:39 -05:00
let value = v8::String::new(scope, "promise rejected").unwrap();
resolver.reject(context, value.into());
}
2019-12-19 08:13:33 -05:00
}
2019-12-20 10:01:45 -05:00
fn mock_script_origin<'sc>(
scope: &mut impl v8::ToLocal<'sc>,
2019-12-23 18:09:03 -05:00
resource_name_: &str,
2019-12-20 10:01:45 -05:00
) -> v8::ScriptOrigin<'sc> {
let resource_name = v8_str(scope, resource_name_);
let resource_line_offset = v8::Integer::new(scope, 0);
let resource_column_offset = v8::Integer::new(scope, 0);
2020-01-04 18:08:27 -05:00
let resource_is_shared_cross_origin = v8::Boolean::new(scope, true);
let script_id = v8::Integer::new(scope, 123);
let source_map_url = v8_str(scope, "source_map_url");
2020-01-04 18:08:27 -05:00
let resource_is_opaque = v8::Boolean::new(scope, true);
let is_wasm = v8::Boolean::new(scope, false);
let is_module = v8::Boolean::new(scope, true);
v8::ScriptOrigin::new(
resource_name.into(),
resource_line_offset,
resource_column_offset,
resource_is_shared_cross_origin,
script_id,
source_map_url.into(),
resource_is_opaque,
is_wasm,
is_module,
)
}
fn mock_source<'sc>(
scope: &mut impl v8::ToLocal<'sc>,
2019-12-24 16:40:41 -05:00
resource_name: &str,
source: &str,
) -> v8::script_compiler::Source {
let source_str = v8_str(scope, source);
let script_origin = mock_script_origin(scope, resource_name);
v8::script_compiler::Source::new(source_str, &script_origin)
2019-12-24 16:40:41 -05:00
}
#[test]
fn script_compiler_source() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
isolate.set_promise_reject_callback(promise_reject_callback);
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let source = "1+2";
2019-12-23 18:09:03 -05:00
let script_origin = mock_script_origin(scope, "foo.js");
2019-12-20 10:01:45 -05:00
let source =
v8::script_compiler::Source::new(v8_str(scope, source), &script_origin);
let result = v8::script_compiler::compile_module(scope, source);
assert!(result.is_some());
}
}
#[test]
fn module_instantiation_failures1() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let source_text = v8_str(
scope,
"import './foo.js';\n\
export {} from './bar.js';",
);
2019-12-23 18:09:03 -05:00
let origin = mock_script_origin(scope, "foo.js");
let source = v8::script_compiler::Source::new(source_text, &origin);
let mut module =
v8::script_compiler::compile_module(scope, source).unwrap();
assert_eq!(v8::ModuleStatus::Uninstantiated, module.get_status());
assert_eq!(2, module.get_module_requests_length());
assert_eq!(
"./foo.js",
module.get_module_request(0).to_rust_string_lossy(scope)
);
let loc = module.get_module_request_location(0);
assert_eq!(0, loc.get_line_number());
assert_eq!(7, loc.get_column_number());
assert_eq!(
"./bar.js",
module.get_module_request(1).to_rust_string_lossy(scope)
);
let loc = module.get_module_request_location(1);
assert_eq!(1, loc.get_line_number());
assert_eq!(15, loc.get_column_number());
2019-12-23 18:09:03 -05:00
// Instantiation should fail.
{
let mut try_catch = v8::TryCatch::new(scope);
let tc = try_catch.enter();
fn resolve_callback<'a>(
context: v8::Local<'a, v8::Context>,
_specifier: v8::Local<'a, v8::String>,
_referrer: v8::Local<'a, v8::Module>,
) -> Option<v8::Local<'a, v8::Module>> {
let mut cbs = v8::CallbackScope::new(context);
let mut hs = v8::HandleScope::new(cbs.enter());
let scope = hs.enter();
let e = v8_str(scope, "boom");
scope.isolate().throw_exception(e.into());
None
2019-12-23 18:09:03 -05:00
}
let result = module.instantiate_module(context, resolve_callback);
assert!(result.is_none());
assert!(tc.has_caught());
assert!(tc
Use correct lifetime for TryCatch::exception()/message() return value (#380) According to v8.h, "the returned handle is valid until this TryCatch block has been destroyed". This is incorrect, as can be demonstrated with the test below. In practice the return value lives no longer and no shorter than the active HandleScope at the time these methods are called. An issue has been opened about this in the V8 bug tracker: https://bugs.chromium.org/p/v8/issues/detail?id=10537. ```rust fn try_catch_bad_lifetimes() { let _setup_guard = setup(); let mut isolate = v8::Isolate::new(Default::default()); let mut hs = v8::HandleScope::new(&mut isolate); let scope = hs.enter(); let context = v8::Context::new(scope); let mut cs = v8::ContextScope::new(scope, context); let scope = cs.enter(); let caught_msg_2 = { let mut try_catch = v8::TryCatch::new(scope); let try_catch = try_catch.enter(); let caught_msg_1 = { let mut hs = v8::HandleScope::new(scope); let scope = hs.enter(); // Throw exception #1. let msg_1 = v8::String::new(scope, "BOOM!").unwrap(); let exc_1 = v8::Exception::type_error(scope, msg_1); scope.isolate().throw_exception(exc_1); // Catch exception #1. let caught_msg_1 = try_catch.message().unwrap(); let caught_str_1 = caught_msg_1.get(scope).to_rust_string_lossy(scope); assert!(caught_str_1.contains("BOOM")); // Move `caught_msg_1` out of the HandleScope it was created in. // The borrow checker allows this because `caught_msg_1`'s // lifetime is contrained to not outlive the TryCatch, but it is // allowed to outlive the HandleScope that was active when the // exception was caught. caught_msg_1 }; // Next line crashes. let caught_str_1 = caught_msg_1.get(scope).to_rust_string_lossy(scope); assert!(caught_str_1.contains("BOOM")); // Throws exception #2. let msg_2 = v8::String::new(scope, "DANG!").unwrap(); let exc_2 = v8::Exception::type_error(scope, msg_2); scope.isolate().throw_exception(exc_2); // Catch exception #2. let caught_msg_2 = try_catch.message().unwrap(); let caught_str_2 = caught_msg_2.get(scope).to_rust_string_lossy(scope); assert!(caught_str_2.contains("DANG")); // Move `caught_msg_2` out of the extent of the TryCatch, but still // within the extent of its HandleScope. This is unnecessarily // rejected at compile time. caught_msg_2 }; let caught_str_2 = caught_msg_2.get(scope).to_rust_string_lossy(scope); assert!(caught_str_2.contains("DANG")); } ```
2020-05-24 14:58:10 -04:00
.exception(scope)
2019-12-23 18:09:03 -05:00
.unwrap()
.strict_equals(v8_str(scope, "boom").into()));
assert_eq!(v8::ModuleStatus::Uninstantiated, module.get_status());
}
}
2019-12-23 18:09:03 -05:00
}
fn compile_specifier_as_module_resolve_callback<'a>(
context: v8::Local<'a, v8::Context>,
specifier: v8::Local<'a, v8::String>,
_referrer: v8::Local<'a, v8::Module>,
) -> Option<v8::Local<'a, v8::Module>> {
let mut cbs = v8::CallbackScope::new_escapable(context);
let mut hs = v8::EscapableHandleScope::new(cbs.enter());
let scope = hs.enter();
let origin = mock_script_origin(scope, "module.js");
let source = v8::script_compiler::Source::new(specifier, &origin);
let module = v8::script_compiler::compile_module(scope, source).unwrap();
Some(scope.escape(module))
}
2019-12-23 18:09:03 -05:00
#[test]
fn module_evaluation() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
2019-12-23 18:09:03 -05:00
let source_text = v8_str(
scope,
"import 'Object.expando = 5';\n\
import 'Object.expando *= 2';",
);
let origin = mock_script_origin(scope, "foo.js");
let source = v8::script_compiler::Source::new(source_text, &origin);
let mut module =
v8::script_compiler::compile_module(scope, source).unwrap();
2019-12-23 18:09:03 -05:00
assert_eq!(v8::ModuleStatus::Uninstantiated, module.get_status());
let result = module.instantiate_module(
context,
compile_specifier_as_module_resolve_callback,
);
2019-12-23 18:09:03 -05:00
assert!(result.unwrap());
assert_eq!(v8::ModuleStatus::Instantiated, module.get_status());
let result = module.evaluate(scope, context);
2019-12-23 18:09:03 -05:00
assert!(result.is_some());
assert_eq!(v8::ModuleStatus::Evaluated, module.get_status());
let result = eval(scope, context, "Object.expando").unwrap();
assert!(result.is_number());
let expected = v8::Number::new(scope, 10.);
assert!(result.strict_equals(expected.into()));
}
}
#[test]
fn primitive_array() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let length = 3;
let array = v8::PrimitiveArray::new(scope, length);
assert_eq!(length, array.length());
for i in 0..length {
let item = array.get(scope, i);
assert!(item.is_undefined());
}
let string = v8_str(scope, "test");
2019-12-30 09:28:39 -05:00
array.set(scope, 1, string.into());
assert!(array.get(scope, 0).is_undefined());
assert!(array.get(scope, 1).is_string());
let num = v8::Number::new(scope, 0.42);
2019-12-30 09:28:39 -05:00
array.set(scope, 2, num.into());
assert!(array.get(scope, 0).is_undefined());
assert!(array.get(scope, 1).is_string());
assert!(array.get(scope, 2).is_number());
}
}
#[test]
fn equality() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
assert!(v8_str(scope, "a").strict_equals(v8_str(scope, "a").into()));
assert!(!v8_str(scope, "a").strict_equals(v8_str(scope, "b").into()));
assert!(v8_str(scope, "a").same_value(v8_str(scope, "a").into()));
assert!(!v8_str(scope, "a").same_value(v8_str(scope, "b").into()));
}
}
2019-12-24 05:50:30 -05:00
#[test]
fn array_buffer_view() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let source =
v8::String::new(scope, "new Uint8Array([23,23,23,23])").unwrap();
let mut script = v8::Script::compile(scope, context, source, None).unwrap();
source.to_rust_string_lossy(scope);
let result: v8::Local<v8::ArrayBufferView> =
script.run(scope, context).unwrap().try_into().unwrap();
2019-12-24 05:50:30 -05:00
assert_eq!(result.byte_length(), 4);
assert_eq!(result.byte_offset(), 0);
let mut dest = [0; 4];
let copy_bytes = result.copy_contents(&mut dest);
assert_eq!(copy_bytes, 4);
assert_eq!(dest, [23, 23, 23, 23]);
let maybe_ab = result.buffer(scope);
2019-12-24 05:50:30 -05:00
assert!(maybe_ab.is_some());
let ab = maybe_ab.unwrap();
assert_eq!(ab.byte_length(), 4);
}
2019-12-24 05:50:30 -05:00
}
#[test]
fn snapshot_creator() {
let _setup_guard = setup();
// First we create the snapshot, there is a single global variable 'a' set to
// the value 3.
let startup_data = {
let mut snapshot_creator = v8::SnapshotCreator::new(None);
{
// TODO(ry) this shouldn't be necessary. workaround unfinished business in
// the scope type system.
let mut isolate = unsafe { snapshot_creator.get_owned_isolate() };
// Check that the SnapshotCreator isolate has been set up correctly.
let _ = isolate.thread_safe_handle();
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let source = v8::String::new(scope, "a = 1 + 2").unwrap();
let mut script =
v8::Script::compile(scope, context, source, None).unwrap();
script.run(scope, context).unwrap();
snapshot_creator.set_default_context(context);
std::mem::forget(isolate); // TODO(ry) this shouldn't be necessary.
}
2019-12-25 08:14:55 -05:00
snapshot_creator
.create_blob(v8::FunctionCodeHandling::Clear)
.unwrap()
};
2019-12-25 08:14:55 -05:00
assert!(startup_data.len() > 0);
// Now we try to load up the snapshot and check that 'a' has the correct
// value.
{
let params = v8::Isolate::create_params().snapshot_blob(startup_data);
let mut isolate = v8::Isolate::new(params);
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let source = v8::String::new(scope, "a === 3").unwrap();
let mut script =
v8::Script::compile(scope, context, source, None).unwrap();
let result = script.run(scope, context).unwrap();
2020-01-04 18:08:27 -05:00
let true_val = v8::Boolean::new(scope, true).into();
assert!(result.same_value(true_val));
}
}
}
2019-12-25 20:37:25 -05:00
lazy_static! {
static ref EXTERNAL_REFERENCES: v8::ExternalReferences =
v8::ExternalReferences::new(&[v8::ExternalReference {
function: fn_callback.map_fn_to()
}]);
}
#[test]
fn external_references() {
let _setup_guard = setup();
// First we create the snapshot, there is a single global variable 'a' set to
// the value 3.
let startup_data = {
let mut snapshot_creator =
v8::SnapshotCreator::new(Some(&EXTERNAL_REFERENCES));
{
// TODO(ry) this shouldn't be necessary. workaround unfinished business in
// the scope type system.
let mut isolate = unsafe { snapshot_creator.get_owned_isolate() };
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
// create function using template
let mut fn_template = v8::FunctionTemplate::new(scope, fn_callback);
let function = fn_template
.get_function(scope, context)
.expect("Unable to create function");
let global = context.global(scope);
2019-12-30 09:28:39 -05:00
global.set(context, v8_str(scope, "F").into(), function.into());
snapshot_creator.set_default_context(context);
std::mem::forget(isolate); // TODO(ry) this shouldn't be necessary.
}
snapshot_creator
.create_blob(v8::FunctionCodeHandling::Clear)
.unwrap()
};
assert!(startup_data.len() > 0);
// Now we try to load up the snapshot and check that 'a' has the correct
// value.
{
let params = v8::Isolate::create_params()
.snapshot_blob(startup_data)
.external_references(&**EXTERNAL_REFERENCES);
let mut isolate = v8::Isolate::new(params);
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let result =
eval(scope, context, "if(F() != 'wrong answer') throw 'boom1'");
assert!(result.is_none());
let result =
eval(scope, context, "if(F() != 'Hello callback!') throw 'boom2'");
assert!(result.is_some());
}
}
}
2019-12-30 20:42:39 -05:00
#[test]
fn create_params_snapshot_blob() {
let static_data = b"abcd";
let _ = v8::CreateParams::default().snapshot_blob(&static_data[..]);
let vec_1 = Vec::from(&b"defg"[..]);
let _ = v8::CreateParams::default().snapshot_blob(vec_1);
2019-12-30 20:42:39 -05:00
let vec_2 = std::fs::read(file!()).unwrap();
let _ = v8::CreateParams::default().snapshot_blob(vec_2);
2019-12-30 20:42:39 -05:00
let arc_slice: std::sync::Arc<[u8]> = std::fs::read(file!()).unwrap().into();
let _ = v8::CreateParams::default().snapshot_blob(arc_slice.clone());
let _ = v8::CreateParams::default().snapshot_blob(arc_slice);
2019-12-30 20:42:39 -05:00
}
2019-12-25 20:37:25 -05:00
#[test]
fn uint8_array() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
2019-12-25 20:37:25 -05:00
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let source =
v8::String::new(scope, "new Uint8Array([23,23,23,23])").unwrap();
let mut script = v8::Script::compile(scope, context, source, None).unwrap();
source.to_rust_string_lossy(scope);
let result: v8::Local<v8::ArrayBufferView> =
script.run(scope, context).unwrap().try_into().unwrap();
2019-12-25 20:37:25 -05:00
assert_eq!(result.byte_length(), 4);
assert_eq!(result.byte_offset(), 0);
let mut dest = [0; 4];
let copy_bytes = result.copy_contents(&mut dest);
assert_eq!(copy_bytes, 4);
assert_eq!(dest, [23, 23, 23, 23]);
let maybe_ab = result.buffer(scope);
2019-12-25 20:37:25 -05:00
assert!(maybe_ab.is_some());
let ab = maybe_ab.unwrap();
let uint8_array = v8::Uint8Array::new(scope, ab, 0, 0);
2019-12-25 20:37:25 -05:00
assert!(uint8_array.is_some());
}
}
2019-12-26 10:45:55 -05:00
#[test]
fn dynamic_import() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
2019-12-26 10:45:55 -05:00
static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
extern "C" fn dynamic_import_cb(
context: v8::Local<v8::Context>,
_referrer: v8::Local<v8::ScriptOrModule>,
specifier: v8::Local<v8::String>,
) -> *mut v8::Promise {
let mut cbs = v8::CallbackScope::new(context);
let mut hs = v8::HandleScope::new(cbs.enter());
let scope = hs.enter();
assert!(specifier.strict_equals(v8_str(scope, "bar.js").into()));
let e = v8_str(scope, "boom");
scope.isolate().throw_exception(e.into());
CALL_COUNT.fetch_add(1, Ordering::SeqCst);
std::ptr::null_mut()
}
isolate.set_host_import_module_dynamically_callback(dynamic_import_cb);
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
2019-12-26 10:45:55 -05:00
let result = eval(
scope,
2019-12-26 10:45:55 -05:00
context,
"(async function () {\n\
let x = await import('bar.js');\n\
})();",
);
assert!(result.is_some());
assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 1);
}
}
2019-12-28 16:29:42 -05:00
#[test]
fn shared_array_buffer() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
2019-12-28 16:29:42 -05:00
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let sab = v8::SharedArrayBuffer::new(scope, 16).unwrap();
let shared_bs_1 = sab.get_backing_store();
shared_bs_1[5].set(12);
shared_bs_1[12].set(52);
let global = context.global(scope);
let r = global
.create_data_property(context, v8_str(scope, "shared").into(), sab.into())
.unwrap();
assert!(r);
2019-12-28 16:29:42 -05:00
let source = v8::String::new(
scope,
r"sharedBytes = new Uint8Array(shared);
sharedBytes[2] = 16;
sharedBytes[14] = 62;
sharedBytes[5] + sharedBytes[12]",
2019-12-28 16:29:42 -05:00
)
.unwrap();
let mut script = v8::Script::compile(scope, context, source, None).unwrap();
let result: v8::Local<v8::Integer> =
script.run(scope, context).unwrap().try_into().unwrap();
2019-12-28 16:29:42 -05:00
assert_eq!(result.value(), 64);
assert_eq!(shared_bs_1[2].get(), 16);
assert_eq!(shared_bs_1[14].get(), 62);
let data: Box<[u8]> = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9].into_boxed_slice();
let bs = v8::SharedArrayBuffer::new_backing_store_from_boxed_slice(data);
assert_eq!(bs.byte_length(), 10);
assert_eq!(bs.is_shared(), true);
let shared_bs_2 = bs.make_shared();
assert_eq!(shared_bs_2.byte_length(), 10);
assert_eq!(shared_bs_2.is_shared(), true);
let ab = v8::SharedArrayBuffer::with_backing_store(scope, &shared_bs_2);
let shared_bs_3 = ab.get_backing_store();
assert_eq!(shared_bs_3.byte_length(), 10);
assert_eq!(shared_bs_3[0].get(), 0);
assert_eq!(shared_bs_3[9].get(), 9);
2019-12-28 16:29:42 -05:00
}
}
2020-01-01 09:56:59 -05:00
#[test]
#[allow(clippy::cognitive_complexity)]
#[allow(clippy::eq_op)]
2020-01-01 09:56:59 -05:00
fn value_checker() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
2020-01-01 09:56:59 -05:00
{
let mut hs = v8::HandleScope::new(&mut isolate);
2020-01-01 09:56:59 -05:00
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "undefined").unwrap();
assert!(value.is_undefined());
assert!(value.is_null_or_undefined());
assert!(value == value);
assert!(value == v8::Local::<v8::Primitive>::try_from(value).unwrap());
assert!(value == v8::undefined(scope));
assert!(value != v8::null(scope));
assert!(value != v8::Boolean::new(scope, false));
assert!(value != v8::Integer::new(scope, 0));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "null").unwrap();
assert!(value.is_null());
assert!(value.is_null_or_undefined());
assert!(value == value);
assert!(value == v8::Local::<v8::Primitive>::try_from(value).unwrap());
assert!(value == v8::null(scope));
assert!(value != v8::undefined(scope));
assert!(value != v8::Boolean::new(scope, false));
assert!(value != v8::Integer::new(scope, 0));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "true").unwrap();
assert!(value.is_boolean());
assert!(value.is_true());
assert!(!value.is_false());
assert!(value == value);
assert!(value == v8::Local::<v8::Boolean>::try_from(value).unwrap());
assert!(value == v8::Boolean::new(scope, true));
assert!(value != v8::Boolean::new(scope, false));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "false").unwrap();
assert!(value.is_boolean());
assert!(!value.is_true());
assert!(value.is_false());
assert!(value == value);
assert!(value == v8::Local::<v8::Boolean>::try_from(value).unwrap());
assert!(value == v8::Boolean::new(scope, false));
assert!(value != v8::Boolean::new(scope, true));
assert!(value != v8::null(scope));
assert!(value != v8::undefined(scope));
assert!(value != v8::Integer::new(scope, 0));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "'name'").unwrap();
assert!(value.is_name());
assert!(value.is_string());
assert!(value == value);
assert!(value == v8::Local::<v8::String>::try_from(value).unwrap());
assert!(value == v8::String::new(scope, "name").unwrap());
assert!(value != v8::String::new(scope, "name\0").unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "Symbol()").unwrap();
assert!(value.is_name());
assert!(value.is_symbol());
assert!(value == value);
assert!(value == v8::Local::<v8::Symbol>::try_from(value).unwrap());
assert!(value == v8::Global::new_from(scope, value).get(scope).unwrap());
assert!(value != eval(scope, context, "Symbol()").unwrap());
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "() => 0").unwrap();
assert!(value.is_function());
assert!(value == value);
assert!(value == v8::Local::<v8::Function>::try_from(value).unwrap());
assert!(value == v8::Global::new_from(scope, value).get(scope).unwrap());
assert!(value != eval(scope, context, "() => 0").unwrap());
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "async () => 0").unwrap();
assert!(value.is_async_function());
assert!(value == value);
assert!(value == v8::Local::<v8::Function>::try_from(value).unwrap());
assert!(value == v8::Global::new_from(scope, value).get(scope).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "[]").unwrap();
assert!(value.is_array());
assert!(value == value);
assert!(value == v8::Local::<v8::Array>::try_from(value).unwrap());
assert!(value != v8::Array::new(scope, 0));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "9007199254740995n").unwrap();
2020-01-01 09:56:59 -05:00
assert!(value.is_big_int());
assert!(value.to_big_int(scope).is_some());
assert!(value == value);
assert!(value == v8::Local::<v8::BigInt>::try_from(value).unwrap());
assert!(value == eval(scope, context, "1801439850948199n * 5n").unwrap());
assert!(value != eval(scope, context, "1801439850948199 * 5").unwrap());
let detail_string = value.to_detail_string(scope).unwrap();
let detail_string = detail_string.to_rust_string_lossy(scope);
assert_eq!("9007199254740995", detail_string);
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "123").unwrap();
assert!(value.is_number());
assert!(value.is_int32());
assert!(value.is_uint32());
assert!(value == value);
assert!(value == v8::Local::<v8::Number>::try_from(value).unwrap());
assert!(value == v8::Integer::new(scope, 123));
assert!(value == v8::Number::new(scope, 123f64));
assert!(value == value.to_int32(scope).unwrap());
assert!(value != value.to_string(scope).unwrap());
assert_eq!(123, value.to_uint32(scope).unwrap().value());
assert_eq!(123, value.to_int32(scope).unwrap().value());
assert_eq!(123, value.to_integer(scope).unwrap().value());
assert_eq!(123, value.integer_value(scope).unwrap());
assert_eq!(123, value.uint32_value(scope).unwrap());
assert_eq!(123, value.int32_value(scope).unwrap());
let value = eval(scope, context, "12.3").unwrap();
assert!(value.is_number());
assert!(!value.is_int32());
assert!(!value.is_uint32());
assert!(value == value);
assert!(value == v8::Local::<v8::Number>::try_from(value).unwrap());
assert!(value == v8::Number::new(scope, 12.3f64));
assert!(value != value.to_integer(scope).unwrap());
assert!(12.3 - value.number_value(scope).unwrap() < 0.00001);
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "-123").unwrap();
assert!(value.is_number());
assert!(value.is_int32());
2020-01-01 09:56:59 -05:00
assert!(!value.is_uint32());
assert!(value == value);
assert!(value == v8::Local::<v8::Int32>::try_from(value).unwrap());
assert!(value == v8::Integer::new(scope, -123));
assert!(value == v8::Number::new(scope, -123f64));
assert!(value != v8::String::new(scope, "-123").unwrap());
assert!(
value
== v8::Integer::new_from_unsigned(scope, -123i32 as u32)
.to_int32(scope)
.unwrap()
);
// The following test does not pass. This appears to be a V8 bug.
// assert!(value != value.to_uint32(scope).unwrap());
let value = eval(scope, context, "NaN").unwrap();
assert!(value.is_number());
assert!(!value.is_int32());
assert!(!value.is_uint32());
assert!(value != value);
assert!(
value.to_string(scope).unwrap() == v8::String::new(scope, "NaN").unwrap()
);
let value = eval(scope, context, "({})").unwrap();
assert!(value.is_object());
assert!(value == value);
assert!(value == v8::Local::<v8::Object>::try_from(value).unwrap());
assert!(value == v8::Global::new_from(scope, value).get(scope).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new Date()").unwrap();
assert!(value.is_date());
assert!(value == value);
assert!(value == v8::Local::<v8::Date>::try_from(value).unwrap());
assert!(value != eval(scope, context, "new Date()").unwrap());
2020-01-01 09:56:59 -05:00
let value =
eval(scope, context, "(function(){return arguments})()").unwrap();
assert!(value.is_arguments_object());
assert!(value == value);
assert!(value == v8::Local::<v8::Object>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new Promise(function(){})").unwrap();
assert!(value.is_promise());
assert!(value == value);
assert!(value == v8::Local::<v8::Promise>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new Map()").unwrap();
assert!(value.is_map());
assert!(value == value);
assert!(value == v8::Local::<v8::Map>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new Set").unwrap();
assert!(value.is_set());
assert!(value == value);
assert!(value == v8::Local::<v8::Set>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new Map().entries()").unwrap();
assert!(value.is_map_iterator());
assert!(value == value);
assert!(value == v8::Local::<v8::Object>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new Set().entries()").unwrap();
assert!(value.is_set_iterator());
assert!(value == value);
assert!(value == v8::Local::<v8::Object>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new WeakMap()").unwrap();
assert!(value.is_weak_map());
assert!(value == value);
assert!(value == v8::Local::<v8::Object>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new WeakSet()").unwrap();
assert!(value.is_weak_set());
assert!(value == value);
assert!(value == v8::Local::<v8::Object>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new ArrayBuffer(8)").unwrap();
assert!(value.is_array_buffer());
assert!(value == value);
assert!(value == v8::Local::<v8::ArrayBuffer>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new Uint8Array([])").unwrap();
assert!(value.is_uint8_array());
assert!(value.is_array_buffer_view());
assert!(value.is_typed_array());
assert!(value == value);
assert!(value == v8::Local::<v8::Uint8Array>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new Uint8ClampedArray([])").unwrap();
assert!(value.is_uint8_clamped_array());
assert!(value.is_array_buffer_view());
assert!(value.is_typed_array());
assert!(value == value);
assert!(
value == v8::Local::<v8::Uint8ClampedArray>::try_from(value).unwrap()
);
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new Int8Array([])").unwrap();
assert!(value.is_int8_array());
assert!(value.is_array_buffer_view());
assert!(value.is_typed_array());
assert!(value == value);
assert!(value == v8::Local::<v8::Int8Array>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new Uint16Array([])").unwrap();
assert!(value.is_uint16_array());
assert!(value.is_array_buffer_view());
assert!(value.is_typed_array());
assert!(value == value);
assert!(value == v8::Local::<v8::Uint16Array>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new Int16Array([])").unwrap();
assert!(value.is_int16_array());
assert!(value.is_array_buffer_view());
assert!(value.is_typed_array());
assert!(value == value);
assert!(value == v8::Local::<v8::Int16Array>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new Uint32Array([])").unwrap();
assert!(value.is_uint32_array());
assert!(value.is_array_buffer_view());
assert!(value.is_typed_array());
assert!(value == value);
assert!(value == v8::Local::<v8::Uint32Array>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new Int32Array([])").unwrap();
assert!(value.is_int32_array());
assert!(value.is_array_buffer_view());
assert!(value.is_typed_array());
assert!(value == value);
assert!(value == v8::Local::<v8::Int32Array>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new Float32Array([])").unwrap();
assert!(value.is_float32_array());
assert!(value.is_array_buffer_view());
assert!(value.is_typed_array());
assert!(value == value);
assert!(value == v8::Local::<v8::Float32Array>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new Float64Array([])").unwrap();
assert!(value.is_float64_array());
assert!(value.is_array_buffer_view());
assert!(value.is_typed_array());
assert!(value == value);
assert!(value == v8::Local::<v8::Float64Array>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new BigInt64Array([])").unwrap();
assert!(value.is_big_int64_array());
assert!(value.is_array_buffer_view());
assert!(value.is_typed_array());
assert!(value == value);
assert!(value == v8::Local::<v8::BigInt64Array>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new BigUint64Array([])").unwrap();
assert!(value.is_big_uint64_array());
assert!(value.is_array_buffer_view());
assert!(value.is_typed_array());
assert!(value == value);
assert!(value == v8::Local::<v8::BigUint64Array>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new SharedArrayBuffer(64)").unwrap();
assert!(value.is_shared_array_buffer());
assert!(value == value);
assert!(
value == v8::Local::<v8::SharedArrayBuffer>::try_from(value).unwrap()
);
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
let value = eval(scope, context, "new Proxy({},{})").unwrap();
assert!(value.is_proxy());
assert!(value == value);
assert!(value == v8::Local::<v8::Proxy>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
2020-01-01 09:56:59 -05:00
// Other checker, Just check if it can be called
value.is_external();
value.is_module_namespace_object();
value.is_wasm_module_object();
2020-01-01 09:56:59 -05:00
}
}
#[test]
fn try_from_local() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
{
let value: v8::Local<v8::Value> = v8::undefined(scope).into();
let _primitive = v8::Local::<v8::Primitive>::try_from(value).unwrap();
assert_eq!(
v8::Local::<v8::Object>::try_from(value)
.err()
.unwrap()
.to_string(),
"Object expected"
);
assert_eq!(
v8::Local::<v8::Int32>::try_from(value)
.err()
.unwrap()
.to_string(),
"Int32 expected"
);
}
{
2020-01-04 18:08:27 -05:00
let value: v8::Local<v8::Value> = v8::Boolean::new(scope, true).into();
let primitive = v8::Local::<v8::Primitive>::try_from(value).unwrap();
let _boolean = v8::Local::<v8::Boolean>::try_from(value).unwrap();
let _boolean = v8::Local::<v8::Boolean>::try_from(primitive).unwrap();
assert_eq!(
v8::Local::<v8::String>::try_from(value)
.err()
.unwrap()
.to_string(),
"String expected"
);
assert_eq!(
v8::Local::<v8::Number>::try_from(primitive)
.err()
.unwrap()
.to_string(),
"Number expected"
);
}
{
let value: v8::Local<v8::Value> = v8::Number::new(scope, -1234f64).into();
let primitive = v8::Local::<v8::Primitive>::try_from(value).unwrap();
let _number = v8::Local::<v8::Number>::try_from(value).unwrap();
let number = v8::Local::<v8::Number>::try_from(primitive).unwrap();
let _integer = v8::Local::<v8::Integer>::try_from(value).unwrap();
let _integer = v8::Local::<v8::Integer>::try_from(primitive).unwrap();
let integer = v8::Local::<v8::Integer>::try_from(number).unwrap();
let _int32 = v8::Local::<v8::Int32>::try_from(value).unwrap();
let _int32 = v8::Local::<v8::Int32>::try_from(primitive).unwrap();
let _int32 = v8::Local::<v8::Int32>::try_from(integer).unwrap();
let _int32 = v8::Local::<v8::Int32>::try_from(number).unwrap();
assert_eq!(
v8::Local::<v8::String>::try_from(value)
.err()
.unwrap()
.to_string(),
"String expected"
);
assert_eq!(
v8::Local::<v8::Boolean>::try_from(primitive)
.err()
.unwrap()
.to_string(),
"Boolean expected"
);
assert_eq!(
v8::Local::<v8::Uint32>::try_from(integer)
.err()
.unwrap()
.to_string(),
"Uint32 expected"
);
}
{
let value: v8::Local<v8::Value> =
eval(scope, context, "(() => {})").unwrap();
let object = v8::Local::<v8::Object>::try_from(value).unwrap();
let _function = v8::Local::<v8::Function>::try_from(value).unwrap();
let _function = v8::Local::<v8::Function>::try_from(object).unwrap();
assert_eq!(
v8::Local::<v8::Primitive>::try_from(value)
.err()
.unwrap()
.to_string(),
"Primitive expected"
);
assert_eq!(
v8::Local::<v8::BigInt>::try_from(value)
.err()
.unwrap()
.to_string(),
"BigInt expected"
);
assert_eq!(
v8::Local::<v8::NumberObject>::try_from(value)
.err()
.unwrap()
.to_string(),
"NumberObject expected"
);
assert_eq!(
v8::Local::<v8::NumberObject>::try_from(object)
.err()
.unwrap()
.to_string(),
"NumberObject expected"
);
assert_eq!(
v8::Local::<v8::Set>::try_from(value)
.err()
.unwrap()
.to_string(),
"Set expected"
);
assert_eq!(
v8::Local::<v8::Set>::try_from(object)
.err()
.unwrap()
.to_string(),
"Set expected"
);
}
}
}
2020-01-16 18:12:25 -05:00
struct ClientCounter {
base: v8::inspector::V8InspectorClientBase,
count_run_message_loop_on_pause: usize,
count_quit_message_loop_on_pause: usize,
count_run_if_waiting_for_debugger: usize,
}
impl ClientCounter {
fn new() -> Self {
Self {
base: v8::inspector::V8InspectorClientBase::new::<Self>(),
count_run_message_loop_on_pause: 0,
count_quit_message_loop_on_pause: 0,
count_run_if_waiting_for_debugger: 0,
}
}
}
2020-01-16 18:12:25 -05:00
impl v8::inspector::V8InspectorClientImpl for ClientCounter {
fn base(&self) -> &v8::inspector::V8InspectorClientBase {
&self.base
}
2020-01-16 18:12:25 -05:00
fn base_mut(&mut self) -> &mut v8::inspector::V8InspectorClientBase {
&mut self.base
2020-01-16 18:12:25 -05:00
}
fn run_message_loop_on_pause(&mut self, context_group_id: i32) {
assert_eq!(context_group_id, 1);
self.count_run_message_loop_on_pause += 1;
2020-01-16 18:12:25 -05:00
}
fn quit_message_loop_on_pause(&mut self) {
self.count_quit_message_loop_on_pause += 1;
2020-01-16 18:12:25 -05:00
}
fn run_if_waiting_for_debugger(&mut self, context_group_id: i32) {
assert_eq!(context_group_id, 1);
self.count_run_message_loop_on_pause += 1;
2020-01-16 18:12:25 -05:00
}
}
2020-01-16 18:12:25 -05:00
struct ChannelCounter {
base: v8::inspector::ChannelBase,
count_send_response: usize,
count_send_notification: usize,
count_flush_protocol_notifications: usize,
}
impl ChannelCounter {
pub fn new() -> Self {
Self {
base: v8::inspector::ChannelBase::new::<Self>(),
count_send_response: 0,
count_send_notification: 0,
count_flush_protocol_notifications: 0,
2020-01-16 18:12:25 -05:00
}
}
}
2020-01-16 18:12:25 -05:00
impl v8::inspector::ChannelImpl for ChannelCounter {
fn base(&self) -> &v8::inspector::ChannelBase {
&self.base
2020-01-16 18:12:25 -05:00
}
fn base_mut(&mut self) -> &mut v8::inspector::ChannelBase {
&mut self.base
}
fn send_response(
&mut self,
call_id: i32,
message: v8::UniquePtr<v8::inspector::StringBuffer>,
) {
println!(
"send_response call_id {} message {}",
call_id,
message.unwrap().string()
);
self.count_send_response += 1;
}
fn send_notification(
&mut self,
message: v8::UniquePtr<v8::inspector::StringBuffer>,
) {
println!("send_notificatio message {}", message.unwrap().string());
self.count_send_notification += 1;
}
fn flush_protocol_notifications(&mut self) {
self.count_flush_protocol_notifications += 1;
}
}
#[test]
fn inspector_dispatch_protocol_message() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
use v8::inspector::*;
let mut default_client = ClientCounter::new();
let mut inspector = V8Inspector::create(&mut isolate, &mut default_client);
2020-01-16 18:12:25 -05:00
let mut hs = v8::HandleScope::new(&mut isolate);
2020-01-16 18:12:25 -05:00
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let _scope = cs.enter();
2020-01-16 18:12:25 -05:00
let name = b"";
let name_view = StringView::from(&name[..]);
2020-05-05 18:06:35 -04:00
inspector.context_created(context, 1, name_view);
let mut channel = ChannelCounter::new();
2020-01-16 18:12:25 -05:00
let state = b"{}";
let state_view = StringView::from(&state[..]);
2020-05-05 18:06:35 -04:00
let mut session = inspector.connect(1, &mut channel, state_view);
2020-01-16 18:12:25 -05:00
let message = String::from(
r#"{"id":1,"method":"Network.enable","params":{"maxPostDataSize":65536}}"#,
);
let message = &message.into_bytes()[..];
let string_view = StringView::from(message);
2020-05-05 18:06:35 -04:00
session.dispatch_protocol_message(string_view);
assert_eq!(channel.count_send_response, 1);
assert_eq!(channel.count_send_notification, 0);
assert_eq!(channel.count_flush_protocol_notifications, 0);
2020-01-16 18:12:25 -05:00
}
#[test]
fn inspector_schedule_pause_on_next_statement() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
use v8::inspector::*;
let mut client = ClientCounter::new();
let mut inspector = V8Inspector::create(&mut isolate, &mut client);
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let mut channel = ChannelCounter::new();
let state = b"{}";
let state_view = StringView::from(&state[..]);
2020-05-05 18:06:35 -04:00
let mut session = inspector.connect(1, &mut channel, state_view);
let name = b"";
let name_view = StringView::from(&name[..]);
2020-05-05 18:06:35 -04:00
inspector.context_created(context, 1, name_view);
// In order for schedule_pause_on_next_statement to work, it seems you need
// to first enable the debugger.
let message = String::from(r#"{"id":1,"method":"Debugger.enable"}"#);
let message = &message.into_bytes()[..];
let message = StringView::from(message);
2020-05-05 18:06:35 -04:00
session.dispatch_protocol_message(message);
// The following commented out block seems to act similarly to
// schedule_pause_on_next_statement. I'm not sure if they have the exact same
// effect tho.
// let message = String::from(r#"{"id":2,"method":"Debugger.pause"}"#);
// let message = &message.into_bytes()[..];
// let message = StringView::from(message);
// session.dispatch_protocol_message(&message);
let reason = b"";
let reason = StringView::from(&reason[..]);
let detail = b"";
let detail = StringView::from(&detail[..]);
2020-05-05 18:06:35 -04:00
session.schedule_pause_on_next_statement(reason, detail);
assert_eq!(channel.count_send_response, 1);
assert_eq!(channel.count_send_notification, 0);
assert_eq!(channel.count_flush_protocol_notifications, 0);
assert_eq!(client.count_run_message_loop_on_pause, 0);
assert_eq!(client.count_quit_message_loop_on_pause, 0);
assert_eq!(client.count_run_if_waiting_for_debugger, 0);
let r = eval(scope, context, "1+2").unwrap();
assert!(r.is_number());
assert_eq!(channel.count_send_response, 1);
assert_eq!(channel.count_send_notification, 3);
2020-03-13 21:32:02 -04:00
assert_eq!(channel.count_flush_protocol_notifications, 1);
assert_eq!(client.count_run_message_loop_on_pause, 1);
assert_eq!(client.count_quit_message_loop_on_pause, 0);
assert_eq!(client.count_run_if_waiting_for_debugger, 0);
}
#[test]
fn inspector_console_api_message() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
use v8::inspector::*;
struct Client {
base: V8InspectorClientBase,
messages: Vec<String>,
}
impl Client {
fn new() -> Self {
Self {
base: V8InspectorClientBase::new::<Self>(),
messages: Vec::new(),
}
}
}
impl V8InspectorClientImpl for Client {
fn base(&self) -> &V8InspectorClientBase {
&self.base
}
fn base_mut(&mut self) -> &mut V8InspectorClientBase {
&mut self.base
}
fn console_api_message(
&mut self,
_context_group_id: i32,
_level: i32,
message: &StringView,
_url: &StringView,
_line_number: u32,
_column_number: u32,
_stack_trace: &mut V8StackTrace,
) {
self.messages.push(message.to_string());
}
}
let mut client = Client::new();
let mut inspector = V8Inspector::create(&mut isolate, &mut client);
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let name = b"";
let name_view = StringView::from(&name[..]);
2020-05-05 18:06:35 -04:00
inspector.context_created(context, 1, name_view);
let source = r#"
console.log("one");
console.error("two");
console.trace("three");
"#;
let _ = eval(scope, context, source).unwrap();
assert_eq!(client.messages, vec!["one", "two", "three"]);
}
#[test]
fn context_from_object_template() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let object_templ = v8::ObjectTemplate::new(scope);
let function_templ = v8::FunctionTemplate::new(scope, fortytwo_callback);
let name = v8_str(scope, "f");
object_templ.set(name.into(), function_templ.into());
let context = v8::Context::new_from_template(scope, object_templ);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let actual = eval(scope, context, "f()").unwrap();
let expected = v8::Integer::new(scope, 42);
assert!(expected.strict_equals(actual));
}
}
2020-02-13 15:03:25 -05:00
#[test]
fn take_heap_snapshot() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let source = r#"
{
class Eyecatcher {}
const eyecatchers = globalThis.eyecatchers = [];
for (let i = 0; i < 1e4; i++) eyecatchers.push(new Eyecatcher);
}
"#;
let _ = eval(scope, context, source).unwrap();
let mut vec = Vec::<u8>::new();
isolate.take_heap_snapshot(|chunk| {
vec.extend_from_slice(chunk);
true
});
let s = std::str::from_utf8(&vec).unwrap();
assert!(s.find(r#""Eyecatcher""#).is_some());
}
}
#[test]
fn test_prototype_api() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let obj = v8::Object::new(scope);
let proto_obj = v8::Object::new(scope);
let key_local: v8::Local<v8::Value> =
v8::String::new(scope, "test_proto_key").unwrap().into();
let value_local: v8::Local<v8::Value> =
v8::String::new(scope, "test_proto_value").unwrap().into();
proto_obj.set(context, key_local, value_local);
obj.set_prototype(context, proto_obj.into());
assert!(obj
.get_prototype(scope)
.unwrap()
.same_value(proto_obj.into()));
let sub_gotten = obj.get(scope, context, key_local).unwrap();
assert!(sub_gotten.is_string());
let sub_gotten = sub_gotten.to_string(scope).unwrap();
assert_eq!(sub_gotten.to_rust_string_lossy(scope), "test_proto_value");
}
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let obj = v8::Object::new(scope);
obj.set_prototype(context, v8::null(scope).into());
assert!(obj.get_prototype(scope).unwrap().is_null());
}
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let val = eval(scope, context, "({ __proto__: null })").unwrap();
let obj = val.to_object(scope).unwrap();
assert!(obj.get_prototype(scope).unwrap().is_null());
}
}
2020-04-02 13:37:13 -04:00
#[test]
fn test_map_api() {
let _setup_guard = setup();
let mut isolate = v8::Isolate::new(Default::default());
2020-04-02 13:37:13 -04:00
{
let mut hs = v8::HandleScope::new(&mut isolate);
let scope = hs.enter();
let context = v8::Context::new(scope);
let mut cs = v8::ContextScope::new(scope, context);
let scope = cs.enter();
let value = eval(scope, context, "new Map([['r','s'],['v',8]])").unwrap();
assert!(value.is_map());
assert!(value == v8::Local::<v8::Map>::try_from(value).unwrap());
assert!(value != v8::Object::new(scope));
assert_eq!(v8::Local::<v8::Map>::try_from(value).unwrap().size(), 2);
let map = v8::Local::<v8::Map>::try_from(value).unwrap();
assert_eq!(map.size(), 2);
let map_array = map.as_array(scope);
assert_eq!(map_array.length(), 4);
assert!(
map_array.get_index(scope, context, 0).unwrap()
== v8::String::new(scope, "r").unwrap()
);
assert!(
map_array.get_index(scope, context, 1).unwrap()
== v8::String::new(scope, "s").unwrap()
);
assert!(
map_array.get_index(scope, context, 2).unwrap()
== v8::String::new(scope, "v").unwrap()
);
assert!(
map_array.get_index(scope, context, 3).unwrap()
== v8::Number::new(scope, 8f64)
);
}
}