<commit_before>//! Serialization for TimeSteward data.
//!
//! TimeSteward has a few special requirements for serialization:
//! * DataTimelineHandle objects get special consideration, to support DAGs and cyclic data structures.
//! * The serialization needs to not block other operations for more than O(1) time at a time.
//! * The serialization must be lossless and platform-independent. For this reason, we always use bincode in low-endian mode.

macro_rules! time_steward_serialization_impls_for_handle {
  (
    [$($bounds:tt)*]
    [$($concrete:tt)*]
    (&$self_hack: ident)
    Data located at (| $handle: ident | &mut $data: expr)
  ) => {

/*thread_local! {
  static SERIALIZATION_CONTEXT: RefCell<Option <SerializationContext>> = RefCell::new (None);
  static DESERIALIZATION_CONTEXT: RefCell<Option <DeserializationContext>> = RefCell::new (None);
}*/

impl <$($bounds)*> $crate::serde::Serialize for $($concrete)* {
  fn serialize <S: $crate::serde::Serializer> (&$self_hack, serializer: S)->Result <S::Ok, S::Error> {
    unimplemented!()
    /*
    SERIALIZATION_CONTEXT.with (| cell | {
      let mut guard = cell.borrow_mut();
      let mut map = &mut guard.unwrap().map;
      map.insert (
    })
    identifier.serialize (serializer)
    */
  }
}

impl <'a, $($bounds)*> $crate::serde::Deserialize <'a> for $($concrete)* {
  fn deserialize <D: $crate::serde::Deserializer<'a>> (deserializer: D)->Result <Self, D::Error> {
    unimplemented!()
    /*
    let old_identifier = Identifier::deserialize (deserializer)?;
    DESERIALIZATION_CONTEXT.with (| cell | {
      let mut guard = cell.borrow_mut();
      let mut map = &mut guard.unwrap().map;
      Ok(map.entry (old_pointer).or_insert_with (| | {
        unimplemented!()
        /*DataTimelineHandle {
          inner: Arc::new (unsafe {::std::mem::uninitialized()}),
        }*/
      }).clone().downcast::<T> ())
    })
    */
  }
}

/*
impl DeserializationContext {
  fn deserialize_(deserializer: D)->Result <(), D::Error> {
    let old_pointer = usize::deserialize (deserializer)?;
    DESERIALIZATION_CONTEXT.with (| cell | {
      let mut guard = cell.borrow_mut();
      let mut map = &mut guard.unwrap().map;
      let $handle = map.entry (old_pointer).or_insert_with (| | {
        unimplemented!()
        /*DataTimelineHandle {
          inner: Arc::new (unsafe {::std::mem::uninitialized()}),
        }*/
      });
      let new_pointer = &mut $data;
      unsafe {::std::ptr::write (
        new_pointer as *mut DataTimelineHandle,
        unimplemented!() //DataTimeline::deserialize (D)
      )?;}
    })?;
    Ok(())
  }
}*/

  };
}

#[doc (hidden)]
#[macro_export]
macro_rules! time_steward_serialization_impls {
  () => {

  enum SerializationElement {
    DataHandleData(u64, PersistentTypeId),
    EventHandleData(u64, PersistentTypeId),
    Finished,
  }

  trait SerializeTargetInto {
    fn serialize_target_into(&self, writer: &mut Write, object_id: u64)->$crate::bincode::Result <()>;
  }
  trait SerializeInto {
    fn serialize_into(&self, writer: &mut Write)->$crate::bincode::Result <()>;
  }
  impl<T: StewardData> SerializeTargetInto for DataHandle <T> {
    fn serialize_target_into(&self, writer: &mut Write, object_id: u64)->$crate::bincode::Result <()> {
      $crate::bincode::serialize_into (writer, SerializationElement::DataHandleData (T::ID), $crate::bincode::Infinite)?;
      $crate::bincode::serialize_into (writer, &*self.data, $crate::bincode::Infinite)?;
    }
  }
  fn data_handle_initialize_function <T: StewardData>(reader: &mut Read, object_id: u64) {
    with_deserialization_context (| context | {
      let handle = context.find_handle::<_, DataHandle <T>> (object_id, || {
        Box::new (DataHandle::new(unsafe {::std::mem::uninitialized()}))
      })?;
      unsafe {::std::ptr::write (
        handle.data as *mut T,
        ::bincode::deserialize_from (reader, $crate::bincode::Infinite)
      )?;}
    })
  }

  impl<B: Basics> SerializeTargetInto for EventHandle <B> {
    fn serialize_target_into(&self, writer: &mut Write, object_id: u64)->$crate::bincode::Result <()> {
      $crate::bincode::serialize_into (writer, SerializationElement::DataHandleData (self.data.data.persistent_type_id()), $crate::bincode::Infinite)?;
      $crate::bincode::serialize_into (writer, self.extended_time(), $crate::bincode::Infinite)?;
      self.data.data.serialize_into (writer)
    }
  }
  impl<T: EventInnerTrait> SerializeInto for T {
    fn serialize_into(&self, writer: &mut Write)->$crate::bincode::Result <()> {
      $crate::bincode::serialize_into (writer, self, $crate::bincode::Infinite)?;
    }
  }
  fn event_handle_initialize_function <T: Event>(reader: &mut Read, object_id: u64) {
    with_deserialization_context (| context | {
      let handle = context.find_handle::<_, <T::Steward as TimeSteward>::EventHandle> (object_id, || {
        Box::new (EventHandle::new(unsafe {::std::mem::uninitialized()}))
      })?;
      let time = ::bincode::deserialize_from (reader, $crate::bincode::Infinite);
      let data = ::bincode::deserialize_from (reader, $crate::bincode::Infinite);
      unsafe {::std::ptr::write (
        handle.data as *mut T,
        EventInner {
          time: time.clone(),
          data: Box::new (data),
          should_be_executed: Cell::new (time > context.time),
          is_prediction: time > context.time,
          prediction_destroyed_by: RefCell::new (None),
          execution_state: RefCell::new (None),
        }
      )?;}
    })
  }

  struct SerializationContext {
    snapshot: Box <Any>,
    handle_targets_observed: HashMap <*const (), u64>,
    handles_to_serialize_target: Vec<(u64, Box <SerializeTargetInto>)>,
    next_object_identifier: u64,
  }
  struct DeserializationContext {
    data_handle_initialize_functions: ::std::collections::HashMap <
      PersistentTypeId, fn (&mut Write)>,
    event_handle_initialize_functions: ::std::collections::HashMap <
      PersistentTypeId, fn (&mut Write)>,
    handles: ::std::collections::HashMap <u64, Box <Any>>,
    uninitialized_handles: ::std::collections::HashSet <u64>,
  }

  thread_local! {
    static SERIALIZATION_CONTEXT: RefCell<Option <SerializationContext>> = RefCell::new (None);
    static DESERIALIZATION_CONTEXT: RefCell<Option <DeserializationContext>> = RefCell::new (None);
  }

  fn with_serialization_context <S: $crate::serde::Serializer, R, F: FnOnce (&mut SerializationContext <Steward>)->R> (callback: F)->Result <R, S::Error> {
    SERIALIZATION_CONTEXT.with (| cell | {
      let mut guard = cell.borrow_mut();
      let mut context = match guard.as_mut() {Some(x)=>x, None=>return S::Error::Custom("Tried to serialize TimeSteward data without a serialization context.")};
      callback (context)
    })
  }
  fn with_deserialization_context <D: $crate::serde::Deserializer, Steward: TimeSteward, R, F: FnOnce (&mut DeserializationContext <Steward>)->R> (callback: F)->Result <R, D::Error> {
    DESERIALIZATION_CONTEXT.with (| cell | {
      let mut guard = cell.borrow_mut();
      let mut context = match guard.as_mut() {Some(x)=>x, None=>return D::Error::Custom("Tried to deserialize TimeSteward data without a deserialization context.")};
      callback (context)
    })
  }

  impl SerializationContext {
    fn find_handle <S: $crate::serde::Serializer, F: FnOnce()->Box <Any>, T> (&mut self, serializer: S, initialize: F)->Result<(), S::Error> {
      let object_identifier = self.handle_targets_observed.entry (self.data as *const ()).or_insert_with (|| {
        let result = self.next_object_identifier;
        self.next_object_identifier += 1;
        self.handles_to_serialize_target.insert (result, initialize());
        result
      });
      object_identifier.serialize (serializer)
    }
  }

  impl DeserializationContext {
    fn find_handle <D: $crate::serde::Deserializer, F: FnOnce()->Box <Any>, T> (&mut self, object_identifier: u64, initialize: F)->Result<&mut T, D::Error> {
      let entry = self.data_handles.entry (object_identifier).or_insert_with (|| {
        let created = initialize();
        context.uninitialized_data_handles.insert (created.clone());
        created
      });
      match (*entry).downcast_mut::<T>() {
        Some (result) => Ok(result),
        None => D::Error::Custom("In the serialized snapshot, DataHandles of different types pointed to the same object."),
      }
    }
  }

  impl <T: StewardData> $crate::serde::Serialize for DataHandle <T> {
    fn serialize <S: $crate::serde::Serializer> (&self, serializer: S)->Result <S::Ok, S::Error> {
      with_serialization_context (| context | {
        context.find_handle::<S, _, DataHandle <T>> (serializer, || {
          Box::new (self.clone())
        })
      })
    }
  }
  impl <'a, T: StewardData> $crate::serde::Deserialize <'a> for DataHandle <T> {
    fn deserialize <D: $crate::serde::Deserializer<'a>> (deserializer: D)->Result <Self, D::Error> {
      with_deserialization_context (| context | {
        let object_identifier = u64::deserialize (deserializer)?;
        context.find_handle::<_, DataHandle <T>> (object_identifier, || {
          Box::new (DataHandle::new(unsafe {::std::mem::uninitialized()}))
        })?.clone()
      })
    }
  }

  impl <B: Basics> $crate::serde::Serialize for EventHandle <B> {
    fn serialize <S: $crate::serde::Serializer> (&self, serializer: S)->Result <S::Ok, S::Error> {
      with_serialization_context (| context | {
        context.find_handle::<S, _, DataHandle <T>> (serializer, || {
          Box::new (self.clone())
        })
      })
    }
  }
  impl <'a, B: Basics> $crate::serde::Deserialize <'a> for EventHandle <B> {
    fn deserialize <D: $crate::serde::Deserializer<'a>> (deserializer: D)->Result <Self, D::Error> {
      with_deserialization_context (| context | {
        let object_identifier = u64::deserialize (deserializer)?;
        context.find_handle::<_, EventHandle <B>> (object_identifier, || {
          Box::new (EventHandle::new(unsafe {::std::mem::uninitialized()}))
        })?.clone()
      })
    }
  }

  impl <T: DataTimeline> $crate::serde::Serialize for DataTimelineCell <T> {
    fn serialize <S: $crate::serde::Serializer> (&self, serializer: S)->Result <S::Ok, S::Error> {
      with_serialization_context (| context | {
        context.snapshot.get_clone (&self).serialize (serializer)
      })
    }
  }
  impl <'a, T: DataTimeline> $crate::serde::Deserialize <'a> for DataTimelineCell <T> {
    fn deserialize <D: $crate::serde::Deserializer<'a>> (deserializer: D)->Result <Self, D::Error> {
      Self::new (T::deserialize (deserializer))
    }
  }

  fn serialize_snapshot <B: Basics, W: Write> (writer: &mut W, snapshot: SnapshotHandle <B>)->$crate::bincode::Result <()> {
    SERIALIZATION_CONTEXT.with (| cell | {
      {
        let guard = cell.borrow_mut();
        assert!(guard.is_none(), "serializing recursively breaks my hacks and probably makes no sense");
        *guard = Some(SerializationContext {

        });
      }
      // serialize inside a closure so that errors can be collected and we still clear the context afterwards
      let result = || {
        $crate::bincode::serialize_into (writer, snapshot.extended_now(), ::std::mem::size_of::<ExtendedTime <B>>())?;
        $crate::bincode::serialize_into (writer, snapshot.globals(), $crate::bincode::Infinite)?;

        loop {
          if let (object_identifier, handle_box) = cell.borrow().data_handles_to_serialize_target.pop() {
            handle_box.serialize_into (writer);
          }
          if let (object_identifier, event_handle) = cell.borrow().event_handles_to_serialize_target.pop() {
            event_handle.serialize_into (writer);
          }
        }

        $crate::bincode::serialize_into (writer, SerializationElement::Finished, $crate::bincode::Infinite)?;

      }();

      {
        let guard = cell.borrow_mut();
        *guard = None;
      }
      result
    })
  }

  fn deserialize_something <R: Read> (reader: &mut R)->$crate::bincode::Result <()> {
    DESERIALIZATION_CONTEXT.with (| cell | {
      {
        let guard = cell.borrow_mut();
        assert!(guard.is_none(), "deserializing recursively breaks my hacks and probably makes no sense");
        *guard = Some(DeserializationContext::new());
      }
      // deserialize inside a closure so that errors can be collected and we still clear the context afterwards
      let result = || {
        let time: ExtendedTime <B> = $crate::bincode::deserialize_from (reader, ::std::mem::size_of::<ExtendedTime <B>>())?;
        let globals: B::Globals = $crate::bincode::deserialize_from (reader, $crate::bincode::Infinite)?;

        while !cell.borrow().uninitialized_handles.is_empty() {
          // TODO: use actual size limits
          let next: SerializationElement = $crate::bincode::deserialize_from (reader, $crate::bincode::Infinite)?;
          match next {
            SerializationElement::DataTimeline (object_id, type_id) => {
              let deserialize_function = cell.borrow().data_handle_initialize_functions.get (type_id);
              (*deserialize_function)(reader, object_id);
            }
            SerializationElement::Event (object_id, type_id) => {
              let deserialize_function = cell.borrow().event_handle_initialize_functions.get (type_id);
              (*deserialize_function)(reader, object_id);
            }
            SerializationElement::Finished => {
              // TODO: clean up broken state
              return $crate::bincode::Result::Custom("Premature end of serialized snapshot")
            }
          };
        }

        let next: SerializationElement = $crate::bincode::deserialize_from (reader, $crate::bincode::Infinite)?;
        match next {
          SerializationElement::Finished => (),
          _ => {
            return $crate::bincode::Result::Custom("Serialized snapshot included additional elements after successfully deserializing the state")
          }
        };

        Ok(Steward::from_globals (globals, ValidSince::Before (time)))
      }();

      {
        let guard = cell.borrow_mut();
        *guard = None;
      }
      result
    })
  }

  };
}

<commit_msg>More serialization work<commit_after>//! Serialization for TimeSteward data.
//!
//! TimeSteward has a few special requirements for serialization:
//! * DataTimelineHandle objects get special consideration, to support DAGs and cyclic data structures.
//! * The serialization needs to not block other operations for more than O(1) time at a time.
//! * The serialization must be lossless and platform-independent. For this reason, we always use bincode in low-endian mode.

macro_rules! time_steward_serialization_impls_for_handle {
  (
    [$($bounds:tt)*]
    [$($concrete:tt)*]
    (&$self_hack: ident)
    Data located at (| $handle: ident | &mut $data: expr)
  ) => {

/*thread_local! {
  static SERIALIZATION_CONTEXT: RefCell<Option <SerializationContext>> = RefCell::new (None);
  static DESERIALIZATION_CONTEXT: RefCell<Option <DeserializationContext>> = RefCell::new (None);
}*/

impl <$($bounds)*> $crate::serde::Serialize for $($concrete)* {
  fn serialize <S: $crate::serde::Serializer> (&$self_hack, serializer: S)->Result <S::Ok, S::Error> {
    unimplemented!()
    /*
    SERIALIZATION_CONTEXT.with (| cell | {
      let mut guard = cell.borrow_mut();
      let mut map = &mut guard.unwrap().map;
      map.insert (
    })
    identifier.serialize (serializer)
    */
  }
}

impl <'a, $($bounds)*> $crate::serde::Deserialize <'a> for $($concrete)* {
  fn deserialize <D: $crate::serde::Deserializer<'a>> (deserializer: D)->Result <Self, D::Error> {
    unimplemented!()
    /*
    let old_identifier = Identifier::deserialize (deserializer)?;
    DESERIALIZATION_CONTEXT.with (| cell | {
      let mut guard = cell.borrow_mut();
      let mut map = &mut guard.unwrap().map;
      Ok(map.entry (old_pointer).or_insert_with (| | {
        unimplemented!()
        /*DataTimelineHandle {
          inner: Arc::new (unsafe {::std::mem::uninitialized()}),
        }*/
      }).clone().downcast::<T> ())
    })
    */
  }
}

/*
impl DeserializationContext {
  fn deserialize_(deserializer: D)->Result <(), D::Error> {
    let old_pointer = usize::deserialize (deserializer)?;
    DESERIALIZATION_CONTEXT.with (| cell | {
      let mut guard = cell.borrow_mut();
      let mut map = &mut guard.unwrap().map;
      let $handle = map.entry (old_pointer).or_insert_with (| | {
        unimplemented!()
        /*DataTimelineHandle {
          inner: Arc::new (unsafe {::std::mem::uninitialized()}),
        }*/
      });
      let new_pointer = &mut $data;
      unsafe {::std::ptr::write (
        new_pointer as *mut DataTimelineHandle,
        unimplemented!() //DataTimeline::deserialize (D)
      )?;}
    })?;
    Ok(())
  }
}*/

  };
}

