1use std::{2 cell::{Cell, OnceCell, RefCell},3 clone::Clone,4 fmt::{self, Debug},5};67use educe::Educe;8use jrsonnet_gcmodule::{Cc, Trace};9use jrsonnet_interner::IStr;1011use crate::{12 Result, SupThis, Thunk, Val,13 analyze::{CaptureSlot, ClosureShape, LSlot, LocalId, LocalSlot},14 bail, error,15 error::ErrorKind::*,16};1718#[derive(Debug, Trace, Clone, Educe)]19#[educe(PartialEq)]20pub struct Context(#[educe(PartialEq(method = Cc::ptr_eq))] pub(crate) Cc<ContextInternal>);2122#[derive(Trace)]23pub(crate) struct ContextInternal {24 25 pub(crate) captures: Cc<Vec<Thunk<Val>>>,26 27 pub(crate) locals: Cc<LocalsFrame>,28 pub(crate) sup_this: Option<SupThis>,29}3031impl Debug for ContextInternal {32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {33 f.debug_struct("ContextInternal")34 .field("captures", &self.captures.len())35 .field("locals", &self.locals)36 .field("sup_this", &self.sup_this.is_some())37 .finish()38 }39}4041#[derive(Trace, Debug)]42pub(crate) struct IterFrame {43 slots: Vec<RefCell<Option<Thunk<Val>>>>,44 captured: Cell<bool>,45}46impl IterFrame {47 pub fn new(n: u16) -> IterFrame {48 let cells: Vec<RefCell<Option<Thunk<Val>>>> = (0..n).map(|_| RefCell::new(None)).collect();49 IterFrame {50 slots: cells,51 captured: Cell::new(false),52 }53 }54 pub fn set(&self, slot: LocalSlot, value: Thunk<Val>) {55 *self.slots[slot.0 as usize].borrow_mut() = Some(value);56 }57}5859#[derive(Trace, Debug)]60pub(crate) enum LocalsFrame {61 Once1(OnceCell<Thunk<Val>>),62 63 Once(Vec<OnceCell<Thunk<Val>>>),64 65 Iter(IterFrame),66}67impl LocalsFrame {68 pub fn set(&self, slot: LocalSlot, value: Thunk<Val>) {69 match self {70 LocalsFrame::Once1(cell) => {71 debug_assert_eq!(slot.0, 0, "Once1 only holds slot 0");72 cell.set(value)73 .map_err(|_| ())74 .expect("slot already filled");75 }76 LocalsFrame::Once(cells) => {77 cells[slot.0 as usize]78 .set(value)79 .map_err(|_| ())80 .expect("slot already filled");81 }82 LocalsFrame::Iter(_) => unreachable!("iter frame has different constructors"),83 }84 }85}8687impl LocalsFrame {88 pub(crate) fn new_once(n: u16) -> Cc<Self> {89 if n == 1 {90 return Cc::new(Self::Once1(OnceCell::new()));91 }92 let cells: Vec<OnceCell<Thunk<Val>>> = (0..n).map(|_| OnceCell::new()).collect();93 Cc::new(Self::Once(cells))94 }95}9697pub(crate) struct IterContext {98 context: Context,99}100impl IterContext {101 pub(crate) fn create(&self, build: impl FnOnce(&IterFrame)) -> Result<Context> {102 if !Cc::is_unique(&self.context.0.locals) {103 bail!(EagerCompspecCaptured);104 }105 let LocalsFrame::Iter(frame) = &*self.context.0.locals else {106 unreachable!("IterContext is only created for Iter ctx");107 };108 if frame.captured.get() {109 bail!(EagerCompspecCaptured);110 }111 build(frame);112 Ok(self.context.clone())113 }114}115116#[derive(Trace, Clone)]117pub(crate) struct PackedContext {118 captures: Cc<Vec<Thunk<Val>>>,119 n_locals: u16,120}121impl PackedContext {122 pub fn enter(self, sup_this: SupThis, build: impl FnOnce(&LocalsFrame, &Context)) -> Context {123 let locals = LocalsFrame::new_once(self.n_locals);124 let val = Context(Cc::new(ContextInternal {125 captures: self.captures.clone(),126 locals,127 sup_this: Some(sup_this),128 }));129 build(&val.0.locals, &val);130 val131 }132}133#[derive(Trace, Clone, Educe, Debug)]134#[educe(PartialEq)]135pub(crate) struct PackedContextSupThis {136 #[educe(PartialEq(method = Cc::ptr_eq))]137 captures: Cc<Vec<Thunk<Val>>>,138 n_locals: u16,139 sup_this: Option<SupThis>,140}141impl PackedContextSupThis {142 pub fn enter(self, build: impl FnOnce(&LocalsFrame, &Context)) -> Context {143 let locals = LocalsFrame::new_once(self.n_locals);144 let val = Context(Cc::new(ContextInternal {145 captures: self.captures.clone(),146 locals,147 sup_this: self.sup_this,148 }));149 build(&val.0.locals, &val);150 val151 }152}153154impl Context {155 #[inline]156 pub fn slot(&self, slot: LSlot) -> Thunk<Val> {157 match slot {158 LSlot::Local(i) => self.local(i),159 LSlot::Capture(i) => self.capture(i),160 }161 }162 163 164 165 166 167 168 169 #[inline]170 pub fn local(&self, slot: LocalSlot) -> Thunk<Val> {171 match &*self.0.locals {172 LocalsFrame::Once1(cell) => {173 debug_assert_eq!(slot.0, 0, "Once1 only holds slot 0");174 cell.get().expect("local read before letrec init").clone()175 }176 LocalsFrame::Once(cells) => cells[slot.0 as usize]177 .get()178 .expect("local read before letrec init")179 .clone(),180 LocalsFrame::Iter(cells) => cells.slots[slot.0 as usize]181 .borrow()182 .as_ref()183 .expect("iter local read before iteration filled it")184 .clone(),185 }186 }187188 189 #[inline]190 pub fn capture(&self, slot: CaptureSlot) -> Thunk<Val> {191 (*self.0.captures)[slot.0 as usize].clone()192 }193194 pub fn sup_this(&self) -> Option<&SupThis> {195 self.0.sup_this.as_ref()196 }197198 pub fn try_sup_this(&self) -> Result<SupThis> {199 self.0200 .sup_this201 .clone()202 .ok_or_else(|| error!(CantUseSelfSupOutsideOfObject))203 }204205 206 207 208 209 pub(crate) fn root(externals: Vec<Thunk<Val>>) -> Self {210 let n: u16 = externals211 .len()212 .try_into()213 .expect("more than u16::MAX externals");214 let cells: Vec<OnceCell<Thunk<Val>>> = externals215 .into_iter()216 .map(|t| {217 let cell = OnceCell::new();218 cell.set(t).map_err(|_| ()).expect("fresh cell");219 cell220 })221 .collect();222 debug_assert_eq!(cells.len(), n as usize);223 let locals = Cc::new(LocalsFrame::Once(cells));224 Self(Cc::new(ContextInternal {225 captures: Cc::new(Vec::new()),226 locals,227 sup_this: None,228 }))229 }230231 pub(crate) fn pack_captures(&self, shape: &ClosureShape) -> PackedContext {232 PackedContext {233 captures: Cc::new(pack_captures(self, &shape.captures)),234 n_locals: shape.n_locals,235 }236 }237 pub(crate) fn pack_captures_sup_this(&self, shape: &ClosureShape) -> PackedContextSupThis {238 PackedContextSupThis {239 captures: Cc::new(pack_captures(self, &shape.captures)),240 n_locals: shape.n_locals,241 sup_this: self.0.sup_this.clone(),242 }243 }244245 pub(crate) fn enter_iter(246 parent: &Context,247 shape: &ClosureShape,248 cb: impl FnOnce(IterContext) -> Result<()>,249 ) -> Result<()> {250 let captures = Cc::new(pack_captures(parent, &shape.captures));251 let locals = IterFrame::new(shape.n_locals);252 cb(IterContext {253 context: Self(Cc::new(ContextInternal {254 captures,255 locals: Cc::new(LocalsFrame::Iter(locals)),256 sup_this: parent.0.sup_this.clone(),257 })),258 })259 }260261 pub(crate) fn enter_using(parent: &Context, shape: &ClosureShape) -> Self {262 debug_assert_eq!(shape.n_locals, 0);263 if shape.captures.is_empty() {264 if let LocalsFrame::Iter(i) = &*parent.0.locals {265 i.captured.set(true);266 }267 268 return parent.clone();269 }270 let captures = Cc::new(pack_captures(parent, &shape.captures));271 Self(Cc::new(ContextInternal {272 captures,273 locals: parent.0.locals.clone(),274 sup_this: parent.0.sup_this.clone(),275 }))276 }277}278279fn pack_captures(parent: &Context, sources: &[LSlot]) -> Vec<Thunk<Val>> {280 sources.iter().map(|src| parent.slot(*src)).collect()281}282283pub struct InitialContextBuilder {284 externals: Vec<(IStr, LocalId)>,285 values: Vec<Thunk<Val>>,286 next_id: u32,287}288289impl InitialContextBuilder {290 pub(crate) fn new() -> Self {291 Self {292 externals: Vec::new(),293 values: Vec::new(),294 next_id: 0,295 }296 }297298 pub fn bind(&mut self, name: impl Into<IStr>, value: Thunk<Val>) {299 let name = name.into();300 let id = LocalId(self.next_id);301 self.next_id += 1;302 self.externals.push((name, id));303 self.values.push(value);304 }305306 pub(crate) fn build(self) -> (Vec<(IStr, LocalId)>, Vec<Thunk<Val>>) {307 (self.externals, self.values)308 }309}310311impl Default for InitialContextBuilder {312 fn default() -> Self {313 Self::new()314 }315}