blob: d7df9317962143c46da45d25abe1e2c52ee145de [file] [edit]
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// These checks are not implemented in the analyzer. If we ever decide to
// implement the static checks in the analyzer, move this test into the
// static_checks subdir to prevent analyzer errors showing up in the IDE.
import 'dart:ffi';
import 'dart:typed_data';
import 'package:expect/expect.dart';
void main() {
testInstantiateDeeplyImmutable();
testOnlyFinalCanBeCaptured();
testOnlyTriviallyImmutableCanBeCaptured();
testCaptureGlobal();
testCaptureEmptyContext();
}
@pragma('vm:deeply-immutable')
final class DeeplyImmutableFoo {
final Function() closure;
DeeplyImmutableFoo(this.closure);
}
testOnlyFinalCanBeCaptured() {
List<String> list = <String>["Hello", "world"];
Expect.throws(() {
final foo = DeeplyImmutableFoo(() { list.add("abc"); return list; } );
print(foo.closure());
}, (e) => e is ArgumentError && e.toString().contains("Only final not-late"));
}
testOnlyTriviallyImmutableCanBeCaptured() {
final List<String> list = <String>["Hello", "world"];
Expect.throws(() {
final foo = DeeplyImmutableFoo(() { list.add("abc"); return list; } );
print(foo.closure());
}, (e) => e is ArgumentError &&
e.toString().contains("Only trivially-immutable values"));
}
final List<String> globalList = <String>["Hello", "world"];
testCaptureGlobal() {
final foo = DeeplyImmutableFoo(() { globalList.add("abc"); return globalList; });
print(foo.closure());
}
testCaptureEmptyContext() {
final foo = DeeplyImmutableFoo(() => 42);
print(foo.closure());
}
@pragma('vm:deeply-immutable')
final class EmptyClass {}
@pragma('vm:deeply-immutable')
final class Class3 {
Class3(this.a);
final int a;
}
@pragma('vm:deeply-immutable')
final class Class4 {
// Static fields are not part of the instance.
static late final int a;
}
@pragma('vm:deeply-immutable')
final class Class5 {
// External fields are defined as setter/getter pairs.
external int a;
}
final class NotDeeplyImmutable {
late int a;
}
void testInstantiateDeeplyImmutable() {
Class7(
someString: 'someString',
someNullableString: 'someString',
someInt: 3,
someDouble: 3.3,
someBool: false,
someNull: null,
someInt32x4: Int32x4(0, 1, 2, 3),
someFloat32x4: Float32x4(0.0, 1.1, 2.2, 3.3),
someFloat64x2: Float64x2(4.4, 5.5),
someClass7: Class7(
someString: 'someString',
someInt: 3,
someDouble: 3.3,
someBool: false,
someNull: null,
someInt32x4: Int32x4(0, 1, 2, 3),
someFloat32x4: Float32x4(0.0, 1.1, 2.2, 3.3),
someFloat64x2: Float64x2(4.4, 5.5),
someClass7: null,
somePointer: Pointer.fromAddress(0x8badf00d),
someClosure: () => 31,
),
somePointer: Pointer.fromAddress(0xdeadbeef),
someClosure: () => 42
);
}
@pragma('vm:deeply-immutable')
final class Class7 {
final String someString;
final String? someNullableString;
final int someInt;
final double someDouble;
final bool someBool;
final Null someNull;
final Int32x4 someInt32x4;
final Float32x4 someFloat32x4;
final Float64x2 someFloat64x2;
final Class7? someClass7;
final Pointer somePointer;
final Function() someClosure;
// Note that UnmodifiableUint8ListView has been deprecated. Which means there
// currently is no way to intentionally have a typed data as a field in a
// class which is deeply immutable.
// See: https://github.com/dart-lang/sdk/issues/53218.
// Note that RegExp, SendPort, and Capability can be implemented. So fields
// are not allowed to be of these types either.
Class7({
required this.someString,
this.someNullableString,
required this.someInt,
required this.someDouble,
required this.someBool,
required this.someNull,
required this.someInt32x4,
required this.someFloat32x4,
required this.someFloat64x2,
required this.someClass7,
required this.somePointer,
required this.someClosure,
});
}
void testInstantiateImmutableHierarchy() {
Class8(animal: Cat(numberOfLegs: 4, averageNumberOfMeowsPerDay: 42.0));
Class8(animal: Dog(numberOfLegs: 4, averageNumberOfWoofsPerDay: 1337.0));
}
@pragma('vm:deeply-immutable')
final class Animal {
final int numberOfLegs;
Animal({required this.numberOfLegs});
}
@pragma('vm:deeply-immutable')
final class Cat extends Animal {
final double averageNumberOfMeowsPerDay;
Cat({required super.numberOfLegs, required this.averageNumberOfMeowsPerDay});
}
@pragma('vm:deeply-immutable')
final class Dog extends Animal {
final double averageNumberOfWoofsPerDay;
Dog({required super.numberOfLegs, required this.averageNumberOfWoofsPerDay});
}
@pragma('vm:deeply-immutable')
final class Class8 {
final Animal animal;
Class8({required this.animal});
}
@pragma('vm:deeply-immutable')
abstract final class DeeplyImmutableInterface {}
@pragma('vm:deeply-immutable')
final class Class9 implements DeeplyImmutableInterface {}
@pragma('vm:deeply-immutable')
final class Class10 implements DeeplyImmutableInterface {}
@pragma('vm:deeply-immutable')
sealed class Class11 {}
@pragma('vm:deeply-immutable')
final class Class14<T extends DeeplyImmutableInterface> {
final T deeplyImmutable;
Class14({required this.deeplyImmutable});
}