From fd9f5b3ca14485091e886d16e43fcda62a8167b5 Mon Sep 17 00:00:00 2001 From: Haled Odat <8566042+HalidOdat@users.noreply.github.com> Date: Wed, 21 Jun 2023 18:51:30 +0200 Subject: [PATCH] Implement serialize and deserialize for primitive types --- boa_engine/src/builtins/iterable/mod.rs | 23 ++ boa_engine/src/builtins/uri/mod.rs | 21 + boa_engine/src/context/intrinsics.rs | 63 +++ boa_engine/src/context/mod.rs | 1 + boa_engine/src/environments/compile.rs | 36 ++ .../runtime/declarative/function.rs | 39 ++ .../runtime/declarative/global.rs | 11 + .../runtime/declarative/lexical.rs | 9 + .../environments/runtime/declarative/mod.rs | 51 +++ .../runtime/declarative/module.rs | 59 +++ boa_engine/src/error.rs | 83 ++++ boa_engine/src/module/mod.rs | 42 ++ boa_engine/src/module/source.rs | 27 ++ boa_engine/src/object/property_map.rs | 2 + boa_engine/src/object/shape/mod.rs | 29 ++ boa_engine/src/object/shape/property_table.rs | 24 ++ .../src/object/shape/shared_shape/mod.rs | 34 ++ .../src/object/shape/shared_shape/template.rs | 10 + boa_engine/src/object/shape/slot.rs | 21 + boa_engine/src/object/shape/unique_shape.rs | 21 + boa_engine/src/property/mod.rs | 40 ++ boa_engine/src/realm.rs | 25 ++ boa_engine/src/snapshot/deserializer.rs | 206 +++++++++- boa_engine/src/snapshot/error.rs | 3 + boa_engine/src/snapshot/header.rs | 28 ++ boa_engine/src/snapshot/mod.rs | 22 +- boa_engine/src/snapshot/serializer.rs | 358 +++++++++++++++--- boa_engine/src/string/mod.rs | 30 ++ boa_engine/src/symbol.rs | 35 +- docs/snapshot.md | 48 +-- 30 files changed, 1274 insertions(+), 127 deletions(-) create mode 100644 boa_engine/src/snapshot/header.rs diff --git a/boa_engine/src/builtins/iterable/mod.rs b/boa_engine/src/builtins/iterable/mod.rs index 13a31260252..31ebeb43532 100644 --- a/boa_engine/src/builtins/iterable/mod.rs +++ b/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] diff --git a/boa_engine/src/builtins/uri/mod.rs b/boa_engine/src/builtins/uri/mod.rs index d059fa98468..8323e0f2815 100644 --- a/boa_engine/src/builtins/uri/mod.rs +++ b/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 { + todo!() + } +} + impl Default for UriFunctions { fn default() -> Self { Self { diff --git a/boa_engine/src/context/intrinsics.rs b/boa_engine/src/context/intrinsics.rs index 0e75d2e5ef0..2086a76661f 100644 --- a/boa_engine/src/context/intrinsics.rs +++ b/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(); diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index 2766b91e0aa..0b03102d9f4 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -1004,6 +1004,7 @@ impl crate::snapshot::Deserialize for Context<'_> { ) -> Result { let strict = d.read_bool()?; let optimizer_options = OptimizerOptions::deserialize(d)?; + // let realm = Realm::deserialize(d)?; let mut context = Context::default(); context.strict(strict); diff --git a/boa_engine/src/environments/compile.rs b/boa_engine/src/environments/compile.rs index 5906fa9e12b..b6cd1c17b54 100644 --- a/boa_engine/src/environments/compile.rs +++ b/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!(); diff --git a/boa_engine/src/environments/runtime/declarative/function.rs b/boa_engine/src/environments/runtime/declarative/function.rs index 125c440461f..7bcbb68ec4c 100644 --- a/boa_engine/src/environments/runtime/declarative/function.rs +++ b/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, } +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( diff --git a/boa_engine/src/environments/runtime/declarative/global.rs b/boa_engine/src/environments/runtime/declarative/global.rs index 34183a51ada..5a23c60dad7 100644 --- a/boa_engine/src/environments/runtime/declarative/global.rs +++ b/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 { diff --git a/boa_engine/src/environments/runtime/declarative/lexical.rs b/boa_engine/src/environments/runtime/declarative/lexical.rs index 4969fdd8d5f..9752240bf8b 100644 --- a/boa_engine/src/environments/runtime/declarative/lexical.rs +++ b/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 { diff --git a/boa_engine/src/environments/runtime/declarative/mod.rs b/boa_engine/src/environments/runtime/declarative/mod.rs index 5e2a804fba4..46604720409 100644 --- a/boa_engine/src/environments/runtime/declarative/mod.rs +++ b/boa_engine/src/environments/runtime/declarative/mod.rs @@ -45,6 +45,17 @@ pub(crate) struct DeclarativeEnvironment { compile: Rc>, } +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, } +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 { diff --git a/boa_engine/src/environments/runtime/declarative/module.rs b/boa_engine/src/environments/runtime/declarative/module.rs index 4ff09940026..1cb0c77e23f 100644 --- a/boa_engine/src/environments/runtime/declarative/module.rs +++ b/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, } +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>, } +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 { diff --git a/boa_engine/src/error.rs b/boa_engine/src/error.rs index 1a66e7caee7..efa22867767 100644 --- a/boa_engine/src/error.rs +++ b/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, } +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 for JsNativeErrorKind { fn eq(&self, other: &ErrorKind) -> bool { matches!( diff --git a/boa_engine/src/module/mod.rs b/boa_engine/src/module/mod.rs index abd610d04ea..8533eb25933 100644 --- a/boa_engine/src/module/mod.rs +++ b/boa_engine/src/module/mod.rs @@ -269,6 +269,16 @@ pub struct Module { inner: Gc, } +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. diff --git a/boa_engine/src/module/source.rs b/boa_engine/src/module/source.rs index d73a26ee019..45ff459257a 100644 --- a/boa_engine/src/module/source.rs +++ b/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, } +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, diff --git a/boa_engine/src/object/property_map.rs b/boa_engine/src/object/property_map.rs index ae08a0a73a6..494703aa36a 100644 --- a/boa_engine/src/object/property_map.rs +++ b/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(()) } diff --git a/boa_engine/src/object/shape/mod.rs b/boa_engine/src/object/shape/mod.rs index 352ca91567c..b560994f5b1 100644 --- a/boa_engine/src/object/shape/mod.rs +++ b/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 { diff --git a/boa_engine/src/object/shape/property_table.rs b/boa_engine/src/object/shape/property_table.rs index cb2d7513042..56bfbbd7930 100644 --- a/boa_engine/src/object/shape/property_table.rs +++ b/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 { @@ -73,6 +84,19 @@ pub(crate) struct PropertyTable { pub(super) inner: Rc>, } +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 { diff --git a/boa_engine/src/object/shape/shared_shape/mod.rs b/boa_engine/src/object/shape/shared_shape/mod.rs index 0eff31474f6..74681b3f429 100644 --- a/boa_engine/src/object/shape/shared_shape/mod.rs +++ b/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, } +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 diff --git a/boa_engine/src/object/shape/shared_shape/template.rs b/boa_engine/src/object/shape/shared_shape/template.rs index 14316cd21fb..e22e5897482 100644 --- a/boa_engine/src/object/shape/shared_shape/template.rs +++ b/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 { diff --git a/boa_engine/src/object/shape/slot.rs b/boa_engine/src/object/shape/slot.rs index 64fb3c8f43f..ecddba0fb46 100644 --- a/boa_engine/src/object/shape/slot.rs +++ b/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 { diff --git a/boa_engine/src/object/shape/unique_shape.rs b/boa_engine/src/object/shape/unique_shape.rs index 68872a96c82..3c90fc5bef2 100644 --- a/boa_engine/src/object/shape/unique_shape.rs +++ b/boa_engine/src/object/shape/unique_shape.rs @@ -22,6 +22,17 @@ struct Inner { prototype: GcRefCell, } +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, } +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 { diff --git a/boa_engine/src/property/mod.rs b/boa_engine/src/property/mod.rs index 8adcd5a761c..f5a9985a294 100644 --- a/boa_engine/src/property/mod.rs +++ b/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 { + 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(mut input: I) -> Option where diff --git a/boa_engine/src/realm.rs b/boa_engine/src/realm.rs index 91932fd3bf1..32c75656cb5 100644 --- a/boa_engine/src/realm.rs +++ b/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 { + 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 { + // let intrinsics = Intrinsics::deserialize(d)?; + + // Ok(Inner::) + todo!() + } +} + impl Realm { /// Create a new Realm. #[inline] diff --git a/boa_engine/src/snapshot/deserializer.rs b/boa_engine/src/snapshot/deserializer.rs index cdf324c1510..88308688d65 100644 --- a/boa_engine/src/snapshot/deserializer.rs +++ b/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; + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult; } /// TODO: doc @@ -111,3 +115,201 @@ impl SnapshotDeserializer<'_> { Ok(bytes) } } + +impl Deserialize for bool { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + d.read_bool() + } +} + +impl Deserialize for u8 { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + d.read_u8() + } +} + +impl Deserialize for i8 { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + d.read_i8() + } +} + +impl Deserialize for u16 { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + d.read_u16() + } +} + +impl Deserialize for i16 { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + d.read_i16() + } +} + +impl Deserialize for u32 { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + d.read_u32() + } +} + +impl Deserialize for i32 { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + d.read_i32() + } +} + +impl Deserialize for u64 { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + d.read_u64() + } +} + +impl Deserialize for i64 { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + d.read_i64() + } +} + +impl Deserialize for usize { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + d.read_usize() + } +} + +impl Deserialize for isize { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + d.read_isize() + } +} + +impl Deserialize for f32 { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + d.read_f32() + } +} + +impl Deserialize for f64 { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + d.read_f64() + } +} + +impl Deserialize for Option { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + let is_some = bool::deserialize(d)?; + if is_some { + return Ok(Some(T::deserialize(d)?)); + } + + Ok(None) + } +} + +impl Deserialize for Result { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + let is_ok = bool::deserialize(d)?; + Ok(if is_ok { + Ok(T::deserialize(d)?) + } else { + Err(E::deserialize(d)?) + }) + } +} + +impl Deserialize for Vec { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + 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 Deserialize for ThinVec { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + 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 Deserialize for Box { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + let value = T::deserialize(d)?; + Ok(Box::new(value)) + } +} + +impl Deserialize for Box { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + 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 { + // Deserialize nothing, zero size type. + Ok(()) + } +} + +impl Deserialize for (T1,) { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + let v1 = T1::deserialize(d)?; + Ok((v1,)) + } +} + +impl Deserialize for (T1, T2) { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + let v1 = T1::deserialize(d)?; + let v2 = T2::deserialize(d)?; + Ok((v1, v2)) + } +} + +impl Deserialize for (T1, T2, T3) { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + let v1 = T1::deserialize(d)?; + let v2 = T2::deserialize(d)?; + let v3 = T3::deserialize(d)?; + Ok((v1, v2, v3)) + } +} + +impl Deserialize + for (T1, T2, T3, T4) +{ + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + 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 Deserialize for FxHashMap { + fn deserialize(d: &mut SnapshotDeserializer<'_>) -> SnapshotResult { + 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) + } +} diff --git a/boa_engine/src/snapshot/error.rs b/boa_engine/src/snapshot/error.rs index 3abb209d295..630651d471f 100644 --- a/boa_engine/src/snapshot/error.rs +++ b/boa_engine/src/snapshot/error.rs @@ -23,3 +23,6 @@ impl From for SnapshotError { Self::Io(value) } } + +/// Type alias for [`Result`] return type snapshot operations. +pub type SnapshotResult = Result; diff --git a/boa_engine/src/snapshot/header.rs b/boa_engine/src/snapshot/header.rs new file mode 100644 index 00000000000..241c7330628 --- /dev/null +++ b/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 { + 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 }) + } +} diff --git a/boa_engine/src/snapshot/mod.rs b/boa_engine/src/snapshot/mod.rs index aa34de6548c..25d0d2cba36 100644 --- a/boa_engine/src/snapshot/mod.rs +++ b/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 { - 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 { diff --git a/boa_engine/src/snapshot/serializer.rs b/boa_engine/src/snapshot/serializer.rs index 2f3e99f8fc3..eee403ae91e 100644 --- a/boa_engine/src/snapshot/serializer.rs +++ b/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, symbols: IndexMap, bigints: IndexSet, + shared_shapes: IndexMap, + + pub(crate) internal_reference: IndexMap, external_references: IndexSet, } @@ -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 { // 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(&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::() + size_of::()) 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 Serialize for Option { + 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 Serialize for Result { + 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 Serialize for Vec { - 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 Serialize for ThinVec { + fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> { s.write_usize(self.len())?; for element in self { element.serialize(s)?; @@ -193,37 +345,127 @@ impl Serialize for Vec { } } -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 Serialize for Box { + fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> { + T::serialize(&self, s) + } +} - s.write_u32(index as u32)?; +impl Serialize for Box { + 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 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 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 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 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 Serialize for FxHashMap { + 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 Serialize for Gc { + fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> { + let ptr: *const _ = &*self; + s.reference_or(ptr as usize, |s| T::serialize(&*self, s)) + } +} + +impl Serialize for Rc { + fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> { + let ptr: *const _ = &*self; + s.reference_or(ptr as usize, |s| T::serialize(&*self, s)) + } +} + +impl Serialize for RefCell { + fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> { + self.borrow().serialize(s) + } +} + +impl Serialize for Cell { + fn serialize(&self, s: &mut SnapshotSerializer) -> SnapshotResult<()> { + self.get().serialize(s) + } +} diff --git a/boa_engine/src/string/mod.rs b/boa_engine/src/string/mod.rs index 26b0b4fc8ec..7e7174efc1d 100644 --- a/boa_engine/src/string/mod.rs +++ b/boa_engine/src/string/mod.rs @@ -209,6 +209,36 @@ pub struct JsString { pub(crate) ptr: Tagged, } +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 { + todo!() + } +} + // JsString should always be pointer sized. sa::assert_eq_size!(JsString, *const ()); diff --git a/boa_engine/src/symbol.rs b/boa_engine/src/symbol.rs index be7c774a419..046f5ba60c3 100644 --- a/boa_engine/src/symbol.rs +++ b/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, + repr: Tagged, +} + +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 { + todo!() + } } // SAFETY: `JsSymbol` uses `Arc` to do the reference counting, making this type thread-safe. diff --git a/docs/snapshot.md b/docs/snapshot.md index df0a1621a06..92641aca825 100644 --- a/docs/snapshot.md +++ b/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