Skip to content

Commit

Permalink
Include static member information for classes (#1454)
Browse files Browse the repository at this point in the history
  • Loading branch information
elliette authored Dec 2, 2021
1 parent 6184a08 commit 5564eaa
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 75 deletions.
1 change: 1 addition & 0 deletions dwds/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- Fix chrome detection in iPhone emulation mode in chrome or edge browsers.
- Reliably find unused port for extension backend http service.
- Ignore offset / count parameters in getObject if the object has no length
- Include static member information for classes

## 11.4.0

Expand Down
64 changes: 59 additions & 5 deletions dwds/lib/src/debugging/classes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class ClassHelper extends Domain {
"isStatic": false,
}
}
// TODO(jakemac): static fields once ddc supports them
var fields = sdkUtils.getFields(clazz);
var fieldNames = fields ? Object.keys(fields) : [];
descriptor['fields'] = {};
Expand All @@ -135,6 +135,34 @@ class ClassHelper extends Domain {
"classRefLibraryId" : field["type"][libraryUri],
}
}
// TODO(elliette): The following static member information is minimal and
// should be replaced once DDC provides full symbol information (see
// https://github.com/dart-lang/sdk/issues/40273):
descriptor['staticFields'] = {};
var staticFieldNames = sdkUtils.getStaticFields(clazz) ?? [];
for (const name of staticFieldNames) {
descriptor['staticFields'][name] = {
"isStatic": true,
// DDC only provides names of static members, we set isConst/isFinal
// to false even though they could be true.
"isConst": false,
"isFinal": false,
}
}
descriptor['staticMethods'] = {};
var staticMethodNames = sdkUtils.getStaticMethods(clazz) ?? [];
for (var name of staticMethodNames) {
descriptor['methods'][name] = {
// DDC only provides names of static members, we set isConst
// to false even though it could be true.
"isConst": false,
"isStatic": true,
}
}
return descriptor;
})()
''';
Expand All @@ -153,6 +181,9 @@ class ClassHelper extends Domain {
var classDescriptor = result.value as Map<String, dynamic>;
var methodRefs = <FuncRef>[];
var methodDescriptors = classDescriptor['methods'] as Map<String, dynamic>;
var staticMethodDescriptors =
classDescriptor['staticMethods'] as Map<String, dynamic>;
methodDescriptors.addAll(staticMethodDescriptors);
methodDescriptors.forEach((name, descriptor) {
var methodId = 'methods|${classRef.id}|$name';
methodRefs.add(FuncRef(
Expand All @@ -176,16 +207,39 @@ class ClassHelper extends Domain {
name: name,
owner: classRef,
declaredType: InstanceRef(
identityHashCode: createId().hashCode,
id: createId(),
kind: InstanceKind.kType,
classRef: classMetaData.classRef),
identityHashCode: createId().hashCode,
id: createId(),
kind: InstanceKind.kType,
// TODO(elliette): Is this the same as classRef?
classRef: classMetaData.classRef,
),
isConst: descriptor['isConst'] as bool,
isFinal: descriptor['isFinal'] as bool,
isStatic: descriptor['isStatic'] as bool,
id: createId()));
});

var staticFieldDescriptors =
classDescriptor['staticFields'] as Map<String, dynamic>;
staticFieldDescriptors.forEach((name, descriptor) async {
fieldRefs.add(
FieldRef(
name: name,
owner: classRef,
declaredType: InstanceRef(
identityHashCode: createId().hashCode,
id: createId(),
kind: InstanceKind.kType,
classRef: classRef,
),
isConst: descriptor['isConst'] as bool,
isFinal: descriptor['isFinal'] as bool,
isStatic: descriptor['isStatic'] as bool,
id: createId(),
),
);
});

// TODO: Implement the rest of these
// https://github.com/dart-lang/webdev/issues/176.
return Class(
Expand Down
174 changes: 104 additions & 70 deletions dwds/test/chrome_proxy_service_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import 'package:dwds/src/services/chrome_proxy_service.dart';
import 'package:dwds/src/utilities/dart_uri.dart';
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as path;
import 'package:pub_semver/pub_semver.dart' as semver;
import 'package:test/test.dart';
import 'package:vm_service/vm_service.dart';
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
Expand Down Expand Up @@ -445,38 +446,55 @@ void main() {
expect(library1, equals(library2));
});

test('Classes', () async {
var testClass = await service.getObject(
isolate.id, rootLibrary.classes.first.id) as Class;
expect(
testClass.functions,
unorderedEquals([
predicate((FuncRef f) => f.name == 'message' && !f.isStatic),
predicate((FuncRef f) => f.name == 'notFinal' && !f.isStatic),
predicate((FuncRef f) => f.name == 'hello' && !f.isStatic),
predicate((FuncRef f) => f.name == '_equals' && !f.isStatic),
predicate((FuncRef f) => f.name == 'hashCode' && !f.isStatic),
predicate((FuncRef f) => f.name == 'toString' && !f.isStatic),
predicate((FuncRef f) => f.name == 'noSuchMethod' && !f.isStatic),
predicate((FuncRef f) => f.name == 'runtimeType' && !f.isStatic),
]));
expect(
testClass.fields,
unorderedEquals([
predicate((FieldRef f) =>
f.name == 'message' &&
f.declaredType != null &&
!f.isStatic &&
!f.isConst &&
f.isFinal),
predicate((FieldRef f) =>
f.name == 'notFinal' &&
f.declaredType != null &&
!f.isStatic &&
!f.isConst &&
!f.isFinal),
]));
});
test(
'Classes',
() async {
var testClass = await service.getObject(
isolate.id, rootLibrary.classes.first.id) as Class;
expect(
testClass.functions,
unorderedEquals([
predicate((FuncRef f) => f.name == 'staticHello' && f.isStatic),
predicate((FuncRef f) => f.name == 'message' && !f.isStatic),
predicate((FuncRef f) => f.name == 'notFinal' && !f.isStatic),
predicate((FuncRef f) => f.name == 'hello' && !f.isStatic),
predicate((FuncRef f) => f.name == '_equals' && !f.isStatic),
predicate((FuncRef f) => f.name == 'hashCode' && !f.isStatic),
predicate((FuncRef f) => f.name == 'toString' && !f.isStatic),
predicate(
(FuncRef f) => f.name == 'noSuchMethod' && !f.isStatic),
predicate(
(FuncRef f) => f.name == 'runtimeType' && !f.isStatic),
]));
expect(
testClass.fields,
unorderedEquals([
predicate((FieldRef f) =>
f.name == 'message' &&
f.declaredType != null &&
!f.isStatic &&
!f.isConst &&
f.isFinal),
predicate((FieldRef f) =>
f.name == 'notFinal' &&
f.declaredType != null &&
!f.isStatic &&
!f.isConst &&
!f.isFinal),
predicate((FieldRef f) =>
f.name == 'staticMessage' &&
f.declaredType != null &&
f.isStatic &&
!f.isConst &&
!f.isFinal),
]));
},
// TODO(elliette): Remove once 2.15.0 is the stable release.
skip: semver.Version.parse(Platform.version.split(' ').first) >=
semver.Version.parse('2.15.0-268.18.beta')
? null
: 'SDK does not expose static member information.',
);

test('Runtime classes', () async {
var testClass = await service.getObject(
Expand Down Expand Up @@ -701,44 +719,60 @@ void main() {
expect(world.offset, 3);
});

test('offset/count parameters are ignored for Classes', () async {
var testClass = await service.getObject(
isolate.id,
rootLibrary.classes.first.id,
offset: 100,
count: 100,
) as Class;
expect(
testClass.functions,
unorderedEquals([
predicate((FuncRef f) => f.name == 'message' && !f.isStatic),
predicate((FuncRef f) => f.name == 'notFinal' && !f.isStatic),
predicate((FuncRef f) => f.name == 'hello' && !f.isStatic),
predicate((FuncRef f) => f.name == '_equals' && !f.isStatic),
predicate((FuncRef f) => f.name == 'hashCode' && !f.isStatic),
predicate((FuncRef f) => f.name == 'toString' && !f.isStatic),
predicate(
(FuncRef f) => f.name == 'noSuchMethod' && !f.isStatic),
predicate(
(FuncRef f) => f.name == 'runtimeType' && !f.isStatic),
]));
expect(
testClass.fields,
unorderedEquals([
predicate((FieldRef f) =>
f.name == 'message' &&
f.declaredType != null &&
!f.isStatic &&
!f.isConst &&
f.isFinal),
predicate((FieldRef f) =>
f.name == 'notFinal' &&
f.declaredType != null &&
!f.isStatic &&
!f.isConst &&
!f.isFinal),
]));
});
test(
'offset/count parameters are ignored for Classes',
() async {
var testClass = await service.getObject(
isolate.id,
rootLibrary.classes.first.id,
offset: 100,
count: 100,
) as Class;
expect(
testClass.functions,
unorderedEquals([
predicate(
(FuncRef f) => f.name == 'staticHello' && f.isStatic),
predicate((FuncRef f) => f.name == 'message' && !f.isStatic),
predicate((FuncRef f) => f.name == 'notFinal' && !f.isStatic),
predicate((FuncRef f) => f.name == 'hello' && !f.isStatic),
predicate((FuncRef f) => f.name == '_equals' && !f.isStatic),
predicate((FuncRef f) => f.name == 'hashCode' && !f.isStatic),
predicate((FuncRef f) => f.name == 'toString' && !f.isStatic),
predicate(
(FuncRef f) => f.name == 'noSuchMethod' && !f.isStatic),
predicate(
(FuncRef f) => f.name == 'runtimeType' && !f.isStatic),
]));
expect(
testClass.fields,
unorderedEquals([
predicate((FieldRef f) =>
f.name == 'message' &&
f.declaredType != null &&
!f.isStatic &&
!f.isConst &&
f.isFinal),
predicate((FieldRef f) =>
f.name == 'notFinal' &&
f.declaredType != null &&
!f.isStatic &&
!f.isConst &&
!f.isFinal),
predicate((FieldRef f) =>
f.name == 'staticMessage' &&
f.declaredType != null &&
f.isStatic &&
!f.isConst &&
!f.isFinal),
]));
},
// TODO(elliette): Remove once 2.15.0 is the stable release.
skip: semver.Version.parse(Platform.version.split(' ').first) >=
semver.Version.parse('2.15.0-268.18.beta')
? null
: 'SDK does not expose static member information.',
);

test('offset/count parameters are ignored for bools', () async {
var ref = await service.evaluate(
Expand Down
4 changes: 4 additions & 0 deletions fixtures/_test/example/hello_world/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ class MyTestClass {

String notFinal;

static final String staticMessage = 'static';

static String staticHello() => 'static hello';

MyTestClass({this.message = 'world'});

String hello() => message;
Expand Down

0 comments on commit 5564eaa

Please sign in to comment.