mirror of
https://github.com/denoland/rusty_v8.git
synced 2024-12-26 00:59:28 -05:00
Support dynamic import (#136)
This commit is contained in:
parent
0303984286
commit
ce38f674f7
4 changed files with 160 additions and 27 deletions
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue