Browse Source

Implement serialize and deserialize for primitive types

feature/snapshot
Haled Odat 11 months ago
parent
commit
fd9f5b3ca1
  1. 23
      boa_engine/src/builtins/iterable/mod.rs
  2. 21
      boa_engine/src/builtins/uri/mod.rs
  3. 63
      boa_engine/src/context/intrinsics.rs
  4. 1
      boa_engine/src/context/mod.rs
  5. 36
      boa_engine/src/environments/compile.rs
  6. 39
      boa_engine/src/environments/runtime/declarative/function.rs
  7. 11
      boa_engine/src/environments/runtime/declarative/global.rs
  8. 9
      boa_engine/src/environments/runtime/declarative/lexical.rs
  9. 51
      boa_engine/src/environments/runtime/declarative/mod.rs
  10. 59
      boa_engine/src/environments/runtime/declarative/module.rs
  11. 83
      boa_engine/src/error.rs
  12. 42
      boa_engine/src/module/mod.rs
  13. 27
      boa_engine/src/module/source.rs
  14. 2
      boa_engine/src/object/property_map.rs
  15. 29
      boa_engine/src/object/shape/mod.rs
  16. 24
      boa_engine/src/object/shape/property_table.rs
  17. 34
      boa_engine/src/object/shape/shared_shape/mod.rs
  18. 10
      boa_engine/src/object/shape/shared_shape/template.rs
  19. 21
      boa_engine/src/object/shape/slot.rs
  20. 21
      boa_engine/src/object/shape/unique_shape.rs
  21. 40
      boa_engine/src/property/mod.rs
  22. 25
      boa_engine/src/realm.rs
  23. 206
      boa_engine/src/snapshot/deserializer.rs
  24. 3
      boa_engine/src/snapshot/error.rs
  25. 28
      boa_engine/src/snapshot/header.rs
  26. 22
      boa_engine/src/snapshot/mod.rs
  27. 358
      boa_engine/src/snapshot/serializer.rs
  28. 30
      boa_engine/src/string/mod.rs
  29. 35
      boa_engine/src/symbol.rs
  30. 48
      docs/snapshot.md

23
boa_engine/src/builtins/iterable/mod.rs

@ -74,6 +74,29 @@ pub struct IteratorPrototypes {
segment: JsObject,
}
impl crate::snapshot::Serialize for IteratorPrototypes {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
self.iterator.serialize(s)?;
self.async_iterator.serialize(s)?;
self.async_from_sync_iterator.serialize(s)?;
self.array.serialize(s)?;
self.set.serialize(s)?;
self.string.serialize(s)?;
self.regexp_string.serialize(s)?;
self.map.serialize(s)?;
self.for_in.serialize(s)?;
#[cfg(feature = "intl")]
{
self.segment.serialize(s)?;
}
Ok(())
}
}
impl IteratorPrototypes {
/// Returns the `ArrayIteratorPrototype` object.
#[inline]

21
boa_engine/src/builtins/uri/mod.rs

@ -47,6 +47,27 @@ pub struct UriFunctions {
encode_uri_component: JsFunction,
}
impl crate::snapshot::Serialize for UriFunctions {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
self.decode_uri.serialize(s)?;
self.decode_uri_component.serialize(s)?;
self.encode_uri.serialize(s)?;
self.encode_uri_component.serialize(s)?;
Ok(())
}
}
impl crate::snapshot::Deserialize for UriFunctions {
fn deserialize(
_d: &mut crate::snapshot::SnapshotDeserializer<'_>,
) -> crate::snapshot::SnapshotResult<Self> {
todo!()
}
}
impl Default for UriFunctions {
fn default() -> Self {
Self {

63
boa_engine/src/context/intrinsics.rs

@ -32,6 +32,8 @@ impl crate::snapshot::Serialize for Intrinsics {
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
self.constructors.serialize(s)?;
self.objects.serialize(s)?;
self.templates.serialize(s)?;
Ok(())
}
}
@ -896,6 +898,40 @@ pub struct IntrinsicObjects {
segments_prototype: JsObject,
}
impl crate::snapshot::Serialize for IntrinsicObjects {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
self.reflect.serialize(s)?;
self.math.serialize(s)?;
self.json.serialize(s)?;
self.throw_type_error.serialize(s)?;
self.array_prototype_values.serialize(s)?;
self.iterator_prototypes.serialize(s)?;
self.generator.serialize(s)?;
self.async_generator.serialize(s)?;
self.eval.serialize(s)?;
self.uri_functions.serialize(s)?;
self.is_finite.serialize(s)?;
self.is_nan.serialize(s)?;
self.parse_float.serialize(s)?;
self.parse_int.serialize(s)?;
#[cfg(feature = "annex-b")]
{
self.escape.serialize(s)?;
self.unescape.serialize(s)?;
}
#[cfg(feature = "intl")]
{
self.intl.serialize(s)?;
self.segments_prototype.serialize(s)?;
}
Ok(())
}
}
impl Default for IntrinsicObjects {
fn default() -> Self {
Self {
@ -1085,6 +1121,33 @@ pub(crate) struct ObjectTemplates {
namespace: ObjectTemplate,
}
impl crate::snapshot::Serialize for ObjectTemplates {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
self.iterator_result.serialize(s)?;
self.ordinary_object.serialize(s)?;
self.array.serialize(s)?;
self.number.serialize(s)?;
self.string.serialize(s)?;
self.symbol.serialize(s)?;
self.bigint.serialize(s)?;
self.boolean.serialize(s)?;
self.unmapped_arguments.serialize(s)?;
self.mapped_arguments.serialize(s)?;
self.function_with_prototype.serialize(s)?;
self.function_prototype.serialize(s)?;
self.function.serialize(s)?;
self.async_function.serialize(s)?;
self.function_without_proto.serialize(s)?;
self.function_with_prototype_without_proto.serialize(s)?;
self.namespace.serialize(s)?;
Ok(())
}
}
impl ObjectTemplates {
pub(crate) fn new(root_shape: &RootShape, constructors: &StandardConstructors) -> Self {
let root_shape = root_shape.shape();

1
boa_engine/src/context/mod.rs

@ -1004,6 +1004,7 @@ impl crate::snapshot::Deserialize for Context<'_> {
) -> Result<Self, crate::snapshot::SnapshotError> {
let strict = d.read_bool()?;
let optimizer_options = OptimizerOptions::deserialize(d)?;
// let realm = Realm::deserialize(d)?;
let mut context = Context::default();
context.strict(strict);

36
boa_engine/src/environments/compile.rs

@ -19,6 +19,19 @@ struct CompileTimeBinding {
strict: bool,
}
impl crate::snapshot::Serialize for CompileTimeBinding {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
self.index.serialize(s)?;
self.mutable.serialize(s)?;
self.lex.serialize(s)?;
self.strict.serialize(s)?;
Ok(())
}
}
/// A compile time environment maps bound identifiers to their binding positions.
///
/// A compile time environment also indicates, if it is a function environment.
@ -30,6 +43,29 @@ pub(crate) struct CompileTimeEnvironment {
function_scope: bool,
}
impl crate::snapshot::Serialize for Identifier {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
self.sym().get().serialize(s)?;
Ok(())
}
}
impl crate::snapshot::Serialize for CompileTimeEnvironment {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
self.outer.serialize(s)?;
self.environment_index.serialize(s)?;
self.bindings.serialize(s)?;
self.function_scope.serialize(s)?;
Ok(())
}
}
// Safety: Nothing in this struct needs tracing, so this is safe.
unsafe impl Trace for CompileTimeEnvironment {
empty_trace!();

39
boa_engine/src/environments/runtime/declarative/function.rs

@ -10,6 +10,17 @@ pub(crate) struct FunctionEnvironment {
slots: FunctionSlots,
}
impl crate::snapshot::Serialize for FunctionEnvironment {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
self.inner.serialize(s)?;
self.slots.serialize(s)?;
Ok(())
}
}
impl FunctionEnvironment {
/// Creates a new `FunctionEnvironment`.
pub(crate) fn new(bindings: u32, poisoned: bool, with: bool, slots: FunctionSlots) -> Self {
@ -160,6 +171,22 @@ pub(crate) enum ThisBindingStatus {
Initialized(JsValue),
}
impl crate::snapshot::Serialize for ThisBindingStatus {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
match self {
ThisBindingStatus::Lexical => b'L'.serialize(s),
ThisBindingStatus::Uninitialized => b'U'.serialize(s),
ThisBindingStatus::Initialized(v) => {
b'I'.serialize(s)?;
v.serialize(s)
}
}
}
}
unsafe impl Trace for ThisBindingStatus {
custom_trace!(this, {
match this {
@ -182,6 +209,18 @@ pub(crate) struct FunctionSlots {
new_target: Option<JsObject>,
}
impl crate::snapshot::Serialize for FunctionSlots {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
self.this.borrow().serialize(s)?;
self.function_object.serialize(s)?;
self.new_target.serialize(s)?;
Ok(())
}
}
impl FunctionSlots {
/// Creates a new `FunctionSluts`.
pub(crate) fn new(

11
boa_engine/src/environments/runtime/declarative/global.rs

@ -10,6 +10,17 @@ pub(crate) struct GlobalEnvironment {
global_this: JsObject,
}
impl crate::snapshot::Serialize for GlobalEnvironment {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
self.inner.serialize(s)?;
self.global_this.serialize(s)?;
Ok(())
}
}
impl GlobalEnvironment {
/// Creates a new `GlobalEnvironment`.
pub(crate) fn new(global_this: JsObject) -> Self {

9
boa_engine/src/environments/runtime/declarative/lexical.rs

@ -9,6 +9,15 @@ pub(crate) struct LexicalEnvironment {
inner: PoisonableEnvironment,
}
impl crate::snapshot::Serialize for LexicalEnvironment {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
self.inner.serialize(s)
}
}
impl LexicalEnvironment {
/// Creates a new `LexicalEnvironment`.
pub(crate) fn new(bindings: u32, poisoned: bool, with: bool) -> Self {

51
boa_engine/src/environments/runtime/declarative/mod.rs

@ -45,6 +45,17 @@ pub(crate) struct DeclarativeEnvironment {
compile: Rc<RefCell<CompileTimeEnvironment>>,
}
impl crate::snapshot::Serialize for DeclarativeEnvironment {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
self.kind.serialize(s)?;
self.compile.serialize(s)?;
Ok(())
}
}
impl DeclarativeEnvironment {
/// Creates a new global `DeclarativeEnvironment`.
pub(crate) fn global(global_this: JsObject) -> Self {
@ -145,6 +156,34 @@ pub(crate) enum DeclarativeEnvironmentKind {
Module(ModuleEnvironment),
}
impl crate::snapshot::Serialize for DeclarativeEnvironmentKind {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
match self {
DeclarativeEnvironmentKind::Lexical(env) => {
s.write_u8(b'L')?;
env.serialize(s)?;
}
DeclarativeEnvironmentKind::Global(env) => {
s.write_u8(b'G')?;
env.serialize(s)?;
}
DeclarativeEnvironmentKind::Function(env) => {
s.write_u8(b'F')?;
env.serialize(s)?;
}
DeclarativeEnvironmentKind::Module(env) => {
s.write_u8(b'M')?;
env.serialize(s)?;
}
}
Ok(())
}
}
impl DeclarativeEnvironmentKind {
/// Unwraps the inner function environment if possible. Returns `None` otherwise.
pub(crate) const fn as_function(&self) -> Option<&FunctionEnvironment> {
@ -278,6 +317,18 @@ pub(crate) struct PoisonableEnvironment {
with: Cell<bool>,
}
impl crate::snapshot::Serialize for PoisonableEnvironment {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
self.bindings.borrow().serialize(s)?;
self.poisoned.serialize(s)?;
self.with.serialize(s)?;
Ok(())
}
}
impl PoisonableEnvironment {
/// Creates a new `PoisonableEnvironment`.
pub(crate) fn new(bindings_count: u32, poisoned: bool, with: bool) -> Self {

59
boa_engine/src/environments/runtime/declarative/module.rs

@ -12,6 +12,25 @@ enum BindingAccessor {
Index(u32),
}
impl crate::snapshot::Serialize for BindingAccessor {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
match self {
BindingAccessor::Identifier(i) => {
s.write_u8(b'I')?;
i.serialize(s)?;
}
BindingAccessor::Index(i) => {
s.write_u8(b'X')?;
i.serialize(s)?;
}
}
Ok(())
}
}
/// An indirect reference to a binding inside an environment.
#[derive(Clone, Debug, Trace, Finalize)]
struct IndirectBinding {
@ -20,6 +39,17 @@ struct IndirectBinding {
accessor: Cell<BindingAccessor>,
}
impl crate::snapshot::Serialize for IndirectBinding {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
self.module.serialize(s)?;
self.accessor.serialize(s)?;
Ok(())
}
}
/// The type of binding a [`ModuleEnvironment`] can contain.
#[derive(Clone, Debug, Trace, Finalize)]
enum BindingType {
@ -27,6 +57,25 @@ enum BindingType {
Indirect(IndirectBinding),
}
impl crate::snapshot::Serialize for BindingType {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
match self {
BindingType::Direct(v) => {
s.write_u8(b'D')?;
v.serialize(s)?;
}
BindingType::Indirect(i) => {
s.write_u8(b'I')?;
i.serialize(s)?;
}
}
Ok(())
}
}
/// A [**Module Environment Record**][spec].
///
/// Module environments allow referencing bindings inside other environments, in addition
@ -39,6 +88,16 @@ pub(crate) struct ModuleEnvironment {
bindings: GcRefCell<Vec<BindingType>>,
}
impl crate::snapshot::Serialize for ModuleEnvironment {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
self.bindings.borrow().serialize(s)?;
Ok(())
}
}
impl ModuleEnvironment {
/// Creates a new `LexicalEnvironment`.
pub(crate) fn new(bindings: u32) -> Self {

83
boa_engine/src/error.rs

@ -49,6 +49,15 @@ pub struct JsError {
inner: Repr,
}
impl crate::snapshot::Serialize for JsError {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
self.inner.serialize(s)
}
}
/// Internal representation of a [`JsError`].
///
/// `JsError` is represented by an opaque enum because it restricts
@ -65,6 +74,24 @@ enum Repr {
Opaque(JsValue),
}
impl crate::snapshot::Serialize for Repr {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
match self {
Repr::Native(v) => {
b'N'.serialize(s)?;
v.serialize(s)?;
}
Repr::Opaque(v) => {
v.serialize(s)?;
}
}
Ok(())
}
}
/// The error type returned by the [`JsError::try_native`] method.
#[derive(Debug, Clone, Error)]
pub enum TryNativeError {
@ -428,6 +455,19 @@ pub struct JsNativeError {
realm: Option<Realm>,
}
impl crate::snapshot::Serialize for JsNativeError {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
self.kind.serialize(s)?;
self.message.serialize(s)?;
self.cause.serialize(s)?;
self.realm.serialize(s)?;
Ok(())
}
}
impl std::fmt::Debug for JsNativeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("JsNativeError")
@ -953,6 +993,49 @@ pub enum JsNativeErrorKind {
RuntimeLimit,
}
impl crate::snapshot::Serialize for JsNativeErrorKind {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
match self {
JsNativeErrorKind::Aggregate(errors) => {
b'0'.serialize(s)?;
errors.serialize(s)?;
}
JsNativeErrorKind::Error => {
b'1'.serialize(s)?;
}
JsNativeErrorKind::Eval => {
b'2'.serialize(s)?;
}
JsNativeErrorKind::Range => {
b'3'.serialize(s)?;
}
JsNativeErrorKind::Reference => {
b'4'.serialize(s)?;
}
JsNativeErrorKind::Syntax => {
b'5'.serialize(s)?;
}
JsNativeErrorKind::Type => {
b'6'.serialize(s)?;
}
JsNativeErrorKind::Uri => {
b'7'.serialize(s)?;
}
JsNativeErrorKind::RuntimeLimit => {
b'8'.serialize(s)?;
}
#[cfg(feature = "fuzz")]
JsNativeErrorKind::NoInstructionsRemain => {
b'9'.serialize(s)?;
}
}
Ok(())
}
}
impl PartialEq<ErrorKind> for JsNativeErrorKind {
fn eq(&self, other: &ErrorKind) -> bool {
matches!(

42
boa_engine/src/module/mod.rs

@ -269,6 +269,16 @@ pub struct Module {
inner: Gc<Inner>,
}
impl crate::snapshot::Serialize for Module {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
self.inner.serialize(s)?;
Ok(())
}
}
impl std::fmt::Debug for Module {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Module")
@ -290,6 +300,20 @@ struct Inner {
host_defined: (),
}
impl crate::snapshot::Serialize for Inner {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
self.realm.serialize(s)?;
self.environment.borrow().serialize(s)?;
self.namespace.borrow().serialize(s)?;
self.kind.serialize(s)?;
self.host_defined.serialize(s)?;
Ok(())
}
}
/// The kind of a [`Module`].
#[derive(Debug, Trace, Finalize)]
pub(crate) enum ModuleKind {
@ -300,6 +324,24 @@ pub(crate) enum ModuleKind {
Synthetic,
}
impl crate::snapshot::Serialize for ModuleKind {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
match self {
ModuleKind::SourceText(v) => {
s.write_u8(b'S')?;
v.serialize(s)?;
}
ModuleKind::Synthetic => {
s.write_u8(b'C')?;
}
}
Ok(())
}
}
/// Return value of the [`Module::resolve_export`] operation.
///
/// Indicates how to access a specific export in a module.

27
boa_engine/src/module/source.rs

@ -93,6 +93,15 @@ enum Status {
},
}
impl crate::snapshot::Serialize for Status {
fn serialize(
&self,
_s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
todo!()
}
}
// SAFETY: This must be synced with `Status` to mark any new data added that needs to be traced.
// `Status` doesn't implement `Drop`, making this manual implementation safe.
//
@ -251,6 +260,15 @@ pub(crate) struct SourceTextModule {
inner: Gc<Inner>,
}
impl crate::snapshot::Serialize for SourceTextModule {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
self.inner.serialize(s)
}
}
impl std::fmt::Debug for SourceTextModule {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let limiter = RecursionLimiter::new(&*self.inner);
@ -279,6 +297,15 @@ struct Inner {
code: ModuleCode,
}
impl crate::snapshot::Serialize for Inner {
fn serialize(
&self,
_s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
todo!()
}
}
#[derive(Debug)]
struct ModuleCode {
has_tla: bool,

2
boa_engine/src/object/property_map.rs

@ -231,6 +231,8 @@ impl crate::snapshot::Serialize for PropertyMap {
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
// FIXME: indexed properties.
self.shape.serialize(s)?;
self.storage.serialize(s)?;
Ok(())
}

29
boa_engine/src/object/shape/mod.rs

@ -58,12 +58,41 @@ enum Inner {
Shared(SharedShape),
}
impl crate::snapshot::Serialize for Inner {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
match self {
Inner::Unique(shape) => {
b'U'.serialize(s)?;
shape.serialize(s)?;
}
Inner::Shared(shape) => {
b'S'.serialize(s)?;
shape.serialize(s)?;
}
}
Ok(())
}
}
/// Represents the shape of an object.
#[derive(Debug, Trace, Finalize, Clone)]
pub struct Shape {
inner: Inner,
}
impl crate::snapshot::Serialize for Shape {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
self.inner.serialize(s)?;
Ok(())
}
}
impl Default for Shape {
#[inline]
fn default() -> Self {

24
boa_engine/src/object/shape/property_table.rs

@ -14,6 +14,17 @@ pub(crate) struct PropertyTableInner {
pub(crate) keys: Vec<(PropertyKey, Slot)>,
}
impl crate::snapshot::Serialize for PropertyTableInner {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
self.map.serialize(s)?;
self.keys.serialize(s)?;
Ok(())
}
}
impl PropertyTableInner {
/// Returns all the keys, in insertion order.
pub(crate) fn keys(&self) -> Vec<PropertyKey> {
@ -73,6 +84,19 @@ pub(crate) struct PropertyTable {
pub(super) inner: Rc<RefCell<PropertyTableInner>>,
}
impl crate::snapshot::Serialize for PropertyTable {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
let ptr = self.inner.as_ptr() as usize;
s.reference_or(ptr, |s| {
self.inner.borrow().serialize(s)?;
Ok(())
})
}
}
impl PropertyTable {
/// Returns the inner representation of a [`PropertyTable`].
pub(super) fn inner(&self) -> &RefCell<PropertyTableInner> {

34
boa_engine/src/object/shape/shared_shape/mod.rs

@ -49,6 +49,16 @@ bitflags! {
}
}
impl crate::snapshot::Serialize for ShapeFlags {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
self.bits().serialize(s)?;
Ok(())
}
}
impl Default for ShapeFlags {
fn default() -> Self {
Self::empty()
@ -113,12 +123,36 @@ struct Inner {
flags: ShapeFlags,
}
impl crate::snapshot::Serialize for Inner {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
self.property_count.serialize(s)?;
self.prototype.serialize(s)?;
self.property_table.serialize(s)?;
self.previous.serialize(s)?;
self.transition_count.serialize(s)?;
self.flags.serialize(s)?;
Ok(())
}
}
/// Represents a shared object shape.
#[derive(Debug, Trace, Finalize, Clone)]
pub struct SharedShape {
inner: Gc<Inner>,
}
impl crate::snapshot::Serialize for SharedShape {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
self.inner.serialize(s)
}
}
impl SharedShape {
fn property_table(&self) -> &PropertyTable {
&self.inner.property_table

10
boa_engine/src/object/shape/shared_shape/template.rs

@ -16,6 +16,16 @@ pub(crate) struct ObjectTemplate {
shape: SharedShape,
}
impl crate::snapshot::Serialize for ObjectTemplate {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
self.shape.serialize(s)?;
Ok(())
}
}
impl ObjectTemplate {
/// Create a new [`ObjectTemplate`]
pub(crate) fn new(shape: &SharedShape) -> Self {

21
boa_engine/src/object/shape/slot.rs

@ -13,6 +13,16 @@ bitflags! {
}
}
impl crate::snapshot::Serialize for SlotAttributes {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
self.bits().serialize(s)?;
Ok(())
}
}
impl SlotAttributes {
pub(crate) const fn is_accessor_descriptor(self) -> bool {
self.contains(Self::GET) || self.contains(Self::SET)
@ -48,6 +58,17 @@ pub(crate) struct Slot {
pub(crate) attributes: SlotAttributes,
}
impl crate::snapshot::Serialize for Slot {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
self.index.serialize(s)?;
self.attributes.serialize(s)?;
Ok(())
}
}
impl Slot {
/// Get the width of the slot.
pub(crate) fn width(self) -> u32 {

21
boa_engine/src/object/shape/unique_shape.rs

@ -22,6 +22,17 @@ struct Inner {
prototype: GcRefCell<JsPrototype>,
}
impl crate::snapshot::Serialize for Inner {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
self.property_table.borrow().serialize(s)?;
self.prototype.borrow().serialize(s)?;
Ok(())
}
}
/// Represents a [`Shape`] that is not shared with any other object.
///
/// This is useful for objects that are inherently unique like,
@ -33,6 +44,16 @@ pub(crate) struct UniqueShape {
inner: Gc<Inner>,
}
impl crate::snapshot::Serialize for UniqueShape {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
self.inner.serialize(s)?;
Ok(())
}
}
impl UniqueShape {
/// Create a new [`UniqueShape`].
pub(crate) fn new(prototype: JsPrototype, property_table: PropertyTableInner) -> Self {

40
boa_engine/src/property/mod.rs

@ -582,6 +582,46 @@ pub enum PropertyKey {
Index(u32),
}
impl crate::snapshot::Serialize for PropertyKey {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
match self {
PropertyKey::String(v) => {
s.write_u8(0)?;
v.serialize(s)?
}
PropertyKey::Symbol(v) => {
s.write_u8(1)?;
v.serialize(s)?
}
PropertyKey::Index(v) => {
s.write_u8(2)?;
v.serialize(s)?
}
}
Ok(())
}
}
impl crate::snapshot::Deserialize for PropertyKey {
fn deserialize(
d: &mut crate::snapshot::SnapshotDeserializer<'_>,
) -> crate::snapshot::SnapshotResult<Self> {
let typ = u8::deserialize(d)?;
let result = match typ {
0 => Self::String(JsString::deserialize(d)?),
1 => Self::Symbol(JsSymbol::deserialize(d)?),
2 => Self::Index(u32::deserialize(d)?),
_ => unreachable!("corrupted snapshot!"),
};
Ok(result)
}
}
/// Utility function for parsing [`PropertyKey`].
fn parse_u32_index<I, T>(mut input: I) -> Option<u32>
where

25
boa_engine/src/realm.rs

@ -35,6 +35,17 @@ impl crate::snapshot::Serialize for Realm {
}
}
impl crate::snapshot::Deserialize for Realm {
fn deserialize(
d: &mut crate::snapshot::SnapshotDeserializer<'_>,
) -> Result<Self, crate::snapshot::SnapshotError> {
let inner = Inner::deserialize(d)?;
Ok(Realm {
inner: Gc::new(inner),
})
}
}
impl Eq for Realm {}
impl PartialEq for Realm {
@ -70,10 +81,24 @@ impl crate::snapshot::Serialize for Inner {
s: &mut crate::snapshot::SnapshotSerializer,
) -> Result<(), crate::snapshot::SnapshotError> {
self.intrinsics.serialize(s)?;
self.global_object.serialize(s)?;
self.global_this.serialize(s)?;
self.template_map.borrow().serialize(s)?;
Ok(())
}
}
impl crate::snapshot::Deserialize for Inner {
fn deserialize(
_d: &mut crate::snapshot::SnapshotDeserializer<'_>,
) -> Result<Self, crate::snapshot::SnapshotError> {
// let intrinsics = Intrinsics::deserialize(d)?;
// Ok(Inner::)
todo!()
}
}
impl Realm {
/// Create a new Realm.
#[inline]

206
boa_engine/src/snapshot/deserializer.rs

@ -1,11 +1,15 @@
use std::hash::Hash;
use indexmap::IndexSet;
use rustc_hash::FxHashMap;
use thin_vec::ThinVec;
use super::SnapshotError;
use super::{SnapshotError, SnapshotResult};
/// TODO: doc
pub trait Deserialize: Sized {
/// TODO: doc
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> Result<Self, SnapshotError>;
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self>;
}
/// TODO: doc
@ -111,3 +115,201 @@ impl SnapshotDeserializer<'_> {
Ok(bytes)
}
}
impl Deserialize for bool {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
d.read_bool()
}
}
impl Deserialize for u8 {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
d.read_u8()
}
}
impl Deserialize for i8 {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
d.read_i8()
}
}
impl Deserialize for u16 {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
d.read_u16()
}
}
impl Deserialize for i16 {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
d.read_i16()
}
}
impl Deserialize for u32 {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
d.read_u32()
}
}
impl Deserialize for i32 {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
d.read_i32()
}
}
impl Deserialize for u64 {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
d.read_u64()
}
}
impl Deserialize for i64 {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
d.read_i64()
}
}
impl Deserialize for usize {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
d.read_usize()
}
}
impl Deserialize for isize {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
d.read_isize()
}
}
impl Deserialize for f32 {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
d.read_f32()
}
}
impl Deserialize for f64 {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
d.read_f64()
}
}
impl<T: Deserialize> Deserialize for Option<T> {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
let is_some = bool::deserialize(d)?;
if is_some {
return Ok(Some(T::deserialize(d)?));
}
Ok(None)
}
}
impl<T: Deserialize, E: Deserialize> Deserialize for Result<T, E> {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
let is_ok = bool::deserialize(d)?;
Ok(if is_ok {
Ok(T::deserialize(d)?)
} else {
Err(E::deserialize(d)?)
})
}
}
impl<T: Deserialize> Deserialize for Vec<T> {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
let len = usize::deserialize(d)?;
let mut values = Vec::with_capacity(len);
for _ in 0..len {
let value = T::deserialize(d)?;
values.push(value);
}
Ok(values)
}
}
impl<T: Deserialize> Deserialize for ThinVec<T> {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
let len = usize::deserialize(d)?;
let mut values = ThinVec::with_capacity(len);
for _ in 0..len {
let value = T::deserialize(d)?;
values.push(value);
}
Ok(values)
}
}
impl<T: Deserialize> Deserialize for Box<T> {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
let value = T::deserialize(d)?;
Ok(Box::new(value))
}
}
impl Deserialize for Box<str> {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
let len = usize::deserialize(d)?;
let bytes = d.read_bytes(len)?;
Ok(String::from_utf8(bytes.into()).unwrap().into_boxed_str())
}
}
impl Deserialize for () {
fn deserialize(_d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
// Deserialize nothing, zero size type.
Ok(())
}
}
impl<T1: Deserialize> Deserialize for (T1,) {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
let v1 = T1::deserialize(d)?;
Ok((v1,))
}
}
impl<T1: Deserialize, T2: Deserialize> Deserialize for (T1, T2) {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
let v1 = T1::deserialize(d)?;
let v2 = T2::deserialize(d)?;
Ok((v1, v2))
}
}
impl<T1: Deserialize, T2: Deserialize, T3: Deserialize> Deserialize for (T1, T2, T3) {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
let v1 = T1::deserialize(d)?;
let v2 = T2::deserialize(d)?;
let v3 = T3::deserialize(d)?;
Ok((v1, v2, v3))
}
}
impl<T1: Deserialize, T2: Deserialize, T3: Deserialize, T4: Deserialize> Deserialize
for (T1, T2, T3, T4)
{
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
let v1 = T1::deserialize(d)?;
let v2 = T2::deserialize(d)?;
let v3 = T3::deserialize(d)?;
let v4 = T4::deserialize(d)?;
Ok((v1, v2, v3, v4))
}
}
impl<K: Deserialize + PartialEq + Eq + Hash, V: Deserialize> Deserialize for FxHashMap<K, V> {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
let len = usize::deserialize(d)?;
let mut result = Self::default();
for _ in 0..len {
let key = K::deserialize(d)?;
let value = V::deserialize(d)?;
let ret = result.insert(key, value);
assert!(ret.is_none());
}
Ok(result)
}
}

