0
0
Fork 0
mirror of https://github.com/denoland/rusty_v8.git synced 2024-11-21 15:04:33 -05:00

Add support for import assertions (#598)

This commit adds support for import assertions.

Major changes include:
- removal of "ResolveCallback" (deprecated in V8) 
in favor of "ModuleResolveCallback"
- removal of "HostImportModuleDynamicallyCallback" (deprecated in V8)
in favor of "HostImportModuleDynamicallyWithImportAssertionsCallback"
This commit is contained in:
Bartek Iwańczuk 2021-02-05 00:22:26 +01:00 committed by GitHub
parent 1fb0e9436c
commit d6be279a06
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 194 additions and 41 deletions

View file

@ -73,19 +73,24 @@ enum InternalSlots {
(isolate->GetNumberOfDataSlots() - 1 - slot)
// This is an extern C calling convention compatible version of
// v8::HostImportModuleDynamicallyCallback
typedef v8::Promise* (*v8__HostImportModuleDynamicallyCallback)(
// v8::HostImportModuleDynamicallyWithImportAssertionsCallback
typedef v8::Promise* (
*v8__HostImportModuleDynamicallyWithImportAssertionsCallback)(
v8::Local<v8::Context> context, v8::Local<v8::ScriptOrModule> referrer,
v8::Local<v8::String> specifier);
v8::Local<v8::String> specifier,
v8::Local<v8::FixedArray> import_assertions);
v8::MaybeLocal<v8::Promise> HostImportModuleDynamicallyCallback(
v8::MaybeLocal<v8::Promise>
HostImportModuleDynamicallyWithImportAssertionsCallback(
v8::Local<v8::Context> context, v8::Local<v8::ScriptOrModule> referrer,
v8::Local<v8::String> specifier) {
v8::Local<v8::String> specifier,
v8::Local<v8::FixedArray> import_assertions) {
auto* isolate = context->GetIsolate();
void* d = isolate->GetData(SLOT_INTERNAL(isolate, kSlotDynamicImport));
auto* callback = reinterpret_cast<v8__HostImportModuleDynamicallyCallback>(d);
auto* callback = reinterpret_cast<
v8__HostImportModuleDynamicallyWithImportAssertionsCallback>(d);
assert(callback != nullptr);
auto* promise_ptr = callback(context, referrer, specifier);
auto* promise_ptr = callback(context, referrer, specifier, import_assertions);
if (promise_ptr == nullptr) {
return v8::MaybeLocal<v8::Promise>();
} else {
@ -221,11 +226,12 @@ void v8__Isolate__SetHostInitializeImportMetaObjectCallback(
}
void v8__Isolate__SetHostImportModuleDynamicallyCallback(
v8::Isolate* isolate, v8__HostImportModuleDynamicallyCallback callback) {
v8::Isolate* isolate,
v8__HostImportModuleDynamicallyWithImportAssertionsCallback callback) {
isolate->SetData(SLOT_INTERNAL(isolate, kSlotDynamicImport),
reinterpret_cast<void*>(callback));
isolate->SetHostImportModuleDynamicallyCallback(
HostImportModuleDynamicallyCallback);
HostImportModuleDynamicallyWithImportAssertionsCallback);
}
bool v8__Isolate__AddMessageListener(v8::Isolate* isolate,
@ -614,6 +620,13 @@ const v8::Boolean* v8__Boolean__New(v8::Isolate* isolate, bool value) {
return local_to_ptr(v8::Boolean::New(isolate, value));
}
int v8__FixedArray__Length(const v8::FixedArray& self) { return self.Length(); }
const v8::Data* v8__FixedArray__Get(const v8::FixedArray& self,
const v8::Context& context, int index) {
return local_to_ptr(ptr_to_local(&self)->Get(ptr_to_local(&context), index));
}
const v8::PrimitiveArray* v8__PrimitiveArray__New(v8::Isolate* isolate,
int length) {
return local_to_ptr(v8::PrimitiveArray::New(isolate, length));
@ -1560,18 +1573,15 @@ const v8::Value* v8__Script__Run(const v8::Script& script,
}
void v8__ScriptOrigin__CONSTRUCT(
v8::Isolate* isolate,
uninit_t<v8::ScriptOrigin>* buf, const v8::Value& resource_name,
int resource_line_offset, int resource_column_offset,
bool resource_is_shared_cross_origin, int script_id,
const v8::Value& source_map_url,
bool resource_is_opaque, bool is_wasm, bool is_module) {
v8::Isolate* isolate, uninit_t<v8::ScriptOrigin>* buf,
const v8::Value& resource_name, int resource_line_offset,
int resource_column_offset, bool resource_is_shared_cross_origin,
int script_id, const v8::Value& source_map_url, bool resource_is_opaque,
bool is_wasm, bool is_module) {
construct_in_place<v8::ScriptOrigin>(
buf, isolate, ptr_to_local(&resource_name),
resource_line_offset, resource_column_offset,
resource_is_shared_cross_origin, script_id,
ptr_to_local(&source_map_url),
resource_is_opaque, is_wasm, is_module);
buf, isolate, ptr_to_local(&resource_name), resource_line_offset,
resource_column_offset, resource_is_shared_cross_origin, script_id,
ptr_to_local(&source_map_url), resource_is_opaque, is_wasm, is_module);
}
const v8::Value* v8__ScriptOrModule__GetResourceName(
@ -2055,7 +2065,7 @@ int v8__Module__ScriptId(const v8::Module& self) {
MaybeBool v8__Module__InstantiateModule(const v8::Module& self,
const v8::Context& context,
v8::Module::ResolveCallback cb) {
v8::Module::ResolveModuleCallback cb) {
return maybe_to_maybe_bool(
ptr_to_local(&self)->InstantiateModule(ptr_to_local(&context), cb));
}

View file

@ -356,6 +356,17 @@ impl_eq! { for Module }
impl_partial_eq! { Data for Module use identity }
impl_partial_eq! { Module for Module use identity }
/// A fixed-sized array with elements of type Data.
#[repr(C)]
#[derive(Debug)]
pub struct FixedArray(Opaque);
impl_deref! { Data for FixedArray }
impl_eq! { for FixedArray }
impl_hash! { for FixedArray }
impl_partial_eq! { Data for FixedArray use identity }
impl_partial_eq! { FixedArray for FixedArray use identity }
/// An array to hold Primitive values. This is used by the embedder to
/// pass host defined options to the ScriptOptions during compilation.
///

39
src/fixed_array.rs Normal file
View file

@ -0,0 +1,39 @@
// Copyright 2019-2020 the Deno authors. All rights reserved. MIT license.
use crate::support::int;
use crate::Context;
use crate::Data;
use crate::FixedArray;
use crate::HandleScope;
use crate::Local;
extern "C" {
fn v8__FixedArray__Length(this: *const FixedArray) -> int;
fn v8__FixedArray__Get(
this: *const FixedArray,
context: *const Context,
index: int,
) -> *const Data;
}
impl FixedArray {
pub fn length(&self) -> usize {
unsafe { v8__FixedArray__Length(self) as usize }
}
pub fn get<'s>(
&self,
scope: &mut HandleScope<'s>,
index: usize,
) -> Option<Local<'s, Data>> {
if index >= self.length() {
return None;
}
unsafe {
scope.cast_local(|sd| {
v8__FixedArray__Get(self, &*sd.get_current_context(), index as int)
})
}
}
}

View file

@ -11,6 +11,7 @@ use crate::support::UnitType;
use crate::wasm::trampoline;
use crate::wasm::WasmStreaming;
use crate::Context;
use crate::FixedArray;
use crate::Function;
use crate::Local;
use crate::Message;
@ -90,7 +91,7 @@ pub type PromiseRejectCallback = extern "C" fn(PromiseRejectMessage);
pub type HostInitializeImportMetaObjectCallback =
extern "C" fn(Local<Context>, Local<Module>, Local<Object>);
/// HostImportModuleDynamicallyCallback is called when we require the
/// HostImportModuleDynamicallyWithImportAssertionsCallback is called when we require the
/// embedder to load a module. This is used as part of the dynamic
/// import syntax.
///
@ -108,11 +109,13 @@ pub type HostInitializeImportMetaObjectCallback =
/// this promise with the exception. If the promise creation itself
/// fails (e.g. due to stack overflow), the embedder must propagate
/// that exception by returning an empty MaybeLocal.
pub type HostImportModuleDynamicallyCallback = extern "C" fn(
pub type HostImportModuleDynamicallyWithImportAssertionsCallback =
extern "C" fn(
Local<Context>,
Local<ScriptOrModule>,
Local<String>,
) -> *mut Promise;
Local<FixedArray>,
) -> *mut Promise;
pub type InterruptCallback =
extern "C" fn(isolate: &mut Isolate, data: *mut c_void);
@ -180,7 +183,7 @@ extern "C" {
);
fn v8__Isolate__SetHostImportModuleDynamicallyCallback(
isolate: *mut Isolate,
callback: HostImportModuleDynamicallyCallback,
callback: HostImportModuleDynamicallyWithImportAssertionsCallback,
);
fn v8__Isolate__RequestInterrupt(
isolate: *const Isolate,
@ -507,7 +510,7 @@ impl Isolate {
/// import() language feature to load modules.
pub fn set_host_import_module_dynamically_callback(
&mut self,
callback: HostImportModuleDynamicallyCallback,
callback: HostImportModuleDynamicallyWithImportAssertionsCallback,
) {
unsafe {
v8__Isolate__SetHostImportModuleDynamicallyCallback(self, callback)

View file

@ -192,6 +192,7 @@ pub(crate) mod raw {
pub embedder_wrapper_type_index: int,
pub embedder_wrapper_object_index: int,
pub cpp_heap_params: SharedPtr<CppHeapCreateParams>,
// NOTE(bartlomieju): this field is deprecated in V8 API.
// This is an std::vector<std::string>. It's usually no bigger
// than three or four words but let's take a generous upper bound.
pub supported_import_assertions: [usize; 8],

View file

@ -42,6 +42,7 @@ mod date;
mod exception;
mod external;
mod external_references;
mod fixed_array;
mod function;
mod handle;
mod isolate;
@ -92,7 +93,7 @@ pub use handle::Global;
pub use handle::Handle;
pub use handle::Local;
pub use isolate::HeapStatistics;
pub use isolate::HostImportModuleDynamicallyCallback;
pub use isolate::HostImportModuleDynamicallyWithImportAssertionsCallback;
pub use isolate::HostInitializeImportMetaObjectCallback;
pub use isolate::Isolate;
pub use isolate::IsolateHandle;

View file

@ -11,6 +11,7 @@ use crate::support::MaybeBool;
use crate::support::ToCFn;
use crate::support::UnitType;
use crate::Context;
use crate::FixedArray;
use crate::HandleScope;
use crate::Isolate;
use crate::Local;
@ -19,7 +20,7 @@ use crate::String;
use crate::Value;
/// Called during Module::instantiate_module. Provided with arguments:
/// (context, specifier, referrer). Return None on error.
/// (context, specifier, import_assertions, referrer). Return None on error.
///
/// Note: this callback has an unusual signature due to ABI incompatibilities
/// between Rust and C++. However end users can implement the callback as
@ -29,6 +30,7 @@ use crate::Value;
/// fn my_resolve_callback<'a>(
/// context: v8::Local<'a, v8::Context>,
/// specifier: v8::Local<'a, v8::String>,
/// import_assertions: v8::Local<'a, v8::FixedArray>,
/// referrer: v8::Local<'a, v8::Module>,
/// ) -> Option<v8::Local<'a, v8::Module>> {
/// // ...
@ -38,34 +40,37 @@ use crate::Value;
// System V AMD64 ABI: Local<Module> returned in a register.
#[cfg(not(target_os = "windows"))]
pub type ResolveCallback<'a> = extern "C" fn(
pub type ResolveModuleCallback<'a> = extern "C" fn(
Local<'a, Context>,
Local<'a, String>,
Local<'a, FixedArray>,
Local<'a, Module>,
) -> *const Module;
// Windows x64 ABI: Local<Module> returned on the stack.
#[cfg(target_os = "windows")]
pub type ResolveCallback<'a> = extern "C" fn(
pub type ResolveModuleCallback<'a> = extern "C" fn(
*mut *const Module,
Local<'a, Context>,
Local<'a, String>,
Local<'a, FixedArray>,
Local<'a, Module>,
) -> *mut *const Module;
impl<'a, F> MapFnFrom<F> for ResolveCallback<'a>
impl<'a, F> MapFnFrom<F> for ResolveModuleCallback<'a>
where
F: UnitType
+ Fn(
Local<'a, Context>,
Local<'a, String>,
Local<'a, FixedArray>,
Local<'a, Module>,
) -> Option<Local<'a, Module>>,
{
#[cfg(not(target_os = "windows"))]
fn mapping() -> Self {
let f = |context, specifier, referrer| {
(F::get())(context, specifier, referrer)
let f = |context, specifier, import_assertions, referrer| {
(F::get())(context, specifier, import_assertions, referrer)
.map(|r| -> *const Module { &*r })
.unwrap_or(null())
};
@ -74,8 +79,8 @@ where
#[cfg(target_os = "windows")]
fn mapping() -> Self {
let f = |ret_ptr, context, specifier, referrer| {
let r = (F::get())(context, specifier, referrer)
let f = |ret_ptr, context, specifier, import_assertions, referrer| {
let r = (F::get())(context, specifier, import_assertions, referrer)
.map(|r| -> *const Module { &*r })
.unwrap_or(null());
unsafe { std::ptr::write(ret_ptr, r) }; // Write result to stack.
@ -144,7 +149,7 @@ extern "C" {
fn v8__Module__InstantiateModule(
this: *const Module,
context: *const Context,
cb: ResolveCallback,
cb: ResolveModuleCallback,
) -> MaybeBool;
fn v8__Module__Evaluate(
this: *const Module,
@ -283,7 +288,7 @@ impl Module {
pub fn instantiate_module<'a>(
&self,
scope: &mut HandleScope,
callback: impl MapFnTo<ResolveCallback<'a>>,
callback: impl MapFnTo<ResolveModuleCallback<'a>>,
) -> Option<bool> {
unsafe {
v8__Module__InstantiateModule(

View file

@ -34,7 +34,7 @@ fn setup() -> SetupGuard {
let mut g = INIT_LOCK.lock().unwrap();
*g += 1;
if *g == 1 {
v8::V8::set_flags_from_string("--expose_gc");
v8::V8::set_flags_from_string("--expose_gc --harmony-import-assertions");
v8::V8::initialize_platform(v8::new_default_platform().unwrap());
v8::V8::initialize();
}
@ -913,6 +913,7 @@ fn add_message_listener() {
fn unexpected_module_resolve_callback<'a>(
_context: v8::Local<'a, v8::Context>,
_specifier: v8::Local<'a, v8::String>,
_import_assertions: v8::Local<'a, v8::FixedArray>,
_referrer: v8::Local<'a, v8::Module>,
) -> Option<v8::Local<'a, v8::Module>> {
unreachable!()
@ -1980,6 +1981,7 @@ fn module_instantiation_failures1() {
fn resolve_callback<'a>(
context: v8::Local<'a, v8::Context>,
_specifier: v8::Local<'a, v8::String>,
_import_assertions: v8::Local<'a, v8::FixedArray>,
_referrer: v8::Local<'a, v8::Module>,
) -> Option<v8::Local<'a, v8::Module>> {
let scope = &mut unsafe { v8::CallbackScope::new(context) };
@ -2003,6 +2005,7 @@ fn module_instantiation_failures1() {
fn compile_specifier_as_module_resolve_callback<'a>(
context: v8::Local<'a, v8::Context>,
specifier: v8::Local<'a, v8::String>,
_import_assertions: v8::Local<'a, v8::FixedArray>,
_referrer: v8::Local<'a, v8::Module>,
) -> Option<v8::Local<'a, v8::Module>> {
let scope = &mut unsafe { v8::CallbackScope::new(context) };
@ -2053,6 +2056,85 @@ fn module_evaluation() {
}
}
#[test]
fn import_assertions() {
let _setup_guard = setup();
let isolate = &mut v8::Isolate::new(Default::default());
fn module_resolve_callback<'a>(
context: v8::Local<'a, v8::Context>,
_specifier: v8::Local<'a, v8::String>,
import_assertions: v8::Local<'a, v8::FixedArray>,
_referrer: v8::Local<'a, v8::Module>,
) -> Option<v8::Local<'a, v8::Module>> {
let scope = &mut unsafe { v8::CallbackScope::new(context) };
// "type" keyword, value and source offset of assertion
assert_eq!(import_assertions.length(), 3);
let assert1 = import_assertions.get(scope, 0).unwrap();
let assert1_val = v8::Local::<v8::Value>::try_from(assert1).unwrap();
assert_eq!(assert1_val.to_rust_string_lossy(scope), "type");
let assert2 = import_assertions.get(scope, 1).unwrap();
let assert2_val = v8::Local::<v8::Value>::try_from(assert2).unwrap();
assert_eq!(assert2_val.to_rust_string_lossy(scope), "json");
let assert3 = import_assertions.get(scope, 2).unwrap();
let assert3_val = v8::Local::<v8::Value>::try_from(assert3).unwrap();
assert_eq!(assert3_val.to_rust_string_lossy(scope), "27");
let origin = mock_script_origin(scope, "module.js");
let src = v8::String::new(scope, "export const a = 'a';").unwrap();
let source = v8::script_compiler::Source::new(src, &origin);
let module = v8::script_compiler::compile_module(scope, source).unwrap();
Some(module)
}
extern "C" fn dynamic_import_cb(
context: v8::Local<v8::Context>,
_referrer: v8::Local<v8::ScriptOrModule>,
_specifier: v8::Local<v8::String>,
import_assertions: v8::Local<v8::FixedArray>,
) -> *mut v8::Promise {
let scope = &mut unsafe { v8::CallbackScope::new(context) };
let scope = &mut v8::HandleScope::new(scope);
// "type" keyword, value
assert_eq!(import_assertions.length(), 2);
let assert1 = import_assertions.get(scope, 0).unwrap();
let assert1_val = v8::Local::<v8::Value>::try_from(assert1).unwrap();
assert_eq!(assert1_val.to_rust_string_lossy(scope), "type");
let assert2 = import_assertions.get(scope, 1).unwrap();
let assert2_val = v8::Local::<v8::Value>::try_from(assert2).unwrap();
assert_eq!(assert2_val.to_rust_string_lossy(scope), "json");
std::ptr::null_mut()
}
isolate.set_host_import_module_dynamically_callback(dynamic_import_cb);
{
let scope = &mut v8::HandleScope::new(isolate);
let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);
let source_text = v8::String::new(
scope,
"import 'foo.json' assert { type: \"json\" };\n\
import('foo.json', { assert: { type: 'json' } });",
)
.unwrap();
let origin = mock_script_origin(scope, "foo.js");
let source = v8::script_compiler::Source::new(source_text, &origin);
let module = v8::script_compiler::compile_module(scope, source).unwrap();
assert!(module.script_id().is_some());
assert!(module.is_source_text_module());
assert!(!module.is_synthetic_module());
assert_eq!(v8::ModuleStatus::Uninstantiated, module.get_status());
module.hash(&mut DefaultHasher::new()); // Should not crash.
let result = module.instantiate_module(scope, module_resolve_callback);
assert!(result.unwrap());
assert_eq!(v8::ModuleStatus::Instantiated, module.get_status());
}
}
#[test]
fn primitive_array() {
let _setup_guard = setup();
@ -2550,6 +2632,7 @@ fn dynamic_import() {
context: v8::Local<v8::Context>,
_referrer: v8::Local<v8::ScriptOrModule>,
specifier: v8::Local<v8::String>,
_import_assertions: v8::Local<v8::FixedArray>,
) -> *mut v8::Promise {
let scope = &mut unsafe { v8::CallbackScope::new(context) };
let scope = &mut v8::HandleScope::new(scope);