Skip to content

Commit 2be5486

Browse files
srawlinsCommit Queue
authored andcommitted
vm_service: Tidy nullability and finality in generator
While exploring this code, I saw some easy wins: * A number of local variables of the form `String? x = a.b.c;` can be made non-nullable as `.c` now returns `String`; no tooling will warn the developer of this. * A number of methods of the form `String? m() {...}` can be made non- nullable, as they only return non-null values; no tooling will warn the developer of this. * A number of methods of the form `m(String? p) {...}` can instead accept non-nullable Strings, as all of the call sites pass non-null Strings. * `_coerceRefType` accepted a `String?` parameter but immediately null- checked it's value. We can use the type system to enforce that this function requires non-null values. * StreamCategory's `_name` and `_events` fields can be made non-nullable by using a little factory constructor; this enhancement reveals that the fields can be made final (analyzer reports this). Then we have unnecessary public getters that expose these final fields; the fields can instead be made public, and the getters removed. * `Api.types` can be made a `List<Type>`. * Some fields can be made final: `MemberType.types`, `TypeRef.name`, `TypeRef.nullable`, `MethodArg.type`, and `MethodArg.name`. * Some fields can be made non-nullable: `TypeRef.name`, `MethodArg.name`, and `EnumValue.name`. Change-Id: Ia36c1edc4686c6a14d76fd053d70da60b7b4aac6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/436420 Commit-Queue: Samuel Rawlins <[email protected]> Reviewed-by: Ben Konyi <[email protected]>
1 parent 22a74f5 commit 2be5486

File tree

3 files changed

+63
-56
lines changed

3 files changed

+63
-56
lines changed

pkg/vm_service/tool/dart/generate_dart_client.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ void addTypeFactory(String name, Function factory) {
533533
gen.writeln();
534534
gen.writeln('final _typeFactories = <String, Function>{');
535535
for (var type in types) {
536-
gen.writeln("'${type!.rawName}': ${type.name}.parse,");
536+
gen.writeln("'${type.rawName}': ${type.name}.parse,");
537537
}
538538
gen.writeln('};');
539539
gen.writeln();
@@ -672,12 +672,12 @@ typedef VmServiceFactory<T extends VmService> = T Function({
672672
}
673673
gen.writeln();
674674
gen.writeln('// types');
675-
types.where((t) => !t!.skip).forEach((t) => t!.generate(gen));
675+
types.where((t) => !t.skip).forEach((t) => t.generate(gen));
676676
}
677677

678678
void setDefaultValue(String typeName, String fieldName, String defaultValue) {
679679
types
680-
.firstWhere((t) => t!.name == typeName)!
680+
.firstWhere((t) => t.name == typeName)
681681
.fields
682682
.firstWhere((f) => f.name == fieldName)
683683
.defaultValue = defaultValue;

pkg/vm_service/tool/dart/generate_dart_common.dart

Lines changed: 59 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
library generate_vm_service_dart;
66

7+
import 'package:collection/collection.dart';
78
import 'package:markdown/markdown.dart';
89

910
import '../common/generate_common.dart';
@@ -13,15 +14,15 @@ import 'src_gen_dart.dart';
1314

1415
export 'src_gen_dart.dart' show DartGenerator;
1516

16-
String? _coerceRefType(String? typeName) {
17+
String _coerceRefType(String typeName) {
1718
if (typeName == 'Object') typeName = 'Obj';
1819
if (typeName == '@Object') typeName = 'ObjRef';
1920
if (typeName == 'Null') typeName = 'NullVal';
2021
if (typeName == '@Null') typeName = 'NullValRef';
2122
if (typeName == 'Function') typeName = 'Func';
2223
if (typeName == '@Function') typeName = 'FuncRef';
2324

24-
if (typeName!.startsWith('@')) typeName = '${typeName.substring(1)}Ref';
25+
if (typeName.startsWith('@')) typeName = '${typeName.substring(1)}Ref';
2526

2627
if (typeName == 'string') typeName = 'String';
2728
if (typeName == 'map') typeName = 'Map';
@@ -30,7 +31,7 @@ String? _coerceRefType(String? typeName) {
3031
}
3132

3233
String typeRefListToString(List<TypeRef> types) =>
33-
'const [${types.map((e) => "'${e.name!}'").join(',')}]';
34+
'const [${types.map((e) => "'${e.name}'").join(',')}]';
3435

3536
final registerServiceImpl = '''
3637
_serviceExtensionRegistry.registerExtension(params!['service'], this);
@@ -89,7 +90,7 @@ abstract class Api with ApiParseUtil {
8990
String? serviceVersion;
9091
List<Method> methods = [];
9192
List<Enum> enums = [];
92-
List<Type?> types = [];
93+
List<Type> types = [];
9394
List<StreamCategory> streamCategories = [];
9495

9596
void parse(List<Node> nodes) {
@@ -132,8 +133,8 @@ abstract class Api with ApiParseUtil {
132133
}
133134
}
134135

135-
for (Type? type in types) {
136-
type!.calculateFieldOverrides();
136+
for (Type type in types) {
137+
type.calculateFieldOverrides();
137138
}
138139

139140
Method streamListenMethod =
@@ -190,32 +191,30 @@ abstract class Api with ApiParseUtil {
190191

191192
void generate(DartGenerator gen);
192193

193-
bool isEnumName(String? typeName) =>
194-
enums.any((Enum e) => e.name == typeName);
194+
bool isEnumName(String typeName) => enums.any((Enum e) => e.name == typeName);
195195

196-
Type? getType(String? name) =>
197-
types.firstWhere((t) => t!.name == name, orElse: () => null);
196+
Type? getType(String name) => types.firstWhereOrNull((t) => t.name == name);
198197
}
199198

200199
class StreamCategory {
201-
String? _name;
202-
List<String>? _events;
200+
final String name;
201+
final List<String> events;
203202

204-
StreamCategory(String line) {
203+
factory StreamCategory(String line) {
205204
// Debug | PauseStart, PauseExit, ...
206-
_name = line.split('|')[0].trim();
205+
String name = line.split('|')[0].trim();
207206

208207
line = line.split('|')[1];
209-
_events = line.split(',').map((w) => w.trim()).toList();
210-
}
208+
List<String> events = line.split(',').map((w) => w.trim()).toList();
211209

212-
String? get name => _name;
210+
return StreamCategory._(name: name, events: events);
211+
}
213212

214-
List<String>? get events => _events;
213+
StreamCategory._({required this.name, required this.events});
215214

216215
void generate(DartGenerator gen) {
217216
gen.writeln();
218-
gen.writeln('// ${events!.join(', ')}');
217+
gen.writeln('// ${events.join(', ')}');
219218
gen.writeln(
220219
"Stream<Event> get on${name}Event => _getEventController('$name').stream;");
221220
}
@@ -258,7 +257,7 @@ class Method extends Member {
258257
.join());
259258

260259
args.where((MethodArg a) => a.optional).forEach((MethodArg arg) {
261-
String? valueRef = arg.name;
260+
String valueRef = arg.name;
262261
// Special case for `getAllocationProfile`. We do not want to add these
263262
// params if they are false.
264263
if (name == 'getAllocationProfile') {
@@ -332,7 +331,7 @@ class Method extends Member {
332331

333332
class MemberType extends Member {
334333
final Api api;
335-
List<TypeRef> types = [];
334+
final List<TypeRef> types = [];
336335

337336
MemberType(this.api);
338337

@@ -353,14 +352,14 @@ class MemberType extends Member {
353352
// @Instance | Sentinel
354353
final token = parser.advance()!;
355354
if (token.isName) {
356-
unionTypes.add(_coerceRefType(token.text)!);
355+
unionTypes.add(_coerceRefType(token.text!));
357356
}
358357
}
359358
}
360359
parser.consume(')');
361360
TypeRef ref;
362361
if (unionTypes.length == 1) {
363-
ref = TypeRef(unionTypes.first)..nullable = nullable;
362+
ref = TypeRef(unionTypes.first, nullable: nullable);
364363
} else {
365364
ref = TypeRef('dynamic');
366365
}
@@ -371,7 +370,7 @@ class MemberType extends Member {
371370
types.add(ref);
372371
} else {
373372
Token t = parser.expectName();
374-
TypeRef ref = TypeRef(_coerceRefType(t.text));
373+
TypeRef ref = TypeRef(_coerceRefType(t.text!));
375374
while (parser.consume('[')) {
376375
parser.expect(']');
377376
ref.arrayDepth++;
@@ -421,12 +420,12 @@ class MemberType extends Member {
421420
}
422421

423422
class TypeRef {
424-
String? name;
423+
final String name;
425424
int arrayDepth = 0;
426-
bool nullable = false;
425+
final bool nullable;
427426
List<TypeRef>? genericTypes;
428427

429-
TypeRef(this.name);
428+
TypeRef(this.name, {this.nullable = false});
430429

431430
String get ref {
432431
if (arrayDepth == 2) {
@@ -436,7 +435,7 @@ class TypeRef {
436435
} else if (genericTypes != null) {
437436
return '$name<${genericTypes!.join(', ')}>';
438437
} else {
439-
return name!.startsWith('_') ? name!.substring(1) : name!;
438+
return name.startsWith('_') ? name.substring(1) : name;
440439
}
441440
}
442441

@@ -450,7 +449,7 @@ class TypeRef {
450449
}
451450
}
452451

453-
String? get listTypeArg => arrayDepth == 2
452+
String get listTypeArg => arrayDepth == 2
454453
? 'List<$name${nullable ? "?" : ""}>'
455454
: '$name${nullable ? "?" : ""}';
456455

@@ -480,9 +479,9 @@ class TypeRef {
480479

481480
class MethodArg extends Member {
482481
final Method parent;
483-
TypeRef type;
482+
final TypeRef type;
484483
@override
485-
String? name;
484+
final String name;
486485
bool optional = false;
487486

488487
MethodArg(this.parent, this.type, this.name);
@@ -539,7 +538,7 @@ class Type extends Member {
539538
bool get isResponse {
540539
if (superName == null) return false;
541540
if (name == 'Response' || superName == 'Response') return true;
542-
return parent.getType(superName)!.isResponse;
541+
return parent.getType(superName!)!.isResponse;
543542
}
544543

545544
bool get isRef => name!.endsWith('Ref');
@@ -549,7 +548,7 @@ class Type extends Member {
549548
return superName == null ? false : getSuper()!.supportsIdentity;
550549
}
551550

552-
Type? getSuper() => superName == null ? null : api.getType(superName);
551+
Type? getSuper() => superName == null ? null : api.getType(superName!);
553552

554553
List<TypeField> getAllFields() {
555554
if (superName == null) return fields;
@@ -575,7 +574,7 @@ class Type extends Member {
575574
gen.write('class $name ');
576575
Type? superType;
577576
if (superName != null) {
578-
superType = parent.getType(superName);
577+
superType = parent.getType(superName!);
579578
gen.write('extends $superName ');
580579
}
581580
if (parent.getType('${name}Ref') != null) {
@@ -963,7 +962,7 @@ void _parseTokenPosTable() {
963962
TypeRef arrayType = type.types.first;
964963
if (arrayType.arrayDepth == 1) {
965964
String assertMethodName =
966-
'assertListOf${arrayType.name!.substring(0, 1).toUpperCase()}${arrayType.name!.substring(1)}';
965+
'assertListOf${arrayType.name.substring(0, 1).toUpperCase()}${arrayType.name.substring(1)}';
967966
gen.writeln('$assertMethodName(obj.${field.generatableName}!);');
968967
} else {
969968
gen.writeln(
@@ -977,7 +976,7 @@ void _parseTokenPosTable() {
977976
gen.writeln(
978977
'if (obj.${field.generatableName} is vms.${typeRef.name}) {');
979978
String assertMethodName =
980-
'assert${typeRef.name!.substring(0, 1).toUpperCase()}${typeRef.name!.substring(1)}';
979+
'assert${typeRef.name.substring(0, 1).toUpperCase()}${typeRef.name.substring(1)}';
981980
gen.writeln('$assertMethodName(obj.${field.generatableName}!);');
982981
}
983982
gen.writeln('} else {');
@@ -1051,7 +1050,7 @@ class TypeField extends Member {
10511050
void setOverrides() => overrides = true;
10521051

10531052
@override
1054-
String? get docs {
1053+
String get docs {
10551054
String str = _docs ?? '';
10561055
if (type.isMultipleReturns) {
10571056
str += '\n\n[$generatableName] can be one of '
@@ -1102,9 +1101,9 @@ class TypeField extends Member {
11021101
@override
11031102
void generate(DartGenerator gen) {
11041103
Type? refType = parent.parent.getType('${parent.name}Ref');
1105-
var interfaceOverride = refType?.hasField(name) ?? false;
1104+
bool interfaceOverride = refType?.hasField(name) ?? false;
11061105

1107-
if (docs!.isNotEmpty) gen.writeDocs(docs!);
1106+
if (docs.isNotEmpty) gen.writeDocs(docs);
11081107
if (optional) gen.write('@optional ');
11091108
if (overrides || interfaceOverride) gen.write('@override ');
11101109
// Special case where Instance extends Obj, but 'classRef' is not optional
@@ -1116,9 +1115,17 @@ class TypeField extends Member {
11161115
gen.writeStatement('covariant late final String valueAsString;');
11171116
} else */
11181117
{
1119-
String? typeName =
1118+
String typeName =
11201119
api.isEnumName(type.name) ? '/*${type.name}*/ String' : type.name;
11211120
if (typeName != 'dynamic') {
1121+
// Since this package is used to interact with DWDS as well as the
1122+
// native VM service, we need to be extremely careful when making types
1123+
// non-nullable. DWDS isn't completely compliant with the service
1124+
// protocol specification, meaning that some fields aren't provided in
1125+
// responses even though they're not marked as optional. This has been a
1126+
// headache for years, but until we're able to make DWDS consistent with
1127+
// the native VM service responses and test package:vm_service against
1128+
// it, we need to be extremely defensive in our parsing logic.
11221129
typeName = '$typeName?';
11231130
}
11241131
gen.writeStatement('$typeName $generatableName;');
@@ -1128,7 +1135,7 @@ class TypeField extends Member {
11281135

11291136
void generateNamedParameter(DartGenerator gen, {bool fromParent = false}) {
11301137
if (fromParent) {
1131-
String? typeName =
1138+
String typeName =
11321139
api.isEnumName(type.name) ? '/*${type.name}*/ String' : type.name;
11331140
gen.writeStatement('required $typeName $generatableName,');
11341141
} else {
@@ -1145,15 +1152,15 @@ class Enum extends Member {
11451152

11461153
List<EnumValue> enums = [];
11471154

1148-
Enum(this.name, String definition, [this.docs]) {
1155+
Enum(this.name, String definition, this.docs) {
11491156
_parse(Tokenizer(definition).tokenize());
11501157
}
11511158

11521159
Enum._(this.name, this.docs);
11531160

11541161
factory Enum.merge(Enum e1, Enum e2) {
11551162
final String name = e1.name;
1156-
final String docs = [e1.docs, e2.docs].where((e) => e != null).join('\n');
1163+
final String docs = [e1.docs, e2.docs].nonNulls.join('\n');
11571164
final Map<String?, EnumValue> map = <String?, EnumValue>{};
11581165
for (EnumValue e in e2.enums.reversed) {
11591166
map[e.name] = e;
@@ -1186,7 +1193,7 @@ class Enum extends Member {
11861193
void generateAssert(DartGenerator gen) {
11871194
gen.writeln('String assert$name(String obj) {');
11881195
List<EnumValue> sorted = enums.toList()
1189-
..sort((EnumValue e1, EnumValue e2) => e1.name!.compareTo(e2.name!));
1196+
..sort((EnumValue e1, EnumValue e2) => e1.name.compareTo(e2.name));
11901197
for (EnumValue value in sorted) {
11911198
gen.writeln(' if (obj == "${value.name}") return obj;');
11921199
}
@@ -1201,7 +1208,7 @@ class Enum extends Member {
12011208
class EnumValue extends Member {
12021209
final Enum parent;
12031210
@override
1204-
final String? name;
1211+
final String name;
12051212
@override
12061213
final String? docs;
12071214

@@ -1257,7 +1264,7 @@ class TextOutputVisitor implements NodeVisitor {
12571264

12581265
@override
12591266
void visitText(Text text) {
1260-
String? t = text.text;
1267+
String t = text.text;
12611268
if (_em) {
12621269
t = _coerceRefType(t);
12631270
} else if (_href) {
@@ -1323,7 +1330,7 @@ class MethodParser extends Parser {
13231330

13241331
while (peek()!.text != ')') {
13251332
Token type = expectName();
1326-
TypeRef ref = TypeRef(_coerceRefType(type.text));
1333+
TypeRef ref = TypeRef(_coerceRefType(type.text!));
13271334
if (peek()!.text == '[') {
13281335
while (consume('[')) {
13291336
expect(']');
@@ -1335,14 +1342,14 @@ class MethodParser extends Parser {
13351342
ref.genericTypes = [];
13361343
while (peek()!.text != '>') {
13371344
Token genericTypeName = expectName();
1338-
ref.genericTypes!.add(TypeRef(_coerceRefType(genericTypeName.text)));
1345+
ref.genericTypes!.add(TypeRef(_coerceRefType(genericTypeName.text!)));
13391346
consume(',');
13401347
}
13411348
expect('>');
13421349
}
13431350

13441351
Token name = expectName();
1345-
MethodArg arg = MethodArg(method, ref, name.text);
1352+
MethodArg arg = MethodArg(method, ref, name.text!);
13461353
if (consume('[')) {
13471354
expect('optional');
13481355
expect(']');
@@ -1374,10 +1381,10 @@ class TypeParser extends Parser {
13741381

13751382
Token t = expectName();
13761383
type.rawName = t.text;
1377-
type.name = _coerceRefType(type.rawName);
1384+
type.name = _coerceRefType(type.rawName!);
13781385
if (consume('extends')) {
13791386
t = expectName();
1380-
type.superName = _coerceRefType(t.text);
1387+
type.superName = _coerceRefType(t.text!);
13811388
}
13821389

13831390
expect('{');
@@ -1431,7 +1438,7 @@ class EnumParser extends Parser {
14311438
t = expectName();
14321439
consume(',');
14331440

1434-
e.enums.add(EnumValue(e, t.text, docs));
1441+
e.enums.add(EnumValue(e, t.text!, docs));
14351442
}
14361443
}
14371444
}

0 commit comments

Comments
 (0)