Skip to content

Commit bbf640b

Browse files
author
Anna Gringauze
authored
Fix error message on expression evaluation for module load error (#1542)
* Fix error message on expression evaluation for module load error * Fix new analyze warning * Updated changelog * Addressed CR Comments * Fix chrome teardown in tests
1 parent 066f393 commit bbf640b

File tree

11 files changed

+105
-16
lines changed

11 files changed

+105
-16
lines changed

dwds/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
restart.
1313
- Remove verbose printing on receiving DevTools events.
1414
- Update `vm_service` version to `^8.2.0`.
15-
15+
- Update error message on expression evaluation using unloaded libraries.
1616

1717
**Breaking changes:**
1818
- `Dwds.start` and `ExpressionCompilerService` now take

dwds/debug_extension/pubspec.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
name: extension
22
publish_to: none
33
version: 1.27.0
4-
author: Dart Team <[email protected]>
54
homepage: https://github.com/dart-lang/webdev
65
description: >-
76
A chrome extension for Dart debugging.

dwds/lib/src/services/chrome_proxy_service.dart

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@ class ChromeProxyService implements VmServiceInterface {
215215
// the expression compiler service will fail to start.
216216
// Issue: https://github.com/dart-lang/webdev/issues/1282
217217
var debugger = await _debugger;
218-
await _initializeEntrypoint(appConnection.request.entrypointPath);
218+
var entrypoint = appConnection.request.entrypointPath;
219+
await _initializeEntrypoint(entrypoint);
219220
var sdkConfiguration = await _sdkConfigurationProvider.configuration;
220221

221222
debugger.notifyPausedAtStart();
@@ -232,7 +233,13 @@ class ChromeProxyService implements VmServiceInterface {
232233

233234
_expressionEvaluator = _compiler == null
234235
? null
235-
: ExpressionEvaluator(_inspector, _locations, _modules, _compiler);
236+
: ExpressionEvaluator(
237+
entrypoint,
238+
_inspector,
239+
_locations,
240+
_modules,
241+
_compiler,
242+
);
236243

237244
await debugger.reestablishBreakpoints(
238245
_previousBreakpoints, _disabledBreakpoints);

dwds/lib/src/services/expression_evaluator.dart

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import '../debugging/dart_scope.dart';
1111
import '../debugging/inspector.dart';
1212
import '../debugging/location.dart';
1313
import '../debugging/modules.dart';
14+
import '../loaders/strategy.dart';
1415
import '../utilities/objects.dart' as chrome;
1516
import 'expression_compiler.dart';
1617

@@ -23,6 +24,7 @@ class ErrorKind {
2324
static const ErrorKind reference = ErrorKind._('ReferenceError');
2425
static const ErrorKind internal = ErrorKind._('InternalError');
2526
static const ErrorKind invalidInput = ErrorKind._('InvalidInputError');
27+
static const ErrorKind loadModule = ErrorKind._('LoadModuleError');
2628

2729
@override
2830
String toString() => _kind;
@@ -33,17 +35,26 @@ class ErrorKind {
3335
/// collect context for evaluation (scope, types, modules), and using
3436
/// ExpressionCompilerInterface to compile dart expressions to JavaScript.
3537
class ExpressionEvaluator {
38+
final String _entrypoint;
3639
final AppInspector _inspector;
3740
final Locations _locations;
3841
final Modules _modules;
3942
final ExpressionCompiler _compiler;
4043
final _logger = Logger('ExpressionEvaluator');
4144

45+
/// Strip synthetic library name from compiler error messages.
4246
static final _syntheticNameFilterRegex =
4347
RegExp('org-dartlang-debug:synthetic_debug_expression:.*:.*Error: ');
4448

45-
ExpressionEvaluator(
46-
this._inspector, this._locations, this._modules, this._compiler);
49+
/// Find module path from the XHR call network error message received from chrome.
50+
///
51+
/// Example:
52+
/// NetworkError: Failed to load 'http://<hostname>.com/path/to/module.js?<cache_busting_token>'
53+
static final _loadModuleErrorRegex =
54+
RegExp(r".*Failed to load '.*\.com/(.*\.js).*");
55+
56+
ExpressionEvaluator(this._entrypoint, this._inspector, this._locations,
57+
this._modules, this._compiler);
4758

4859
RemoteObject _createError(ErrorKind severity, String message) {
4960
return RemoteObject(
@@ -107,10 +118,10 @@ class ExpressionEvaluator {
107118
' return $inner(t);'
108119
'}';
109120
result = await _inspector.callFunction(function, scope.values);
110-
result = _formatEvaluationError(result);
121+
result = await _formatEvaluationError(result);
111122
} else {
112123
result = await _inspector.debugger.evaluate(jsResult);
113-
result = _formatEvaluationError(result);
124+
result = await _formatEvaluationError(result);
114125
}
115126

116127
_logger.finest('Evaluated "$expression" to "$result"');
@@ -199,7 +210,7 @@ class ExpressionEvaluator {
199210
// Send JS expression to chrome to evaluate.
200211
var result = await _inspector.debugger
201212
.evaluateJsOnCallFrameIndex(frameIndex, jsResult);
202-
result = _formatEvaluationError(result);
213+
result = await _formatEvaluationError(result);
203214

204215
_logger.finest('Evaluated "$expression" to "$result"');
205216
return result;
@@ -240,16 +251,26 @@ class ExpressionEvaluator {
240251
return _createError(ErrorKind.compilation, error);
241252
}
242253

243-
RemoteObject _formatEvaluationError(RemoteObject result) {
254+
Future<RemoteObject> _formatEvaluationError(RemoteObject result) async {
244255
if (result.type == 'string') {
245256
var error = '${result.value}';
246257
if (error.startsWith('ReferenceError: ')) {
247258
error = error.replaceFirst('ReferenceError: ', '');
248259
return _createError(ErrorKind.reference, error);
249-
}
250-
if (error.startsWith('TypeError: ')) {
260+
} else if (error.startsWith('TypeError: ')) {
251261
error = error.replaceFirst('TypeError: ', '');
252262
return _createError(ErrorKind.type, error);
263+
} else if (error.startsWith('NetworkError: ')) {
264+
var modulePath = _loadModuleErrorRegex.firstMatch(error)?.group(1);
265+
var module = modulePath != null
266+
? await globalLoadStrategy.moduleForServerPath(
267+
_entrypoint, modulePath)
268+
: 'unknown';
269+
modulePath ??= 'unknown';
270+
error = 'Module is not loaded : $module (path: $modulePath). '
271+
'Accessing libraries that have not yet been used in the '
272+
'application is not supported during expression evaluation.';
273+
return _createError(ErrorKind.loadModule, error);
253274
}
254275
}
255276
return result;

dwds/test/build_daemon_evaluate_test.dart

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ void main() async {
493493
});
494494
});
495495

496-
test('error', () async {
496+
test('compilation error', () async {
497497
await onBreakPoint(isolate.id, mainScript, 'printLocal', () async {
498498
var event = await stream.firstWhere(
499499
(event) => event.kind == EventKind.kPauseBreakpoint);
@@ -508,6 +508,21 @@ void main() async {
508508
});
509509
});
510510

511+
test('module load error', () async {
512+
await onBreakPoint(isolate.id, mainScript, 'printLocal', () async {
513+
var event = await stream.firstWhere(
514+
(event) => event.kind == EventKind.kPauseBreakpoint);
515+
516+
var error = await setup.service.evaluateInFrame(
517+
isolate.id, event.topFrame.index, 'd.deferredPrintLocal()');
518+
519+
expect(
520+
error,
521+
isA<ErrorRef>().having((instance) => instance.message,
522+
'message', contains('LoadModuleError:')));
523+
});
524+
}, skip: 'https://github.com/dart-lang/sdk/issues/48587');
525+
511526
test('cannot evaluate in unsupported isolate', () async {
512527
await onBreakPoint(isolate.id, mainScript, 'printLocal', () async {
513528
var event = await stream.firstWhere(

dwds/test/frontend_server_evaluate_test.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,21 @@ void main() async {
413413
contains('CompilationError:')));
414414
});
415415
});
416+
417+
test('module load error', () async {
418+
await onBreakPoint(isolate.id, mainScript, 'printLocal', () async {
419+
var event = await stream
420+
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
421+
422+
var error = await setup.service.evaluateInFrame(
423+
isolate.id, event.topFrame.index, 'd.deferredPrintLocal()');
424+
425+
expect(
426+
error,
427+
isA<ErrorRef>().having((instance) => instance.message, 'message',
428+
contains('LoadModuleError:')));
429+
});
430+
}, skip: 'https://github.com/dart-lang/sdk/issues/48587');
416431
});
417432

418433
group('evaluate', () {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// A library that we can import.
6+
library test_deferred_library;
7+
8+
void deferredPrintLocal() {
9+
print('hello from deferred library'); // Breakpoint: DeferredPrintLocal
10+
}

fixtures/_testPackage/web/main.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'dart:async';
88
import 'dart:core';
99
import 'dart:html';
1010

11+
import 'package:_test/deferred_library.dart' deferred as d;
1112
import 'package:_test/library.dart';
1213
import 'package:_test_package/test_library.dart';
1314

@@ -94,6 +95,10 @@ void printLoopVariable() {
9495
}
9596
}
9697

98+
Future<void> printDeferred() async {
99+
d.deferredPrintLocal();
100+
}
101+
97102
class MainClass {
98103
final int _field;
99104
MainClass(this._field);

fixtures/_testPackageSound/web/main.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'dart:async';
66
import 'dart:core';
77
import 'dart:html';
88

9+
import 'package:_test/deferred_library.dart' deferred as d;
910
import 'package:_test/library.dart';
1011
import 'package:_test_package/test_library.dart';
1112

@@ -103,6 +104,10 @@ void printLoopVariable() {
103104
}
104105
}
105106

107+
Future<void> printDeferred() async {
108+
d.deferredPrintLocal();
109+
}
110+
106111
class MainClass {
107112
final int _field;
108113
MainClass(this._field);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// A library that we can import.
6+
library test_deferred_library;
7+
8+
void deferredPrintLocal() {
9+
print('hello from deferred library'); // Breakpoint: DeferredPrintLocal
10+
}

webdev/test/chrome_test.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ void main() {
1818
}
1919

2020
tearDown(() async {
21-
var tabs = await chrome.chromeConnection.getTabs();
22-
for (var tab in tabs) {
23-
await chrome.chromeConnection.getUrl('/json/close/${tab.id}');
21+
var tabs = await chrome?.chromeConnection?.getTabs();
22+
if (tabs != null) {
23+
for (var tab in tabs) {
24+
await chrome.chromeConnection.getUrl('/json/close/${tab.id}');
25+
}
2426
}
2527
await chrome?.close();
2628
chrome = null;

0 commit comments

Comments
 (0)