#[doc (hidden)]
#[macro_export]
macro_rules! time_steward_serialization_impls {
  () => {

  trait StaticDowncast <T> {
    fn static_downcast (self)->T;
  }
  impl <T> StaticDowncast <T> for T {
    fn static_downcast (self)->T {self}
  }
  impl <T, U> StaticDowncast <T> for U {
    default fn static_downcast (self)->T {panic!("Tried to do TimeSteward serialization with non-bincode serializer/deserializer. TimeSteward serialization only supports bincode")}
  }

  use $crate::serde::{Serialize, Deserialize};
  use $crate::serde::ser::Error;

  #[derive (Serialize, Deserialize)]
  enum SerializationElement {
    DataHandleData(u64, PersistentTypeId),
    EventHandleData(u64, PersistentTypeId),
    Finished,
  }

  trait SerializeTargetInto {
    fn serialize_target_into(&self, writer: &mut Write, object_id: u64)->$crate::bincode::Result <()>;
  }
  trait SerializeInto {
    fn serialize_into(&self, writer: &mut Write)->$crate::bincode::Result <()>;
  }
  impl<T: StewardData> SerializeTargetInto for DataHandle <T> {
    fn serialize_target_into(&self, writer: &mut Write, object_id: u64)->$crate::bincode::Result <()> {
      $crate::bincode::serialize_into (writer, & SerializationElement::DataHandleData (object_id, T::ID), $crate::bincode::Infinite)?;
      $crate::bincode::serialize_into (writer, &*self.data, $crate::bincode::Infinite)
    }
  }
  fn data_handle_initialize_function <T: StewardData>(reader: &mut Read, object_id: u64)->$crate::bincode::Result <()> {
    with_deserialization_context (| context | {
      let handle = context.find_handle::<_, DataHandle <T>> (object_id, || {
        Box::new (DataHandle::new(unsafe {::std::mem::uninitialized()}))
      })?;
      unsafe {::std::ptr::write (
        &mut*handle.data as *mut T,
        $crate::bincode::deserialize_from (reader, $crate::bincode::Infinite)?
      );}
    })
  }

  impl<B: Basics> SerializeTargetInto for EventHandle <B> {
    fn serialize_target_into(&self, writer: &mut Write, object_id: u64)->$crate::bincode::Result <()> {
      $crate::bincode::serialize_into (writer, & SerializationElement::DataHandleData (object_id, self.data.data.persistent_type_id()), $crate::bincode::Infinite)?;
      $crate::bincode::serialize_into (writer, self.extended_time(), $crate::bincode::Infinite)?;
      self.data.data.serialize_into (writer)
    }
  }
  impl<T: Serialize> SerializeInto for T {
    fn serialize_into(&self, writer: &mut Write)->$crate::bincode::Result <()> {
      $crate::bincode::serialize_into (writer, self, $crate::bincode::Infinite)
    }
  }
  fn event_handle_initialize_function <T: Event>(reader: &mut Read, object_id: u64)->$crate::bincode::Result <()> {
    with_deserialization_context (| context | {
      let handle = context.find_handle::<_, <T::Steward as TimeSteward>::EventHandle> (object_id, || {
        Box::new (EventHandle { data: unsafe {::std::mem::uninitialized()}})
      })?;
      let time = ::bincode::deserialize_from (reader, $crate::bincode::Infinite);
      let data = ::bincode::deserialize_from (reader, $crate::bincode::Infinite);
      unsafe {::std::ptr::write (
        handle.data as *mut T,
        EventInner {
          time: time.clone(),
          data: Box::new (data),
          should_be_executed: Cell::new (time > context.time),
          is_prediction: time > context.time,
          prediction_destroyed_by: RefCell::new (None),
          execution_state: RefCell::new (None),
        }
      )?;}
    })
  }

  struct SerializationContext {
    snapshot: Box <Any>,
    handle_targets_observed: HashMap <*const (), u64>,
    handles_to_serialize_target: Vec<(u64, Box <SerializeTargetInto>)>,
    next_object_identifier: u64,
  }
  struct DeserializationContext {
    data_handle_initialize_functions: ::std::collections::HashMap <
      PersistentTypeId, fn (&mut Write)->$crate::bincode::Result <()>>,
    event_handle_initialize_functions: ::std::collections::HashMap <
      PersistentTypeId, fn (&mut Write)->$crate::bincode::Result <()>>,
    handles: ::std::collections::HashMap <u64, Box <Any>>,
    uninitialized_handles: ::std::collections::HashSet <u64>,
  }

  thread_local! {
    static SERIALIZATION_CONTEXT: RefCell<Option <SerializationContext>> = RefCell::new (None);
    static DESERIALIZATION_CONTEXT: RefCell<Option <DeserializationContext>> = RefCell::new (None);
  }

  fn with_serialization_context <R, F: FnOnce (&mut SerializationContext)->R> (callback: F)->Result <R, $crate::bincode::Error> {
    SERIALIZATION_CONTEXT.with (| cell | {
      let mut guard = cell.borrow_mut();
      let mut context = match guard.as_mut() {Some(x)=>x, None=>return $crate::bincode::Error::custom("Tried to serialize TimeSteward data without a serialization context.")};
      callback (context)
    })
  }
  fn with_deserialization_context <R, F: FnOnce (&mut DeserializationContext)->R> (callback: F)->Result <R, $crate::bincode::Error> {
    DESERIALIZATION_CONTEXT.with (| cell | {
      let mut guard = cell.borrow_mut();
      let mut context = match guard.as_mut() {Some(x)=>x, None=>return $crate::bincode::Error::custom("Tried to deserialize TimeSteward data without a deserialization context.")};
      callback (context)
    })
  }

  impl SerializationContext {
    fn find_handle <F: FnOnce()->Box <Any>, T> (&mut self, initialize: F)->Result<(), $crate::bincode::Error> {
      let object_identifier = self.handle_targets_observed.entry (self.data as *const ()).or_insert_with (|| {
        let result = self.next_object_identifier;
        self.next_object_identifier += 1;
        self.handles_to_serialize_target.insert (result, initialize());
        result
      });
      object_identifier
    }
  }

  impl DeserializationContext {
    fn find_handle <F: FnOnce()->Box <Any>, T> (&mut self, object_identifier: u64, initialize: F)->Result<&mut T, $crate::bincode::Error> {
      let entry = self.data_handles.entry (object_identifier).or_insert_with (|| {
        let created = initialize();
        self.uninitialized_data_handles.insert (created.clone());
        created
      });
      match (*entry).downcast_mut::<T>() {
        Some (result) => Ok(result),
        None => $crate::bincode::Error::custom("In the serialized snapshot, DataHandles of different types pointed to the same object."),
      }
    }
  }

  impl <T: StewardData> $crate::serde::Serialize for DataHandle <T> {
    fn serialize <S: $crate::serde::Serializer> (&self, serializer: S)->Result <S::Ok, S::Error> {
      with_serialization_context (| context | {
        let object_identifier = context.find_handle::<S, _, DataHandle <T>> (serializer, || {
          Box::new (self.clone())
        })?;
        object_identifier.serialize (serializer)
      })
    }
  }
  impl <'a, T: StewardData> $crate::serde::Deserialize <'a> for DataHandle <T> {
    fn deserialize <D: $crate::serde::Deserializer<'a>> (deserializer: D)->Result <Self, D::Error> {
      with_deserialization_context (| context | {
        let object_identifier = u64::deserialize (deserializer)?;
        context.find_handle::<_, DataHandle <T>> (object_identifier, || {
          Box::new (DataHandle::new(unsafe {::std::mem::uninitialized()}))
        })?.clone()
      })
    }
  }

  impl <B: Basics> $crate::serde::Serialize for EventHandle <B> {
    fn serialize <S: $crate::serde::Serializer> (&self, serializer: S)->Result <S::Ok, S::Error> {
      with_serialization_context (| context | {
        let object_identifier = context.find_handle::<S, _, EventHandle <B>> (serializer, || {
          Box::new (self.clone())
        })?;
        object_identifier.serialize (serializer)
      })
    }
  }
  impl <'a, B: Basics> $crate::serde::Deserialize <'a> for EventHandle <B> {
    fn deserialize <D: $crate::serde::Deserializer<'a>> (deserializer: D)->Result <Self, D::Error> {
      with_deserialization_context (| context | {
        let object_identifier = u64::deserialize (deserializer)?;
        context.find_handle::<_, EventHandle <B>> (object_identifier, || {
          Box::new (EventHandle::new(unsafe {::std::mem::uninitialized()}))
        })?.clone()
      })
    }
  }

  impl <T: DataTimeline> $crate::serde::Serialize for DataTimelineCell <T> {
    fn serialize <S: $crate::serde::Serializer> (&self, serializer: S)->Result <S::Ok, S::Error> {
      with_serialization_context (| context | {
        context.snapshot.get_clone (&self).serialize (serializer)
      })
    }
  }
  impl <'a, T: DataTimeline> $crate::serde::Deserialize <'a> for DataTimelineCell <T> {
    fn deserialize <D: $crate::serde::Deserializer<'a>> (deserializer: D)->Result <Self, D::Error> {
      Self::new (T::deserialize (deserializer))
    }
  }

  fn serialize_snapshot <B: Basics, W: Write> (writer: &mut W, snapshot: SnapshotHandle <B>)->$crate::bincode::Result <()> {
    SERIALIZATION_CONTEXT.with (| cell | {
      {
        let guard = cell.borrow_mut();
        assert!(guard.is_none(), "serializing recursively breaks my hacks and probably makes no sense");
        *guard = Some(SerializationContext {
          snapshot: Box::new (snapshot),
          handle_targets_observed: HashMap::new(),
          handles_to_serialize_target: Vec::new(),
          next_object_identifier: 0,
        });
      }
      // serialize inside a closure so that errors can be collected and we still clear the context afterwards
      let result = || {
        $crate::bincode::serialize_into (writer, snapshot.extended_now(), ::std::mem::size_of::<ExtendedTime <B>>())?;
        $crate::bincode::serialize_into (writer, snapshot.globals(), $crate::bincode::Infinite)?;

        loop {
          if let (object_identifier, handle_box) = cell.borrow().unwrap().handles_to_serialize_target.pop() {
            handle_box.serialize_target_into (writer, object_identifier);
          }
        }

        $crate::bincode::serialize_into (writer, SerializationElement::Finished, $crate::bincode::Infinite)?;

      }();

      {
        let guard = cell.borrow_mut();
        *guard = None;
      }
      result
    })
  }

  fn deserialize_something <B: Basics, R: Read> (reader: &mut R)->$crate::bincode::Result <()> {
    DESERIALIZATION_CONTEXT.with (| cell | {
      {
        let guard = cell.borrow_mut();
        assert!(guard.is_none(), "deserializing recursively breaks my hacks and probably makes no sense");
        *guard = Some(DeserializationContext {
          data_handle_initialize_functions: ::std::collections::HashMap::new(),
          event_handle_initialize_functions: ::std::collections::HashMap::new(),
          handles: ::std::collections::HashMap::new(),
          uninitialized_handles: ::std::collections::HashSet::new(),
        });
      }
      // deserialize inside a closure so that errors can be collected and we still clear the context afterwards
      let result = || {
        let time: ExtendedTime <B> = $crate::bincode::deserialize_from (reader, $crate::bincode::Bounded (::std::mem::size_of::<ExtendedTime <B>>()))?;
        let globals: B::Globals = $crate::bincode::deserialize_from (reader, $crate::bincode::Infinite)?;

        while !cell.borrow().unwrap().uninitialized_handles.is_empty() {
          // TODO: use actual size limits
          let next: SerializationElement = $crate::bincode::deserialize_from (reader, $crate::bincode::Infinite)?;
          match next {
            SerializationElement::DataHandleData (object_id, type_id) => {
              let deserialize_function = cell.borrow().unwrap().data_handle_initialize_functions.get (type_id);
              (*deserialize_function)(reader, object_id);
            }
            SerializationElement::EventHandleData (object_id, type_id) => {
              let deserialize_function = cell.borrow().unwrap().event_handle_initialize_functions.get (type_id);
              (*deserialize_function)(reader, object_id);
            }
            SerializationElement::Finished => {
              // TODO: clean up broken state
              return Err($crate::bincode::Error::custom("Premature end of serialized snapshot"))
            }
          };
        }

        let next: SerializationElement = $crate::bincode::deserialize_from (reader, $crate::bincode::Infinite)?;
        match next {
          SerializationElement::Finished => (),
          _ => {
            return Err($crate::bincode::Error::custom("Serialized snapshot included additional elements after successfully deserializing the state"))
          }
        };

        Ok(Steward::from_globals (globals, ValidSince::Before (time)))
      }();

      {
        let guard = cell.borrow_mut();
        *guard = None;
      }
      result
    })
  }

  };
}

<|endoftext|>


<commit_before>//! Write benchmark information to the output repository

use std::fs::{self, read_dir, File, OpenOptions};
use std::io::{BufRead, BufReader, Read, Write};
use std::path::PathBuf;
use std::process::Command;
use std::str;
use std::collections::HashSet;

use std::thread;
use std::time::{self, Instant};
use serde_json;
use collector::CommitData;
use chrono::{Duration, Utc};
use rust_sysroot::git::Commit as GitCommit;
use execute::Benchmark;
use failure::{Error, ResultExt};

pub struct Repo {
    path: PathBuf,
    use_remote: bool,
    retries: Vec<String>,
}

impl Repo {
    fn git(&self, args: &[&str]) -> Result<(), Error> {
        for iteration in 0..5 {
            let mut command = Command::new("git");
            command.current_dir(&self.path);
            info!("[{}/5]: git {:?}", iteration, args);
            command.args(args);
            let mut child = command
                .spawn()
                .with_context(|_| format!("could not spawn git {:?}", args))?;
            let start_time = Instant::now();
            loop {
                if start_time.elapsed().as_secs() > 3 {
                    warn!("killing git command -- timed out");
                    child.kill()?;
                    break;
                }
                match child.try_wait() {
                    Ok(Some(status)) => {
                        if status.success() {
                            return Ok(());
                        } else {
                            bail!(
                                "command `git {:?}` failed in `{}`",
                                args,
                                self.path.display()
                            );
                        }
                    }
                    Ok(None) => thread::sleep(time::Duration::from_millis(250)),
                    Err(err) => bail!("command `git {:?}` failed to try_wait in {:?}: {:?}",
                        args, self.path.display(), err),

                }
            }
        }
        bail!("failed to run git command, timed out too many times")
    }

    pub fn open(path: PathBuf, allow_new_dir: bool, use_remote: bool) -> Result<Self, Error> {
        let mut result = Repo {
            path: path,
            use_remote,
            retries: vec![],
        };

        // Don't nuke random repositories, unless specifically requested.
        if !allow_new_dir && !result.retries_file().exists() {
            bail!("`{}` file not present", result.retries_file().display());
        }

        if result.use_remote {
            result.git(&["fetch"])?;
            result.git(&["reset", "--hard", "@{upstream}"])?;
        }

        fs::create_dir_all(result.times()).context("can't create `times/`")?;
        result.load_retries()?;

        Ok(result)
    }

    pub fn success(&self, data: &CommitData) -> Result<(), Error> {
        self.add_commit_data(data)?;
        self.commit_and_push(&format!("{} - success", data.commit.sha))?;
        Ok(())
    }

    pub fn find_missing_commits<'a>(
        &self,
        commits: &'a [GitCommit],
        benchmarks: &[Benchmark],
        triple: &str,
    ) -> Result<Vec<&'a GitCommit>, Error> {
        let mut have = HashSet::new();
        let path = self.times();
        for entry in read_dir(path)? {
            let entry = entry?;
            let filename = entry.file_name().to_string_lossy().to_string();
            let sha =
                &filename[filename.find("00:00").unwrap() + 6..filename.find("-x86").unwrap()];
            have.insert(sha.to_string());
        }

        if let Ok(file) = File::open(self.broken_commits_file()) {
            let file = BufReader::new(file);
            for line in file.lines() {
                let line = line?;
                let sha = &line[..line.find(":").unwrap()];
                have.insert(sha.to_string());
            }
        }

        let missing = commits
            .iter()
            .filter(|c| Utc::now().signed_duration_since(c.date) < Duration::days(29))
            .filter(|c| {
                !have.contains(&c.sha) || {
                    self.load_commit_data(c, triple)
                        .ok()
                        .map(|data| {
                            benchmarks
                                .iter()
                                .any(|b| data.benchmarks.keys().find(|k| **k == b.name).is_none())
                        })
                        .unwrap_or(true)
                }
            })
            .collect::<Vec<_>>();

        Ok(missing)
    }

    fn commit_and_push(&self, message: &str) -> Result<(), Error> {
        self.write_retries()?;
        self.git(&["add", "retries", "times"])?;

        // dirty index
        if let Err(_) = self.git(&["diff-index", "--quiet", "--cached", "HEAD"]) {
            self.git(&["commit", "-m", message])?;
            if self.use_remote {
                self.git(&["push"])?;
            }
        } else {
            println!("nothing to commit...");
        }
        Ok(())
    }

    pub fn load_commit_data(&self, commit: &GitCommit, triple: &str) -> Result<CommitData, Error> {
        let filepath = self.times().join(format!(
            "{}-{}-{}.json",
            commit.date.to_rfc3339(),
            commit.sha,
            triple
        ));
        trace!("loading file {}", filepath.display());
        let mut file = File::open(&filepath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        let data = serde_json::from_str(&contents)
            .with_context(|_| format!("failed to read JSON from {:?}", filepath))?;
        Ok(data)
    }

    pub fn add_commit_data(&self, data: &CommitData) -> Result<(), Error> {
        let commit = &data.commit;
        let filepath = self.times().join(format!(
            "{}-{}-{}.json",
            commit.date, commit.sha, data.triple
        ));
        info!("creating file {}", filepath.display());
        let mut file = File::create(&filepath)?;
        serde_json::to_writer(&mut file, &data)?;
        Ok(())
    }

    fn load_retries(&mut self) -> Result<(), Error> {
        let mut retries = OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open(self.retries_file())
            .with_context(|_| format!("can't create `{}`", self.retries_file().display()))?;
        let mut retries_s = String::new();
        retries.read_to_string(&mut retries_s)?;
        self.retries = retries_s
            .split('\n')
            .map(|line| line.trim())
            .filter(|line| !line.is_empty())
            .map(|line| {
                if line.len() == 40 {
                    Ok(line.to_owned())
                } else {
                    bail!("bad retry hash `{}`", line)
                }
            })
            .collect::<Result<_, _>>()?;
        info!("loaded retries: {:?}", self.retries);
        Ok(())
    }

    fn write_retries(&self) -> Result<(), Error> {
        info!("writing retries");
        let mut retries = OpenOptions::new()
            .write(true)
            .truncate(true)
            .open(self.retries_file())
            .context("can't create `retries`")?;
        for retry in self.retries.iter() {
            writeln!(retries, "{}", retry)?;
        }
        Ok(())
    }

    pub fn write_broken_commit(&self, commit: &GitCommit, err: Error) -> Result<(), Error> {
        info!("writing broken commit {:?}: {:?}", commit, err);
        let mut broken = OpenOptions::new()
            .write(true)
            .append(true)
            .create(true)
            .open(self.broken_commits_file())
            .context("can't create `broken-commits-log`")?;
        writeln!(broken, "{}: \"{:?}\"", commit.sha, err)?;
        Ok(())
    }

    pub fn next_retry(&mut self) -> Option<String> {
        if self.retries.len() == 0 {
            None
        } else {
            Some(self.retries.remove(0))
        }
    }

    fn broken_commits_file(&self) -> PathBuf {
        self.path.join("broken-commits-log")
    }

    fn retries_file(&self) -> PathBuf {
        self.path.join("retries")
    }

    fn times(&self) -> PathBuf {
        self.path.join("times")
    }
}
<commit_msg>Add debug logging when waiting for command<commit_after>//! Write benchmark information to the output repository

use std::fs::{self, read_dir, File, OpenOptions};
use std::io::{BufRead, BufReader, Read, Write};
use std::path::PathBuf;
use std::process::Command;
use std::str;
use std::collections::HashSet;

use std::thread;
use std::time::{self, Instant};
use serde_json;
use collector::CommitData;
use chrono::{Duration, Utc};
use rust_sysroot::git::Commit as GitCommit;
use execute::Benchmark;
use failure::{Error, ResultExt};

pub struct Repo {
    path: PathBuf,
    use_remote: bool,
    retries: Vec<String>,
}

impl Repo {
    fn git(&self, args: &[&str]) -> Result<(), Error> {
        for iteration in 0..5 {
            let mut command = Command::new("git");
            command.current_dir(&self.path);
            info!("[{}/5]: git {:?}", iteration, args);
            command.args(args);
            let mut child = command
                .spawn()
                .with_context(|_| format!("could not spawn git {:?}", args))?;
            let start_time = Instant::now();
            loop {
                if start_time.elapsed().as_secs() > 3 {
                    warn!("killing git command -- timed out");
                    child.kill()?;
                    break;
                }
                match child.try_wait() {
                    Ok(Some(status)) => {
                        if status.success() {
                            return Ok(());
                        } else {
                            bail!(
                                "command `git {:?}` failed in `{}`",
                                args,
                                self.path.display()
                            );
                        }
                    }
                    Ok(None) => {
                        debug!("waiting 250ms...");
                        thread::sleep(time::Duration::from_millis(250));
                    },
                    Err(err) => bail!("command `git {:?}` failed to try_wait in {:?}: {:?}",
                        args, self.path.display(), err),

                }
            }
        }
        bail!("failed to run git command, timed out too many times")
    }

    pub fn open(path: PathBuf, allow_new_dir: bool, use_remote: bool) -> Result<Self, Error> {
        let mut result = Repo {
            path: path,
            use_remote,
            retries: vec![],
        };

        // Don't nuke random repositories, unless specifically requested.
        if !allow_new_dir && !result.retries_file().exists() {
            bail!("`{}` file not present", result.retries_file().display());
        }

        if result.use_remote {
            result.git(&["fetch"])?;
            result.git(&["reset", "--hard", "@{upstream}"])?;
        }

        fs::create_dir_all(result.times()).context("can't create `times/`")?;
        result.load_retries()?;

        Ok(result)
    }

    pub fn success(&self, data: &CommitData) -> Result<(), Error> {
        self.add_commit_data(data)?;
        self.commit_and_push(&format!("{} - success", data.commit.sha))?;
        Ok(())
    }

