diff --git a/examples/hello-world/package.json b/examples/hello-world/package.json index b30b59b..f9fd444 100644 --- a/examples/hello-world/package.json +++ b/examples/hello-world/package.json @@ -11,8 +11,8 @@ }, "dependencies": { "dayjs": "^1.11.10", - "react": "file://../../packages/react/pkg/react", - "react-dom": "file://../../packages/react-dom/pkg/react-dom", + "react": "file://../../dist/react", + "react-dom": "file://../../dist/react-dom", "vite-plugin-wasm": "^3.3.0" }, "devDependencies": { diff --git a/examples/hello-world/pnpm-lock.yaml b/examples/hello-world/pnpm-lock.yaml index b6ae9ec..35d363b 100644 --- a/examples/hello-world/pnpm-lock.yaml +++ b/examples/hello-world/pnpm-lock.yaml @@ -9,11 +9,11 @@ dependencies: specifier: ^1.11.10 version: 1.11.10 react: - specifier: file://../../packages/react/pkg/react - version: file:../../packages/react/pkg/react + specifier: file://../../dist/react + version: file:../../dist/react react-dom: - specifier: file://../../packages/react-dom/pkg/react-dom - version: file:../../packages/react-dom/pkg/react-dom + specifier: file://../../dist/react-dom + version: file:../../dist/react-dom vite-plugin-wasm: specifier: ^3.3.0 version: 3.3.0(vite@5.2.2) @@ -1839,12 +1839,12 @@ packages: engines: {node: '>=10'} dev: true - file:../../packages/react-dom/pkg/react-dom: - resolution: {directory: ../../packages/react-dom/pkg/react-dom, type: directory} - name: react-dom + file:../../dist/react: + resolution: {directory: ../../dist/react, type: directory} + name: react dev: false - file:../../packages/react/pkg/react: - resolution: {directory: ../../packages/react/pkg/react, type: directory} - name: react + file:../../dist/react-dom: + resolution: {directory: ../../dist/react-dom, type: directory} + name: react-dom dev: false diff --git a/examples/hello-world/src/App.tsx b/examples/hello-world/src/App.tsx index 6786e83..3a7c6a3 100644 --- a/examples/hello-world/src/App.tsx +++ b/examples/hello-world/src/App.tsx @@ -1,8 +1,12 @@ -import dayjs from 'dayjs' +import {useState} from 'react' function App() { + const [name, setName] = useState(() => 'ayou') + setTimeout(() => { + setName('ayouayou') + }, 1000) return ( -
{dayjs().format()}
+
{name}
) } diff --git a/package.json b/package.json index 9d26db3..e1aa8ff 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,7 @@ "example": "examples" }, "scripts": { - "build:react": "wasm-pack build packages/react --out-dir pkg/react --out-name jsx-dev-runtime", - "build:react-dom": "wasm-pack build packages/react-dom --out-dir pkg/react-dom --out-name index", - "build": "npm run build:react && npm run build:react-dom" + "build": "node scripts/build.js" }, "author": "", "license": "ISC" diff --git a/packages/react-dom/src/renderer.rs b/packages/react-dom/src/renderer.rs index 10d5d9d..57f4efa 100644 --- a/packages/react-dom/src/renderer.rs +++ b/packages/react-dom/src/renderer.rs @@ -3,7 +3,6 @@ use std::rc::Rc; use wasm_bindgen::JsValue; use wasm_bindgen::prelude::*; -use wasm_bindgen::prelude::wasm_bindgen; use react_reconciler::fiber::FiberRootNode; use react_reconciler::Reconciler; diff --git a/packages/react-reconciler/src/begin_work.rs b/packages/react-reconciler/src/begin_work.rs index 879d46d..ae7849a 100644 --- a/packages/react-reconciler/src/begin_work.rs +++ b/packages/react-reconciler/src/begin_work.rs @@ -6,8 +6,8 @@ use wasm_bindgen::JsValue; use shared::derive_from_js_value; use crate::child_fiber::{mount_child_fibers, reconcile_child_fibers}; -use crate::fiber::FiberNode; -use crate::fiber_hooks::FiberHooks; +use crate::fiber::{FiberNode, MemoizedState}; +use crate::fiber_hooks::render_with_hooks; use crate::update_queue::process_update_queue; use crate::work_tags::WorkTag; @@ -24,8 +24,7 @@ pub fn begin_work(work_in_progress: Rc>) -> Result>, ) -> Result>>, JsValue> { - let fiber_hooks = &mut FiberHooks::new(); - let next_children = Rc::new(fiber_hooks.render_with_hooks(work_in_progress.clone())?); + let next_children = Rc::new(render_with_hooks(work_in_progress.clone())?); reconcile_children(work_in_progress.clone(), Some(next_children)); Ok(work_in_progress.clone().borrow().child.clone()) } @@ -33,7 +32,12 @@ fn update_function_component( fn update_host_root(work_in_progress: Rc>) -> Option>> { process_update_queue(work_in_progress.clone()); let next_children = work_in_progress.clone().borrow().memoized_state.clone(); - reconcile_children(work_in_progress.clone(), next_children); + if next_children.is_none() { + panic!("update_host_root next_children is none") + } + if let MemoizedState::JsValue(next_children) = next_children.unwrap() { + reconcile_children(work_in_progress.clone(), Some(next_children)); + } work_in_progress.clone().borrow().child.clone() } diff --git a/packages/react-reconciler/src/child_fiber.rs b/packages/react-reconciler/src/child_fiber.rs index 63f220c..47b4ea6 100644 --- a/packages/react-reconciler/src/child_fiber.rs +++ b/packages/react-reconciler/src/child_fiber.rs @@ -81,7 +81,6 @@ fn _reconcile_child_fibers( } } todo!("Unsupported child type when reconcile"); - return None; } pub fn reconcile_child_fibers( diff --git a/packages/react-reconciler/src/commit_work.rs b/packages/react-reconciler/src/commit_work.rs index 6b5ac77..805b731 100644 --- a/packages/react-reconciler/src/commit_work.rs +++ b/packages/react-reconciler/src/commit_work.rs @@ -2,6 +2,8 @@ use std::any::Any; use std::cell::RefCell; use std::rc::Rc; +use shared::log; + use crate::fiber::{FiberNode, StateNode}; use crate::fiber_flags::{Flags, get_mutation_mask}; use crate::HostConfig; @@ -101,6 +103,7 @@ impl CommitWork { let fiber = fiber.clone(); let tag = fiber.borrow().tag.clone(); if tag == WorkTag::HostComponent || tag == WorkTag::HostText { + log!("{:?}", fiber.clone().borrow()._type); let state_node = fiber.clone().borrow().state_node.clone().unwrap(); self.host_config.append_child_to_container( self.get_element_from_state_node(state_node), diff --git a/packages/react-reconciler/src/fiber.rs b/packages/react-reconciler/src/fiber.rs index 7e33bed..10d640c 100644 --- a/packages/react-reconciler/src/fiber.rs +++ b/packages/react-reconciler/src/fiber.rs @@ -11,7 +11,8 @@ use web_sys::js_sys::Reflect; use shared::derive_from_js_value; use crate::fiber_flags::Flags; -use crate::update_queue::{Update, UpdateQueue, UpdateType}; +use crate::fiber_hooks::Hook; +use crate::update_queue::{Update, UpdateQueue}; use crate::work_tags::WorkTag; #[derive(Debug)] @@ -20,6 +21,12 @@ pub enum StateNode { Element(Rc), } +#[derive(Debug, Clone)] +pub enum MemoizedState { + JsValue(Rc), + Hook(Rc>), +} + #[derive(Debug)] pub struct FiberNode { pub tag: WorkTag, @@ -35,7 +42,7 @@ pub struct FiberNode { pub flags: Flags, pub subtree_flags: Flags, pub memoized_props: Option>, - pub memoized_state: Option>, + pub memoized_state: Option, } impl FiberNode { @@ -76,7 +83,7 @@ impl FiberNode { } pub fn enqueue_update(&mut self, update: Update) { - let mut update_queue = match &self.update_queue { + let update_queue = match &self.update_queue { None => { return; } @@ -87,14 +94,6 @@ impl FiberNode { u.shared.pending = Some(update); } - pub fn initialize_update_queue(&mut self) { - self.update_queue = Some(Rc::new(RefCell::new(UpdateQueue { - shared: UpdateType { - pending: Some(Update { action: None }), - }, - }))); - } - pub fn create_work_in_progress( current: Rc>, pending_props: Rc, @@ -131,7 +130,7 @@ impl FiberNode { wip.pending_props = Some(pending_props.clone()); wip.update_queue = Some(c.update_queue.as_ref().unwrap().clone()); wip.flags = c.flags.clone(); - wip.child = Some(Rc::clone(c.child.as_ref().unwrap())); + wip.child = c.child.clone(); wip.memoized_props = c.memoized_props.clone(); wip.memoized_state = c.memoized_state.clone(); w.clone().unwrap() @@ -169,7 +168,7 @@ impl FiberRootNode { impl Debug for FiberRootNode { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let mut root = self.current.clone().borrow().alternate.clone(); + let root = self.current.clone().borrow().alternate.clone(); Ok(if let Some(node) = root { let mut queue = VecDeque::new(); queue.push_back(Rc::clone(&node)); @@ -186,7 +185,8 @@ impl Debug for FiberRootNode { current_borrowed._type.as_ref().unwrap(), current_borrowed.flags, current_borrowed.subtree_flags - ); + ) + .expect("print error"); } WorkTag::HostRoot => { write!( @@ -194,7 +194,8 @@ impl Debug for FiberRootNode { "{:?}(subtreeFlags:{:?})", WorkTag::HostRoot, current_ref.subtree_flags - ); + ) + .expect("print error"); } WorkTag::HostComponent => { let current_borrowed = current.borrow(); @@ -209,7 +210,8 @@ impl Debug for FiberRootNode { .unwrap(), current_borrowed.flags, current_borrowed.subtree_flags - ); + ) + .expect("print error"); } WorkTag::HostText => { let current_borrowed = current.borrow(); @@ -226,7 +228,8 @@ impl Debug for FiberRootNode { .as_string() .unwrap(), current_borrowed.flags - ); + ) + .expect("print error"); } }; if let Some(ref child) = current_ref.child { @@ -244,17 +247,18 @@ impl Debug for FiberRootNode { (current_ref._return.as_ref(), next_ref._return.as_ref()) { if !Rc::ptr_eq(current_parent, next_parent) { - writeln!(f, ""); - writeln!(f, "------------------------------------"); + writeln!(f, "").expect("print error"); + writeln!(f, "------------------------------------") + .expect("print error"); continue; } } if current_ref._return.is_some() { - write!(f, ","); + write!(f, ",").expect("print error"); } else { - writeln!(f, ""); - writeln!(f, "------------------------------------"); + writeln!(f, "").expect("print error"); + writeln!(f, "------------------------------------").expect("print error"); } } } diff --git a/packages/react-reconciler/src/fiber_hooks.rs b/packages/react-reconciler/src/fiber_hooks.rs index 74ded5a..23c9f06 100644 --- a/packages/react-reconciler/src/fiber_hooks.rs +++ b/packages/react-reconciler/src/fiber_hooks.rs @@ -2,56 +2,160 @@ use std::cell::RefCell; use std::rc::Rc; use wasm_bindgen::{JsCast, JsValue}; -use web_sys::js_sys::Function; +use wasm_bindgen::prelude::{Closure, wasm_bindgen}; +use web_sys::js_sys::{Function, Object, Reflect}; use shared::log; -use crate::fiber::FiberNode; - -// -// use wasm_bindgen::JsValue; -// -// use crate::fiber::FiberNode; -// use crate::update_queue::UpdateQueue; -// -// pub struct Hook { -// memoized_state: Option>, -// update_queue: Option>>, -// next: Option>>, -// } -// -pub struct FiberHooks { - currently_rendering_fiber: Option>>, -} - -impl FiberHooks { - pub fn new() -> Self { - FiberHooks { - currently_rendering_fiber: None +use crate::fiber::{FiberNode, MemoizedState}; +use crate::update_queue::{create_update, create_update_queue, enqueue_update, UpdateQueue}; +use crate::work_loop::WorkLoop; + +#[wasm_bindgen] +extern "C" { + fn updateDispatcher(args: &JsValue); +} + +static mut CURRENTLY_RENDERING_FIBER: Option>> = None; +static mut WORK_IN_PROGRESS_HOOK: Option>> = None; +pub static mut WORK_LOOP: Option>> = None; + +#[derive(Debug, Clone)] +pub struct Hook { + memoized_state: Option, + update_queue: Option>>, + next: Option>>, +} + +impl Hook { + fn new() -> Self { + Hook { + memoized_state: None, + update_queue: None, + next: None, } } +} - pub fn render_with_hooks(&mut self, work_in_progress: Rc>) -> Result { - self.currently_rendering_fiber = Some(work_in_progress.clone()); +fn update_mount_hooks_to_dispatcher() { + let object = Object::new(); - let work_in_progress_cloned = work_in_progress.clone(); - { - work_in_progress_cloned.borrow_mut().memoized_state = None; - work_in_progress_cloned.borrow_mut().update_queue = None; - } + let closure = Closure::wrap(Box::new(mount_state) as Box Result, JsValue>>); + let function = closure.as_ref().unchecked_ref::().clone(); + closure.forget(); + Reflect::set(&object, &"use_state".into(), &function).expect("TODO: panic set use_state"); + updateDispatcher(&object.into()); +} + +pub fn render_with_hooks(work_in_progress: Rc>) -> Result { + unsafe { + CURRENTLY_RENDERING_FIBER = Some(work_in_progress.clone()); + } - let current = work_in_progress_cloned.borrow().alternate.clone(); - if current.is_some() { - log!("还未实现update时renderWithHooks"); - } else {} + let work_in_progress_cloned = work_in_progress.clone(); + { + work_in_progress_cloned.borrow_mut().memoized_state = None; + work_in_progress_cloned.borrow_mut().update_queue = None; + } + let current = work_in_progress_cloned.borrow().alternate.clone(); + if current.is_some() { + log!("还未实现update时renderWithHooks"); + } else { + update_mount_hooks_to_dispatcher(); + } + + let _type; + let props; + { let work_in_progress_borrow = work_in_progress_cloned.borrow(); - let _type = work_in_progress_borrow._type.as_ref().unwrap(); - let props = work_in_progress_borrow.pending_props.as_ref().unwrap(); - let component = JsValue::dyn_ref::(_type).unwrap(); - let children = component.call1(&JsValue::null(), props); - children + _type = work_in_progress_borrow._type.clone().unwrap(); + props = work_in_progress_borrow.pending_props.clone().unwrap(); + } + + let component = JsValue::dyn_ref::(&_type).unwrap(); + let children = component.call1(&JsValue::null(), &props); + children +} + +fn mount_work_in_progress_nook() -> Option>> { + let hook = Rc::new(RefCell::new(Hook::new())); + unsafe { + if WORK_IN_PROGRESS_HOOK.is_none() { + if CURRENTLY_RENDERING_FIBER.is_none() { + log!("WORK_IN_PROGRESS_HOOK and CURRENTLY_RENDERING_FIBER is empty") + } else { + CURRENTLY_RENDERING_FIBER + .as_ref() + .unwrap() + .clone() + .borrow_mut() + .memoized_state = Some(MemoizedState::Hook(hook.clone())); + WORK_IN_PROGRESS_HOOK = Some(hook.clone()); + } + } else { + WORK_IN_PROGRESS_HOOK + .as_ref() + .unwrap() + .clone() + .borrow_mut() + .next = Some(hook.clone()); + WORK_IN_PROGRESS_HOOK = Some(hook.clone()); + } + WORK_IN_PROGRESS_HOOK.clone() } } +fn mount_state(initial_state: &JsValue) -> Result, JsValue> { + let hook = mount_work_in_progress_nook(); + let memoized_state: JsValue; + + if initial_state.is_function() { + memoized_state = initial_state + .dyn_ref::() + .unwrap() + .call0(&JsValue::null())?; + } else { + memoized_state = initial_state.clone(); + } + hook.as_ref().unwrap().clone().borrow_mut().memoized_state = + Some(MemoizedState::JsValue(Rc::new((memoized_state.clone())))); + + unsafe { + if CURRENTLY_RENDERING_FIBER.is_none() { + log!("mount_state, currentlyRenderingFiber is empty"); + } + } + let queue = create_update_queue(); + hook.as_ref().unwrap().clone().borrow_mut().update_queue = Option::from(queue.clone()); + + let closure = Closure::wrap(Box::new(move |action: &JsValue| unsafe { + dispatch_set_state( + CURRENTLY_RENDERING_FIBER.clone().unwrap(), + queue.clone(), + action, + ) + }) as Box); + let function = closure.as_ref().unchecked_ref::().clone(); + closure.forget(); + + Ok(vec![memoized_state, function.into()]) +} + +fn dispatch_set_state( + fiber: Rc>, + update_queue: Rc>, + action: &JsValue, +) { + let update = create_update(Rc::new(action.clone())); + enqueue_update(update_queue, update); + unsafe { + WORK_LOOP + .as_ref() + .unwrap() + .clone() + .borrow() + .schedule_update_on_fiber(fiber.clone()); + } +} diff --git a/packages/react-reconciler/src/lib.rs b/packages/react-reconciler/src/lib.rs index 6d0492b..ae1dc50 100644 --- a/packages/react-reconciler/src/lib.rs +++ b/packages/react-reconciler/src/lib.rs @@ -5,20 +5,21 @@ use std::rc::Rc; use wasm_bindgen::JsValue; use crate::fiber::{FiberNode, FiberRootNode, StateNode}; -use crate::update_queue::{create_update, enqueue_update}; +use crate::fiber_hooks::WORK_LOOP; +use crate::update_queue::{create_update, create_update_queue, enqueue_update}; use crate::work_loop::WorkLoop; use crate::work_tags::WorkTag; -pub mod fiber; -mod fiber_flags; -mod work_tags; -mod update_queue; -mod work_loop; mod begin_work; mod child_fiber; -mod complete_work; mod commit_work; +mod complete_work; +pub mod fiber; +pub mod fiber_flags; mod fiber_hooks; +mod update_queue; +mod work_loop; +mod work_tags; pub trait HostConfig { fn create_text_instance(&self, content: String) -> Rc; @@ -37,8 +38,11 @@ impl Reconciler { } pub fn create_container(&self, container: Rc) -> Rc> { let host_root_fiber = Rc::new(RefCell::new(FiberNode::new(WorkTag::HostRoot, None, None))); - host_root_fiber.clone().borrow_mut().initialize_update_queue(); - let root = Rc::new(RefCell::new(FiberRootNode::new(container.clone(), host_root_fiber.clone()))); + host_root_fiber.clone().borrow_mut().update_queue = Some(create_update_queue()); + let root = Rc::new(RefCell::new(FiberRootNode::new( + container.clone(), + host_root_fiber.clone(), + ))); let r1 = root.clone(); host_root_fiber.borrow_mut().state_node = Some(Rc::new(StateNode::FiberRootNode(r1))); root.clone() @@ -47,10 +51,17 @@ impl Reconciler { pub fn update_container(&self, element: Rc, root: Rc>) { let host_root_fiber = Rc::clone(&root).borrow().current.clone(); let update = create_update(element); - enqueue_update(host_root_fiber.borrow(), update); - let mut work_loop = WorkLoop::new(self.host_config.clone()); - work_loop.schedule_update_on_fiber(host_root_fiber); + enqueue_update( + host_root_fiber.borrow().update_queue.clone().unwrap(), + update, + ); + let work_loop = Rc::new(RefCell::new(WorkLoop::new(self.host_config.clone()))); + unsafe { + WORK_LOOP = Some(work_loop.clone()); + } + work_loop + .clone() + .borrow() + .schedule_update_on_fiber(host_root_fiber); } } - - diff --git a/packages/react-reconciler/src/update_queue.rs b/packages/react-reconciler/src/update_queue.rs index 8dfc5ea..f398041 100644 --- a/packages/react-reconciler/src/update_queue.rs +++ b/packages/react-reconciler/src/update_queue.rs @@ -1,4 +1,4 @@ -use std::cell::{Ref, RefCell}; +use std::cell::RefCell; use std::rc::Rc; use wasm_bindgen::{JsCast, JsValue}; @@ -6,12 +6,11 @@ use web_sys::js_sys::Function; use shared::log; -use crate::fiber::FiberNode; +use crate::fiber::{FiberNode, MemoizedState}; #[derive(Clone, Debug)] pub struct UpdateAction; - #[derive(Clone, Debug)] pub struct Update { pub action: Option>, @@ -22,30 +21,33 @@ pub struct UpdateType { pub pending: Option, } - #[derive(Clone, Debug)] pub struct UpdateQueue { pub shared: UpdateType, } - pub fn create_update(action: Rc) -> Update { - Update { action: Some(action) } + Update { + action: Some(action), + } } -pub fn enqueue_update(fiber: Ref, update: Update) { - if fiber.update_queue.is_some() { - let uq = fiber.update_queue.clone().unwrap(); - let mut update_queue = uq.borrow_mut(); - update_queue.shared.pending = Some(update); - } +pub fn enqueue_update(update_queue: Rc>, update: Update) { + update_queue.borrow_mut().shared.pending = Option::from(update); +} + +pub fn create_update_queue() -> Rc> { + Rc::new(RefCell::new(UpdateQueue { + shared: UpdateType { + pending: None, + }, + })) } pub fn process_update_queue(fiber: Rc>) { let rc_fiber = fiber.clone(); - let mut fiber = rc_fiber.borrow_mut(); - let mut new_state = None; - match fiber.update_queue.clone() { + let mut new_state = rc_fiber.borrow().memoized_state.clone(); + match rc_fiber.borrow().update_queue.clone() { None => { log!("{:?} process_update_queue, update_queue is empty", fiber) } @@ -60,8 +62,10 @@ pub fn process_update_queue(fiber: Rc>) { Some(action) => { let f = action.dyn_ref::(); new_state = match f { - None => Some(action.clone()), - Some(f) => Some(Rc::new(f.call0(&JsValue::null()).unwrap())), + None => Some(MemoizedState::JsValue(action.clone())), + Some(f) => Some(MemoizedState::JsValue(Rc::new( + f.call0(&JsValue::null()).unwrap(), + ))), } } } @@ -69,5 +73,5 @@ pub fn process_update_queue(fiber: Rc>) { } } - fiber.memoized_state = new_state -} \ No newline at end of file + fiber.clone().borrow_mut().memoized_state = new_state +} diff --git a/packages/react-reconciler/src/work_loop.rs b/packages/react-reconciler/src/work_loop.rs index 6188116..59bf6c2 100644 --- a/packages/react-reconciler/src/work_loop.rs +++ b/packages/react-reconciler/src/work_loop.rs @@ -13,20 +13,23 @@ use crate::fiber_flags::get_mutation_mask; use crate::HostConfig; use crate::work_tags::WorkTag; +static mut WORK_IN_PROGRESS: Option>> = None; + pub struct WorkLoop { - work_in_progress: Option>>, + // make the first param to be &self not &mut self + // work_in_progress: Option>>, complete_work: CompleteWork, } impl WorkLoop { pub fn new(host_config: Rc) -> Self { Self { - work_in_progress: None, + // work_in_progress: None, complete_work: CompleteWork::new(host_config), } } - pub fn schedule_update_on_fiber(&mut self, fiber: Rc>) { + pub fn schedule_update_on_fiber(&self, fiber: Rc>) { let root = self.mark_update_lane_from_fiber_to_root(fiber); if root.is_none() { return; @@ -68,11 +71,11 @@ impl WorkLoop { None } - fn ensure_root_is_scheduled(&mut self, root: Rc>) { + fn ensure_root_is_scheduled(&self, root: Rc>) { self.perform_sync_work_on_root(root); } - fn perform_sync_work_on_root(&mut self, root: Rc>) { + fn perform_sync_work_on_root(&self, root: Rc>) { self.prepare_fresh_stack(Rc::clone(&root)); loop { @@ -80,10 +83,10 @@ impl WorkLoop { Ok(_) => { break; } - Err(e) => { + Err(e) => unsafe { log!("work_loop error {:?}", e); - self.work_in_progress = None; - } + WORK_IN_PROGRESS = None + }, }; } @@ -116,7 +119,7 @@ impl WorkLoop { let root_has_effect = get_mutation_mask().contains(finished_work.clone().borrow().flags.clone()); - let mut commit_work = &mut CommitWork::new(self.complete_work.host_config.clone()); + let commit_work = &mut CommitWork::new(self.complete_work.host_config.clone()); if subtree_has_effect || root_has_effect { commit_work.commit_mutation_effects(finished_work.clone()); cloned.borrow_mut().current = finished_work.clone(); @@ -125,33 +128,55 @@ impl WorkLoop { } } - fn prepare_fresh_stack(&mut self, root: Rc>) { + fn prepare_fresh_stack(&self, root: Rc>) { let root = Rc::clone(&root); - self.work_in_progress = Some(FiberNode::create_work_in_progress( - root.borrow().current.clone(), - Rc::new(JsValue::null()), - )); + // self.work_in_progress = Some(FiberNode::create_work_in_progress( + // root.borrow().current.clone(), + // Rc::new(JsValue::null()), + // )); + unsafe { + WORK_IN_PROGRESS = Some(FiberNode::create_work_in_progress( + root.borrow().current.clone(), + Rc::new(JsValue::null()), + )); + log!( + "prepare_fresh_stack {:?} {:?}", + WORK_IN_PROGRESS.clone().unwrap().clone().borrow()._type, + WORK_IN_PROGRESS + .clone() + .unwrap() + .clone() + .borrow() + .memoized_state + ); + } } - fn work_loop(&mut self) -> Result<(), JsValue> { - while self.work_in_progress.is_some() { - self.perform_unit_of_work(self.work_in_progress.clone().unwrap())?; + fn work_loop(&self) -> Result<(), JsValue> { + // while self.work_in_progress.is_some() { + // self.perform_unit_of_work(self.work_in_progress.clone().unwrap())?; + // } + unsafe { + while WORK_IN_PROGRESS.is_some() { + self.perform_unit_of_work(WORK_IN_PROGRESS.clone().unwrap())?; + } } Ok(()) } - fn perform_unit_of_work(&mut self, fiber: Rc>) -> Result<(), JsValue> { + fn perform_unit_of_work(&self, fiber: Rc>) -> Result<(), JsValue> { let next = begin_work(fiber.clone())?; if next.is_none() { self.complete_unit_of_work(fiber.clone()); } else { - self.work_in_progress = Some(next.unwrap()); + // self.work_in_progress = Some(next.unwrap()); + unsafe { WORK_IN_PROGRESS = Some(next.unwrap()) } } Ok(()) } - fn complete_unit_of_work(&mut self, fiber: Rc>) { + fn complete_unit_of_work(&self, fiber: Rc>) { let mut node: Option>> = Some(fiber); loop { @@ -160,13 +185,19 @@ impl WorkLoop { .complete_work(node.clone().unwrap().clone()); if next.is_some() { - self.work_in_progress = next.clone(); + // self.work_in_progress = next.clone(); + unsafe { + WORK_IN_PROGRESS = next.clone(); + } return; } let sibling = node.clone().unwrap().clone().borrow().sibling.clone(); if sibling.is_some() { - self.work_in_progress = next.clone(); + // self.work_in_progress = next.clone(); + unsafe { + WORK_IN_PROGRESS = next.clone(); + } return; } @@ -174,11 +205,17 @@ impl WorkLoop { if _return.is_none() { node = None; - self.work_in_progress = None; + // self.work_in_progress = None; + unsafe { + WORK_IN_PROGRESS = None; + } break; } else { node = _return; - self.work_in_progress = node.clone(); + // self.work_in_progress = node.clone(); + unsafe { + WORK_IN_PROGRESS = node.clone(); + } } } } diff --git a/packages/react/src/current_dispatcher.rs b/packages/react/src/current_dispatcher.rs new file mode 100644 index 0000000..d29414c --- /dev/null +++ b/packages/react/src/current_dispatcher.rs @@ -0,0 +1,39 @@ +use js_sys::{Function, Reflect}; +use wasm_bindgen::JsValue; +use wasm_bindgen::prelude::*; + +#[derive(Debug)] +pub struct Dispatcher { + pub use_state: Function, + // pub use_callback: *const dyn Fn(), +} + +unsafe impl Send for Dispatcher {} + +impl Dispatcher { + pub fn new(use_state: Function/*, use_callback: *const dyn Fn()*/) -> Self { + Dispatcher { + use_state, + // use_callback, + } + } +} + +pub struct CurrentDispatcher { + pub current: Option>, +} + +pub static mut CURRENT_DISPATCHER: CurrentDispatcher = CurrentDispatcher { current: None }; + +fn derive_function_from_js_value(js_value: &JsValue, name: &str) -> Function { + Reflect::get(js_value, &name.into()).unwrap().dyn_into::().unwrap() +} + +#[wasm_bindgen(js_name = updateDispatcher)] +pub unsafe fn update_dispatcher(args: &JsValue) { + let use_state = derive_function_from_js_value(args, "use_state"); + CURRENT_DISPATCHER.current = Some(Box::new(Dispatcher::new(use_state))) +} + + + diff --git a/packages/react/src/lib.rs b/packages/react/src/lib.rs index 5bdaaab..1de88e7 100644 --- a/packages/react/src/lib.rs +++ b/packages/react/src/lib.rs @@ -3,8 +3,12 @@ use wasm_bindgen::prelude::*; use shared::REACT_ELEMENT_TYPE; +use crate::current_dispatcher::CURRENT_DISPATCHER; + +pub mod current_dispatcher; + #[wasm_bindgen(js_name = jsxDEV)] -pub fn jsx_dev(_type: &JsValue, config: &JsValue, key: &JsValue) -> JsValue { +pub unsafe fn jsx_dev(_type: &JsValue, config: &JsValue, key: &JsValue) -> JsValue { let react_element = Object::new(); Reflect::set( &react_element, @@ -34,3 +38,11 @@ pub fn jsx_dev(_type: &JsValue, config: &JsValue, key: &JsValue) -> JsValue { Reflect::set(&react_element, &"props".into(), &props).expect("props panic"); react_element.into() } + + +#[wasm_bindgen(js_name = useState)] +pub unsafe fn use_state(initial_state: &JsValue) -> Result { + let use_state = &CURRENT_DISPATCHER.current.as_ref().unwrap().use_state; + use_state.call1(&JsValue::null(), initial_state) +} + diff --git a/scripts/build.js b/scripts/build.js new file mode 100644 index 0000000..3cf780f --- /dev/null +++ b/scripts/build.js @@ -0,0 +1,21 @@ +const {execSync} = require('child_process') +const fs = require('fs') + +const cwd = process.cwd() + +execSync(`wasm-pack build packages/react --out-dir ${cwd}/dist/react --out-name jsx-dev-runtime`) +execSync(`wasm-pack build packages/react --out-dir ${cwd}/dist/react --out-name index`) +execSync(`wasm-pack build packages/react-dom --out-dir ${cwd}/dist/react-dom --out-name index`) + +// modify react/package.json +const packageJsonFilename = `${cwd}/dist/react/package.json` +const packageJson = JSON.parse(fs.readFileSync(packageJsonFilename).toString("utf-8")) +packageJson.files.push('jsx-dev-runtime.wasm', 'jsx-dev-runtime.js', 'jsx-dev-runtime_bg.js', 'jsx-dev-runtime_bg.wasm') +fs.writeFileSync(packageJsonFilename, JSON.stringify(packageJson)) + +// modify react-dom/index_bg.js +const reactDomIndexBgFilename = `${cwd}/dist/react-dom/index_bg.js` +const reactDomIndexBgData = fs.readFileSync(reactDomIndexBgFilename) +fs.writeFileSync(reactDomIndexBgFilename, 'import {updateDispatcher} from "react"\n' + reactDomIndexBgData) + +