0
0
Fork 0
mirror of https://github.com/denoland/rusty_v8.git synced 2024-12-25 08:39:15 -05:00

Support dynamic import (#136)

This commit is contained in:
Ry Dahl 2019-12-26 10:45:55 -05:00 committed by GitHub
parent 0303984286
commit ce38f674f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 160 additions and 27 deletions

View file

@ -8,6 +8,8 @@
#include "v8/include/v8-platform.h"
#include "v8/include/v8.h"
const uint32_t kSlotDynamicImport = 1000;
using namespace support;
static_assert(sizeof(v8::ScriptOrigin) == sizeof(size_t) * 7,
@ -39,6 +41,27 @@ static_assert(sizeof(v8::Location) == sizeof(size_t) * 1,
static_assert(sizeof(v8::SnapshotCreator) == sizeof(size_t) * 1,
"SnapshotCreator size mismatch");
// This is an extern C calling convention compatible version of
// v8::HostImportModuleDynamicallyCallback
typedef v8::Promise* (*v8__HostImportModuleDynamicallyCallback)(
v8::Local<v8::Context> context, v8::Local<v8::ScriptOrModule> referrer,
v8::Local<v8::String> specifier);
v8::MaybeLocal<v8::Promise> HostImportModuleDynamicallyCallback(
v8::Local<v8::Context> context, v8::Local<v8::ScriptOrModule> referrer,
v8::Local<v8::String> specifier) {
auto* isolate = context->GetIsolate();
auto* callback = reinterpret_cast<v8__HostImportModuleDynamicallyCallback>(
isolate->GetData(kSlotDynamicImport));
assert(callback != nullptr);
auto* promise_ptr = callback(context, referrer, specifier);
if (promise_ptr == nullptr) {
return v8::MaybeLocal<v8::Promise>();
} else {
return v8::MaybeLocal<v8::Promise>(ptr_to_local(promise_ptr));
}
}
extern "C" {
void v8__V8__SetFlagsFromCommandLine(int* argc, char** argv) {
@ -102,6 +125,13 @@ void v8__Isolate__SetHostInitializeImportMetaObjectCallback(
isolate->SetHostInitializeImportMetaObjectCallback(callback);
}
void v8__Isolate__SetHostImportModuleDynamicallyCallback(
v8::Isolate* isolate, v8__HostImportModuleDynamicallyCallback callback) {
isolate->SetData(kSlotDynamicImport, reinterpret_cast<void*>(callback));
isolate->SetHostImportModuleDynamicallyCallback(
HostImportModuleDynamicallyCallback);
}
bool v8__Isolate__AddMessageListener(v8::Isolate& isolate,
v8::MessageCallback callback) {
return isolate.AddMessageListener(callback);
@ -546,8 +576,10 @@ void v8__TryCatch__SetCaptureMessage(v8::TryCatch& self, bool value) {
self.SetCaptureMessage(value);
}
v8::Uint8Array* v8__Uint8Array__New(v8::ArrayBuffer* buf_ptr, size_t byte_offset, size_t length) {
return local_to_ptr(v8::Uint8Array::New(ptr_to_local(buf_ptr), byte_offset, length));
v8::Uint8Array* v8__Uint8Array__New(v8::ArrayBuffer* buf_ptr,
size_t byte_offset, size_t length) {
return local_to_ptr(
v8::Uint8Array::New(ptr_to_local(buf_ptr), byte_offset, length));
}
v8::Script* v8__Script__Compile(v8::Context* context, v8::String* source,

View file

@ -9,20 +9,55 @@ use crate::Local;
use crate::Message;
use crate::Module;
use crate::Object;
use crate::Promise;
use crate::ScriptOrModule;
use crate::StartupData;
use crate::String;
use crate::Value;
use std::ffi::c_void;
use std::ops::Deref;
use std::ops::DerefMut;
use std::ptr::NonNull;
type MessageCallback = extern "C" fn(Local<Message>, Local<Value>);
pub type MessageCallback = extern "C" fn(Local<Message>, Local<Value>);
type PromiseRejectCallback = extern "C" fn(PromiseRejectMessage);
pub type PromiseRejectCallback = extern "C" fn(PromiseRejectMessage);
type HostInitializeImportMetaObjectCallback =
/// HostInitializeImportMetaObjectCallback is called the first time import.meta
/// is accessed for a module. Subsequent access will reuse the same value.
///
/// The method combines two implementation-defined abstract operations into one:
/// HostGetImportMetaProperties and HostFinalizeImportMeta.
///
/// The embedder should use v8::Object::CreateDataProperty to add properties on
/// the meta object.
pub type HostInitializeImportMetaObjectCallback =
extern "C" fn(Local<Context>, Local<Module>, Local<Object>);
/// HostImportModuleDynamicallyCallback is called when we require the
/// embedder to load a module. This is used as part of the dynamic
/// import syntax.
///
/// The referrer contains metadata about the script/module that calls
/// import.
///
/// The specifier is the name of the module that should be imported.
///
/// The embedder must compile, instantiate, evaluate the Module, and
/// obtain it's namespace object.
///
/// The Promise returned from this function is forwarded to userland
/// JavaScript. The embedder must resolve this promise with the module
/// namespace object. In case of an exception, the embedder must reject
/// 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(
Local<Context>,
Local<ScriptOrModule>,
Local<String>,
) -> *mut Promise;
extern "C" {
fn v8__Isolate__New(params: *mut CreateParams) -> *mut Isolate;
fn v8__Isolate__Dispose(this: *mut Isolate);
@ -48,6 +83,10 @@ extern "C" {
isolate: *mut Isolate,
callback: HostInitializeImportMetaObjectCallback,
);
fn v8__Isolate__SetHostImportModuleDynamicallyCallback(
isolate: *mut Isolate,
callback: HostImportModuleDynamicallyCallback,
);
fn v8__Isolate__ThrowException(
isolate: &Isolate,
exception: &Value,
@ -174,6 +213,17 @@ impl Isolate {
}
}
/// This specifies the callback called by the upcoming dynamic
/// import() language feature to load modules.
pub fn set_host_import_module_dynamically_callback(
&mut self,
callback: HostImportModuleDynamicallyCallback,
) {
unsafe {
v8__Isolate__SetHostImportModuleDynamicallyCallback(self, callback)
}
}
/// Schedules an exception to be thrown when returning to JavaScript. When an
/// exception has been scheduled it is illegal to invoke any JavaScript
/// operation; the caller must return immediately and only after the exception

View file

@ -55,7 +55,7 @@ pub use function::{
};
pub use global::Global;
pub use handle_scope::{EscapableHandleScope, HandleScope, ToLocal};
pub use isolate::{InIsolate, Isolate, OwnedIsolate};
pub use isolate::*;
pub use local::Local;
pub use locker::Locker;
pub use module::*;

View file

@ -5,6 +5,7 @@ extern crate lazy_static;
use rusty_v8 as v8;
use rusty_v8::{new_null, FunctionCallbackInfo, InIsolate, Local, ToLocal};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex;
lazy_static! {
@ -339,7 +340,6 @@ fn add_message_listener() {
let mut isolate = v8::Isolate::new(params);
isolate.set_capture_stack_trace_for_uncaught_exceptions(true, 32);
use std::sync::atomic::{AtomicUsize, Ordering};
static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
extern "C" fn check_message_0(
@ -389,7 +389,6 @@ fn set_host_initialize_import_meta_object_callback() {
params.set_array_buffer_allocator(v8::new_default_allocator());
let mut isolate = v8::Isolate::new(params);
use std::sync::atomic::{AtomicUsize, Ordering};
static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
extern "C" fn callback(
@ -791,25 +790,6 @@ fn promise_rejected() {
drop(locker);
}
extern "C" fn fn_callback(info: &FunctionCallbackInfo) {
assert_eq!(info.length(), 0);
{
let rv = &mut info.get_return_value();
#[allow(mutable_transmutes)]
#[allow(clippy::transmute_ptr_to_ptr)]
let info: &mut FunctionCallbackInfo = unsafe { std::mem::transmute(info) };
{
let mut hs = v8::HandleScope::new(info);
let scope = hs.enter();
let s = v8::String::new(scope, "Hello callback!").unwrap();
let value: Local<v8::Value> = s.into();
let rv_value = rv.get(scope);
assert!(rv_value.is_undefined());
rv.set(value);
}
}
}
#[test]
fn function() {
setup();
@ -817,6 +797,27 @@ fn function() {
params.set_array_buffer_allocator(v8::new_default_allocator());
let isolate = v8::Isolate::new(params);
let mut locker = v8::Locker::new(&isolate);
extern "C" fn fn_callback(info: &FunctionCallbackInfo) {
assert_eq!(info.length(), 0);
{
let rv = &mut info.get_return_value();
#[allow(mutable_transmutes)]
#[allow(clippy::transmute_ptr_to_ptr)]
let info: &mut FunctionCallbackInfo =
unsafe { std::mem::transmute(info) };
{
let mut hs = v8::HandleScope::new(info);
let scope = hs.enter();
let s = v8::String::new(scope, "Hello callback!").unwrap();
let value: Local<v8::Value> = s.into();
let rv_value = rv.get(scope);
assert!(rv_value.is_undefined());
rv.set(value);
}
}
}
{
let mut hs = v8::HandleScope::new(&mut locker);
let scope = hs.enter();
@ -1305,3 +1306,53 @@ fn uint8_array() {
isolate.exit();
drop(g);
}
#[test]
fn dynamic_import() {
let g = setup();
let mut params = v8::Isolate::create_params();
params.set_array_buffer_allocator(v8::new_default_allocator());
let mut isolate = v8::Isolate::new(params);
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);
isolate.enter();
let mut locker = v8::Locker::new(&isolate);
{
let mut hs = v8::HandleScope::new(&mut locker);
let s = hs.enter();
let mut context = v8::Context::new(s);
context.enter();
let result = eval(
s,
context,
"(async function () {\n\
let x = await import('bar.js');\n\
})();",
);
assert!(result.is_some());
assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 1);
context.exit();
}
drop(locker);
isolate.exit();
drop(g);
}