Files
sdk/tests/language/enum/enum_test.dart
T
Lasse R.H. Nielsen 18f37dd8f3 Add extension name getter on Enum.
We use an extension getter instead of an instance getter because
it doesn't conflict with any potential existing or future enums
which want an element named `name`.
Keeping the namespace for enum elements open is a priority.
We currently only reserve `index` and `values`.

BUG: https://github.com/dart-lang/language/issues/1511

Fixes language issue #1511, which is a long-standing request,
and should replace a number of alternative implementations
which are based on parsing the `toString()`.


This version has two fields on the shared superclass, the index
and private name, and has a separate `toString` for each `enum` class
which hard-codes that enum's class name.

An earlier version had both `"name"` and `"ClassName.name"` as fields
to be able to reuse the same `toString` method on all enum classes,
but that cost too much for JS compiled code.
Even having just `ClassName.` as a field and then combining inside
`toString` requires more code to create the enum instances.
Instead this version hardcodes the `ClassName.` string once
in the `toString` method, which means each enum class has its own
toString (which can *potentially* be tree-shaken then.)

This still tree-shakes slightly worse than the previous implementation
where every enum class had its own `index` and `_name` fields
independent of each other, which could then be tree-shaken independently.
However, the `index` was already made an interface member with the
addition of the `Enum` interface, so code which accesses `.index`
on something of the `Enum` supertype could prevent tree-shaking of
all enum classes' `index` fields.
Likewise any general access to the "name" of an enum would necessarily
do the same for the name.
This CL makes up for some of that by sharing more implementation
between enum classes.

DartVM AOT CodeSize impact: ~0.15% regression on gallery (little less on big g3 app)

TEST= New tests added to enum_test.dart

Change-Id: Id25334e6c987f470f558de3c141d0e3ff542b020
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210480
Commit-Queue: Lasse R.H. Nielsen <lrn@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
2021-09-22 14:20:16 +00:00

189 lines
4.6 KiB
Dart

// Copyright (c) 2014, 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.
import 'package:expect/expect.dart';
enum Enum1 { _ }
enum Enum2 { A }
enum Enum3 { B, C }
enum Enum4 {
D,
E,
}
enum Enum5 { F, G, H }
enum _Enum6 { I, _J }
enum _IsNot { IsNot }
enum NamedName { name, address }
// Regression test for https://github.com/dart-lang/sdk/issues/33348
enum JSFunctionPrototype {
length,
prototype,
__proto__,
arguments,
caller,
calee,
name,
constructor,
apply,
bind,
call
}
void expectIs<T>(T t, bool Function(Object?) test) {
Object? obj = t;
Expect.isTrue(test(obj), '$obj is $T');
Expect.isFalse(obj is _IsNot, '$obj is _IsNot');
// test cast
t = obj as T;
Expect.throwsTypeError(() => obj as _IsNot, '$obj as _IsNot');
}
main() {
expectIs(Enum1._, (e) => e is Enum1);
expectIs(Enum2.A, (e) => e is Enum2);
expectIs(Enum3.B, (e) => e is Enum3);
expectIs(Enum4.E, (e) => e is Enum4);
expectIs(Enum5.G, (e) => e is Enum5);
Expect.equals('Enum1._', Enum1._.toString());
Expect.equals(0, Enum1._.index);
Expect.listEquals([Enum1._], Enum1.values);
Enum1.values.forEach(test1);
Expect.equals('Enum2.A', Enum2.A.toString());
Expect.equals(0, Enum2.A.index);
Expect.listEquals([Enum2.A], Enum2.values);
Expect.identical(const <Enum2>[Enum2.A], Enum2.values);
Enum2.values.forEach(test2);
Expect.equals('Enum3.B', Enum3.B.toString());
Expect.equals('Enum3.C', Enum3.C.toString());
Expect.equals(0, Enum3.B.index);
Expect.equals(1, Enum3.C.index);
Expect.listEquals([Enum3.B, Enum3.C], Enum3.values);
Enum3.values.forEach(test3);
Expect.equals('Enum4.D', Enum4.D.toString());
Expect.equals('Enum4.E', Enum4.E.toString());
Expect.equals(0, Enum4.D.index);
Expect.equals(1, Enum4.E.index);
Expect.listEquals([Enum4.D, Enum4.E], Enum4.values);
Enum4.values.forEach(test4);
Expect.equals('Enum5.F', Enum5.F.toString());
Expect.equals('Enum5.G', Enum5.G.toString());
Expect.equals('Enum5.H', Enum5.H.toString());
Expect.equals(0, Enum5.F.index);
Expect.equals(1, Enum5.G.index);
Expect.equals(2, Enum5.H.index);
Expect.listEquals([Enum5.F, Enum5.G, Enum5.H], Enum5.values);
Enum5.values.forEach(test5);
Expect.equals('_Enum6.I', _Enum6.I.toString());
Expect.equals('_Enum6._J', _Enum6._J.toString());
for (var value in JSFunctionPrototype.values) {
expectIs(value, (e) => e is JSFunctionPrototype);
}
Expect.equals(JSFunctionPrototype.length, JSFunctionPrototype.values[0]);
// Enums implement Enum.
Expect.type<Enum>(Enum1._);
Enum enumValue = Enum1._;
Expect.equals(0, enumValue.index);
// Enum.compareByIndex orders enums correctly.
var enumValues = [Enum5.G, Enum5.H, Enum5.F];
for (var i = 0; i < 10; i++) {
enumValues.sort(Enum.compareByIndex);
enumValues.fold<int>(-1, (previousValue, element) {
Expect.isTrue(previousValue < element.index, "$enumValues");
return element.index;
});
enumValues.shuffle();
}
// Can be used at the type `Enum` to compare different enums.
Expect.isTrue(Enum.compareByIndex<Enum>(Enum4.D, Enum5.H) < 0);
Expect.isTrue(Enum.compareByIndex<Enum>(Enum4.E, Enum5.F) > 0);
Expect.isTrue(Enum.compareByIndex<Enum>(Enum4.D, Enum5.F) == 0);
Expect.equals("A", Enum2.A.name);
Expect.equals("_", Enum1._.name);
Expect.equals("name", EnumName(NamedName.name).name);
Expect.identical(Enum2.A, Enum2.values.byName("A"));
Expect.identical(Enum1._, Enum1.values.byName("_"));
Expect.identical(NamedName.name, NamedName.values.byName("name"));
var map = NamedName.values.asNameMap();
Expect.type<Map<String, NamedName>>(map);
Expect.identical(NamedName.name, map["name"]);
Expect.identical(NamedName.address, map["address"]);
}
test1(Enum1 e) {
int index;
switch (e) {
case Enum1._:
index = 0;
break;
}
Expect.equals(e.index, index);
}
test2(Enum2 e) {
int index;
switch (e) {
case Enum2.A:
index = 0;
break;
}
Expect.equals(e.index, index);
}
test3(Enum3 e) {
int index;
switch (e) {
case Enum3.C:
index = 1;
break;
case Enum3.B:
index = 0;
break;
}
Expect.equals(e.index, index);
}
test4(Enum4 e) {
int index;
switch (e) {
case Enum4.D:
index = 0;
break;
case Enum4.E:
index = 1;
break;
}
Expect.equals(e.index, index);
}
test5(Enum5 e) {
int index;
switch (e) {
case Enum5.H:
index = 2;
break;
case Enum5.F:
index = 0;
break;
case Enum5.G:
index = 1;
break;
}
Expect.equals(e.index, index);
}