[quickjs][oden][oden-js] Source maps

The worst support but it should cost very little if nobody is using
them (psst we're using them)
This commit is contained in:
John Doty 2023-09-20 09:31:57 -07:00
parent eb9fed759a
commit c96a1a4979
10 changed files with 554 additions and 374 deletions

View file

@ -2,6 +2,7 @@ use crate::{ContextRef, Runtime, ValueResult};
use oden_js_sys as sys;
use std::ops::Deref;
#[derive(Eq, PartialEq, Debug, Hash)]
pub struct AtomRef {
pub(crate) atom: sys::JSAtom,
}
@ -39,6 +40,11 @@ impl Atom {
rt: Runtime::from_raw(unsafe { sys::JS_GetRuntime(ctx.ctx) }),
}
}
pub(crate) fn consume(atom: Atom) -> u32 {
let atom = std::mem::ManuallyDrop::new(atom);
atom.atom.atom
}
}
impl Deref for Atom {
@ -56,3 +62,17 @@ impl Drop for Atom {
}
}
}
impl PartialEq for Atom {
fn eq(&self, other: &Self) -> bool {
self.atom == other.atom
}
}
impl Eq for Atom {}
impl std::hash::Hash for Atom {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.atom.hash(state)
}
}

View file

@ -1,6 +1,5 @@
use super::ModuleRef;
use crate::{throw_error, ContextRef, Error, Result};
use oden_js_sys as sys;
use crate::{Atom, AtomRef, ContextRef, Error, Result};
use std::path::Path;
pub enum ModuleSource<'a> {
@ -9,7 +8,22 @@ pub enum ModuleSource<'a> {
}
pub trait ModuleLoader {
fn load(&self, context: &ContextRef, name: &str) -> Result<ModuleSource>;
fn load<'a>(&mut self, context: &'a ContextRef, name: &str) -> Result<ModuleSource<'a>>;
// Return true if this loader also supports source maps, otherwise false.
// NOTE: This must return `true` if you want to have a non-trivial
// implementation of `map_source`; if this returns `false` (the
// default) then your object will never even receive a map_source
// call.
fn support_source_map(&self) -> bool {
false
}
// Map a given file/line pair to the source file/line pair. This is only
// called if `support_source_map` returns true.
fn map_source(&self, context: &ContextRef, file: &AtomRef, line: i32) -> Result<(Atom, i32)> {
Ok((file.dup(context), line))
}
}
pub struct DefaultModuleLoader {}
@ -21,7 +35,7 @@ impl DefaultModuleLoader {
}
impl ModuleLoader for DefaultModuleLoader {
fn load(&self, _context: &ContextRef, name: &str) -> Result<ModuleSource> {
fn load<'a>(&mut self, _context: &'a ContextRef, name: &str) -> Result<ModuleSource<'a>> {
// Attempt to open the file.
let path = Path::new(name);
match std::fs::read_to_string(path) {
@ -30,24 +44,3 @@ impl ModuleLoader for DefaultModuleLoader {
}
}
}
pub(crate) fn load_module(
context: &ContextRef,
name: &str,
loader: &Box<dyn ModuleLoader>,
) -> *mut sys::JSModuleDef {
match loader.load(context, name) {
Ok(ModuleSource::Native(native)) => native.module,
Ok(ModuleSource::JavaScript(js)) => match context.eval_module(&js, name) {
Ok(v) => v.module,
Err(e) => {
throw_error(context, e);
return std::ptr::null_mut();
}
},
Err(e) => {
throw_error(context, e);
return std::ptr::null_mut();
}
}
}

View file

@ -8,7 +8,7 @@ pub mod native;
#[derive(Debug, Clone)]
pub struct ModuleRef {
module: *mut sys::JSModuleDef,
pub(crate) module: *mut sys::JSModuleDef,
}
impl ModuleRef {

View file

@ -1,8 +1,8 @@
use crate::{
module::loader::{load_module, DefaultModuleLoader, ModuleLoader},
module::loader::{DefaultModuleLoader, ModuleLoader, ModuleSource},
promise::{PromiseEvent, PromiseHandle},
ContextRef, DefaultRejectedPromiseTracker, Promise, RejectedPromiseTracker, Result, Value,
ValueRef,
throw_error, Atom, AtomRef, ContextRef, DefaultRejectedPromiseTracker, Promise,
RejectedPromiseTracker, Result, Value, ValueRef,
};
use oden_js_sys as sys;
use std::cell::{RefCell, RefMut};
@ -42,7 +42,7 @@ impl Drop for PromiseEntry {
struct PrivateState {
refs: u64,
loader: Arc<Box<dyn ModuleLoader>>,
loader: Arc<RefCell<Box<dyn ModuleLoader>>>,
promise_send: Sender<(PromiseHandle, PromiseEvent)>,
promise_recv: Receiver<(PromiseHandle, PromiseEvent)>,
promise_table: HashMap<PromiseHandle, PromiseEntry>, // !
@ -54,7 +54,7 @@ impl PrivateState {
let (send, recv) = channel();
Box::new(RefCell::new(PrivateState {
refs: 1,
loader: Arc::new(Box::new(DefaultModuleLoader::new())),
loader: Arc::new(RefCell::new(Box::new(DefaultModuleLoader::new()))),
promise_send: send,
promise_recv: recv,
promise_table: HashMap::new(),
@ -90,7 +90,64 @@ impl PrivateState {
.clone()
};
let context = ContextRef::from_raw(ctx);
load_module(&context, path, &loader)
let result = {
let mut loader = loader.borrow_mut();
loader.load(&context, path)
};
match result {
Ok(ModuleSource::Native(native)) => native.module,
Ok(ModuleSource::JavaScript(js)) => match context.eval_module(&js, path) {
Ok(v) => v.module,
Err(e) => {
throw_error(&context, e);
std::ptr::null_mut()
}
},
Err(e) => {
throw_error(&context, e);
std::ptr::null_mut()
}
}
}
unsafe extern "C" fn source_mapper(
ctx: *mut sys::JSContext,
opaque: *mut std::os::raw::c_void,
file: u32,
line: i32,
pfile: *mut u32,
pline: *mut i32,
) {
let ctx = ContextRef::from_raw(ctx);
let file = AtomRef::from_raw(file, &ctx);
let loader = unsafe {
let ptr = opaque as *const RefCell<PrivateState>;
ptr.as_ref()
.expect("We already know this runtime is one of ours!")
.borrow()
.loader
.clone()
};
let result = {
let loader = loader.borrow();
loader.map_source(&ctx, &file, line)
};
match result {
Ok((file, line)) => {
*pfile = Atom::consume(file);
*pline = line;
}
Err(e) => {
throw_error(&ctx, e);
*pfile = file.atom;
*pline = line;
}
}
}
unsafe extern "C" fn promise_rejection_tracker(
@ -171,7 +228,17 @@ impl Runtime {
T: ModuleLoader + 'static,
{
let mut state = unsafe { PrivateState::from_rt_mut(self.rt) };
state.loader = Arc::new(Box::new(loader));
if loader.support_source_map() {
unsafe {
let opaque = sys::JS_GetRuntimeOpaque(self.rt);
sys::JS_SetSourceMapFunc(self.rt, Some(PrivateState::source_mapper), opaque);
}
} else {
unsafe {
sys::JS_SetSourceMapFunc(self.rt, None, std::ptr::null_mut());
}
}
state.loader = Arc::new(RefCell::new(Box::new(loader)));
}
/// Set a tracker to be notified whenever a promise is rejected. By