diff --git a/src/binding.cc b/src/binding.cc index de927bf7..4e04e01f 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -33,6 +33,9 @@ static_assert(sizeof(v8::ReturnValue) == sizeof(size_t) * 1, static_assert(sizeof(v8::TryCatch) == sizeof(size_t) * 6, "TryCatch size mismatch"); +static_assert(sizeof(v8::Location) == sizeof(size_t) * 1, + "Location size mismatch"); + extern "C" { void v8__V8__SetFlagsFromCommandLine(int* argc, char** argv) { @@ -729,9 +732,48 @@ v8_inspector::StringBuffer* v8_inspector__StringBuffer__create( return v8_inspector::StringBuffer::create(source).release(); } +int v8__Location__GetLineNumber(v8::Location& self) { + return self.GetLineNumber(); +} + +int v8__Location__GetColumnNumber(v8::Location& self) { + return self.GetColumnNumber(); +} + +v8::Module::Status v8__Module__GetStatus(const v8::Module& self) { + return self.GetStatus(); +} + +v8::Value* v8__Module__GetException(const v8::Module& self) { + return local_to_ptr(self.GetException()); +} + +int v8__Module__GetModuleRequestsLength(const v8::Module& self) { + return self.GetModuleRequestsLength(); +} + +v8::String* v8__Module__GetModuleRequest(const v8::Module& self, int i) { + return local_to_ptr(self.GetModuleRequest(i)); +} + +void v8__Module__GetModuleRequestLocation(const v8::Module& self, int i, + v8::Location* out) { + *out = self.GetModuleRequestLocation(i); +} + +int v8__Module__GetIdentityHash(const v8::Module& self) { + return self.GetIdentityHash(); +} + MaybeBool v8__Module__InstantiateModule(v8::Module& self, v8::Local context, v8::Module::ResolveCallback callback) { return maybe_to_maybe_bool(self.InstantiateModule(context, callback)); } + +v8::Value* v8__Module__Evaluate(v8::Module& self, + v8::Local context) { + return maybe_local_to_ptr(self.Evaluate(context)); +} + } // extern "C" diff --git a/src/lib.rs b/src/lib.rs index b3a9d826..b4c186ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,7 @@ pub use isolate::OwnedIsolate; pub use local::Local; pub use locker::Locker; pub use module::Module; +pub use module::ModuleStatus; pub use number::{Integer, Number}; pub use object::Object; pub use primitive_array::PrimitiveArray; diff --git a/src/module.rs b/src/module.rs index 578f4c1b..09c7cbe5 100644 --- a/src/module.rs +++ b/src/module.rs @@ -5,17 +5,48 @@ use crate::Context; use crate::Local; use crate::String; use crate::Value; +use std::mem::MaybeUninit; -// Ideally the return value would be Option>... but not FFI-safe type ResolveCallback = extern "C" fn(Local, Local, Local) -> *mut Module; extern "C" { + fn v8__Module__GetStatus(this: *const Module) -> ModuleStatus; + fn v8__Module__GetException(this: *const Module) -> *mut Value; + fn v8__Module__GetModuleRequestsLength(this: *const Module) -> int; + fn v8__Module__GetModuleRequest(this: *const Module, i: usize) + -> *mut String; + fn v8__Module__GetModuleRequestLocation( + this: *const Module, + i: usize, + out: &mut MaybeUninit, + ) -> Location; + fn v8__Module__GetIdentityHash(this: *const Module) -> int; fn v8__Module__InstantiateModule( this: *mut Module, context: Local, callback: ResolveCallback, ) -> MaybeBool; + fn v8__Module__Evaluate( + this: *mut Module, + context: *mut Context, + ) -> *mut Value; + fn v8__Location__GetLineNumber(this: &Location) -> int; + fn v8__Location__GetColumnNumber(this: &Location) -> int; +} + +#[repr(C)] +/// A location in JavaScript source. +pub struct Location([usize; 1]); + +impl Location { + pub fn get_line_number(&self) -> int { + unsafe { v8__Location__GetLineNumber(self) } + } + + pub fn get_column_number(&self) -> int { + unsafe { v8__Location__GetColumnNumber(self) } + } } /// The different states a module can be in. @@ -25,7 +56,7 @@ extern "C" { /// respectively. #[derive(Debug, PartialEq)] #[repr(C)] -pub enum Status { +pub enum ModuleStatus { Uninstantiated, Instantiating, Instantiated, @@ -40,29 +71,39 @@ pub struct Module(Opaque); /// A compiled JavaScript module. impl Module { /// Returns the module's current status. - pub fn get_status(&self) -> Status { - unimplemented!(); + pub fn get_status(&self) -> ModuleStatus { + unsafe { v8__Module__GetStatus(self) } } /// For a module in kErrored status, this returns the corresponding exception. pub fn get_exception(&self) -> Local { - unimplemented!(); + unsafe { Local::from_raw(v8__Module__GetException(self)).unwrap() } } /// Returns the number of modules requested by this module. pub fn get_module_requests_length(&self) -> int { - unimplemented!(); + unsafe { v8__Module__GetModuleRequestsLength(self) } } /// Returns the ith module specifier in this module. /// i must be < self.get_module_requests_length() and >= 0. - pub fn get_module_request(&self, _i: usize) -> Local { - unimplemented!(); + pub fn get_module_request(&self, i: usize) -> Local { + unsafe { Local::from_raw(v8__Module__GetModuleRequest(self, i)).unwrap() } + } + + /// Returns the source location (line number and column number) of the ith + /// module specifier's first occurrence in this module. + pub fn get_module_request_location(&self, i: usize) -> Location { + let mut out = MaybeUninit::::uninit(); + unsafe { + v8__Module__GetModuleRequestLocation(self, i, &mut out); + out.assume_init() + } } /// Returns the identity hash for this object. pub fn get_identity_hash(&self) -> int { - unimplemented!(); + unsafe { v8__Module__GetIdentityHash(self) } } /// Instantiates the module and its dependencies. @@ -86,7 +127,10 @@ impl Module { /// kErrored and propagate the thrown exception (which is then also available /// via |GetException|). #[must_use] - pub fn evaluate(&self, _context: Local) -> Option> { - unimplemented!(); + pub fn evaluate( + &mut self, + mut context: Local, + ) -> Option> { + unsafe { Local::from_raw(v8__Module__Evaluate(&mut *self, &mut *context)) } } } diff --git a/tests/test_api.rs b/tests/test_api.rs index 7a9ba068..1d541862 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -715,8 +715,8 @@ fn mock_script_origin<'sc>( scope: &mut HandleScope<'sc>, ) -> v8::ScriptOrigin<'sc> { let resource_name = v8_str(scope, "foo.js"); - let resource_line_offset = v8::Integer::new(scope, 4); - let resource_column_offset = v8::Integer::new(scope, 5); + let resource_line_offset = v8::Integer::new(scope, 0); + let resource_column_offset = v8::Integer::new(scope, 0); let resource_is_shared_cross_origin = v8::new_true(scope); let script_id = v8::Integer::new(scope, 123); let source_map_url = v8_str(scope, "source_map_url"); @@ -769,6 +769,61 @@ fn script_compiler_source() { drop(g); } +#[test] +fn module_instantiation_failures1() { + let g = setup(); + let mut params = v8::Isolate::create_params(); + params.set_array_buffer_allocator(v8::Allocator::new_default_allocator()); + let mut isolate = v8::Isolate::new(params); + isolate.enter(); + let mut locker = v8::Locker::new(&isolate); + v8::HandleScope::enter(&mut locker, |scope| { + let mut context = v8::Context::new(scope); + context.enter(); + + let source_text = v8_str( + scope, + "import './foo.js';\n\ + export {} from './bar.js';", + ); + let origin = mock_script_origin(scope); + let source = v8::script_compiler::Source::new(source_text, &origin); + + let module = v8::script_compiler::compile_module( + &isolate, + source, + v8::script_compiler::CompileOptions::NoCompileOptions, + v8::script_compiler::NoCacheReason::NoReason, + ) + .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()); + + // TODO(ry) Instantiation should fail. + + context.exit(); + }); + drop(locker); + isolate.exit(); + drop(g); +} + #[test] fn primitive_array() { let g = setup();