diff --git a/dwds/CHANGELOG.md b/dwds/CHANGELOG.md index 1a92c2961..7c2ed1ad1 100644 --- a/dwds/CHANGELOG.md +++ b/dwds/CHANGELOG.md @@ -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 diff --git a/dwds/lib/src/debugging/classes.dart b/dwds/lib/src/debugging/classes.dart index c3fc1dbdb..7f258199e 100644 --- a/dwds/lib/src/debugging/classes.dart +++ b/dwds/lib/src/debugging/classes.dart @@ -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'] = {}; @@ -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; })() '''; @@ -153,6 +181,9 @@ class ClassHelper extends Domain { var classDescriptor = result.value as Map; var methodRefs = []; var methodDescriptors = classDescriptor['methods'] as Map; + var staticMethodDescriptors = + classDescriptor['staticMethods'] as Map; + methodDescriptors.addAll(staticMethodDescriptors); methodDescriptors.forEach((name, descriptor) { var methodId = 'methods|${classRef.id}|$name'; methodRefs.add(FuncRef( @@ -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; + 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( diff --git a/dwds/test/chrome_proxy_service_test.dart b/dwds/test/chrome_proxy_service_test.dart index 83cfb3070..f7be25886 100644 --- a/dwds/test/chrome_proxy_service_test.dart +++ b/dwds/test/chrome_proxy_service_test.dart @@ -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'; @@ -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( @@ -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( diff --git a/fixtures/_test/example/hello_world/main.dart b/fixtures/_test/example/hello_world/main.dart index 05f9cb195..da0bd5a9d 100644 --- a/fixtures/_test/example/hello_world/main.dart +++ b/fixtures/_test/example/hello_world/main.dart @@ -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;