| # Deeply immutable instances and types |
| |
| The Dart VM has a concept of deeply immutable instances. |
| |
| Deeply immutable instances can be shared across isolates within the same group. |
| |
| ## Deeply immutable types |
| |
| A deeply immutable type is a type for which all instances that have this type are deeply immutable. |
| |
| This is useful for static checks on classes annotated `@pragma('vm:deeply-immutable')`. |
| All the instance fields of such classes must have a deeply immutable type. |
| |
| A list of immutable types: |
| |
| * `bool` |
| * `double` |
| * `int` |
| * `Null` |
| * `String` |
| * `Float32x4` |
| * `Float64x2` |
| * `Int32x4` |
| * `Pointer` |
| * classes annotated with `@pragma('vm:deeply-immutable')` |
| * type parameters bound by a deeply immutable type |
| |
| ## Deeply immutable instances without a deeply immutable type |
| |
| In addition to instances from deeply immutable types, |
| instances can also be deeply immutable while their type is not deeply immutable: |
| |
| * `SendPort` (implemented externally `package:isolate`, so cannot be `final` https://github.com/dart-lang/sdk/issues/54885#issuecomment-1967329435) |
| * `Capability` (has `SendPort` as subtype so cannot be `final`) |
| * `RegExp` (can be implemented externally, not `final`) |
| * `StackTrace` (can be implemented externally, not `final`) |
| * `Type` (can be implemented externally, not `final`) |
| * const object (the class can be deeply immutable) |
| |
| This means users cannot mark classes with fields typed with these types as `@pragma('vm:deeply-immutable')`. |
| |
| ## Shallowly immutable instances |
| |
| The VM also has shallow immutability. |
| |
| * unmodifiable typed data views (the backing view might not be immutable) |
| * closures (the context might not be empty) |
| |
| ## Implementation details |
| |
| ### Deeply and shallowly immutable instances |
| |
| The `UntaggedObject::ImmutableBit` tracks whether an instance is deeply or shallowly immutable at runtime. |
| For shallow immutable objects, the VM needs to know the layout and what to check when to check for to check deep immutability at runtime. |
| |
| ### Deeply immutable types |
| |
| The `Class::is_deeply_immutable` tracks whether all instances of a class are deeply immutable. |
| |
| This bit can be set in two ways: |
| |
| 1. For recognized classes, in the VM initialization. |
| 2. For classes with a Dart source, with the `vm:deeply-immutable` pragma. |
| |
| The `vm:deeply-immutable` pragma is added to classes of which their _type_ is deeply immutable. |
| |
| This puts the following restrictions on these classes: |
| |
| 1. All instance fields must |
| 1. have a deeply immutable type, |
| 2. be final, and |
| 3. be non-late. |
| 2. The class must be `final` or `sealed`. |
| This ensures no non-deeply-immutable subtypes are added by external code. |
| 3. All subtypes must be deeply immutable. |
| This ensures 1.1. can be trusted. |
| 4. The super type must be deeply immutable (except for Object). |
| |
| These restructions are enforced by [DeeplyImmutableValidator](../../pkg/vm/lib/transformations/ffi/deeply_immutable.dart). |