3
boa_engine/src/snapshot/error.rs

@ -23,3 +23,6 @@ impl From<std::io::Error> for SnapshotError {
Self::Io(value)
}
}
/// Type alias for [`Result`] return type snapshot operations.
pub type SnapshotResult<T> = Result<T, SnapshotError>;

28
boa_engine/src/snapshot/header.rs

@ -0,0 +1,28 @@
use super::{Deserialize, Serialize, SnapshotDeserializer, SnapshotResult, SnapshotSerializer};
/// TODO: doc
#[derive(Debug, Clone, Copy)]
pub struct Header {
pub(crate) signature: [u8; 4],
pub(crate) version: u32,
// checksum: u64,
}
impl Serialize for Header {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
s.write_bytes(&self.signature)?;
s.write_u32(self.version)?;
Ok(())
}
}
impl Deserialize for Header {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult<Self> {
let signature = d.read_bytes(4)?;
let signature = [signature[0], signature[1], signature[2], signature[3]];
let version = d.read_u32()?;
Ok(Self { signature, version })
}
}

22
boa_engine/src/snapshot/mod.rs

@ -5,34 +5,16 @@
mod deserializer;
mod error;
mod header;
mod serializer;
pub use deserializer::*;
pub use error::*;
pub use header::*;
pub use serializer::*;
use crate::Context;
use indexmap::IndexSet;
use std::fmt::Debug;
/// TODO: doc
#[derive(Debug, Clone, Copy)]
pub struct Header {
signature: [u8; 4],
version: u32,
// checksum: u64,
}
impl Deserialize for Header {
fn deserialize(d: &mut SnapshotDeserializer<'_>) -> Result<Self, SnapshotError> {
let signature = d.read_bytes(4)?;
let signature = [signature[0], signature[1], signature[2], signature[3]];
let version = d.read_u32()?;
Ok(Self { signature, version })
}
}
/// TODO: doc
pub struct Snapshot {

358
boa_engine/src/snapshot/serializer.rs

@ -1,19 +1,44 @@
use std::{
cell::{Cell, RefCell},
hash::Hash,
mem::size_of,
rc::Rc,
};
use boa_gc::{Gc, Trace};
use indexmap::{IndexMap, IndexSet};
use rustc_hash::FxHashMap;
use thin_vec::ThinVec;
use crate::{Context, JsBigInt, JsObject, JsString, JsSymbol};
use crate::{object::shape::SharedShape, Context, JsBigInt, JsObject, JsString, JsSymbol};
use super::{Header, Snapshot, SnapshotError};
use super::{Header, Snapshot, SnapshotError, SnapshotResult};
/// TODO: doc
pub trait Serialize {
/// Serialize type
fn serialize(&self, s: &mut SnapshotSerializer) -> Result<(), SnapshotError>;
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()>;
}
/// TODO: doc
struct Reference {
is_inlined: u8,
index: u32,
}
impl Reference {
fn new(is_inlined: bool, index: u32) -> Self {
Self {
is_inlined: if is_inlined { b'I' } else { b'R' },
index,
}
}
}
impl Serialize for Header {
fn serialize(&self, s: &mut SnapshotSerializer) -> Result<(), SnapshotError> {
s.write_bytes(&self.signature)?;
s.write_u32(self.version)?;
impl Serialize for Reference {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
self.is_inlined.serialize(s)?;
self.index.serialize(s)?;
Ok(())
}
}
@ -25,6 +50,9 @@ pub struct SnapshotSerializer {
strings: IndexMap<usize, JsString>,
symbols: IndexMap<u64, JsSymbol>,
bigints: IndexSet<JsBigInt>,
shared_shapes: IndexMap<usize, SharedShape>,
pub(crate) internal_reference: IndexMap<usize, u32>,
external_references: IndexSet<usize>,
}
@ -37,6 +65,8 @@ impl SnapshotSerializer {
strings: IndexMap::default(),
symbols: IndexMap::default(),
bigints: IndexSet::default(),
shared_shapes: IndexMap::default(),
internal_reference: IndexMap::default(),
external_references: IndexSet::default(),
}
}
@ -45,6 +75,8 @@ impl SnapshotSerializer {
pub fn serialize(mut self, context: &mut Context<'_>) -> Result<Snapshot, SnapshotError> {
// Remove any garbage objects before serialization.
boa_gc::force_collect();
boa_gc::force_collect();
boa_gc::force_collect();
// boa_gc::walk_gc_alloc_pointers(|address| {
// });
@ -82,23 +114,6 @@ impl SnapshotSerializer {
}
}
for i in 0..self.strings.len() {
let string = self
.strings
.get_index(i)
.expect("There should be an string")
.1
.clone();
// string.
string.serialize(&mut self)?;
self.write_bool(string.is_static())?;
self.write_usize(string.len())?;
for elem in string.as_slice() {
self.write_u16(*elem)?;
}
}
Ok(Snapshot {
bytes: self.bytes,
external_references: self.external_references,
@ -106,85 +121,222 @@ impl SnapshotSerializer {
}
/// TODO: doc
pub fn write_bool(&mut self, v: bool) -> Result<(), SnapshotError> {
pub fn write_bool(&mut self, v: bool) -> SnapshotResult<()> {
Ok(self.write_u8(if v { 1 } else { 0 })?)
}
/// TODO: doc
pub fn write_u8(&mut self, v: u8) -> Result<(), SnapshotError> {
pub fn write_u8(&mut self, v: u8) -> SnapshotResult<()> {
Ok(self.write_bytes(&[v])?)
}
/// TODO: doc
pub fn write_i8(&mut self, v: i8) -> Result<(), SnapshotError> {
pub fn write_i8(&mut self, v: i8) -> SnapshotResult<()> {
Ok(self.write_bytes(&v.to_le_bytes())?)
}
/// TODO: doc
pub fn write_u16(&mut self, v: u16) -> Result<(), SnapshotError> {
pub fn write_u16(&mut self, v: u16) -> SnapshotResult<()> {
Ok(self.write_bytes(&v.to_le_bytes())?)
}
/// TODO: doc
pub fn write_i16(&mut self, v: i16) -> Result<(), SnapshotError> {
pub fn write_i16(&mut self, v: i16) -> SnapshotResult<()> {
Ok(self.write_bytes(&v.to_le_bytes())?)
}
/// TODO: doc
pub fn write_u32(&mut self, v: u32) -> Result<(), SnapshotError> {
pub fn write_u32(&mut self, v: u32) -> SnapshotResult<()> {
Ok(self.write_bytes(&v.to_le_bytes())?)
}
/// TODO: doc
pub fn write_i32(&mut self, v: i32) -> Result<(), SnapshotError> {
pub fn write_i32(&mut self, v: i32) -> SnapshotResult<()> {
Ok(self.write_bytes(&v.to_le_bytes())?)
}
/// TODO: doc
pub fn write_f32(&mut self, v: f32) -> Result<(), SnapshotError> {
pub fn write_f32(&mut self, v: f32) -> SnapshotResult<()> {
Ok(self.write_bytes(&v.to_le_bytes())?)
}
/// TODO: doc
pub fn write_f64(&mut self, v: f64) -> Result<(), SnapshotError> {
pub fn write_f64(&mut self, v: f64) -> SnapshotResult<()> {
Ok(self.write_bytes(&v.to_le_bytes())?)
}
/// TODO: doc
pub fn write_u64(&mut self, v: u64) -> Result<(), SnapshotError> {
pub fn write_u64(&mut self, v: u64) -> SnapshotResult<()> {
Ok(self.write_bytes(&v.to_le_bytes())?)
}
/// TODO: doc
pub fn write_i64(&mut self, v: i64) -> Result<(), SnapshotError> {
pub fn write_i64(&mut self, v: i64) -> SnapshotResult<()> {
Ok(self.write_bytes(&v.to_le_bytes())?)
}
/// TODO: doc
pub fn write_u128(&mut self, v: u128) -> Result<(), SnapshotError> {
pub fn write_u128(&mut self, v: u128) -> SnapshotResult<()> {
Ok(self.write_bytes(&v.to_le_bytes())?)
}
/// TODO: doc
pub fn write_i128(&mut self, v: i128) -> Result<(), SnapshotError> {
pub fn write_i128(&mut self, v: i128) -> SnapshotResult<()> {
Ok(self.write_bytes(&v.to_le_bytes())?)
}
/// TODO: doc
pub fn write_usize(&mut self, v: usize) -> Result<(), SnapshotError> {
pub fn write_usize(&mut self, v: usize) -> SnapshotResult<()> {
Ok(self.write_bytes(&(v as u64).to_le_bytes())?)
}
/// TODO: doc
pub fn write_isize(&mut self, v: isize) -> Result<(), SnapshotError> {
pub fn write_isize(&mut self, v: isize) -> SnapshotResult<()> {
Ok(self.write_bytes(&(v as i64).to_le_bytes())?)
}
/// TODO: doc
pub fn write_string(&mut self, v: &str) -> Result<(), SnapshotError> {
pub fn write_string(&mut self, v: &str) -> SnapshotResult<()> {
let asb = v.as_bytes();
self.write_usize(asb.len())?;
self.bytes.extend_from_slice(asb);
Ok(())
}
/// TODO: doc
pub fn write_bytes(&mut self, v: &[u8]) -> Result<(), SnapshotError> {
pub fn write_bytes(&mut self, v: &[u8]) -> SnapshotResult<()> {
self.bytes.extend_from_slice(v);
Ok(())
}
/// TODO: doc
pub fn reference_or<F>(&mut self, ptr: usize, f: F) -> SnapshotResult<()>
where
F: FnOnce(&mut SnapshotSerializer) -> SnapshotResult<()>,
{
match self.internal_reference.entry(ptr) {
indexmap::map::Entry::Occupied(entry) => {
let index = *entry.get();
Reference::new(false, index).serialize(self)?;
return Ok(());
}
indexmap::map::Entry::Vacant(entry) => {
let index =
*entry.insert((self.bytes.len() + size_of::<u8>() + size_of::<u32>()) as u32);
Reference::new(true, index).serialize(self)?;
}
}
f(self)
}
}
impl Serialize for bool {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
s.write_bool(*self)
}
}
impl Serialize for u8 {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
s.write_u8(*self)
}
}
impl Serialize for i8 {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
s.write_i8(*self)
}
}
impl Serialize for u16 {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
s.write_u16(*self)
}
}
impl Serialize for i16 {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
s.write_i16(*self)
}
}
impl Serialize for u32 {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
s.write_u32(*self)
}
}
impl Serialize for i32 {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
s.write_i32(*self)
}
}
impl Serialize for u64 {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
s.write_u64(*self)
}
}
impl Serialize for i64 {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
s.write_i64(*self)
}
}
impl Serialize for usize {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
s.write_usize(*self)
}
}
impl Serialize for isize {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
s.write_isize(*self)
}
}
impl Serialize for f32 {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
s.write_f32(*self)
}
}
impl Serialize for f64 {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
s.write_f64(*self)
}
}
impl<T: Serialize> Serialize for Option<T> {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
if let Some(value) = self {
s.write_bool(true)?;
value.serialize(s)?
} else {
s.write_bool(false)?;
}
Ok(())
}
}
impl<T: Serialize, E: Serialize> Serialize for Result<T, E> {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
match self {
Ok(value) => {
s.write_bool(true)?;
value.serialize(s)?;
}
Err(err) => {
s.write_bool(false)?;
err.serialize(s)?;
}
}
Ok(())
}
}
impl<T: Serialize> Serialize for Vec<T> {
fn serialize(&self, s: &mut SnapshotSerializer) -> Result<(), SnapshotError> {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
s.write_usize(self.len())?;
for element in self {
element.serialize(s)?;
}
Ok(())
}
}
impl<T: Serialize> Serialize for ThinVec<T> {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
s.write_usize(self.len())?;
for element in self {
element.serialize(s)?;
@ -193,37 +345,127 @@ impl<T: Serialize> Serialize for Vec<T> {
}
}
impl Serialize for JsString {
fn serialize(&self, s: &mut SnapshotSerializer) -> Result<(), SnapshotError> {
let index = s.strings.insert_full(self.ptr.addr(), self.clone()).0;
impl<T: Serialize> Serialize for Box<T> {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
T::serialize(&self, s)
}
}
s.write_u32(index as u32)?;
impl Serialize for Box<str> {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
self.len().serialize(s)?;
s.write_bytes(self.as_bytes())?;
Ok(())
}
}
impl Serialize for JsSymbol {
fn serialize(&self, s: &mut SnapshotSerializer) -> Result<(), SnapshotError> {
let index = s.symbols.insert_full(self.hash(), self.clone()).0;
impl Serialize for String {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
self.len().serialize(s)?;
s.write_bytes(self.as_bytes())?;
Ok(())
}
}
s.write_u32(index as u32)?;
impl Serialize for () {
fn serialize(&self, _s: &mut SnapshotSerializer) -> SnapshotResult<()> {
// Serialize nothing
Ok(())
}
}
impl Serialize for JsBigInt {
fn serialize(&self, s: &mut SnapshotSerializer) -> Result<(), SnapshotError> {
let index = s.bigints.insert_full(self.clone()).0;
s.write_u32(index as u32)?;
impl<T1: Serialize> Serialize for (T1,) {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
self.0.serialize(s)?;
Ok(())
}
}
impl Serialize for JsObject {
fn serialize(&self, s: &mut SnapshotSerializer) -> Result<(), SnapshotError> {
let value = s.objects.insert_full(self.clone()).0;
impl<T1: Serialize, T2: Serialize> Serialize for (T1, T2) {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
self.0.serialize(s)?;
self.1.serialize(s)?;
Ok(())
}
}
s.write_u32(value as u32)?;
impl<T1: Serialize, T2: Serialize, T3: Serialize> Serialize for (T1, T2, T3) {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
self.0.serialize(s)?;
self.1.serialize(s)?;
self.2.serialize(s)?;
Ok(())
}
}
impl<T1: Serialize, T2: Serialize, T3: Serialize, T4: Serialize> Serialize for (T1, T2, T3, T4) {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
self.0.serialize(s)?;
self.1.serialize(s)?;
self.2.serialize(s)?;
self.3.serialize(s)?;
Ok(())
}
}
impl<K: Serialize + PartialEq + Eq + Hash, V: Serialize> Serialize for FxHashMap<K, V> {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
self.len().serialize(s)?;
for (key, value) in self {
key.serialize(s)?;
value.serialize(s)?;
}
Ok(())
}
}
impl Serialize for JsBigInt {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
let ptr: *const _ = self.as_inner();
s.reference_or(ptr as usize, |s| {
let (sign, bytes) = self.as_inner().to_bytes_le();
match sign {
num_bigint::Sign::Minus => b'-',
num_bigint::Sign::NoSign => b' ',
num_bigint::Sign::Plus => b'+',
}
.serialize(s)?;
bytes.serialize(s)
})
}
}
impl Serialize for JsObject {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
let ptr: *const _ = self.inner();
s.reference_or(ptr as usize, |s| self.inner().serialize(s))
}
}
impl<T: Serialize + Trace> Serialize for Gc<T> {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
let ptr: *const _ = &*self;
s.reference_or(ptr as usize, |s| T::serialize(&*self, s))
}
}
impl<T: Serialize> Serialize for Rc<T> {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
let ptr: *const _ = &*self;
s.reference_or(ptr as usize, |s| T::serialize(&*self, s))
}
}
impl<T: Serialize> Serialize for RefCell<T> {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
self.borrow().serialize(s)
}
}
impl<T: Serialize + Copy> Serialize for Cell<T> {
fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> {
self.get().serialize(s)
}
}

30
boa_engine/src/string/mod.rs

@ -209,6 +209,36 @@ pub struct JsString {
pub(crate) ptr: Tagged<RawJsString>,
}
impl crate::snapshot::Serialize for JsString {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
let addr = self.ptr.addr();
s.reference_or(addr, |s| {
s.write_bool(self.is_static())?;
if !self.is_static() {
s.write_usize(self.len())?;
for elem in self.as_slice() {
s.write_u16(*elem)?;
}
} else {
s.write_usize(addr)?;
}
Ok(())
})
}
}
impl crate::snapshot::Deserialize for JsString {
fn deserialize(
_d: &mut crate::snapshot::SnapshotDeserializer<'_>,
) -> crate::snapshot::SnapshotResult<Self> {
todo!()
}
}
// JsString should always be pointer sized.
sa::assert_eq_size!(JsString, *const ());

