mirror of
https://github.com/denoland/rusty_v8.git
synced 2024-11-21 15:04:33 -05:00
feat: add v8::Isolate::snapshot_creator_from_existing_snapshot API (#973)
This commit adds "v8::Isolate::snapshot_creator_from_existing_snapshot" API, that allows to create a new snapshot from already existing snapshot. Following APIs were added as well: "v8::Context::from_snapshot" "v8::Isolate::add_context" Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com> Co-authored-by: Bert Belder <bertbelder@gmail.com>
This commit is contained in:
parent
4b704ed8a9
commit
885e0163a1
6 changed files with 299 additions and 25 deletions
|
@ -1670,6 +1670,13 @@ void v8__Context__SetPromiseHooks(v8::Context& self, v8::Function& init_hook,
|
|||
ptr_to_local(&after_hook), ptr_to_local(&resolve_hook));
|
||||
}
|
||||
|
||||
const v8::Context* v8__Context__FromSnapshot(v8::Isolate* isolate,
|
||||
size_t context_snapshot_index) {
|
||||
v8::MaybeLocal<v8::Context> maybe_local =
|
||||
v8::Context::FromSnapshot(isolate, context_snapshot_index);
|
||||
return maybe_local_to_ptr(maybe_local);
|
||||
}
|
||||
|
||||
const v8::String* v8__Message__Get(const v8::Message& self) {
|
||||
return local_to_ptr(self.Get());
|
||||
}
|
||||
|
@ -2316,8 +2323,10 @@ bool v8__Proxy__IsRevoked(const v8::Proxy& self) {
|
|||
void v8__Proxy__Revoke(const v8::Proxy& self) { ptr_to_local(&self)->Revoke(); }
|
||||
|
||||
void v8__SnapshotCreator__CONSTRUCT(uninit_t<v8::SnapshotCreator>* buf,
|
||||
const intptr_t* external_references) {
|
||||
construct_in_place<v8::SnapshotCreator>(buf, external_references);
|
||||
const intptr_t* external_references,
|
||||
v8::StartupData* existing_blob) {
|
||||
construct_in_place<v8::SnapshotCreator>(buf, external_references,
|
||||
existing_blob);
|
||||
}
|
||||
|
||||
void v8__SnapshotCreator__DESTRUCT(v8::SnapshotCreator* self) {
|
||||
|
@ -2351,6 +2360,11 @@ void v8__SnapshotCreator__SetDefaultContext(v8::SnapshotCreator* self,
|
|||
self->SetDefaultContext(ptr_to_local(&context), SerializeInternalFields);
|
||||
}
|
||||
|
||||
size_t v8__SnapshotCreator__AddContext(v8::SnapshotCreator* self,
|
||||
const v8::Context& context) {
|
||||
return self->AddContext(ptr_to_local(&context), SerializeInternalFields);
|
||||
}
|
||||
|
||||
size_t v8__SnapshotCreator__AddData_to_isolate(v8::SnapshotCreator* self,
|
||||
const v8::Data& data) {
|
||||
return self->AddData(ptr_to_local(&data));
|
||||
|
|
|
@ -44,6 +44,10 @@ extern "C" {
|
|||
index: c_int,
|
||||
value: *mut c_void,
|
||||
);
|
||||
fn v8__Context__FromSnapshot(
|
||||
isolate: *mut Isolate,
|
||||
context_snapshot_index: usize,
|
||||
) -> *const Context;
|
||||
}
|
||||
|
||||
impl Context {
|
||||
|
@ -302,6 +306,20 @@ impl Context {
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new context from a (non-default) context snapshot. There
|
||||
/// is no way to provide a global object template since we do not create
|
||||
/// a new global object from template, but we can reuse a global object.
|
||||
pub fn from_snapshot<'s>(
|
||||
scope: &mut HandleScope<'s, ()>,
|
||||
context_snapshot_index: usize,
|
||||
) -> Option<Local<'s, Context>> {
|
||||
unsafe {
|
||||
scope.cast_local(|sd| {
|
||||
v8__Context__FromSnapshot(sd.get_isolate_mut(), context_snapshot_index)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ContextAnnex {
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::isolate_create_params::CreateParams;
|
|||
use crate::promise::PromiseRejectMessage;
|
||||
use crate::scope::data::ScopeData;
|
||||
use crate::snapshot::SnapshotCreator;
|
||||
use crate::support::Allocated;
|
||||
use crate::support::MapFnFrom;
|
||||
use crate::support::MapFnTo;
|
||||
use crate::support::Opaque;
|
||||
|
@ -543,6 +544,17 @@ impl Isolate {
|
|||
SnapshotCreator::new(external_references)
|
||||
}
|
||||
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn snapshot_creator_from_existing_snapshot(
|
||||
existing_snapshot_blob: impl Allocated<[u8]>,
|
||||
external_references: Option<&'static ExternalReferences>,
|
||||
) -> OwnedIsolate {
|
||||
SnapshotCreator::from_existing_snapshot(
|
||||
existing_snapshot_blob,
|
||||
external_references,
|
||||
)
|
||||
}
|
||||
|
||||
/// Initial configuration parameters for a new Isolate.
|
||||
#[inline(always)]
|
||||
pub fn create_params() -> CreateParams {
|
||||
|
@ -1144,6 +1156,24 @@ impl Isolate {
|
|||
snapshot_creator.set_default_context(context);
|
||||
}
|
||||
|
||||
/// Add additional context to be included in the snapshot blob.
|
||||
/// The snapshot will include the global proxy.
|
||||
///
|
||||
/// Returns the index of the context in the snapshot blob.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the isolate was not created using [`Isolate::snapshot_creator`]
|
||||
#[inline(always)]
|
||||
pub fn add_context(&mut self, context: Local<Context>) -> usize {
|
||||
let snapshot_creator = self
|
||||
.get_annex_mut()
|
||||
.maybe_snapshot_creator
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
snapshot_creator.add_context(context)
|
||||
}
|
||||
|
||||
/// Attach arbitrary `v8::Data` to the isolate snapshot, which can be
|
||||
/// retrieved via `HandleScope::get_context_data_from_snapshot_once()` after
|
||||
/// deserialization. This data does not survive when a new snapshot is created
|
||||
|
@ -1412,7 +1442,6 @@ impl OwnedIsolate {
|
|||
) -> Option<StartupData> {
|
||||
let mut snapshot_creator =
|
||||
self.get_annex_mut().maybe_snapshot_creator.take().unwrap();
|
||||
ScopeData::get_root_mut(&mut self);
|
||||
unsafe { self.cxx_isolate.as_mut().clear_scope_and_annex() };
|
||||
// The isolate is owned by the snapshot creator; we need to forget it
|
||||
// here as the snapshot creator will drop it when running the destructor.
|
||||
|
|
|
@ -237,7 +237,7 @@ pub(crate) mod raw {
|
|||
}
|
||||
|
||||
impl StartupData {
|
||||
pub(super) fn boxed_header(data: &Allocation<[u8]>) -> Box<Self> {
|
||||
pub(crate) fn boxed_header(data: &Allocation<[u8]>) -> Box<Self> {
|
||||
Box::new(Self {
|
||||
data: &data[0] as *const _ as *const char,
|
||||
raw_size: int::try_from(data.len()).unwrap(),
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
use crate::external_references::ExternalReferences;
|
||||
use crate::isolate_create_params::raw;
|
||||
use crate::scope::data::ScopeData;
|
||||
use crate::support::char;
|
||||
use crate::support::int;
|
||||
use crate::support::intptr_t;
|
||||
use crate::support::Allocated;
|
||||
use crate::support::Allocation;
|
||||
use crate::Context;
|
||||
use crate::Data;
|
||||
use crate::Isolate;
|
||||
|
@ -13,11 +16,13 @@ use std::borrow::Borrow;
|
|||
use std::convert::TryFrom;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ops::Deref;
|
||||
use std::ptr::null;
|
||||
|
||||
extern "C" {
|
||||
fn v8__SnapshotCreator__CONSTRUCT(
|
||||
buf: *mut MaybeUninit<SnapshotCreator>,
|
||||
external_references: *const intptr_t,
|
||||
existing_blob: *const raw::StartupData,
|
||||
);
|
||||
fn v8__SnapshotCreator__DESTRUCT(this: *mut SnapshotCreator);
|
||||
fn v8__SnapshotCreator__GetIsolate(
|
||||
|
@ -31,6 +36,10 @@ extern "C" {
|
|||
this: *mut SnapshotCreator,
|
||||
context: *const Context,
|
||||
);
|
||||
fn v8__SnapshotCreator__AddContext(
|
||||
this: *mut SnapshotCreator,
|
||||
context: *const Context,
|
||||
) -> usize;
|
||||
fn v8__SnapshotCreator__AddData_to_isolate(
|
||||
this: *mut SnapshotCreator,
|
||||
data: *const Data,
|
||||
|
@ -52,6 +61,12 @@ pub struct StartupData {
|
|||
raw_size: int,
|
||||
}
|
||||
|
||||
impl Drop for StartupData {
|
||||
fn drop(&mut self) {
|
||||
unsafe { v8__StartupData__DESTRUCT(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for StartupData {
|
||||
type Target = [u8];
|
||||
fn deref(&self) -> &Self::Target {
|
||||
|
@ -73,12 +88,6 @@ impl Borrow<[u8]> for StartupData {
|
|||
}
|
||||
}
|
||||
|
||||
impl Drop for StartupData {
|
||||
fn drop(&mut self) {
|
||||
unsafe { v8__StartupData__DESTRUCT(self) }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub enum FunctionCodeHandling {
|
||||
|
@ -92,12 +101,34 @@ pub enum FunctionCodeHandling {
|
|||
pub(crate) struct SnapshotCreator([usize; 1]);
|
||||
|
||||
impl SnapshotCreator {
|
||||
/// Create and enter an isolate, and set it up for serialization.
|
||||
/// Create an isolate, and set it up for serialization.
|
||||
/// The isolate is created from scratch.
|
||||
#[inline(always)]
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub(crate) fn new(
|
||||
external_references: Option<&'static ExternalReferences>,
|
||||
) -> OwnedIsolate {
|
||||
Self::new_impl(external_references, None::<&[u8]>)
|
||||
}
|
||||
|
||||
/// Create an isolate, and set it up for serialization.
|
||||
/// The isolate is created from scratch.
|
||||
#[inline(always)]
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub(crate) fn from_existing_snapshot(
|
||||
existing_snapshot_blob: impl Allocated<[u8]>,
|
||||
external_references: Option<&'static ExternalReferences>,
|
||||
) -> OwnedIsolate {
|
||||
Self::new_impl(external_references, Some(existing_snapshot_blob))
|
||||
}
|
||||
|
||||
/// Create and enter an isolate, and set it up for serialization.
|
||||
/// The isolate is created from scratch.
|
||||
#[inline(always)]
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
fn new_impl(
|
||||
external_references: Option<&'static ExternalReferences>,
|
||||
existing_snapshot_blob: Option<impl Allocated<[u8]>>,
|
||||
) -> OwnedIsolate {
|
||||
let mut snapshot_creator: MaybeUninit<Self> = MaybeUninit::uninit();
|
||||
let external_references_ptr = if let Some(er) = external_references {
|
||||
|
@ -105,10 +136,24 @@ impl SnapshotCreator {
|
|||
} else {
|
||||
std::ptr::null()
|
||||
};
|
||||
|
||||
let snapshot_blob_ptr;
|
||||
let snapshot_allocations;
|
||||
if let Some(snapshot_blob) = existing_snapshot_blob {
|
||||
let data = Allocation::of(snapshot_blob);
|
||||
let header = Allocation::of(raw::StartupData::boxed_header(&data));
|
||||
snapshot_blob_ptr = &*header as *const _;
|
||||
snapshot_allocations = Some((header, data));
|
||||
} else {
|
||||
snapshot_blob_ptr = null();
|
||||
snapshot_allocations = None;
|
||||
}
|
||||
|
||||
let snapshot_creator = unsafe {
|
||||
v8__SnapshotCreator__CONSTRUCT(
|
||||
&mut snapshot_creator,
|
||||
external_references_ptr,
|
||||
snapshot_blob_ptr,
|
||||
);
|
||||
snapshot_creator.assume_init()
|
||||
};
|
||||
|
@ -117,7 +162,7 @@ impl SnapshotCreator {
|
|||
unsafe { v8__SnapshotCreator__GetIsolate(&snapshot_creator) };
|
||||
let mut owned_isolate = OwnedIsolate::new(isolate_ptr);
|
||||
ScopeData::new_root(&mut owned_isolate);
|
||||
owned_isolate.create_annex(Box::new(()));
|
||||
owned_isolate.create_annex(Box::new(snapshot_allocations));
|
||||
owned_isolate.set_snapshot_creator(snapshot_creator);
|
||||
owned_isolate
|
||||
}
|
||||
|
@ -138,6 +183,15 @@ impl SnapshotCreator {
|
|||
unsafe { v8__SnapshotCreator__SetDefaultContext(self, &*context) };
|
||||
}
|
||||
|
||||
/// Add additional context to be included in the snapshot blob.
|
||||
/// The snapshot will include the global proxy.
|
||||
///
|
||||
/// Returns the index of the context in the snapshot blob.
|
||||
#[inline(always)]
|
||||
pub(crate) fn add_context(&mut self, context: Local<Context>) -> usize {
|
||||
unsafe { v8__SnapshotCreator__AddContext(self, &*context) }
|
||||
}
|
||||
|
||||
/// Attach arbitrary `v8::Data` to the isolate snapshot, which can be
|
||||
/// retrieved via `HandleScope::get_context_data_from_snapshot_once()` after
|
||||
/// deserialization. This data does not survive when a new snapshot is created
|
||||
|
|
|
@ -3787,6 +3787,22 @@ fn snapshot_creator() {
|
|||
let context_data_index_2;
|
||||
let startup_data = {
|
||||
let mut snapshot_creator = v8::Isolate::snapshot_creator(None);
|
||||
{
|
||||
let scope = &mut v8::HandleScope::new(&mut snapshot_creator);
|
||||
let context = v8::Context::new(scope);
|
||||
let scope = &mut v8::ContextScope::new(scope, context);
|
||||
eval(scope, "b = 2 + 3").unwrap();
|
||||
scope.set_default_context(context);
|
||||
}
|
||||
|
||||
snapshot_creator
|
||||
.create_blob(v8::FunctionCodeHandling::Clear)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let startup_data = {
|
||||
let mut snapshot_creator =
|
||||
v8::Isolate::snapshot_creator_from_existing_snapshot(startup_data, None);
|
||||
{
|
||||
// Check that the SnapshotCreator isolate has been set up correctly.
|
||||
let _ = snapshot_creator.thread_safe_handle();
|
||||
|
@ -3794,10 +3810,7 @@ fn snapshot_creator() {
|
|||
let scope = &mut v8::HandleScope::new(&mut snapshot_creator);
|
||||
let context = v8::Context::new(scope);
|
||||
let scope = &mut v8::ContextScope::new(scope, context);
|
||||
|
||||
let source = v8::String::new(scope, "a = 1 + 2").unwrap();
|
||||
let script = v8::Script::compile(scope, source, None).unwrap();
|
||||
script.run(scope).unwrap();
|
||||
eval(scope, "a = 1 + 2").unwrap();
|
||||
|
||||
scope.set_default_context(context);
|
||||
|
||||
|
@ -3822,9 +3835,11 @@ fn snapshot_creator() {
|
|||
let scope = &mut v8::HandleScope::new(isolate);
|
||||
let context = v8::Context::new(scope);
|
||||
let scope = &mut v8::ContextScope::new(scope, context);
|
||||
let source = v8::String::new(scope, "a === 3").unwrap();
|
||||
let script = v8::Script::compile(scope, source, None).unwrap();
|
||||
let result = script.run(scope).unwrap();
|
||||
let result = eval(scope, "a === 3").unwrap();
|
||||
let true_val = v8::Boolean::new(scope, true).into();
|
||||
assert!(result.same_value(true_val));
|
||||
|
||||
let result = eval(scope, "b === 5").unwrap();
|
||||
let true_val = v8::Boolean::new(scope, true).into();
|
||||
assert!(result.same_value(true_val));
|
||||
|
||||
|
@ -3851,6 +3866,154 @@ fn snapshot_creator() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snapshot_creator_multiple_contexts() {
|
||||
let _setup_guard = setup();
|
||||
let startup_data = {
|
||||
let mut snapshot_creator = v8::Isolate::snapshot_creator(None);
|
||||
{
|
||||
let mut scope = v8::HandleScope::new(&mut snapshot_creator);
|
||||
let context = v8::Context::new(&mut scope);
|
||||
let scope = &mut v8::ContextScope::new(&mut scope, context);
|
||||
eval(scope, "globalThis.__bootstrap = { defaultContextProp: 1};")
|
||||
.unwrap();
|
||||
{
|
||||
let value =
|
||||
eval(scope, "globalThis.__bootstrap.defaultContextProp").unwrap();
|
||||
let one_val = v8::Number::new(scope, 1.0).into();
|
||||
assert!(value.same_value(one_val));
|
||||
}
|
||||
scope.set_default_context(context);
|
||||
}
|
||||
{
|
||||
let scope = &mut v8::HandleScope::new(&mut snapshot_creator);
|
||||
let context = v8::Context::new(scope);
|
||||
let scope = &mut v8::ContextScope::new(scope, context);
|
||||
eval(scope, "globalThis.__bootstrap = { context0Prop: 2 };").unwrap();
|
||||
{
|
||||
let value = eval(scope, "globalThis.__bootstrap.context0Prop").unwrap();
|
||||
let two_val = v8::Number::new(scope, 2.0).into();
|
||||
assert!(value.same_value(two_val));
|
||||
}
|
||||
assert_eq!(0, scope.add_context(context));
|
||||
}
|
||||
|
||||
snapshot_creator
|
||||
.create_blob(v8::FunctionCodeHandling::Clear)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let startup_data = {
|
||||
let mut snapshot_creator =
|
||||
v8::Isolate::snapshot_creator_from_existing_snapshot(startup_data, None);
|
||||
{
|
||||
let scope = &mut v8::HandleScope::new(&mut snapshot_creator);
|
||||
let context = v8::Context::new(scope);
|
||||
let scope = &mut v8::ContextScope::new(scope, context);
|
||||
{
|
||||
let value =
|
||||
eval(scope, "globalThis.__bootstrap.defaultContextProp").unwrap();
|
||||
let one_val = v8::Number::new(scope, 1.0).into();
|
||||
assert!(value.same_value(one_val));
|
||||
}
|
||||
{
|
||||
let value = eval(scope, "globalThis.__bootstrap.context0Prop").unwrap();
|
||||
assert!(value.is_undefined());
|
||||
}
|
||||
{
|
||||
eval(scope, "globalThis.__bootstrap.defaultContextProp2 = 3;").unwrap();
|
||||
let value =
|
||||
eval(scope, "globalThis.__bootstrap.defaultContextProp2").unwrap();
|
||||
let three_val = v8::Number::new(scope, 3.0).into();
|
||||
assert!(value.same_value(three_val));
|
||||
}
|
||||
scope.set_default_context(context);
|
||||
}
|
||||
{
|
||||
let scope = &mut v8::HandleScope::new(&mut snapshot_creator);
|
||||
let context = v8::Context::from_snapshot(scope, 0).unwrap();
|
||||
let scope = &mut v8::ContextScope::new(scope, context);
|
||||
{
|
||||
let value =
|
||||
eval(scope, "globalThis.__bootstrap.defaultContextProp").unwrap();
|
||||
assert!(value.is_undefined());
|
||||
}
|
||||
{
|
||||
let value = eval(scope, "globalThis.__bootstrap.context0Prop").unwrap();
|
||||
let two_val = v8::Number::new(scope, 2.0).into();
|
||||
assert!(value.same_value(two_val));
|
||||
}
|
||||
{
|
||||
eval(scope, "globalThis.__bootstrap.context0Prop2 = 4;").unwrap();
|
||||
let value =
|
||||
eval(scope, "globalThis.__bootstrap.context0Prop2").unwrap();
|
||||
let four_val = v8::Number::new(scope, 4.0).into();
|
||||
assert!(value.same_value(four_val));
|
||||
}
|
||||
assert_eq!(scope.add_context(context), 0);
|
||||
}
|
||||
snapshot_creator
|
||||
.create_blob(v8::FunctionCodeHandling::Clear)
|
||||
.unwrap()
|
||||
};
|
||||
{
|
||||
let params = v8::Isolate::create_params().snapshot_blob(startup_data);
|
||||
let isolate = &mut v8::Isolate::new(params);
|
||||
{
|
||||
let scope = &mut v8::HandleScope::new(isolate);
|
||||
let context = v8::Context::new(scope);
|
||||
let scope = &mut v8::ContextScope::new(scope, context);
|
||||
{
|
||||
let value = eval(scope, "globalThis.__bootstrap.context0Prop").unwrap();
|
||||
assert!(value.is_undefined());
|
||||
}
|
||||
{
|
||||
let value =
|
||||
eval(scope, "globalThis.__bootstrap.context0Prop2").unwrap();
|
||||
assert!(value.is_undefined());
|
||||
}
|
||||
{
|
||||
let value =
|
||||
eval(scope, "globalThis.__bootstrap.defaultContextProp").unwrap();
|
||||
let one_val = v8::Number::new(scope, 1.0).into();
|
||||
assert!(value.same_value(one_val));
|
||||
}
|
||||
{
|
||||
let value =
|
||||
eval(scope, "globalThis.__bootstrap.defaultContextProp2").unwrap();
|
||||
let three_val = v8::Number::new(scope, 3.0).into();
|
||||
assert!(value.same_value(three_val));
|
||||
}
|
||||
}
|
||||
{
|
||||
let scope = &mut v8::HandleScope::new(isolate);
|
||||
let context = v8::Context::from_snapshot(scope, 0).unwrap();
|
||||
let scope = &mut v8::ContextScope::new(scope, context);
|
||||
{
|
||||
let value =
|
||||
eval(scope, "globalThis.__bootstrap.defaultContextProp").unwrap();
|
||||
assert!(value.is_undefined());
|
||||
}
|
||||
{
|
||||
let value =
|
||||
eval(scope, "globalThis.__bootstrap.defaultContextProp2").unwrap();
|
||||
assert!(value.is_undefined());
|
||||
}
|
||||
{
|
||||
let value = eval(scope, "globalThis.__bootstrap.context0Prop").unwrap();
|
||||
let two_val = v8::Number::new(scope, 2.0).into();
|
||||
assert!(value.same_value(two_val));
|
||||
}
|
||||
{
|
||||
let value =
|
||||
eval(scope, "globalThis.__bootstrap.context0Prop2").unwrap();
|
||||
let four_val = v8::Number::new(scope, 4.0).into();
|
||||
assert!(value.same_value(four_val));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn external_references() {
|
||||
let _setup_guard = setup();
|
||||
|
@ -5350,14 +5513,10 @@ fn module_snapshot() {
|
|||
|
||||
let true_val = v8::Boolean::new(scope, true).into();
|
||||
|
||||
let source = v8::String::new(scope, "a === 3").unwrap();
|
||||
let script = v8::Script::compile(scope, source, None).unwrap();
|
||||
let result = script.run(scope).unwrap();
|
||||
let result = eval(scope, "a === 3").unwrap();
|
||||
assert!(result.same_value(true_val));
|
||||
|
||||
let source = v8::String::new(scope, "b === 42").unwrap();
|
||||
let script = v8::Script::compile(scope, source, None).unwrap();
|
||||
let result = script.run(scope).unwrap();
|
||||
let result = eval(scope, "b === 42").unwrap();
|
||||
assert!(result.same_value(true_val));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue