1use crate::{2 error::Error::*, future_wrapper, map::LayeredHashMap, rc_fn_helper, resolved_lazy_val,3 LazyBinding, LazyVal, ObjValue, Result, Val,4};5use rustc_hash::FxHashMap;6use std::hash::BuildHasherDefault;7use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};89rc_fn_helper!(10 ContextCreator,11 context_creator,12 dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Result<Context>13);1415future_wrapper!(Context, FutureContext);1617struct ContextInternals {18 dollar: Option<ObjValue>,19 this: Option<ObjValue>,20 super_obj: Option<ObjValue>,21 bindings: LayeredHashMap<Rc<str>, LazyVal>,22}23impl Debug for ContextInternals {24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {25 f.debug_struct("Context")26 .field("this", &self.this.as_ref().map(|e| Rc::as_ptr(&e.0)))27 .field("bindings", &self.bindings)28 .finish()29 }30}3132#[derive(Debug, Clone)]33pub struct Context(Rc<ContextInternals>);34impl Context {35 pub fn new_future() -> FutureContext {36 FutureContext(Rc::new(RefCell::new(None)))37 }3839 pub fn dollar(&self) -> &Option<ObjValue> {40 &self.0.dollar41 }4243 pub fn this(&self) -> &Option<ObjValue> {44 &self.0.this45 }4647 pub fn super_obj(&self) -> &Option<ObjValue> {48 &self.0.super_obj49 }5051 pub fn new() -> Self {52 Self(Rc::new(ContextInternals {53 dollar: None,54 this: None,55 super_obj: None,56 bindings: LayeredHashMap::default(),57 }))58 }5960 pub fn binding(&self, name: Rc<str>) -> Result<LazyVal> {61 Ok(self62 .063 .bindings64 .get(&name)65 .cloned()66 .ok_or_else(|| VariableIsNotDefined(name))?)67 }68 pub fn into_future(self, ctx: FutureContext) -> Self {69 {70 ctx.0.borrow_mut().replace(self);71 }72 ctx.unwrap()73 }7475 pub fn with_var(self, name: Rc<str>, value: Val) -> Self {76 let mut new_bindings =77 FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default());78 new_bindings.insert(name, resolved_lazy_val!(value));79 self.extend(new_bindings, None, None, None)80 }8182 pub fn extend(83 self,84 new_bindings: FxHashMap<Rc<str>, LazyVal>,85 new_dollar: Option<ObjValue>,86 new_this: Option<ObjValue>,87 new_super_obj: Option<ObjValue>,88 ) -> Self {89 match Rc::try_unwrap(self.0) {90 Ok(mut ctx) => {91 92 if let Some(dollar) = new_dollar {93 ctx.dollar = Some(dollar);94 }95 if let Some(this) = new_this {96 ctx.this = Some(this);97 }98 if let Some(super_obj) = new_super_obj {99 ctx.super_obj = Some(super_obj);100 }101 if !new_bindings.is_empty() {102 ctx.bindings = ctx.bindings.extend(new_bindings);103 }104 Self(Rc::new(ctx))105 }106 Err(ctx) => {107 let dollar = new_dollar.or_else(|| ctx.dollar.clone());108 let this = new_this.or_else(|| ctx.this.clone());109 let super_obj = new_super_obj.or_else(|| ctx.super_obj.clone());110 let bindings = if new_bindings.is_empty() {111 ctx.bindings.clone()112 } else {113 ctx.bindings.clone().extend(new_bindings)114 };115 Self(Rc::new(ContextInternals {116 dollar,117 this,118 super_obj,119 bindings,120 }))121 }122 }123 }124 pub fn extend_unbound(125 self,126 new_bindings: HashMap<Rc<str>, LazyBinding>,127 new_dollar: Option<ObjValue>,128 new_this: Option<ObjValue>,129 new_super_obj: Option<ObjValue>,130 ) -> Result<Self> {131 let this = new_this.or_else(|| self.0.this.clone());132 let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone());133 let mut new =134 FxHashMap::with_capacity_and_hasher(new_bindings.len(), BuildHasherDefault::default());135 for (k, v) in new_bindings.into_iter() {136 new.insert(k, v.evaluate(this.clone(), super_obj.clone())?);137 }138 Ok(self.extend(new, new_dollar, this, super_obj))139 }140 #[cfg(feature = "unstable")]141 pub fn into_weak(self) -> WeakContext {142 WeakContext(Rc::downgrade(&self.0))143 }144}145146impl Default for Context {147 fn default() -> Self {148 Self::new()149 }150}151152impl PartialEq for Context {153 fn eq(&self, other: &Self) -> bool {154 Rc::ptr_eq(&self.0, &other.0)155 }156}157158#[cfg(feature = "unstable")]159#[derive(Debug, Clone)]160pub struct WeakContext(std::rc::Weak<ContextInternals>);161#[cfg(feature = "unstable")]162impl WeakContext {163 pub fn upgrade(&self) -> Context {164 Context(self.0.upgrade().expect("context is removed"))165 }166}167#[cfg(feature = "unstable")]168impl PartialEq for WeakContext {169 fn eq(&self, other: &Self) -> bool {170 self.0.ptr_eq(&other.0)171 }172}