35
boa_engine/src/symbol.rs

@ -118,7 +118,40 @@ pub(crate) struct Inner {
/// This represents a JavaScript symbol primitive.
pub struct JsSymbol {
pub(crate) repr: Tagged<Inner>,
repr: Tagged<Inner>,
}
impl crate::snapshot::Serialize for JsSymbol {
fn serialize(
&self,
s: &mut crate::snapshot::SnapshotSerializer,
) -> crate::snapshot::SnapshotResult<()> {
let addr = self.repr.addr();
s.reference_or(addr, |s| {
s.write_bool(self.repr.is_tagged())?;
if !self.repr.is_tagged() {
self.hash().serialize(s)?;
if let Some(desc) = self.description() {
s.write_bool(true)?;
desc.serialize(s)?;
} else {
s.write_bool(false)?;
}
} else {
s.write_usize(addr)?;
}
Ok(())
})
}
}
impl crate::snapshot::Deserialize for JsSymbol {
fn deserialize(
_d: &mut crate::snapshot::SnapshotDeserializer<'_>,
) -> crate::snapshot::SnapshotResult<Self> {
todo!()
}
}
// SAFETY: `JsSymbol` uses `Arc` to do the reference counting, making this type thread-safe.

48
docs/snapshot.md

@ -1,6 +1,6 @@
# Snapshot File format
This docoment describes the binary file format of the boa snapshot files.
This document describes the binary file format of the boa snapshot files.
## Header
@ -12,52 +12,8 @@ The header composes the first part of the snapshot.
| guid | Guid generated in compile time and backed into the binary, that is used to check if snapshot is compatibile. |
| checksum | Checksum that is used to check that the snapshot is not corrupted. |
## JsString Table
## Internal Reference Map
After the `Header` the table containing `JsString`s each entry contains
| static? `: u8` | length: `: usize` | `JsString` elements `: [u16]` |
| -------------- | ----------------- | ----------------------------- |
| 0 | 5 | `'H', 'e', 'l', 'l', 'o'` |
| 1 | - | 3 |
| ... | ... | ... |
If it's a static string then it's elements comprise the index into the `STATIC_STRING`s.
## JsSymbol Table
| `JsSymbol` hash `: u64` | Description (index into `JsString` table) `: usize` |
| ----------------------- | --------------------------------------------------- |
| 200 | 0 |
| ... | ... |
## JsBigInt Table
| Length in bytes `: u64` | Content |
| ----------------------- | ------- |
| 32 | ... |
## Shapes (Hidden classes) Table
### Unique Shapes
| `[[prototype]]` `: u32` (index into `JsObject` table) | property count `: u32` | key-value pairs |
| ----------------------------------------------------- | ---------------------- | --------------- |
| | 0 | |
| | ... | |
### Shared Shapes
| previous `: u32` | flags | transition |
| ---------------- | ----- | ---------- |
| `MAX` (root) | ... | `x` |
## JsObject Table
| length | Content |
| ------ | ------- |
| 200 | ... |
| ... | ... |
## JsValue Encoding

Loading…
Cancel
Save