    pub fn find_missing_commits<'a>(
        &self,
        commits: &'a [GitCommit],
        benchmarks: &[Benchmark],
        triple: &str,
    ) -> Result<Vec<&'a GitCommit>, Error> {
        let mut have = HashSet::new();
        let path = self.times();
        for entry in read_dir(path)? {
            let entry = entry?;
            let filename = entry.file_name().to_string_lossy().to_string();
            let sha =
                &filename[filename.find("00:00").unwrap() + 6..filename.find("-x86").unwrap()];
            have.insert(sha.to_string());
        }

        if let Ok(file) = File::open(self.broken_commits_file()) {
            let file = BufReader::new(file);
            for line in file.lines() {
                let line = line?;
                let sha = &line[..line.find(":").unwrap()];
                have.insert(sha.to_string());
            }
        }

        let missing = commits
            .iter()
            .filter(|c| Utc::now().signed_duration_since(c.date) < Duration::days(29))
            .filter(|c| {
                !have.contains(&c.sha) || {
                    self.load_commit_data(c, triple)
                        .ok()
                        .map(|data| {
                            benchmarks
                                .iter()
                                .any(|b| data.benchmarks.keys().find(|k| **k == b.name).is_none())
                        })
                        .unwrap_or(true)
                }
            })
            .collect::<Vec<_>>();

        Ok(missing)
    }

    fn commit_and_push(&self, message: &str) -> Result<(), Error> {
        self.write_retries()?;
        self.git(&["add", "retries", "times"])?;

        // dirty index
        if let Err(_) = self.git(&["diff-index", "--quiet", "--cached", "HEAD"]) {
            self.git(&["commit", "-m", message])?;
            if self.use_remote {
                self.git(&["push"])?;
            }
        } else {
            println!("nothing to commit...");
        }
        Ok(())
    }

    pub fn load_commit_data(&self, commit: &GitCommit, triple: &str) -> Result<CommitData, Error> {
        let filepath = self.times().join(format!(
            "{}-{}-{}.json",
            commit.date.to_rfc3339(),
            commit.sha,
            triple
        ));
        trace!("loading file {}", filepath.display());
        let mut file = File::open(&filepath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        let data = serde_json::from_str(&contents)
            .with_context(|_| format!("failed to read JSON from {:?}", filepath))?;
        Ok(data)
    }

    pub fn add_commit_data(&self, data: &CommitData) -> Result<(), Error> {
        let commit = &data.commit;
        let filepath = self.times().join(format!(
            "{}-{}-{}.json",
            commit.date, commit.sha, data.triple
        ));
        info!("creating file {}", filepath.display());
        let mut file = File::create(&filepath)?;
        serde_json::to_writer(&mut file, &data)?;
        Ok(())
    }

    fn load_retries(&mut self) -> Result<(), Error> {
        let mut retries = OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open(self.retries_file())
            .with_context(|_| format!("can't create `{}`", self.retries_file().display()))?;
        let mut retries_s = String::new();
        retries.read_to_string(&mut retries_s)?;
        self.retries = retries_s
            .split('\n')
            .map(|line| line.trim())
            .filter(|line| !line.is_empty())
            .map(|line| {
                if line.len() == 40 {
                    Ok(line.to_owned())
                } else {
                    bail!("bad retry hash `{}`", line)
                }
            })
            .collect::<Result<_, _>>()?;
        info!("loaded retries: {:?}", self.retries);
        Ok(())
    }

    fn write_retries(&self) -> Result<(), Error> {
        info!("writing retries");
        let mut retries = OpenOptions::new()
            .write(true)
            .truncate(true)
            .open(self.retries_file())
            .context("can't create `retries`")?;
        for retry in self.retries.iter() {
            writeln!(retries, "{}", retry)?;
        }
        Ok(())
    }

    pub fn write_broken_commit(&self, commit: &GitCommit, err: Error) -> Result<(), Error> {
        info!("writing broken commit {:?}: {:?}", commit, err);
        let mut broken = OpenOptions::new()
            .write(true)
            .append(true)
            .create(true)
            .open(self.broken_commits_file())
            .context("can't create `broken-commits-log`")?;
        writeln!(broken, "{}: \"{:?}\"", commit.sha, err)?;
        Ok(())
    }

    pub fn next_retry(&mut self) -> Option<String> {
        if self.retries.len() == 0 {
            None
        } else {
            Some(self.retries.remove(0))
        }
    }

    fn broken_commits_file(&self) -> PathBuf {
        self.path.join("broken-commits-log")
    }

    fn retries_file(&self) -> PathBuf {
        self.path.join("retries")
    }

    fn times(&self) -> PathBuf {
        self.path.join("times")
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::util::{
    is_relay_custom_inline_fragment_directive, PointerAddress, CUSTOM_METADATA_DIRECTIVES,
};
use graphql_ir::{
    Condition, Directive, FragmentDefinition, InlineFragment, LinkedField, OperationDefinition,
    Program, ScalarField, Selection,
};
use schema::TypeReference;

use crate::node_identifier::{LocationAgnosticPartialEq, NodeIdentifier};
use common::NamedItem;
use fnv::FnvHashMap;
use parking_lot::RwLock;
use rayon::prelude::*;
use schema::Schema;
use std::sync::Arc;

type SeenLinkedFields = Arc<RwLock<FnvHashMap<PointerAddress, Arc<LinkedField>>>>;

///
/// Transform that flattens inline fragments, fragment spreads, merges linked fields selections.
///
/// Inline fragments are inlined (replaced with their selections) when:
/// - The fragment type matches the type of its parent, and it `is_for_codegen`,
///   or the inline fragment doesn't have directives .
///
/// with the exception that it never flattens the inline fragment with relay
/// directives (@defer, @__clientExtensions).
///
pub fn flatten(program: &Program, is_for_codegen: bool) -> Program {
    let next_program = Arc::new(RwLock::new(Program::new(Arc::clone(&program.schema))));
    let transform = FlattenTransform::new(program, is_for_codegen);

    program.par_operations().for_each(|operation| {
        let operation = Arc::new(transform.transform_operation(operation));
        {
            let mut next_program = next_program.write();
            next_program.insert_operation(operation);
        }
    });
    program.par_fragments().for_each(|fragment| {
        let fragment = Arc::new(transform.transform_fragment(fragment));
        {
            let mut next_program = next_program.write();
            next_program.insert_fragment(fragment);
        }
    });
    Arc::try_unwrap(next_program).unwrap().into_inner()
}

struct FlattenTransform {
    schema: Arc<Schema>,
    is_for_codegen: bool,
    seen_linked_fields: SeenLinkedFields,
}

impl FlattenTransform {
    fn new(program: &'_ Program, is_for_codegen: bool) -> Self {
        Self {
            schema: Arc::clone(&program.schema),
            is_for_codegen,
            seen_linked_fields: Default::default(),
        }
    }

    fn transform_operation(&self, operation: &OperationDefinition) -> OperationDefinition {
        OperationDefinition {
            kind: operation.kind,
            name: operation.name,
            type_: operation.type_,
            directives: operation.directives.clone(),
            variable_definitions: operation.variable_definitions.clone(),
            selections: self.tranform_selections(
                &operation.selections,
                &TypeReference::Named(operation.type_),
            ),
        }
    }

    fn transform_fragment(&self, fragment: &FragmentDefinition) -> FragmentDefinition {
        FragmentDefinition {
            name: fragment.name,
            type_condition: fragment.type_condition,
            directives: fragment.directives.clone(),
            variable_definitions: fragment.variable_definitions.clone(),
            used_global_variables: fragment.used_global_variables.clone(),
            selections: self.tranform_selections(
                &fragment.selections,
                &TypeReference::Named(fragment.type_condition),
            ),
        }
    }

    fn tranform_selections(
        &self,
        selections: &[Selection],
        parent_type: &TypeReference,
    ) -> Vec<Selection> {
        let next_selections = selections
            .iter()
            .map(|s| self.transform_selection(s, parent_type))
            .collect::<Vec<_>>();
        let mut flattened_selections = Vec::with_capacity(next_selections.len());
        self.flatten_selections(&mut flattened_selections, &next_selections, parent_type);

        flattened_selections
    }

    fn transform_linked_field(&self, linked_field: &Arc<LinkedField>) -> Arc<LinkedField> {
        let key = PointerAddress::new(Arc::as_ref(linked_field));
        {
            let seen_linked_fields = self.seen_linked_fields.read();
            if let Some(prev) = seen_linked_fields.get(&key) {
                return Arc::clone(prev);
            }
        }
        let type_ = &self
            .schema
            .field(linked_field.definition.item)
            .type_
            .clone();
        let result = Arc::new(LinkedField {
            alias: linked_field.alias,
            definition: linked_field.definition,
            arguments: linked_field.arguments.clone(),
            directives: linked_field.directives.clone(),
            selections: self.tranform_selections(&linked_field.selections, &type_),
        });
        let mut seen_linked_fields = self.seen_linked_fields.write();
        // If another thread computed this in the meantime, use that result
        if let Some(prev) = seen_linked_fields.get(&key) {
            return Arc::clone(prev);
        }
        seen_linked_fields.insert(key, Arc::clone(&result));
        result
    }

    fn transform_selection(&self, selection: &Selection, parent_type: &TypeReference) -> Selection {
        match selection {
            Selection::InlineFragment(node) => {
                let next_parent_type: TypeReference = match node.type_condition {
                    Some(type_condition) => TypeReference::Named(type_condition),
                    None => parent_type.clone(),
                };
                Selection::InlineFragment(Arc::new(InlineFragment {
                    type_condition: node.type_condition,
                    directives: node.directives.clone(),
                    selections: self.tranform_selections(&node.selections, &next_parent_type),
                }))
            }
            Selection::LinkedField(node) => {
                Selection::LinkedField(self.transform_linked_field(node))
            }
            Selection::Condition(node) => Selection::Condition(Arc::new(Condition {
                value: node.value.clone(),
                passing_value: node.passing_value,
                selections: self.tranform_selections(&node.selections, parent_type),
            })),
            Selection::FragmentSpread(node) => Selection::FragmentSpread(Arc::clone(node)),
            Selection::ScalarField(node) => Selection::ScalarField(Arc::clone(node)),
        }
    }

    fn flatten_selections(
        &self,
        flattened_selections: &mut Vec<Selection>,
        selections: &[Selection],
        parent_type: &TypeReference,
    ) {
        for selection in selections {
            if let Selection::InlineFragment(inline_fragment) = selection {
                if should_flatten_inline_fragment(inline_fragment, parent_type, self.is_for_codegen)
                {
                    self.flatten_selections(
                        flattened_selections,
                        &inline_fragment.selections,
                        parent_type,
                    );
                    continue;
                }
            }

            let flattened_selection = flattened_selections
                .iter_mut()
                .find(|sel| NodeIdentifier::are_equal(&self.schema, sel, selection));

            match flattened_selection {
                None => {
                    flattened_selections.push(selection.clone());
                }
                Some(flattened_selection) => {
                    match flattened_selection {
                        Selection::InlineFragment(flattened_node) => {
                            let type_condition: TypeReference = match flattened_node.type_condition
                            {
                                Some(type_condition) => TypeReference::Named(type_condition),
                                None => parent_type.clone(),
                            };

                            let node_selections = match selection {
                                Selection::InlineFragment(node) => &node.selections,
                                _ => unreachable!("FlattenTransform: Expected an InlineFragment."),
                            };

                            *flattened_selection =
                                Selection::InlineFragment(Arc::new(InlineFragment {
                                    type_condition: flattened_node.type_condition,
                                    directives: flattened_node.directives.clone(),
                                    selections: self.merge_selections(
                                        &flattened_node.selections,
                                        &node_selections,
                                        &type_condition,
                                    ),
                                }));
                        }
                        Selection::LinkedField(flattened_node) => {
                            let node_selections = match selection {
                                Selection::LinkedField(node) => &node.selections,
                                _ => unreachable!("FlattenTransform: Expected a LinkedField."),
                            };
                            let type_ = &self
                                .schema
                                .field(flattened_node.definition.item)
                                .type_
                                .clone();
                            let should_merge_handles = selection.directives().iter().any(|d| {
                                CUSTOM_METADATA_DIRECTIVES.is_handle_field_directive(d.name.item)
                            });
                            let next_directives = if should_merge_handles {
                                merge_handle_directives(
                                    &flattened_node.directives,
                                    selection.directives(),
                                )
                            } else {
                                flattened_node.directives.clone()
                            };
                            *flattened_selection = Selection::LinkedField(Arc::new(LinkedField {
                                alias: flattened_node.alias,
                                definition: flattened_node.definition,
                                arguments: flattened_node.arguments.clone(),
                                directives: next_directives,
                                selections: self.merge_selections(
                                    &flattened_node.selections,
                                    &node_selections,
                                    &type_,
                                ),
                            }));
                        }
                        Selection::Condition(flattened_node) => {
                            let node_selections = match selection {
                                Selection::Condition(node) => &node.selections,
                                _ => unreachable!("FlattenTransform: Expected a Condition."),
                            };

                            *flattened_selection = Selection::Condition(Arc::new(Condition {
                                value: flattened_node.value.clone(),
                                passing_value: flattened_node.passing_value,
                                selections: self.merge_selections(
                                    &flattened_node.selections,
                                    &node_selections,
                                    parent_type,
                                ),
                            }));
                        }
                        Selection::ScalarField(flattened_node) => {
                            let should_merge_handles = selection.directives().iter().any(|d| {
                                CUSTOM_METADATA_DIRECTIVES.is_handle_field_directive(d.name.item)
                            });
                            if should_merge_handles {
                                *flattened_selection =
                                    Selection::ScalarField(Arc::new(ScalarField {
                                        alias: flattened_node.alias,
                                        definition: flattened_node.definition,
                                        arguments: flattened_node.arguments.clone(),
                                        directives: merge_handle_directives(
                                            &flattened_node.directives,
                                            selection.directives(),
                                        ),
                                    }))
                            }
                        }
                        Selection::FragmentSpread(_) => {}
                    };
                }
            }
        }
    }

    fn merge_selections(
        &self,
        selections_a: &[Selection],
        selections_b: &[Selection],
        parent_type: &TypeReference,
    ) -> Vec<Selection> {
        let mut flattened_selections = Vec::with_capacity(selections_a.len());
        self.flatten_selections(&mut flattened_selections, selections_a, parent_type);
        self.flatten_selections(&mut flattened_selections, selections_b, parent_type);
        flattened_selections
    }
}

fn should_flatten_inline_with_directives(directives: &[Directive], is_for_codegen: bool) -> bool {
    if is_for_codegen {
        !directives
            .iter()
            .any(is_relay_custom_inline_fragment_directive)
    } else {
        directives.is_empty()
    }
}

fn should_flatten_inline_fragment(
    inline_fragment: &InlineFragment,
    parent_type: &TypeReference,
    is_for_codegen: bool,
) -> bool {
    match inline_fragment.type_condition {
        None => should_flatten_inline_with_directives(&inline_fragment.directives, is_for_codegen),
        Some(type_condition) => {
            type_condition == parent_type.inner()
                && should_flatten_inline_with_directives(
                    &inline_fragment.directives,
                    is_for_codegen,
                )
        }
    }
}

fn merge_handle_directives(
    directives_a: &[Directive],
    directives_b: &[Directive],
) -> Vec<Directive> {
    let (mut handles, mut directives): (Vec<_>, Vec<_>) =
        directives_a.iter().cloned().partition(|directive| {
            CUSTOM_METADATA_DIRECTIVES.is_handle_field_directive(directive.name.item)
        });
    for directive in directives_b {
        if CUSTOM_METADATA_DIRECTIVES.is_handle_field_directive(directive.name.item) {
            if handles.is_empty() {
                handles.push(directive.clone());
            } else {
                let current_handler_arg = directive.arguments.named(
                    CUSTOM_METADATA_DIRECTIVES
                        .handle_field_constants
                        .handler_arg_name,
                );
                let current_name_arg = directive.arguments.named(
                    CUSTOM_METADATA_DIRECTIVES
                        .handle_field_constants
                        .key_arg_name,
                );
                let is_duplicate_handle = handles.iter().any(|handle| {
                    current_handler_arg.location_agnostic_eq(
                        &handle.arguments.named(
                            CUSTOM_METADATA_DIRECTIVES
                                .handle_field_constants
                                .handler_arg_name,
                        ),
                    ) && current_name_arg.location_agnostic_eq(
                        &handle.arguments.named(
                            CUSTOM_METADATA_DIRECTIVES
                                .handle_field_constants
                                .key_arg_name,
                        ),
                    )
                });
                if !is_duplicate_handle {
                    handles.push(directive.clone());
                }
            }
        }
    }
    directives.extend(handles.into_iter());
    directives
}
<commit_msg>optimize flatten; memoize inline fragments<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::util::{
    is_relay_custom_inline_fragment_directive, PointerAddress, CUSTOM_METADATA_DIRECTIVES,
};
use graphql_ir::{
    Condition, Directive, FragmentDefinition, InlineFragment, LinkedField, OperationDefinition,
    Program, ScalarField, Selection,
};
use schema::Type;

use crate::node_identifier::{LocationAgnosticPartialEq, NodeIdentifier};
use common::NamedItem;
use fnv::FnvHashMap;
use parking_lot::RwLock;
use rayon::prelude::*;
use schema::Schema;
use std::sync::Arc;

type SeenLinkedFields = Arc<RwLock<FnvHashMap<PointerAddress, Arc<LinkedField>>>>;
type SeenInlineFragments = Arc<RwLock<FnvHashMap<(PointerAddress, Type), Arc<InlineFragment>>>>;

///
/// Transform that flattens inline fragments, fragment spreads, merges linked fields selections.
///
/// Inline fragments are inlined (replaced with their selections) when:
/// - The fragment type matches the type of its parent, and it `is_for_codegen`,
///   or the inline fragment doesn't have directives .
///
/// with the exception that it never flattens the inline fragment with relay
/// directives (@defer, @__clientExtensions).
///
pub fn flatten(program: &Program, is_for_codegen: bool) -> Program {
    let next_program = Arc::new(RwLock::new(Program::new(Arc::clone(&program.schema))));
    let transform = FlattenTransform::new(program, is_for_codegen);

    program.par_operations().for_each(|operation| {
        let operation = Arc::new(transform.transform_operation(operation));
        {
            let mut next_program = next_program.write();
            next_program.insert_operation(operation);
        }
    });
    program.par_fragments().for_each(|fragment| {
        let fragment = Arc::new(transform.transform_fragment(fragment));
        {
            let mut next_program = next_program.write();
            next_program.insert_fragment(fragment);
        }
    });
    Arc::try_unwrap(next_program).unwrap().into_inner()
}

struct FlattenTransform {
    schema: Arc<Schema>,
    is_for_codegen: bool,
    seen_linked_fields: SeenLinkedFields,
    seen_inline_fragments: SeenInlineFragments,
}

impl FlattenTransform {
    fn new(program: &'_ Program, is_for_codegen: bool) -> Self {
        Self {
            schema: Arc::clone(&program.schema),
            is_for_codegen,
            seen_linked_fields: Default::default(),
            seen_inline_fragments: Default::default(),
        }
    }

    fn transform_operation(&self, operation: &OperationDefinition) -> OperationDefinition {
        OperationDefinition {
            kind: operation.kind,
            name: operation.name,
            type_: operation.type_,
            directives: operation.directives.clone(),
            variable_definitions: operation.variable_definitions.clone(),
            selections: self.tranform_selections(&operation.selections, operation.type_),
        }
    }

    fn transform_fragment(&self, fragment: &FragmentDefinition) -> FragmentDefinition {
        FragmentDefinition {
            name: fragment.name,
            type_condition: fragment.type_condition,
            directives: fragment.directives.clone(),
            variable_definitions: fragment.variable_definitions.clone(),
            used_global_variables: fragment.used_global_variables.clone(),
            selections: self.tranform_selections(&fragment.selections, fragment.type_condition),
        }
    }

    fn tranform_selections(&self, selections: &[Selection], parent_type: Type) -> Vec<Selection> {
        let next_selections = selections
            .iter()
            .map(|s| self.transform_selection(s, parent_type))
            .collect::<Vec<_>>();
        let mut flattened_selections = Vec::with_capacity(next_selections.len());
        self.flatten_selections(&mut flattened_selections, &next_selections, parent_type);

        flattened_selections
    }

    fn transform_linked_field(&self, linked_field: &Arc<LinkedField>) -> Arc<LinkedField> {
        let should_cache = Arc::strong_count(linked_field) > 1;
        let key = PointerAddress::new(Arc::as_ref(linked_field));
        if should_cache {
            let seen_linked_fields = self.seen_linked_fields.read();
            if let Some(prev) = seen_linked_fields.get(&key) {
                return Arc::clone(prev);
            }
        }
        let type_ = self
            .schema
            .field(linked_field.definition.item)
            .type_
            .inner();
        let result = Arc::new(LinkedField {
            alias: linked_field.alias,
            definition: linked_field.definition,
            arguments: linked_field.arguments.clone(),
            directives: linked_field.directives.clone(),
            selections: self.tranform_selections(&linked_field.selections, type_),
        });
        if should_cache {
            let mut seen_linked_fields = self.seen_linked_fields.write();
            // If another thread computed this in the meantime, use that result
            if let Some(prev) = seen_linked_fields.get(&key) {
                return Arc::clone(prev);
            }
            seen_linked_fields.insert(key, Arc::clone(&result));
        }
        result
    }

    fn transform_inline_fragment(
        &self,
        fragment: &Arc<InlineFragment>,
        parent_type: Type,
    ) -> Arc<InlineFragment> {
        let should_cache = Arc::strong_count(fragment) > 1;
        let key = (PointerAddress::new(Arc::as_ref(fragment)), parent_type);
        if should_cache {
            let seen_inline_fragments = self.seen_inline_fragments.read();
            if let Some(prev) = seen_inline_fragments.get(&key) {
                return Arc::clone(prev);
            }
        }
        let next_parent_type = match fragment.type_condition {
            Some(type_condition) => type_condition,
            None => parent_type,
        };
        let result = Arc::new(InlineFragment {
            type_condition: fragment.type_condition,
            directives: fragment.directives.clone(),
            selections: self.tranform_selections(&fragment.selections, next_parent_type),
        });
        if should_cache {
            let mut seen_inline_fragments = self.seen_inline_fragments.write();
            // If another thread computed this in the meantime, use that result
            if let Some(prev) = seen_inline_fragments.get(&key) {
                return Arc::clone(prev);
            }
            seen_inline_fragments.insert(key, Arc::clone(&result));
        }
        result
    }

    fn transform_selection(&self, selection: &Selection, parent_type: Type) -> Selection {
        match selection {
            Selection::InlineFragment(node) => {
                Selection::InlineFragment(self.transform_inline_fragment(node, parent_type))
            }
            Selection::LinkedField(node) => {
                Selection::LinkedField(self.transform_linked_field(node))
            }
            Selection::Condition(node) => Selection::Condition(Arc::new(Condition {
                value: node.value.clone(),
                passing_value: node.passing_value,
                selections: self.tranform_selections(&node.selections, parent_type),
            })),
            Selection::FragmentSpread(node) => Selection::FragmentSpread(Arc::clone(node)),
            Selection::ScalarField(node) => Selection::ScalarField(Arc::clone(node)),
        }
    }

    fn flatten_selections(
        &self,
        flattened_selections: &mut Vec<Selection>,
        selections: &[Selection],
        parent_type: Type,
    ) {
        for selection in selections {
            if let Selection::InlineFragment(inline_fragment) = selection {
                if should_flatten_inline_fragment(inline_fragment, parent_type, self.is_for_codegen)
                {
                    self.flatten_selections(
                        flattened_selections,
                        &inline_fragment.selections,
                        parent_type,
                    );
                    continue;
                }
            }

            let flattened_selection = flattened_selections
                .iter_mut()
                .find(|sel| NodeIdentifier::are_equal(&self.schema, sel, selection));

            match flattened_selection {
                None => {
                    flattened_selections.push(selection.clone());
                }
                Some(flattened_selection) => {
                    match flattened_selection {
                        Selection::InlineFragment(flattened_node) => {
                            let type_condition = match flattened_node.type_condition {
                                Some(type_condition) => type_condition,
                                None => parent_type,
                            };

                            let node_selections = match selection {
                                Selection::InlineFragment(node) => &node.selections,
                                _ => unreachable!("FlattenTransform: Expected an InlineFragment."),
                            };

                            *flattened_selection =
                                Selection::InlineFragment(Arc::new(InlineFragment {
                                    type_condition: flattened_node.type_condition,
                                    directives: flattened_node.directives.clone(),
                                    selections: self.merge_selections(
                                        &flattened_node.selections,
                                        &node_selections,
                                        type_condition,
                                    ),
                                }));
                        }
                        Selection::LinkedField(flattened_node) => {
                            let node_selections = match selection {
                                Selection::LinkedField(node) => &node.selections,
                                _ => unreachable!("FlattenTransform: Expected a LinkedField."),
                            };
                            let type_ = self
                                .schema
                                .field(flattened_node.definition.item)
                                .type_
                                .inner();
                            let should_merge_handles = selection.directives().iter().any(|d| {
                                CUSTOM_METADATA_DIRECTIVES.is_handle_field_directive(d.name.item)
                            });
                            let next_directives = if should_merge_handles {
                                merge_handle_directives(
                                    &flattened_node.directives,
                                    selection.directives(),
                                )
                            } else {
                                flattened_node.directives.clone()
                            };
                            *flattened_selection = Selection::LinkedField(Arc::new(LinkedField {
                                alias: flattened_node.alias,
                                definition: flattened_node.definition,
                                arguments: flattened_node.arguments.clone(),
                                directives: next_directives,
                                selections: self.merge_selections(
                                    &flattened_node.selections,
                                    &node_selections,
                                    type_,
                                ),
                            }));
                        }
                        Selection::Condition(flattened_node) => {
                            let node_selections = match selection {
                                Selection::Condition(node) => &node.selections,
                                _ => unreachable!("FlattenTransform: Expected a Condition."),
                            };

                            *flattened_selection = Selection::Condition(Arc::new(Condition {
                                value: flattened_node.value.clone(),
                                passing_value: flattened_node.passing_value,
                                selections: self.merge_selections(
                                    &flattened_node.selections,
                                    &node_selections,
                                    parent_type,
                                ),
                            }));
                        }
                        Selection::ScalarField(flattened_node) => {
                            let should_merge_handles = selection.directives().iter().any(|d| {
                                CUSTOM_METADATA_DIRECTIVES.is_handle_field_directive(d.name.item)
                            });
                            if should_merge_handles {
                                *flattened_selection =
                                    Selection::ScalarField(Arc::new(ScalarField {
                                        alias: flattened_node.alias,
                                        definition: flattened_node.definition,
                                        arguments: flattened_node.arguments.clone(),
                                        directives: merge_handle_directives(
                                            &flattened_node.directives,
                                            selection.directives(),
                                        ),
                                    }))
                            }
                        }
                        Selection::FragmentSpread(_) => {}
                    };
                }
            }
        }
    }

    fn merge_selections(
        &self,
        selections_a: &[Selection],
        selections_b: &[Selection],
        parent_type: Type,
    ) -> Vec<Selection> {
        let mut flattened_selections = Vec::with_capacity(selections_a.len());
        self.flatten_selections(&mut flattened_selections, selections_a, parent_type);
        self.flatten_selections(&mut flattened_selections, selections_b, parent_type);
        flattened_selections
    }
}

fn should_flatten_inline_with_directives(directives: &[Directive], is_for_codegen: bool) -> bool {
    if is_for_codegen {
        !directives
            .iter()
            .any(is_relay_custom_inline_fragment_directive)
    } else {
        directives.is_empty()
    }
}

fn should_flatten_inline_fragment(
    inline_fragment: &InlineFragment,
    parent_type: Type,
    is_for_codegen: bool,
) -> bool {
    match inline_fragment.type_condition {
        None => should_flatten_inline_with_directives(&inline_fragment.directives, is_for_codegen),
        Some(type_condition) => {
            type_condition == parent_type
                && should_flatten_inline_with_directives(
                    &inline_fragment.directives,
                    is_for_codegen,
                )
        }
    }
}

fn merge_handle_directives(
    directives_a: &[Directive],
    directives_b: &[Directive],
) -> Vec<Directive> {
    let (mut handles, mut directives): (Vec<_>, Vec<_>) =
        directives_a.iter().cloned().partition(|directive| {
            CUSTOM_METADATA_DIRECTIVES.is_handle_field_directive(directive.name.item)
        });
    for directive in directives_b {
        if CUSTOM_METADATA_DIRECTIVES.is_handle_field_directive(directive.name.item) {
            if handles.is_empty() {
                handles.push(directive.clone());
            } else {
                let current_handler_arg = directive.arguments.named(
                    CUSTOM_METADATA_DIRECTIVES
                        .handle_field_constants
                        .handler_arg_name,
                );
                let current_name_arg = directive.arguments.named(
                    CUSTOM_METADATA_DIRECTIVES
                        .handle_field_constants
                        .key_arg_name,
                );
                let is_duplicate_handle = handles.iter().any(|handle| {
                    current_handler_arg.location_agnostic_eq(
                        &handle.arguments.named(
                            CUSTOM_METADATA_DIRECTIVES
                                .handle_field_constants
                                .handler_arg_name,
                        ),
                    ) && current_name_arg.location_agnostic_eq(
                        &handle.arguments.named(
                            CUSTOM_METADATA_DIRECTIVES
                                .handle_field_constants
                                .key_arg_name,
                        ),
                    )
                });
                if !is_duplicate_handle {
                    handles.push(directive.clone());
                }
            }
        }
    }
    directives.extend(handles.into_iter());
    directives
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! This module implements structured cloning, as defined by [HTML]
//! (https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data).

use dom::bindings::error::{Error, Fallible};
use dom::globalscope::GlobalScope;
use js::jsapi::{HandleValue, MutableHandleValue};
use js::jsapi::{JSContext, JS_ReadStructuredClone, JS_STRUCTURED_CLONE_VERSION};
use js::jsapi::{JS_ClearPendingException, JS_WriteStructuredClone};
use libc::size_t;
use std::ptr;
use std::slice;

/// A buffer for a structured clone.
pub enum StructuredCloneData {
    /// A non-serializable (default) variant
    Struct(*mut u64, size_t),
    /// A variant that can be serialized
    Vector(Vec<u8>)
}

impl StructuredCloneData {
    /// Writes a structured clone. Returns a `DataClone` error if that fails.
    pub fn write(cx: *mut JSContext, message: HandleValue) -> Fallible<StructuredCloneData> {
        let mut data = ptr::null_mut();
        let mut nbytes = 0;
        let result = unsafe {
            JS_WriteStructuredClone(cx,
                                    message,
                                    &mut data,
                                    &mut nbytes,
                                    ptr::null(),
                                    ptr::null_mut(),
                                    HandleValue::undefined())
        };
        if !result {
            unsafe {
                JS_ClearPendingException(cx);
            }
            return Err(Error::DataClone);
        }
        Ok(StructuredCloneData::Struct(data, nbytes))
    }

    /// Converts a StructuredCloneData to Vec<u8> for inter-thread sharing
    pub fn move_to_arraybuffer(self) -> Vec<u8> {
        match self {
            StructuredCloneData::Struct(data, nbytes) => {
                unsafe {
                    slice::from_raw_parts(data as *mut u8, nbytes).to_vec()
                }
            }
            StructuredCloneData::Vector(msg) => msg
        }
    }

    /// Reads a structured clone.
    ///
    /// Panics if `JS_ReadStructuredClone` fails.
    fn read_clone(global: &GlobalScope, data: *mut u64, nbytes: size_t, rval: MutableHandleValue) {
        unsafe {
            assert!(JS_ReadStructuredClone(global.get_cx(),
                                           data,
                                           nbytes,
                                           JS_STRUCTURED_CLONE_VERSION,
                                           rval,
                                           ptr::null(),
                                           ptr::null_mut()));
        }
    }

    /// Thunk for the actual `read_clone` method. Resolves proper variant for read_clone.
    pub fn read(self, global: &GlobalScope, rval: MutableHandleValue) {
        match self {
            StructuredCloneData::Vector(mut vec_msg) => {
                let nbytes = vec_msg.len();
                let data = vec_msg.as_mut_ptr() as *mut u64;
                StructuredCloneData::read_clone(global, data, nbytes, rval);
            }
            StructuredCloneData::Struct(data, nbytes) => StructuredCloneData::read_clone(global, data, nbytes, rval)
        }
    }
}

unsafe impl Send for StructuredCloneData {}
<commit_msg>implement stubs for structured clone callbacks<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! This module implements structured cloning, as defined by [HTML]
//! (https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data).

use dom::bindings::error::{Error, Fallible};
use dom::globalscope::GlobalScope;
use js::jsapi::{Handle, HandleObject, HandleValue, MutableHandleValue};
use js::jsapi::{Heap, JSContext};
use js::jsapi::{JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter};
use js::jsapi::{JS_ClearPendingException, JSObject, JS_ReadStructuredClone};
use js::jsapi::{JS_STRUCTURED_CLONE_VERSION, JS_WriteStructuredClone};
use js::jsapi::{MutableHandleObject, TransferableOwnership};
use libc::size_t;
use std::os::raw;
use std::ptr;
use std::slice;

#[allow(dead_code)]
unsafe extern "C" fn read_callback(_cx: *mut JSContext,
                                   _r: *mut JSStructuredCloneReader,
                                   _tag: u32,
                                   _data: u32,
                                   _closure: *mut raw::c_void) -> *mut JSObject {
    Heap::default().get()
}

#[allow(dead_code)]
unsafe extern "C" fn write_callback(_cx: *mut JSContext,
                                    _w: *mut JSStructuredCloneWriter,
                                    _obj: HandleObject,
                                    _closure: *mut raw::c_void) -> bool {
    false
}

#[allow(dead_code)]
unsafe extern "C" fn read_transfer_callback(_cx: *mut JSContext,
                                            _r: *mut JSStructuredCloneReader,
                                            _tag: u32,
                                            _content: *mut raw::c_void,
                                            _extra_data: u64,
                                            _closure: *mut raw::c_void,
                                            _return_object: MutableHandleObject) -> bool {
    false
}

#[allow(dead_code)]
unsafe extern "C" fn write_transfer_callback(_cx: *mut JSContext,
                                             _obj: Handle<*mut JSObject>,
                                             _closure: *mut raw::c_void,
                                             _tag: *mut u32,
                                             _ownership: *mut TransferableOwnership,
                                             _content:  *mut *mut raw::c_void,
                                             _extra_data: *mut u64) -> bool {
    false
}

#[allow(dead_code)]
unsafe extern "C" fn free_transfer_callback(_tag: u32,
                                            _ownership: TransferableOwnership,
                                            _content: *mut raw::c_void,
                                            _extra_data: u64,
                                            _closure: *mut raw::c_void) {
}

#[allow(dead_code)]
unsafe extern "C" fn report_error_callback(_cx: *mut JSContext, _errorid: u32) {
}

#[allow(dead_code)]
static STRUCTURED_CLONE_CALLBACKS: JSStructuredCloneCallbacks = JSStructuredCloneCallbacks {
    read: Some(read_callback),
    write: Some(write_callback),
    reportError: Some(report_error_callback),
    readTransfer: Some(read_transfer_callback),
    writeTransfer: Some(write_transfer_callback),
    freeTransfer: Some(free_transfer_callback),
};

/// A buffer for a structured clone.
pub enum StructuredCloneData {
    /// A non-serializable (default) variant
    Struct(*mut u64, size_t),
    /// A variant that can be serialized
    Vector(Vec<u8>)
}

impl StructuredCloneData {
    /// Writes a structured clone. Returns a `DataClone` error if that fails.
    pub fn write(cx: *mut JSContext, message: HandleValue) -> Fallible<StructuredCloneData> {
        let mut data = ptr::null_mut();
        let mut nbytes = 0;
        let result = unsafe {
            JS_WriteStructuredClone(cx,
                                    message,
                                    &mut data,
                                    &mut nbytes,
                                    ptr::null(),
                                    ptr::null_mut(),
                                    HandleValue::undefined())
        };
        if !result {
            unsafe {
                JS_ClearPendingException(cx);
            }
            return Err(Error::DataClone);
        }
        Ok(StructuredCloneData::Struct(data, nbytes))
    }

    /// Converts a StructuredCloneData to Vec<u8> for inter-thread sharing
    pub fn move_to_arraybuffer(self) -> Vec<u8> {
        match self {
            StructuredCloneData::Struct(data, nbytes) => {
                unsafe {
                    slice::from_raw_parts(data as *mut u8, nbytes).to_vec()
                }
            }
            StructuredCloneData::Vector(msg) => msg
        }
    }

    /// Reads a structured clone.
    ///
    /// Panics if `JS_ReadStructuredClone` fails.
    fn read_clone(global: &GlobalScope, data: *mut u64, nbytes: size_t, rval: MutableHandleValue) {
        unsafe {
            assert!(JS_ReadStructuredClone(global.get_cx(),
                                           data,
                                           nbytes,
                                           JS_STRUCTURED_CLONE_VERSION,
                                           rval,
                                           ptr::null(),
                                           ptr::null_mut()));
        }
    }

    /// Thunk for the actual `read_clone` method. Resolves proper variant for read_clone.
    pub fn read(self, global: &GlobalScope, rval: MutableHandleValue) {
        match self {
            StructuredCloneData::Vector(mut vec_msg) => {
                let nbytes = vec_msg.len();
                let data = vec_msg.as_mut_ptr() as *mut u64;
                StructuredCloneData::read_clone(global, data, nbytes, rval);
            }
            StructuredCloneData::Struct(data, nbytes) => StructuredCloneData::read_clone(global, data, nbytes, rval)
        }
    }
}

unsafe impl Send for StructuredCloneData {}
<|endoftext|>


<commit_before>#![cfg(target_arch = "x86_64")]

use crate::structures::paging::{frame::PhysFrame, mapper::*, page_table::PageTable};

/// A Mapper implementation that requires that the complete physically memory is mapped at some
/// offset in the virtual address space.
#[derive(Debug)]
pub struct OffsetPageTable<'a> {
    inner: MappedPageTable<'a, PhysOffset>,
}

impl<'a> OffsetPageTable<'a> {
    /// Creates a new `OffsetPageTable` that uses the given offset for converting virtual
    /// to physical addresses.
    ///
    /// This function is unsafe because the caller must guarantee that the passed `phys_offset`
    /// is correct. Also, the passed `level_4_table` must point to the level 4 page table
    /// of a valid page table hierarchy. Otherwise this function might break memory safety, e.g.
    /// by writing to an illegal memory location.
    pub unsafe fn new(level_4_table: &'a mut PageTable, phys_offset: VirtAddr) -> Self {
        let phys_offset = PhysOffset {
            offset: phys_offset,
        };
        Self {
            inner: MappedPageTable::new(level_4_table, phys_offset),
        }
    }
}

#[derive(Debug)]
struct PhysOffset {
    offset: VirtAddr,
}

impl PhysToVirt for PhysOffset {
    fn phys_to_virt(&self, frame: PhysFrame) -> *mut PageTable {
        let phys = frame.start_address().as_u64();
        let virt = self.offset + phys;
        virt.as_mut_ptr()
    }
}

// delegate all trait implementations to inner

impl<'a> Mapper<Size1GiB> for OffsetPageTable<'a> {
    unsafe fn map_to<A>(
        &mut self,
        page: Page<Size1GiB>,
        frame: PhysFrame<Size1GiB>,
        flags: PageTableFlags,
        allocator: &mut A,
    ) -> Result<MapperFlush<Size1GiB>, MapToError>
    where
        A: FrameAllocator<Size4KiB>,
    {
        self.inner.map_to(page, frame, flags, allocator)
    }

    fn unmap(
        &mut self,
        page: Page<Size1GiB>,
    ) -> Result<(PhysFrame<Size1GiB>, MapperFlush<Size1GiB>), UnmapError> {
        self.inner.unmap(page)
    }

    fn update_flags(
        &mut self,
        page: Page<Size1GiB>,
        flags: PageTableFlags,
    ) -> Result<MapperFlush<Size1GiB>, FlagUpdateError> {
        self.inner.update_flags(page, flags)
    }

    fn translate_page(&self, page: Page<Size1GiB>) -> Result<PhysFrame<Size1GiB>, TranslateError> {
        self.inner.translate_page(page)
    }
}

impl<'a> Mapper<Size2MiB> for OffsetPageTable<'a> {
    unsafe fn map_to<A>(
        &mut self,
        page: Page<Size2MiB>,
        frame: PhysFrame<Size2MiB>,
        flags: PageTableFlags,
        allocator: &mut A,
    ) -> Result<MapperFlush<Size2MiB>, MapToError>
    where
        A: FrameAllocator<Size4KiB>,
    {
        self.inner.map_to(page, frame, flags, allocator)
    }

    fn unmap(
        &mut self,
        page: Page<Size2MiB>,
    ) -> Result<(PhysFrame<Size2MiB>, MapperFlush<Size2MiB>), UnmapError> {
        self.inner.unmap(page)
    }

    fn update_flags(
        &mut self,
        page: Page<Size2MiB>,
        flags: PageTableFlags,
    ) -> Result<MapperFlush<Size2MiB>, FlagUpdateError> {
        self.inner.update_flags(page, flags)
    }

    fn translate_page(&self, page: Page<Size2MiB>) -> Result<PhysFrame<Size2MiB>, TranslateError> {
        self.inner.translate_page(page)
    }
}

impl<'a> Mapper<Size4KiB> for OffsetPageTable<'a> {
    unsafe fn map_to<A>(
        &mut self,
        page: Page<Size4KiB>,
        frame: PhysFrame<Size4KiB>,
        flags: PageTableFlags,
        allocator: &mut A,
    ) -> Result<MapperFlush<Size4KiB>, MapToError>
    where
        A: FrameAllocator<Size4KiB>,
    {
        self.inner.map_to(page, frame, flags, allocator)
    }

    fn unmap(
        &mut self,
        page: Page<Size4KiB>,
    ) -> Result<(PhysFrame<Size4KiB>, MapperFlush<Size4KiB>), UnmapError> {
        self.inner.unmap(page)
    }

    fn update_flags(
        &mut self,
        page: Page<Size4KiB>,
        flags: PageTableFlags,
    ) -> Result<MapperFlush<Size4KiB>, FlagUpdateError> {
        self.inner.update_flags(page, flags)
    }

    fn translate_page(&self, page: Page<Size4KiB>) -> Result<PhysFrame<Size4KiB>, TranslateError> {
        self.inner.translate_page(page)
    }
}

impl<'a> MapperAllSizes for OffsetPageTable<'a> {
    fn translate(&self, addr: VirtAddr) -> TranslateResult {
        self.inner.translate(addr)
    }
}
<commit_msg>Improve documentation<commit_after>#![cfg(target_arch = "x86_64")]

use crate::structures::paging::{frame::PhysFrame, mapper::*, page_table::PageTable};

/// A Mapper implementation that requires that the complete physically memory is mapped at some
/// offset in the virtual address space.
#[derive(Debug)]
pub struct OffsetPageTable<'a> {
    inner: MappedPageTable<'a, PhysOffset>,
}

impl<'a> OffsetPageTable<'a> {
    /// Creates a new `OffsetPageTable` that uses the given offset for converting virtual
    /// to physical addresses.
    ///
    /// The complete physical memory must be mapped in the virtual address space starting at
    /// address `phys_offset`. This means that for example physical address `0x5000` can be
    /// accessed through virtual address `phys_offset + 0x5000`. This mapping is required because
    /// the mapper needs to access page tables, which are not mapped into the virtual address
    /// space by default.
    ///
    /// This function is unsafe because the caller must guarantee that the passed `phys_offset`
    /// is correct. Also, the passed `level_4_table` must point to the level 4 page table
    /// of a valid page table hierarchy. Otherwise this function might break memory safety, e.g.
    /// by writing to an illegal memory location.
    pub unsafe fn new(level_4_table: &'a mut PageTable, phys_offset: VirtAddr) -> Self {
        let phys_offset = PhysOffset {
            offset: phys_offset,
        };
        Self {
            inner: MappedPageTable::new(level_4_table, phys_offset),
        }
    }
}

#[derive(Debug)]
struct PhysOffset {
    offset: VirtAddr,
}

impl PhysToVirt for PhysOffset {
    fn phys_to_virt(&self, frame: PhysFrame) -> *mut PageTable {
        let phys = frame.start_address().as_u64();
        let virt = self.offset + phys;
        virt.as_mut_ptr()
    }
}

// delegate all trait implementations to inner

impl<'a> Mapper<Size1GiB> for OffsetPageTable<'a> {
    unsafe fn map_to<A>(
        &mut self,
        page: Page<Size1GiB>,
        frame: PhysFrame<Size1GiB>,
        flags: PageTableFlags,
        allocator: &mut A,
    ) -> Result<MapperFlush<Size1GiB>, MapToError>
    where
        A: FrameAllocator<Size4KiB>,
    {
        self.inner.map_to(page, frame, flags, allocator)
    }

    fn unmap(
        &mut self,
        page: Page<Size1GiB>,
    ) -> Result<(PhysFrame<Size1GiB>, MapperFlush<Size1GiB>), UnmapError> {
        self.inner.unmap(page)
    }

    fn update_flags(
        &mut self,
        page: Page<Size1GiB>,
        flags: PageTableFlags,
    ) -> Result<MapperFlush<Size1GiB>, FlagUpdateError> {
        self.inner.update_flags(page, flags)
    }

    fn translate_page(&self, page: Page<Size1GiB>) -> Result<PhysFrame<Size1GiB>, TranslateError> {
        self.inner.translate_page(page)
    }
}

impl<'a> Mapper<Size2MiB> for OffsetPageTable<'a> {
    unsafe fn map_to<A>(
        &mut self,
        page: Page<Size2MiB>,
        frame: PhysFrame<Size2MiB>,
        flags: PageTableFlags,
        allocator: &mut A,
    ) -> Result<MapperFlush<Size2MiB>, MapToError>
    where
        A: FrameAllocator<Size4KiB>,
    {
        self.inner.map_to(page, frame, flags, allocator)
    }

    fn unmap(
        &mut self,
        page: Page<Size2MiB>,
    ) -> Result<(PhysFrame<Size2MiB>, MapperFlush<Size2MiB>), UnmapError> {
        self.inner.unmap(page)
    }

    fn update_flags(
        &mut self,
        page: Page<Size2MiB>,
        flags: PageTableFlags,
    ) -> Result<MapperFlush<Size2MiB>, FlagUpdateError> {
        self.inner.update_flags(page, flags)
    }

    fn translate_page(&self, page: Page<Size2MiB>) -> Result<PhysFrame<Size2MiB>, TranslateError> {
        self.inner.translate_page(page)
    }
}

impl<'a> Mapper<Size4KiB> for OffsetPageTable<'a> {
    unsafe fn map_to<A>(
        &mut self,
        page: Page<Size4KiB>,
        frame: PhysFrame<Size4KiB>,
        flags: PageTableFlags,
        allocator: &mut A,
    ) -> Result<MapperFlush<Size4KiB>, MapToError>
    where
        A: FrameAllocator<Size4KiB>,
    {
        self.inner.map_to(page, frame, flags, allocator)
    }

    fn unmap(
        &mut self,
        page: Page<Size4KiB>,
    ) -> Result<(PhysFrame<Size4KiB>, MapperFlush<Size4KiB>), UnmapError> {
        self.inner.unmap(page)
    }

    fn update_flags(
        &mut self,
        page: Page<Size4KiB>,
        flags: PageTableFlags,
    ) -> Result<MapperFlush<Size4KiB>, FlagUpdateError> {
        self.inner.update_flags(page, flags)
    }

    fn translate_page(&self, page: Page<Size4KiB>) -> Result<PhysFrame<Size4KiB>, TranslateError> {
        self.inner.translate_page(page)
    }
}

impl<'a> MapperAllSizes for OffsetPageTable<'a> {
    fn translate(&self, addr: VirtAddr) -> TranslateResult {
        self.inner.translate(addr)
    }
}
<|endoftext|>