diff --git a/dwds/test/instances/common/class_inspection_common.dart b/dwds/test/instances/common/class_inspection_common.dart index 32151e631..ed0d22f7f 100644 --- a/dwds/test/instances/common/class_inspection_common.dart +++ b/dwds/test/instances/common/class_inspection_common.dart @@ -49,6 +49,7 @@ void runTests({ compilationMode: compilationMode, enableExpressionEvaluation: true, verboseCompiler: debug, + experiments: ['dot-shorthands'], canaryFeatures: canaryFeatures, moduleFormat: provider.ddcModuleFormat, ), diff --git a/dwds/test/instances/common/dot_shorthands_common.dart b/dwds/test/instances/common/dot_shorthands_common.dart new file mode 100644 index 000000000..94c5bd077 --- /dev/null +++ b/dwds/test/instances/common/dot_shorthands_common.dart @@ -0,0 +1,129 @@ +// Copyright (c) 2025, 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. + +import 'package:dwds/src/services/expression_compiler.dart' show ModuleFormat; +import 'package:path/path.dart' show basename; +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 '../../fixtures/utilities.dart'; +import 'test_inspector.dart'; + +void runTests({ + required TestSdkConfigurationProvider provider, + required CompilationMode compilationMode, + required bool canaryFeatures, + required bool debug, +}) { + final context = TestContext(TestProject.testExperiment, provider); + final testInspector = TestInspector(context); + + late VmService service; + late Stream stream; + late String isolateId; + late ScriptRef mainScript; + + Future onBreakpoint( + String breakPointId, + Future Function(Event) body, + ) => testInspector.onBreakPoint( + stream, + isolateId, + mainScript, + breakPointId, + body, + ); + + Future getInstanceRef(frame, expression) => + testInspector.getInstanceRef(isolateId, frame, expression); + + group('$compilationMode | dot shorthands:', () { + setUp(() async { + setCurrentLogWriter(debug: debug); + await context.setUp( + testSettings: TestSettings( + compilationMode: compilationMode, + enableExpressionEvaluation: true, + verboseCompiler: debug, + experiments: ['dot-shorthands'], + canaryFeatures: canaryFeatures, + moduleFormat: provider.ddcModuleFormat, + ), + ); + service = context.debugConnection.vmService; + + final vm = await service.getVM(); + isolateId = vm.isolates!.first.id!; + final scripts = await service.getScripts(isolateId); + + await service.streamListen(EventStreams.kDebug); + stream = service.onDebugEvent; + + mainScript = scripts.scripts!.firstWhere( + (each) => each.uri!.contains('main.dart'), + ); + }); + + tearDown(() async { + await context.tearDown(); + }); + + test('expression evaluation and single-stepping', () async { + await onBreakpoint('testDotShorthands', (event) async { + final frame = event.topFrame!.index!; + + var instanceRef = await getInstanceRef(frame, '(c = .two).value'); + expect(instanceRef.valueAsString, '2'); + + instanceRef = await getInstanceRef(frame, '(c = .three).value'); + expect(instanceRef.valueAsString, '3'); + + instanceRef = await getInstanceRef(frame, '(c = .four()).value'); + expect(instanceRef.valueAsString, '4'); + + final scriptBasename = basename(mainScript.uri!); + + const lineA = 116; + const lineB = 118; + const lineC = 119; + const lineD = 120; + const lineE = 127; + const lineF = 129; + const lineG = 131; + const lineH = 132; + + final expected = [ + '$scriptBasename:$lineE:3', // on 'c' + // TODO(2638): Investigate why this conditional exclusion is needed. + if (provider.ddcModuleFormat == ModuleFormat.ddc) + '$scriptBasename:$lineB:20', // on '2' + '$scriptBasename:$lineF:3', // on 'c' + '$scriptBasename:$lineC:25', // on 'C' + '$scriptBasename:$lineA:10', // on 'v' of 'value' + '$scriptBasename:$lineA:16', // on ';' + '$scriptBasename:$lineC:27', // on '3' + '$scriptBasename:$lineG:3', // on 'c' + '$scriptBasename:$lineD:22', // on 'C' + '$scriptBasename:$lineA:10', // on 'v' of 'value' + '$scriptBasename:$lineA:16', // on ';' + '$scriptBasename:$lineD:24', // on '4' + '$scriptBasename:$lineH:3', // on 'p' of 'print' + ]; + + final stops = []; + await testInspector.runStepIntoThroughProgramRecordingStops( + isolateId, + stops, + provider.ddcModuleFormat == ModuleFormat.ddc ? 13 : 12, + ); + + expect(stops, expected); + }); + }); + }); +} diff --git a/dwds/test/instances/common/patterns_inspection_common.dart b/dwds/test/instances/common/patterns_inspection_common.dart index 97cb45a29..b2f75b5fe 100644 --- a/dwds/test/instances/common/patterns_inspection_common.dart +++ b/dwds/test/instances/common/patterns_inspection_common.dart @@ -57,7 +57,7 @@ void runTests({ compilationMode: compilationMode, enableExpressionEvaluation: true, verboseCompiler: debug, - experiments: ['records', 'patterns'], + experiments: ['dot-shorthands'], canaryFeatures: canaryFeatures, ), ); diff --git a/dwds/test/instances/common/record_inspection_common.dart b/dwds/test/instances/common/record_inspection_common.dart index 55ad8b650..25e404df4 100644 --- a/dwds/test/instances/common/record_inspection_common.dart +++ b/dwds/test/instances/common/record_inspection_common.dart @@ -63,7 +63,7 @@ void runTests({ compilationMode: compilationMode, enableExpressionEvaluation: true, verboseCompiler: debug, - experiments: ['records', 'patterns'], + experiments: ['dot-shorthands'], canaryFeatures: canaryFeatures, moduleFormat: provider.ddcModuleFormat, ), diff --git a/dwds/test/instances/common/record_type_inspection_common.dart b/dwds/test/instances/common/record_type_inspection_common.dart index 059a2d029..8967f49fc 100644 --- a/dwds/test/instances/common/record_type_inspection_common.dart +++ b/dwds/test/instances/common/record_type_inspection_common.dart @@ -62,7 +62,7 @@ void runTests({ compilationMode: compilationMode, enableExpressionEvaluation: true, verboseCompiler: debug, - experiments: ['records', 'patterns'], + experiments: ['dot-shorthands'], canaryFeatures: canaryFeatures, moduleFormat: provider.ddcModuleFormat, ), diff --git a/dwds/test/instances/common/test_inspector.dart b/dwds/test/instances/common/test_inspector.dart index d6f3791f5..0dc201e3d 100644 --- a/dwds/test/instances/common/test_inspector.dart +++ b/dwds/test/instances/common/test_inspector.dart @@ -2,6 +2,9 @@ // 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. +import 'dart:async' show Completer, StreamSubscription; + +import 'package:path/path.dart' show basename; import 'package:test/test.dart'; import 'package:vm_service/vm_service.dart'; @@ -214,6 +217,56 @@ class TestInspector { ), ); } + + Future _locationToString( + VmService service, + String isolateId, + SourceLocation location, + ) async { + final script = + await service.getObject(isolateId, location.script!.id!) as Script; + final scriptName = basename(script.uri!); + final tokenPos = location.tokenPos!; + final line = script.getLineNumberFromTokenPos(tokenPos); + final column = script.getColumnNumberFromTokenPos(tokenPos); + return '$scriptName:$line:$column'; + } + + Future runStepIntoThroughProgramRecordingStops( + String isolateId, + + /// A list to which the pause location is added after each single-step. + List recordedStops, + + /// A limit on the number of stops to record. + /// + /// The program will not be resumed after the length of [recordedStops] + /// becomes [numStops]. + int numStops, + ) async { + final completer = Completer(); + + late StreamSubscription subscription; + subscription = service.onDebugEvent.listen((event) async { + if (event.kind == EventKind.kPauseInterrupted) { + final isolate = await service.getIsolate(isolateId); + final frame = isolate.pauseEvent!.topFrame!; + recordedStops.add( + await _locationToString(service, isolateId, frame.location!), + ); + if (recordedStops.length == numStops) { + await subscription.cancel(); + completer.complete(); + } + await service.resume(isolateId, step: StepOption.kInto); + } else if (event.kind == EventKind.kPauseExit) { + await subscription.cancel(); + completer.complete(); + } + }); + await service.resume(isolateId, step: StepOption.kInto); + await completer.future; + } } Map _associationsToMap( diff --git a/dwds/test/instances/common/type_inspection_common.dart b/dwds/test/instances/common/type_inspection_common.dart index 909946db5..9de28ca92 100644 --- a/dwds/test/instances/common/type_inspection_common.dart +++ b/dwds/test/instances/common/type_inspection_common.dart @@ -80,7 +80,7 @@ void runTests({ compilationMode: compilationMode, enableExpressionEvaluation: true, verboseCompiler: debug, - experiments: ['records', 'patterns'], + experiments: ['dot-shorthands'], canaryFeatures: canaryFeatures, ), ); diff --git a/dwds/test/instances/dot_shorthands_amd_canary_test.dart b/dwds/test/instances/dot_shorthands_amd_canary_test.dart new file mode 100644 index 000000000..fb7ec7ccf --- /dev/null +++ b/dwds/test/instances/dot_shorthands_amd_canary_test.dart @@ -0,0 +1,39 @@ +// Copyright (c) 2025, 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)) +library; + +import 'package:dwds/expression_compiler.dart'; +import 'package:test/test.dart'; +import 'package:test_common/test_sdk_configuration.dart'; + +import '../fixtures/context.dart'; +import 'common/dot_shorthands_common.dart'; + +void main() { + // Enable verbose logging for debugging. + const debug = false; + const canaryFeatures = true; + + group('canary: $canaryFeatures |', () { + final provider = TestSdkConfigurationProvider( + verbose: debug, + canaryFeatures: canaryFeatures, + ddcModuleFormat: ModuleFormat.amd, + ); + tearDownAll(provider.dispose); + + for (final compilationMode in CompilationMode.values) { + runTests( + provider: provider, + compilationMode: compilationMode, + canaryFeatures: canaryFeatures, + debug: debug, + ); + } + }); +} diff --git a/dwds/test/instances/dot_shorthands_amd_test.dart b/dwds/test/instances/dot_shorthands_amd_test.dart new file mode 100644 index 000000000..577e08648 --- /dev/null +++ b/dwds/test/instances/dot_shorthands_amd_test.dart @@ -0,0 +1,39 @@ +// Copyright (c) 2025, 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)) +library; + +import 'package:dwds/src/services/expression_compiler.dart'; +import 'package:test/test.dart'; +import 'package:test_common/test_sdk_configuration.dart'; + +import '../fixtures/context.dart'; +import 'common/dot_shorthands_common.dart'; + +void main() { + // Enable verbose logging for debugging. + const debug = false; + const canaryFeatures = false; + + group('canary: $canaryFeatures |', () { + final provider = TestSdkConfigurationProvider( + verbose: debug, + canaryFeatures: canaryFeatures, + ddcModuleFormat: ModuleFormat.amd, + ); + tearDownAll(provider.dispose); + + for (final compilationMode in CompilationMode.values) { + runTests( + provider: provider, + compilationMode: compilationMode, + canaryFeatures: canaryFeatures, + debug: debug, + ); + } + }); +} diff --git a/dwds/test/instances/dot_shorthands_ddc_library_bundle_test.dart b/dwds/test/instances/dot_shorthands_ddc_library_bundle_test.dart new file mode 100644 index 000000000..dcf223e37 --- /dev/null +++ b/dwds/test/instances/dot_shorthands_ddc_library_bundle_test.dart @@ -0,0 +1,37 @@ +// Copyright (c) 2025, 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)) +library; + +import 'package:dwds/expression_compiler.dart'; +import 'package:test/test.dart'; +import 'package:test_common/test_sdk_configuration.dart'; + +import '../fixtures/context.dart'; +import 'common/dot_shorthands_common.dart'; + +void main() { + // Enable verbose logging for debugging. + const debug = false; + const canaryFeatures = true; + const compilationMode = CompilationMode.frontendServer; + + group('canary: $canaryFeatures |', () { + final provider = TestSdkConfigurationProvider( + verbose: debug, + canaryFeatures: canaryFeatures, + ddcModuleFormat: ModuleFormat.ddc, + ); + tearDownAll(provider.dispose); + runTests( + provider: provider, + compilationMode: compilationMode, + canaryFeatures: canaryFeatures, + debug: debug, + ); + }); +} diff --git a/fixtures/_experimentSound/web/main.dart b/fixtures/_experimentSound/web/main.dart index 09827fd9b..7618cb6c8 100644 --- a/fixtures/_experimentSound/web/main.dart +++ b/fixtures/_experimentSound/web/main.dart @@ -2,6 +2,8 @@ // 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. +// @dart = 3.9 + import 'dart:async'; import 'dart:core'; // TODO: https://github.com/dart-lang/webdev/issues/2508 @@ -24,6 +26,8 @@ void main() { testPattern2(); print('Classes'); testClass(); + print('Dot shorthands'); + testDotShorthands(); }); document.body!.appendText('Program is running!'); @@ -106,3 +110,24 @@ class GreeterClass { print('Bonjour $greeteeName'); } } + +class C { + int value; + C(this.value); // lineA + + static C two = C(2); // lineB + static C get three => C(3); // lineC + static C four() => C(4); // lineD +} + +void testDotShorthands() { + C c = C(1); + print('breakpoint'); // Breakpoint: testDotShorthands + // ignore: experiment_not_enabled + c = .two; // lineE + // ignore: experiment_not_enabled + c = .three; // lineF + // ignore: experiment_not_enabled + c = .four(); // lineG + print(c.value); // lineH +} diff --git a/webdev/test/tls_test.dart b/webdev/test/tls_test.dart index 4aac11828..08c22a8af 100644 --- a/webdev/test/tls_test.dart +++ b/webdev/test/tls_test.dart @@ -53,6 +53,7 @@ void main() { '--hostname=0.0.0.0', '--tls-cert-chain=localhost+2.pem', '--tls-cert-key=localhost+2-key.pem', + '--enable-experiment=dot-shorthands', ]; final process = @@ -83,6 +84,7 @@ void main() { '--hostname=0.0.0.0', '--tls-cert-chain=localhost+2.pem', '--tls-cert-key=localhost+2-key.pem', + '--enable-experiment=dot-shorthands', ]; final process =