Skip to content

Commit 5564eaa

Browse files
authored
Include static member information for classes (#1454)
1 parent 6184a08 commit 5564eaa

File tree

4 files changed

+168
-75
lines changed

4 files changed

+168
-75
lines changed

dwds/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
- Fix chrome detection in iPhone emulation mode in chrome or edge browsers.
1515
- Reliably find unused port for extension backend http service.
1616
- Ignore offset / count parameters in getObject if the object has no length
17+
- Include static member information for classes
1718

1819
## 11.4.0
1920

dwds/lib/src/debugging/classes.dart

+59-5
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class ClassHelper extends Domain {
117117
"isStatic": false,
118118
}
119119
}
120-
// TODO(jakemac): static fields once ddc supports them
120+
121121
var fields = sdkUtils.getFields(clazz);
122122
var fieldNames = fields ? Object.keys(fields) : [];
123123
descriptor['fields'] = {};
@@ -135,6 +135,34 @@ class ClassHelper extends Domain {
135135
"classRefLibraryId" : field["type"][libraryUri],
136136
}
137137
}
138+
139+
// TODO(elliette): The following static member information is minimal and
140+
// should be replaced once DDC provides full symbol information (see
141+
// https://github.com/dart-lang/sdk/issues/40273):
142+
143+
descriptor['staticFields'] = {};
144+
var staticFieldNames = sdkUtils.getStaticFields(clazz) ?? [];
145+
for (const name of staticFieldNames) {
146+
descriptor['staticFields'][name] = {
147+
"isStatic": true,
148+
// DDC only provides names of static members, we set isConst/isFinal
149+
// to false even though they could be true.
150+
"isConst": false,
151+
"isFinal": false,
152+
}
153+
}
154+
155+
descriptor['staticMethods'] = {};
156+
var staticMethodNames = sdkUtils.getStaticMethods(clazz) ?? [];
157+
for (var name of staticMethodNames) {
158+
descriptor['methods'][name] = {
159+
// DDC only provides names of static members, we set isConst
160+
// to false even though it could be true.
161+
"isConst": false,
162+
"isStatic": true,
163+
}
164+
}
165+
138166
return descriptor;
139167
})()
140168
''';
@@ -153,6 +181,9 @@ class ClassHelper extends Domain {
153181
var classDescriptor = result.value as Map<String, dynamic>;
154182
var methodRefs = <FuncRef>[];
155183
var methodDescriptors = classDescriptor['methods'] as Map<String, dynamic>;
184+
var staticMethodDescriptors =
185+
classDescriptor['staticMethods'] as Map<String, dynamic>;
186+
methodDescriptors.addAll(staticMethodDescriptors);
156187
methodDescriptors.forEach((name, descriptor) {
157188
var methodId = 'methods|${classRef.id}|$name';
158189
methodRefs.add(FuncRef(
@@ -176,16 +207,39 @@ class ClassHelper extends Domain {
176207
name: name,
177208
owner: classRef,
178209
declaredType: InstanceRef(
179-
identityHashCode: createId().hashCode,
180-
id: createId(),
181-
kind: InstanceKind.kType,
182-
classRef: classMetaData.classRef),
210+
identityHashCode: createId().hashCode,
211+
id: createId(),
212+
kind: InstanceKind.kType,
213+
// TODO(elliette): Is this the same as classRef?
214+
classRef: classMetaData.classRef,
215+
),
183216
isConst: descriptor['isConst'] as bool,
184217
isFinal: descriptor['isFinal'] as bool,
185218
isStatic: descriptor['isStatic'] as bool,
186219
id: createId()));
187220
});
188221

222+
var staticFieldDescriptors =
223+
classDescriptor['staticFields'] as Map<String, dynamic>;
224+
staticFieldDescriptors.forEach((name, descriptor) async {
225+
fieldRefs.add(
226+
FieldRef(
227+
name: name,
228+
owner: classRef,
229+
declaredType: InstanceRef(
230+
identityHashCode: createId().hashCode,
231+
id: createId(),
232+
kind: InstanceKind.kType,
233+
classRef: classRef,
234+
),
235+
isConst: descriptor['isConst'] as bool,
236+
isFinal: descriptor['isFinal'] as bool,
237+
isStatic: descriptor['isStatic'] as bool,
238+
id: createId(),
239+
),
240+
);
241+
});
242+
189243
// TODO: Implement the rest of these
190244
// https://github.com/dart-lang/webdev/issues/176.
191245
return Class(

dwds/test/chrome_proxy_service_test.dart

+104-70
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import 'package:dwds/src/services/chrome_proxy_service.dart';
1515
import 'package:dwds/src/utilities/dart_uri.dart';
1616
import 'package:http/http.dart' as http;
1717
import 'package:path/path.dart' as path;
18+
import 'package:pub_semver/pub_semver.dart' as semver;
1819
import 'package:test/test.dart';
1920
import 'package:vm_service/vm_service.dart';
2021
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
@@ -445,38 +446,55 @@ void main() {
445446
expect(library1, equals(library2));
446447
});
447448

448-
test('Classes', () async {
449-
var testClass = await service.getObject(
450-
isolate.id, rootLibrary.classes.first.id) as Class;
451-
expect(
452-
testClass.functions,
453-
unorderedEquals([
454-
predicate((FuncRef f) => f.name == 'message' && !f.isStatic),
455-
predicate((FuncRef f) => f.name == 'notFinal' && !f.isStatic),
456-
predicate((FuncRef f) => f.name == 'hello' && !f.isStatic),
457-
predicate((FuncRef f) => f.name == '_equals' && !f.isStatic),
458-
predicate((FuncRef f) => f.name == 'hashCode' && !f.isStatic),
459-
predicate((FuncRef f) => f.name == 'toString' && !f.isStatic),
460-
predicate((FuncRef f) => f.name == 'noSuchMethod' && !f.isStatic),
461-
predicate((FuncRef f) => f.name == 'runtimeType' && !f.isStatic),
462-
]));
463-
expect(
464-
testClass.fields,
465-
unorderedEquals([
466-
predicate((FieldRef f) =>
467-
f.name == 'message' &&
468-
f.declaredType != null &&
469-
!f.isStatic &&
470-
!f.isConst &&
471-
f.isFinal),
472-
predicate((FieldRef f) =>
473-
f.name == 'notFinal' &&
474-
f.declaredType != null &&
475-
!f.isStatic &&
476-
!f.isConst &&
477-
!f.isFinal),
478-
]));
479-
});
449+
test(
450+
'Classes',
451+
() async {
452+
var testClass = await service.getObject(
453+
isolate.id, rootLibrary.classes.first.id) as Class;
454+
expect(
455+
testClass.functions,
456+
unorderedEquals([
457+
predicate((FuncRef f) => f.name == 'staticHello' && f.isStatic),
458+
predicate((FuncRef f) => f.name == 'message' && !f.isStatic),
459+
predicate((FuncRef f) => f.name == 'notFinal' && !f.isStatic),
460+
predicate((FuncRef f) => f.name == 'hello' && !f.isStatic),
461+
predicate((FuncRef f) => f.name == '_equals' && !f.isStatic),
462+
predicate((FuncRef f) => f.name == 'hashCode' && !f.isStatic),
463+
predicate((FuncRef f) => f.name == 'toString' && !f.isStatic),
464+
predicate(
465+
(FuncRef f) => f.name == 'noSuchMethod' && !f.isStatic),
466+
predicate(
467+
(FuncRef f) => f.name == 'runtimeType' && !f.isStatic),
468+
]));
469+
expect(
470+
testClass.fields,
471+
unorderedEquals([
472+
predicate((FieldRef f) =>
473+
f.name == 'message' &&
474+
f.declaredType != null &&
475+
!f.isStatic &&
476+
!f.isConst &&
477+
f.isFinal),
478+
predicate((FieldRef f) =>
479+
f.name == 'notFinal' &&
480+
f.declaredType != null &&
481+
!f.isStatic &&
482+
!f.isConst &&
483+
!f.isFinal),
484+
predicate((FieldRef f) =>
485+
f.name == 'staticMessage' &&
486+
f.declaredType != null &&
487+
f.isStatic &&
488+
!f.isConst &&
489+
!f.isFinal),
490+
]));
491+
},
492+
// TODO(elliette): Remove once 2.15.0 is the stable release.
493+
skip: semver.Version.parse(Platform.version.split(' ').first) >=
494+
semver.Version.parse('2.15.0-268.18.beta')
495+
? null
496+
: 'SDK does not expose static member information.',
497+
);
480498

481499
test('Runtime classes', () async {
482500
var testClass = await service.getObject(
@@ -701,44 +719,60 @@ void main() {
701719
expect(world.offset, 3);
702720
});
703721

704-
test('offset/count parameters are ignored for Classes', () async {
705-
var testClass = await service.getObject(
706-
isolate.id,
707-
rootLibrary.classes.first.id,
708-
offset: 100,
709-
count: 100,
710-
) as Class;
711-
expect(
712-
testClass.functions,
713-
unorderedEquals([
714-
predicate((FuncRef f) => f.name == 'message' && !f.isStatic),
715-
predicate((FuncRef f) => f.name == 'notFinal' && !f.isStatic),
716-
predicate((FuncRef f) => f.name == 'hello' && !f.isStatic),
717-
predicate((FuncRef f) => f.name == '_equals' && !f.isStatic),
718-
predicate((FuncRef f) => f.name == 'hashCode' && !f.isStatic),
719-
predicate((FuncRef f) => f.name == 'toString' && !f.isStatic),
720-
predicate(
721-
(FuncRef f) => f.name == 'noSuchMethod' && !f.isStatic),
722-
predicate(
723-
(FuncRef f) => f.name == 'runtimeType' && !f.isStatic),
724-
]));
725-
expect(
726-
testClass.fields,
727-
unorderedEquals([
728-
predicate((FieldRef f) =>
729-
f.name == 'message' &&
730-
f.declaredType != null &&
731-
!f.isStatic &&
732-
!f.isConst &&
733-
f.isFinal),
734-
predicate((FieldRef f) =>
735-
f.name == 'notFinal' &&
736-
f.declaredType != null &&
737-
!f.isStatic &&
738-
!f.isConst &&
739-
!f.isFinal),
740-
]));
741-
});
722+
test(
723+
'offset/count parameters are ignored for Classes',
724+
() async {
725+
var testClass = await service.getObject(
726+
isolate.id,
727+
rootLibrary.classes.first.id,
728+
offset: 100,
729+
count: 100,
730+
) as Class;
731+
expect(
732+
testClass.functions,
733+
unorderedEquals([
734+
predicate(
735+
(FuncRef f) => f.name == 'staticHello' && f.isStatic),
736+
predicate((FuncRef f) => f.name == 'message' && !f.isStatic),
737+
predicate((FuncRef f) => f.name == 'notFinal' && !f.isStatic),
738+
predicate((FuncRef f) => f.name == 'hello' && !f.isStatic),
739+
predicate((FuncRef f) => f.name == '_equals' && !f.isStatic),
740+
predicate((FuncRef f) => f.name == 'hashCode' && !f.isStatic),
741+
predicate((FuncRef f) => f.name == 'toString' && !f.isStatic),
742+
predicate(
743+
(FuncRef f) => f.name == 'noSuchMethod' && !f.isStatic),
744+
predicate(
745+
(FuncRef f) => f.name == 'runtimeType' && !f.isStatic),
746+
]));
747+
expect(
748+
testClass.fields,
749+
unorderedEquals([
750+
predicate((FieldRef f) =>
751+
f.name == 'message' &&
752+
f.declaredType != null &&
753+
!f.isStatic &&
754+
!f.isConst &&
755+
f.isFinal),
756+
predicate((FieldRef f) =>
757+
f.name == 'notFinal' &&
758+
f.declaredType != null &&
759+
!f.isStatic &&
760+
!f.isConst &&
761+
!f.isFinal),
762+
predicate((FieldRef f) =>
763+
f.name == 'staticMessage' &&
764+
f.declaredType != null &&
765+
f.isStatic &&
766+
!f.isConst &&
767+
!f.isFinal),
768+
]));
769+
},
770+
// TODO(elliette): Remove once 2.15.0 is the stable release.
771+
skip: semver.Version.parse(Platform.version.split(' ').first) >=
772+
semver.Version.parse('2.15.0-268.18.beta')
773+
? null
774+
: 'SDK does not expose static member information.',
775+
);
742776

743777
test('offset/count parameters are ignored for bools', () async {
744778
var ref = await service.evaluate(

fixtures/_test/example/hello_world/main.dart

+4
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ class MyTestClass {
117117

118118
String notFinal;
119119

120+
static final String staticMessage = 'static';
121+
122+
static String staticHello() => 'static hello';
123+
120124
MyTestClass({this.message = 'world'});
121125

122126
String hello() => message;

0 commit comments

Comments
 (0)