Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prepare a hotfix release of DWDS 21.0.0+1 #2319

Merged
merged 11 commits into from
Dec 28, 2023
4 changes: 4 additions & 0 deletions dwds/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 21.0.0+1

- Fix a null cast error when debugging a `Class` from VS Code. - [#2303](https://github.com/dart-lang/webdev/pull/2303)

## 21.0.0

- Update Dart SDK constraint to `>=3.2.0-36.0.dev <4.0.0`. - [#2207](https://github.com/dart-lang/webdev/pull/2207)
Expand Down
10 changes: 6 additions & 4 deletions dwds/lib/src/debugging/classes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,9 @@ class ClassHelper extends Domain {
throw ChromeDebugException(e.json, evalContents: expression);
}

final classDescriptor = result.value as Map<String, dynamic>;
final classDescriptor = _mapify(result.value);
final methodRefs = <FuncRef>[];
final methodDescriptors =
classDescriptor['methods'] as Map<String, dynamic>;
final methodDescriptors = _mapify(classDescriptor['methods']);
methodDescriptors.forEach((name, descriptor) {
final methodId = 'methods|$classId|$name';
methodRefs.add(
Expand All @@ -118,7 +117,7 @@ class ClassHelper extends Domain {
});
final fieldRefs = <FieldRef>[];

final fieldDescriptors = classDescriptor['fields'] as Map<String, dynamic>;
final fieldDescriptors = _mapify(classDescriptor['fields']);
fieldDescriptors.forEach((name, descriptor) {
final classMetaData = ClassMetaData(
runtimeKind: RuntimeObjectKind.type,
Expand Down Expand Up @@ -168,4 +167,7 @@ class ClassHelper extends Domain {
superClass: superClassRef,
);
}

Map<String, dynamic> _mapify(dynamic map) =>
(map as Map<String, dynamic>?) ?? <String, dynamic>{};
}
2 changes: 1 addition & 1 deletion dwds/lib/src/debugging/location.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class DartLocation {
int get hashCode => Object.hashAll([uri, line, column]);

@override
bool operator ==(Object? other) {
bool operator ==(Object other) {
if (other is! DartLocation) {
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion dwds/lib/src/version.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dwds/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: dwds
# Every time this changes you need to run `dart run build_runner build`.
version: 21.0.0
version: 21.0.0+1
description: >-
A service that proxies between the Chrome debug protocol and the Dart VM
service protocol.
Expand Down
124 changes: 124 additions & 0 deletions dwds/test/instances/class_inspection_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Copyright (c) 2023, 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.

@Tags(['daily'])
@TestOn('vm')
@Timeout(Duration(minutes: 2))

import 'package:test/test.dart';
import 'package:test_common/logging.dart';
import 'package:test_common/test_sdk_configuration.dart';
import 'package:vm_service/vm_service.dart';

import '../fixtures/context.dart';
import '../fixtures/project.dart';
import 'common/test_inspector.dart';

void main() {
// Enable verbose logging for debugging.
elliette marked this conversation as resolved.
Show resolved Hide resolved
final debug = false;

final provider = TestSdkConfigurationProvider(
verbose: debug,
);

final context =
TestContext(TestProject.testExperimentWithSoundNullSafety, provider);
final testInspector = TestInspector(context);

late VmService service;
late Stream<Event> stream;
late String isolateId;
late ScriptRef mainScript;

onBreakPoint(breakPointId, body) => testInspector.onBreakPoint(
stream,
isolateId,
mainScript,
breakPointId,
body,
);

getObject(instanceId) => service.getObject(isolateId, instanceId);

group('Class |', () {
tearDownAll(provider.dispose);

for (var compilationMode in CompilationMode.values) {
group('$compilationMode |', () {
setUpAll(() async {
setCurrentLogWriter(debug: debug);
await context.setUp(
compilationMode: compilationMode,
enableExpressionEvaluation: true,
verboseCompiler: debug,
);
service = context.debugConnection.vmService;

final vm = await service.getVM();
isolateId = vm.isolates!.first.id!;
final scripts = await service.getScripts(isolateId);

await service.streamListen('Debug');
stream = service.onEvent('Debug');

mainScript = scripts.scripts!
.firstWhere((each) => each.uri!.contains('main.dart'));
});

tearDownAll(() async {
await context.tearDown();
});

setUp(() => setCurrentLogWriter(debug: debug));
tearDown(() => service.resume(isolateId));

group('calling getObject for an existent class', () {
test('returns the correct class representation', () async {
await onBreakPoint('testClass1Case1', (event) async {
// classes|dart:core|Object_Diagnosticable
final result = await getObject(
'classes|org-dartlang-app:///web/main.dart|GreeterClass',
);
final clazz = result as Class?;
expect(clazz!.name, equals('GreeterClass'));
expect(
clazz.fields!.map((field) => field.name),
unorderedEquals([
'greeteeName',
'useFrench',
]),
);
expect(
clazz.functions!.map((fn) => fn.name),
containsAll([
'sayHello',
'greetInEnglish',
'greetInFrench',
]),
);
});
});
});

group('calling getObject for a non-existent class', () {
// TODO(https://github.com/dart-lang/webdev/issues/2297): Ideally we
// should throw an error in this case for the client to catch instead
// of returning an empty class.
test('returns an empty class representation', () async {
await onBreakPoint('testClass1Case1', (event) async {
final result = await getObject(
'classes|dart:core|Object_Diagnosticable',
);
final clazz = result as Class?;
expect(clazz!.name, equals('Object_Diagnosticable'));
expect(clazz.fields, isEmpty);
expect(clazz.functions, isEmpty);
});
});
});
});
}
});
}
29 changes: 29 additions & 0 deletions fixtures/_experimentSound/web/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ void main() {
testPattern([3.14, 'b']);
testPattern([0, 1]);
testPattern2();
print('Classes');
testClass();
});

document.body!.appendText('Program is running!');
Expand Down Expand Up @@ -55,6 +57,11 @@ void printNestedNamedLocalRecord() {
print(record); // Breakpoint: printNestedNamedLocalRecord
}

void testClass() {
final greeter = GreeterClass(greeteeName: 'Charlie Brown');
greeter.sayHello();
}

String testPattern(Object obj) {
switch (obj) {
case [var a, int n] || [int n, var a] when n == 1 && a is String:
Expand All @@ -73,3 +80,25 @@ String testPattern2() {
print(firstCat); // Breakpoint: testPattern2Case2
return '$dog, $firstCat, $secondCat';
}

class GreeterClass {
final String greeteeName;
final bool useFrench;

GreeterClass({
this.greeteeName = 'Snoopy',
this.useFrench = false,
});

void sayHello() {
useFrench ? greetInFrench() : greetInEnglish();
}

void greetInEnglish() {
print('Hello $greeteeName'); // Breakpoint: testClass1Case1
}

void greetInFrench() {
print('Bonjour $greeteeName');
}
}
Loading