Skip to content

Commit

Permalink
Add winterjs core and node:async_hooks modules
Browse files Browse the repository at this point in the history
  • Loading branch information
Arshia001 committed Feb 5, 2024
1 parent 63c839f commit ab366c3
Show file tree
Hide file tree
Showing 16 changed files with 836 additions and 39 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ __pycache__
.vscode
.DS_Store

**/.wrangler
**/.wrangler

**/node_modules
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use std::process::Command;

fn main() {
let mut dir = std::env::current_dir().unwrap();
dir.extend(["src", "ion_runner", "internal_js_modules"]);
assert!(Command::new("npx")
.arg("tsc")
.current_dir(dir)
.output()
.unwrap()
.status
.success());
}
3 changes: 3 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#! /bin/sh

set -euo pipefail
set -x

# Note: cargo-wasix automatically runs wasm-opt with -O2, which makes the resulting binary unusable.
# Instead, we use the toolchain to build (cargo +wasix instead of cargo wasix) and optimize manually.
cargo +wasix build --target wasm32-wasmer-wasi -r
Expand Down
25 changes: 25 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"devDependencies": {
"typescript": "^5.3.3"
}
}
4 changes: 4 additions & 0 deletions src/builtins/core/core.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const getPromiseState = ________winterjs_core_Internal______.getPromiseState;
export const setPromiseHooks = ________winterjs_core_Internal______.setPromiseHooks;

export default Object.freeze(________winterjs_core_Internal______);
125 changes: 125 additions & 0 deletions src/builtins/core/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
use std::{cell::RefCell, os::raw::c_void};

use ion::{function_spec, Context, Function, Local, Object, Promise, Result, TracedHeap, Value};
use mozjs::{
glue::{CreatePromiseLifecycleCallbacks, PromiseLifecycleTraps},
jsapi::{Handle, SetPromiseLifecycleCallbacks},
};
use mozjs_sys::jsapi::{JSContext, JSFunction, JSFunctionSpec, JSObject};
use runtime::modules::NativeModule;

use crate::ion_mk_err;

thread_local! {
static CALLBACKS_REGISTERED: RefCell<bool> = RefCell::new(false);
static INIT: RefCell<Option<TracedHeap<*mut JSFunction>>> = RefCell::new(None);
static BEFORE: RefCell<Option<TracedHeap<*mut JSFunction>>> = RefCell::new(None);
static AFTER: RefCell<Option<TracedHeap<*mut JSFunction>>> = RefCell::new(None);
static RESOLVE: RefCell<Option<TracedHeap<*mut JSFunction>>> = RefCell::new(None);
}

static TRAPS: PromiseLifecycleTraps = PromiseLifecycleTraps {
onNewPromise: Some(on_new_promise),
onBeforePromiseReaction: Some(on_before_promise_reaction),
onAfterPromiseReaction: Some(on_after_promise_reaction),
onPromiseSettled: Some(on_promise_settled),
};

#[js_fn]
fn get_promise_state(cx: &Context, promise: Object) -> Result<i32> {
let promise = Promise::from(promise.into_local())
.ok_or_else(|| ion_mk_err!("The given object is not a promise", Type))?;
Ok(promise.state(cx) as i32)
}

#[js_fn]
fn set_promise_hooks(
cx: &Context,
init: Function,
before: Function,
after: Function,
resolve: Function,
) {
CALLBACKS_REGISTERED.with(|c| {
if !*c.borrow() {
unsafe {
SetPromiseLifecycleCallbacks(
cx.as_ptr(),
CreatePromiseLifecycleCallbacks(&TRAPS, std::ptr::null()),
)
};
*c.borrow_mut() = true;
}
});

INIT.set(Some(TracedHeap::from_local(&init)));
BEFORE.set(Some(TracedHeap::from_local(&before)));
AFTER.set(Some(TracedHeap::from_local(&after)));
RESOLVE.set(Some(TracedHeap::from_local(&resolve)));
}

fn call_handler(
handler: &'static std::thread::LocalKey<RefCell<Option<TracedHeap<*mut JSFunction>>>>,
cx: *mut JSContext,
promise: Handle<*mut JSObject>,
) {
let cx = unsafe { Context::new_unchecked(cx) };
if let Some(f) = handler.with(|f| f.borrow().as_ref().map(|f| f.root(&cx))) {
let promise = Value::object(&cx, &unsafe { Local::from_marked(promise.ptr) }.into());
if let Err(e) = Function::from(f).call(&cx, &Object::null(&cx), &[promise]) {
tracing::error!("Promise hook callback failed with {e:?}");
}
}
}

