diff --git a/.github/workflows/dcm.yml b/.github/workflows/dcm.yml index adc6be4c5..c5725d874 100644 --- a/.github/workflows/dcm.yml +++ b/.github/workflows/dcm.yml @@ -19,7 +19,7 @@ jobs: wget -qO- https://dcm.dev/pgp-key.public | sudo gpg --dearmor -o /usr/share/keyrings/dcm.gpg echo 'deb [signed-by=/usr/share/keyrings/dcm.gpg arch=amd64] https://dcm.dev/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list sudo apt-get update - sudo apt-get install dcm=1.16.2-1 # To avoid errors add `-1` (build number) to the version + sudo apt-get install dcm=1.26.0-1 # To avoid errors add `-1` (build number) to the version sudo chmod +x /usr/bin/dcm - name: Setup Dart SDK uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94 diff --git a/dwds/CHANGELOG.md b/dwds/CHANGELOG.md index 205d061ab..b7d2c8fd1 100644 --- a/dwds/CHANGELOG.md +++ b/dwds/CHANGELOG.md @@ -1,4 +1,6 @@ ## 24.3.3-wip +- Added support for some debugging APIs with the DDC library bundle format. - [#2563](https://github.com/dart-lang/webdev/issues/2563) +- Update `DCM` version to `1.26.0-1` ## 24.3.2 diff --git a/dwds/lib/src/debugging/dart_runtime_debugger.dart b/dwds/lib/src/debugging/dart_runtime_debugger.dart index e09c86e5d..ed633ee48 100644 --- a/dwds/lib/src/debugging/dart_runtime_debugger.dart +++ b/dwds/lib/src/debugging/dart_runtime_debugger.dart @@ -18,11 +18,8 @@ class DartRuntimeDebugger { String _generateJsExpression( String ddcExpression, String libraryBundleExpression, - ) { - return _useLibraryBundleExpression - ? libraryBundleExpression - : ddcExpression; - } + ) => + _useLibraryBundleExpression ? libraryBundleExpression : ddcExpression; /// Wraps a JS function call with SDK loader logic. String _wrapWithSdkLoader(String args, String functionCall) { @@ -199,4 +196,11 @@ class DartRuntimeDebugger { ), ); } + + String invokeExtensionJsExpression(String methodName, String encodedJson) { + return _generateJsExpression( + "${_loadStrategy.loadModuleSnippet}('dart_sdk').developer.invokeExtension('$methodName', JSON.stringify($encodedJson));", + "dartDevEmbedder.debugger.invokeExtension('$methodName', JSON.stringify($encodedJson));", + ); + } } diff --git a/dwds/lib/src/services/chrome_proxy_service.dart b/dwds/lib/src/services/chrome_proxy_service.dart index 8690b39dc..92aedf9a7 100644 --- a/dwds/lib/src/services/chrome_proxy_service.dart +++ b/dwds/lib/src/services/chrome_proxy_service.dart @@ -518,10 +518,8 @@ class ChromeProxyService implements VmServiceInterface { v is String ? v : jsonEncode(v), ), ); - final expression = ''' -${globalToolConfiguration.loadStrategy.loadModuleSnippet}("dart_sdk").developer.invokeExtension( - "$method", JSON.stringify(${jsonEncode(stringArgs)})); -'''; + final expression = globalToolConfiguration.loadStrategy.dartRuntimeDebugger + .invokeExtensionJsExpression(method, jsonEncode(stringArgs)); final result = await inspector.jsEvaluate(expression, awaitPromise: true); final decodedResponse = jsonDecode(result.value as String) as Map; diff --git a/dwds/test/chrome_proxy_service_amd_test.dart b/dwds/test/chrome_proxy_service_amd_test.dart new file mode 100644 index 000000000..296914d5d --- /dev/null +++ b/dwds/test/chrome_proxy_service_amd_test.dart @@ -0,0 +1,40 @@ +// 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. + +@TestOn('vm') +@Tags(['daily']) +@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 'common/chrome_proxy_service_common.dart'; +import 'fixtures/context.dart'; + +void main() { + // Enable verbose logging for debugging. + final debug = false; + final canaryFeatures = false; + final moduleFormat = ModuleFormat.amd; + final compilationMode = CompilationMode.buildDaemon; + + group('canary: $canaryFeatures |', () { + final provider = TestSdkConfigurationProvider( + verbose: debug, + canaryFeatures: canaryFeatures, + ddcModuleFormat: moduleFormat, + ); + tearDownAll(provider.dispose); + + runTests( + provider: provider, + moduleFormat: moduleFormat, + compilationMode: compilationMode, + canaryFeatures: canaryFeatures, + debug: debug, + ); + }); +} diff --git a/dwds/test/chrome_proxy_service_ddc_library_bundle_test.dart b/dwds/test/chrome_proxy_service_ddc_library_bundle_test.dart new file mode 100644 index 000000000..a9f31437a --- /dev/null +++ b/dwds/test/chrome_proxy_service_ddc_library_bundle_test.dart @@ -0,0 +1,40 @@ +// 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. + +@TestOn('vm') +@Tags(['daily']) +@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 'common/chrome_proxy_service_common.dart'; +import 'fixtures/context.dart'; + +void main() { + // Enable verbose logging for debugging. + final debug = false; + final canaryFeatures = true; + final moduleFormat = ModuleFormat.ddc; + final compilationMode = CompilationMode.frontendServer; + + group('canary: $canaryFeatures |', () { + final provider = TestSdkConfigurationProvider( + verbose: debug, + canaryFeatures: canaryFeatures, + ddcModuleFormat: moduleFormat, + ); + tearDownAll(provider.dispose); + + runTests( + provider: provider, + moduleFormat: moduleFormat, + compilationMode: compilationMode, + canaryFeatures: canaryFeatures, + debug: debug, + ); + }); +} diff --git a/dwds/test/chrome_proxy_service_test.dart b/dwds/test/common/chrome_proxy_service_common.dart similarity index 94% rename from dwds/test/chrome_proxy_service_test.dart rename to dwds/test/common/chrome_proxy_service_common.dart index 1cddd4cfc..f640ae0d1 100644 --- a/dwds/test/chrome_proxy_service_test.dart +++ b/dwds/test/common/chrome_proxy_service_common.dart @@ -11,6 +11,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:dwds/expression_compiler.dart'; import 'package:dwds/src/services/chrome_proxy_service.dart'; import 'package:dwds/src/utilities/dart_uri.dart'; import 'package:dwds/src/utilities/shared.dart'; @@ -22,18 +23,19 @@ import 'package:test_common/test_sdk_configuration.dart'; import 'package:vm_service/vm_service.dart'; import 'package:vm_service_interface/vm_service_interface.dart'; -import 'fixtures/context.dart'; -import 'fixtures/project.dart'; -import 'fixtures/utilities.dart'; +import '../fixtures/context.dart'; +import '../fixtures/project.dart'; +import '../fixtures/utilities.dart'; -void main() { - // Change to true to see verbose output from the tests. - final debug = false; - - final provider = TestSdkConfigurationProvider(verbose: debug); - tearDownAll(provider.dispose); - - final context = TestContext(TestProject.test, provider); +void runTests({ + required TestSdkConfigurationProvider provider, + required ModuleFormat moduleFormat, + required CompilationMode compilationMode, + required bool canaryFeatures, + required bool debug, +}) { + final project = TestProject.test; + final context = TestContext(project, provider); group('shared context', () { setUpAll(() async { @@ -42,6 +44,9 @@ void main() { testSettings: TestSettings( enableExpressionEvaluation: true, verboseCompiler: false, + moduleFormat: provider.ddcModuleFormat, + canaryFeatures: canaryFeatures, + compilationMode: compilationMode, ), ); }); @@ -734,10 +739,15 @@ void main() { for (final scriptRef in scripts.scripts!) { final script = await service.getObject(isolate.id!, scriptRef.id!) as Script; - final serverPath = DartUri(script.uri!, 'hello_world/').serverPath; + final serverPath = DartUri(script.uri!, '').serverPath; final result = await http .get(Uri.parse('http://localhost:${context.port}/$serverPath')); - expect(script.source, result.body); + // TODO: Figure out if we can encode the sript as utf8 and avoid this + final body = + (moduleFormat == ModuleFormat.ddc && canaryFeatures == true) + ? utf8.decode(result.body.codeUnits) + : result.body; + expect(script.source, body); expect(scriptRef.uri, endsWith('.dart')); expect(script.tokenPosTable, isNotEmpty); } @@ -1540,7 +1550,7 @@ void main() { // TODO: Make this more precise once this case doesn't // also include all the libraries. expect(first.vars, hasLength(greaterThanOrEqualTo(1))); - final underscore = first.vars!.firstWhere((v) => v.name == '_'); + final underscore = first.vars!.firstWhere((v) => v.name == 'timer'); expect(underscore, isNotNull); }); @@ -1700,115 +1710,145 @@ void main() { ); }); - test('helloString', () async { - final remote = await service.invoke( - isolate.id!, - bootstrap!.id!, - 'helloString', - ['#StringInstanceRef#abc'], - ); - expect( - remote, - const TypeMatcher().having( - (instance) => instance.valueAsString, + test( + 'helloString', + () async { + final remote = await service.invoke( + isolate.id!, + bootstrap!.id!, 'helloString', - 'abc', - ), - ); - expect( - remote, - const TypeMatcher() - .having((instance) => instance.kind, 'kind', 'String'), - ); - }); + ['#StringInstanceRef#abc'], + ); + expect( + remote, + const TypeMatcher().having( + (instance) => instance.valueAsString, + 'helloString', + 'abc', + ), + ); + expect( + remote, + const TypeMatcher() + .having((instance) => instance.kind, 'kind', 'String'), + ); + }, + skip: moduleFormat == ModuleFormat.ddc && canaryFeatures == true + ? 'https://github.com/dart-lang/webdev/issues/2566' + : null, + ); - test('null argument', () async { - final remote = await service.invoke( - isolate.id!, - bootstrap!.id!, - 'helloString', - ['objects/null'], - ); - expect( - remote, - const TypeMatcher().having( - (instance) => instance.valueAsString, + test( + 'null argument', + () async { + final remote = await service.invoke( + isolate.id!, + bootstrap!.id!, 'helloString', - 'null', - ), - ); - expect( - remote, - const TypeMatcher() - .having((instance) => instance.kind, 'kind', 'Null'), - ); - }); + ['objects/null'], + ); + expect( + remote, + const TypeMatcher().having( + (instance) => instance.valueAsString, + 'helloString', + 'null', + ), + ); + expect( + remote, + const TypeMatcher() + .having((instance) => instance.kind, 'kind', 'Null'), + ); + }, + skip: moduleFormat == ModuleFormat.ddc && canaryFeatures == true + ? 'https://github.com/dart-lang/webdev/issues/2566' + : null, + ); - test('helloBool', () async { - final remote = await service.invoke( - isolate.id!, - bootstrap!.id!, - 'helloBool', - ['objects/bool-true'], - ); - expect( - remote, - const TypeMatcher().having( - (instance) => instance.valueAsString, + test( + 'helloBool', + () async { + final remote = await service.invoke( + isolate.id!, + bootstrap!.id!, 'helloBool', - 'true', - ), - ); - expect( - remote, - const TypeMatcher() - .having((instance) => instance.kind, 'kind', 'Bool'), - ); - }); + ['objects/bool-true'], + ); + expect( + remote, + const TypeMatcher().having( + (instance) => instance.valueAsString, + 'helloBool', + 'true', + ), + ); + expect( + remote, + const TypeMatcher() + .having((instance) => instance.kind, 'kind', 'Bool'), + ); + }, + skip: moduleFormat == ModuleFormat.ddc && canaryFeatures == true + ? 'https://github.com/dart-lang/webdev/issues/2566' + : null, + ); - test('helloNum', () async { - final remote = await service.invoke( - isolate.id!, - bootstrap!.id!, - 'helloNum', - ['objects/int-123'], - ); - expect( - remote, - const TypeMatcher().having( - (instance) => instance.valueAsString, + test( + 'helloNum', + () async { + final remote = await service.invoke( + isolate.id!, + bootstrap!.id!, 'helloNum', - '123', - ), - ); - expect( - remote, - const TypeMatcher() - .having((instance) => instance.kind, 'kind', 'Double'), - ); - }); + ['objects/int-123'], + ); + expect( + remote, + const TypeMatcher().having( + (instance) => instance.valueAsString, + 'helloNum', + '123', + ), + ); + expect( + remote, + const TypeMatcher() + .having((instance) => instance.kind, 'kind', 'Double'), + ); + }, + skip: moduleFormat == ModuleFormat.ddc && canaryFeatures == true + ? 'https://github.com/dart-lang/webdev/issues/2566' + : null, + ); - test('two object arguments', () async { - final remote = await service.invoke( - isolate.id!, - bootstrap!.id!, - 'messagesCombined', - [testInstance.id, testInstance.id], - ); - expect( - remote, - const TypeMatcher().having( - (instance) => instance.valueAsString, + test( + 'two object arguments', + () async { + final remote = await service.invoke( + isolate.id!, + bootstrap!.id!, 'messagesCombined', - 'worldworld', - ), - ); - expect( - remote, - const TypeMatcher() - .having((instance) => instance.kind, 'kind', 'String'), - ); - }); + [testInstance.id, testInstance.id], + ); + expect( + remote, + const TypeMatcher().having( + (instance) => instance.valueAsString, + 'messagesCombined', + 'worldworld', + ), + ); + expect( + remote, + const TypeMatcher() + .having((instance) => instance.kind, 'kind', 'String'), + ); + }, + skip: moduleFormat == ModuleFormat.ddc && canaryFeatures == true + ? 'https://github.com/dart-lang/webdev/issues/2566' + : null, + ); }); test('kill', () async { diff --git a/fixtures/_testSound/example/hello_world/index.html b/fixtures/_testSound/example/hello_world/index.html index d93440a94..9bd15c874 100644 --- a/fixtures/_testSound/example/hello_world/index.html +++ b/fixtures/_testSound/example/hello_world/index.html @@ -1,6 +1,7 @@ + diff --git a/fixtures/_testSound/example/hello_world/main.dart b/fixtures/_testSound/example/hello_world/main.dart index cca39c9ce..b817d5b70 100644 --- a/fixtures/_testSound/example/hello_world/main.dart +++ b/fixtures/_testSound/example/hello_world/main.dart @@ -76,7 +76,7 @@ void main() async { scheduleMicrotask(() => throw Exception('UncaughtException')); }; - Timer.periodic(const Duration(seconds: 1), (_) { + Timer.periodic(const Duration(seconds: 1), (timer) { printCount(); // Breakpoint: callPrintCount });