unsafe extern "C" fn on_new_promise(
_: *const c_void,
cx: *mut JSContext,
promise: Handle<*mut JSObject>,
) {
call_handler(&INIT, cx, promise);
}

unsafe extern "C" fn on_before_promise_reaction(
_: *const c_void,
cx: *mut JSContext,
promise: Handle<*mut JSObject>,
) {
call_handler(&BEFORE, cx, promise);
}

unsafe extern "C" fn on_after_promise_reaction(
_: *const c_void,
cx: *mut JSContext,
promise: Handle<*mut JSObject>,
) {
call_handler(&AFTER, cx, promise);
}

unsafe extern "C" fn on_promise_settled(
_: *const c_void,
cx: *mut JSContext,
promise: Handle<*mut JSObject>,
) {
call_handler(&RESOLVE, cx, promise);
}

const METHODS: &[JSFunctionSpec] = &[
function_spec!(get_promise_state, "getPromiseState", 1),
function_spec!(set_promise_hooks, "setPromiseHooks", 4),
JSFunctionSpec::ZERO,
];

#[derive(Default)]
pub struct CoreModule;

impl NativeModule for CoreModule {
const NAME: &'static str = "__winterjs_core_";

const SOURCE: &'static str = include_str!("core.js");

fn module(cx: &Context) -> Option<Object> {
let mut ret = Object::new(cx);
unsafe { ret.define_methods(cx, METHODS) }.then_some(ret)
}
}
4 changes: 0 additions & 4 deletions src/builtins/crypto/crypto.js

This file was deleted.

32 changes: 8 additions & 24 deletions src/builtins/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use mozjs::{
};
use mozjs_sys::jsapi::{JSFunctionSpec, JSObject, JS_InstanceOf};
use rand::RngCore;
use runtime::modules::NativeModule;

#[js_fn]
fn get_random_values(cx: &Context, array: ArrayBufferView) -> Result<*mut JSObject> {
Expand Down Expand Up @@ -52,30 +51,15 @@ const METHODS: &[JSFunctionSpec] = &[
JSFunctionSpec::ZERO,
];

#[derive(Default)]
pub struct CryptoModule;

impl NativeModule for CryptoModule {
const NAME: &'static str = "crypto";

const SOURCE: &'static str = include_str!("crypto.js");

fn module(cx: &ion::Context) -> Option<ion::Object> {
let mut ret = Object::new(cx);

let subtle = Object::new(cx);
ret.set(cx, "subtle", &subtle.as_value(cx));

if (unsafe { ret.define_methods(cx, METHODS) } && subtle::define(cx, subtle)) {
Some(ret)
} else {
None
}
}
}

pub fn define(cx: &Context, global: &mut ion::Object) -> bool {
subtle::crypto_key::CryptoKey::init_class(cx, global).0
let mut crypto = Object::new(cx);
let subtle = Object::new(cx);

crypto.set(cx, "subtle", &subtle.as_value(cx))
&& global.set(cx, "crypto", &ion::Value::object(cx, &crypto))
&& subtle::define(cx, subtle)
&& subtle::crypto_key::CryptoKey::init_class(cx, global).0
&& subtle::crypto_key::KeyAlgorithm::init_class(cx, global).0
&& subtle::algorithm::hmac::HmacKeyAlgorithm::init_class(cx, global).0
&& unsafe { crypto.define_methods(cx, METHODS) }
}
1 change: 1 addition & 0 deletions src/builtins/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod cache;
pub mod core;
pub mod crypto;
pub mod event_listener;
pub mod fetch_event;
Expand Down
12 changes: 12 additions & 0 deletions src/ion_runner/internal_js_modules/__types/__winterjs_core_.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
type PromiseHook = (promise: Promise<unknown>) => void;

declare module "__winterjs_core_" {
function getPromiseState(promise: Promise<unknown>): number;

function setPromiseHooks(
init: PromiseHook,
before: PromiseHook,
after: PromiseHook,
resolve: PromiseHook
): void;
}
Loading

0 comments on commit ab366c3

Please sign in to comment.