Skip to content

Commit 07743a3

Browse files
author
Anna Gringauze
authored
Fix exception mapping from JS to dart (#2004)
* Prepare to release dwds * Use dev channel to run publish * Correctly switch to using dev SDK for publish task * Try disabling publsh verification * Merge with master * Prepare webdev for release * Make test_common depend on published dwds for the webdev release * Fix dwds pub get failure * Add missing new line * Build * Fix exception mapping from JS to dart * Fix exception mapping from JS to dart * Fix pub upgrade failure * Fix test failures * Fix analyzer errors * Fix typo * Cleanup * Address CR comments
1 parent eb63b31 commit 07743a3

File tree

15 files changed

+126
-204
lines changed

15 files changed

+126
-204
lines changed

dwds/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 18.0.0-dev
2+
3+
- Fix failure to map JS exceptions to dart.
4+
15
## 18.0.0
26

37
- Cleanup `getObject` code for lists and maps.

dwds/lib/src/debugging/debugger.dart

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'dart:math' as math;
88
import 'package:dwds/src/debugging/dart_scope.dart';
99
import 'package:dwds/src/debugging/frame_computer.dart';
1010
import 'package:dwds/src/debugging/location.dart';
11+
import 'package:dwds/src/debugging/metadata/class.dart';
1112
import 'package:dwds/src/debugging/remote_debugger.dart';
1213
import 'package:dwds/src/debugging/skip_list.dart';
1314
import 'package:dwds/src/loaders/strategy.dart';
@@ -613,10 +614,9 @@ class Debugger extends Domain {
613614
if (e.data is Map<String, dynamic>) {
614615
final map = e.data as Map<String, dynamic>;
615616
if (map['type'] == 'object') {
616-
// The className here is generally 'DartError'.
617617
final obj = RemoteObject(map);
618618
exception = await inspector.instanceRefFor(obj);
619-
if (exception != null && isNativeJsObject(exception)) {
619+
if (exception != null && isNativeJsError(exception)) {
620620
if (obj.description != null) {
621621
// Create a string exception object.
622622
final description =
@@ -787,18 +787,21 @@ Future<T> sendCommandAndValidateResult<T>(
787787
return result;
788788
}
789789

790+
/// Returns true for objects we display for the user.
790791
bool isDisplayableObject(Object? object) =>
791-
object is Sentinel || object is InstanceRef && !isNativeJsObject(object);
792+
object is Sentinel ||
793+
object is InstanceRef &&
794+
!isNativeJsObject(object) &&
795+
!isNativeJsError(object);
792796

797+
/// Returns true for non-dart JavaScript objects.
793798
bool isNativeJsObject(InstanceRef instanceRef) {
794-
// New type representation of JS objects reifies them to a type suffixed with
795-
// JavaScriptObject.
796-
final className = instanceRef.classRef?.name;
797-
return (className != null &&
798-
className.endsWith('JavaScriptObject') &&
799-
instanceRef.classRef?.library?.uri == 'dart:_interceptors') ||
800-
// Old type representation still needed to support older SDK versions.
801-
className == 'NativeJavaScriptObject';
799+
return isNativeJsObjectRef(instanceRef.classRef);
800+
}
801+
802+
/// Returns true of JavaScript exceptions.
803+
bool isNativeJsError(InstanceRef instanceRef) {
804+
return instanceRef.classRef == classRefForNativeJsError;
802805
}
803806

804807
/// Returns the Dart line number for the provided breakpoint.

dwds/lib/src/debugging/instance.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ class InstanceHelper extends Domain {
128128
} else if (metaData.isRecord) {
129129
return await _recordInstanceFor(classRef, remoteObject,
130130
offset: offset, count: count, length: metaData.length);
131+
} else if (metaData.isNativeError) {
132+
return await _plainInstanceFor(classRefForNativeJsError, remoteObject,
133+
offset: offset, count: count, length: metaData.length);
131134
} else {
132135
return await _plainInstanceFor(classRef, remoteObject,
133136
offset: offset, count: count, length: metaData.length);
@@ -630,6 +633,14 @@ class InstanceHelper extends Domain {
630633
classRef: metaData.classRef)
631634
..length = metaData.length;
632635
}
636+
if (metaData.isNativeError) {
637+
return InstanceRef(
638+
kind: InstanceKind.kPlainInstance,
639+
id: objectId,
640+
identityHashCode: remoteObject.objectId.hashCode,
641+
classRef: classRefForNativeJsError)
642+
..length = metaData.length;
643+
}
633644
return InstanceRef(
634645
kind: InstanceKind.kPlainInstance,
635646
id: objectId,

dwds/lib/src/debugging/metadata/class.dart

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import 'package:dwds/src/debugging/remote_debugger.dart';
66
import 'package:dwds/src/loaders/strategy.dart';
77
import 'package:dwds/src/services/chrome_debug_exception.dart';
88
import 'package:dwds/src/utilities/domain.dart';
9+
import 'package:logging/logging.dart';
910
import 'package:vm_service/vm_service.dart';
1011
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
1112

1213
const _dartCoreLibrary = 'dart:core';
14+
const _dartInterceptorsLibrary = 'dart:_interceptors';
1315

1416
/// A hard-coded ClassRef for the Closure class.
1517
final classRefForClosure = classRefFor(_dartCoreLibrary, 'Closure');
@@ -20,6 +22,31 @@ final classRefForString = classRefFor(_dartCoreLibrary, InstanceKind.kString);
2022
/// A hard-coded ClassRef for a (non-existent) class called Unknown.
2123
final classRefForUnknown = classRefFor(_dartCoreLibrary, 'Unknown');
2224

25+
/// A hard-coded ClassRef for a JS exception.
26+
///
27+
/// Exceptions are instances of NativeError and its subtypes.
28+
/// We detect their common base type in class metadata and replace their
29+
/// classRef by hard-coded reference in instances and instance refs.
30+
///
31+
/// TODO(annagrin): this breaks on name changes for JS types.
32+
/// https://github.com/dart-lang/sdk/issues/51583
33+
final classRefForNativeJsError =
34+
classRefFor(_dartInterceptorsLibrary, 'NativeError');
35+
36+
/// Returns true for non-dart JavaScript classes.
37+
///
38+
/// TODO(annagrin): this breaks on name changes for JS types.
39+
/// https://github.com/dart-lang/sdk/issues/51583
40+
bool isNativeJsObjectRef(ClassRef? classRef) {
41+
final className = classRef?.name;
42+
final libraryUri = classRef?.library?.uri;
43+
// Non-dart JS objects are all instances of JavaScriptObject
44+
// and its subtypes with names that end with 'JavaScriptObject'.
45+
return className != null &&
46+
libraryUri == _dartInterceptorsLibrary &&
47+
className.endsWith('JavaScriptObject');
48+
}
49+
2350
/// A hard-coded LibraryRef for a a dart:core library.
2451
final libraryRefForCore = LibraryRef(
2552
id: _dartCoreLibrary,
@@ -43,6 +70,8 @@ ClassRef classRefFor(String libraryId, String? name) => ClassRef(
4370

4471
/// Meta data for a remote Dart class in Chrome.
4572
class ClassMetaData {
73+
static final _logger = Logger('ClassMetadata');
74+
4675
/// The name of the JS constructor for the object.
4776
///
4877
/// This may be a constructor for a Dart, but it's still a JS name. For
@@ -67,6 +96,7 @@ class ClassMetaData {
6796
Object? length,
6897
bool isFunction = false,
6998
bool isRecord = false,
99+
bool isNativeError = false,
70100
}) {
71101
return ClassMetaData._(
72102
jsName as String?,
@@ -75,6 +105,7 @@ class ClassMetaData {
75105
int.tryParse('$length'),
76106
isFunction,
77107
isRecord,
108+
isNativeError,
78109
);
79110
}
80111

@@ -85,6 +116,7 @@ class ClassMetaData {
85116
this.length,
86117
this.isFunction,
87118
this.isRecord,
119+
this.isNativeError,
88120
);
89121

90122
/// Returns the ID of the class.
@@ -100,18 +132,22 @@ class ClassMetaData {
100132
try {
101133
final evalExpression = '''
102134
function(arg) {
103-
const sdkUtils = ${globalLoadStrategy.loadModuleSnippet}('dart_sdk').dart;
104-
const classObject = sdkUtils.getReifiedType(arg);
105-
const isFunction = classObject instanceof sdkUtils.AbstractFunctionType;
106-
const isRecord = classObject instanceof sdkUtils.RecordType;
135+
const sdk = ${globalLoadStrategy.loadModuleSnippet}('dart_sdk');
136+
const dart = sdk.dart;
137+
const interceptors = sdk._interceptors;
138+
const classObject = dart.getReifiedType(arg);
139+
const isFunction = classObject instanceof dart.AbstractFunctionType;
140+
const isRecord = classObject instanceof dart.RecordType;
141+
const isNativeError = dart.is(arg, interceptors.NativeError);
107142
const result = {};
108143
var name = isFunction ? 'Function' : classObject.name;
109144
110145
result['name'] = name;
111-
result['libraryId'] = sdkUtils.getLibraryUri(classObject);
112-
result['dartName'] = sdkUtils.typeName(classObject);
146+
result['libraryId'] = dart.getLibraryUri(classObject);
147+
result['dartName'] = dart.typeName(classObject);
113148
result['isFunction'] = isFunction;
114149
result['isRecord'] = isRecord;
150+
result['isNativeError'] = isNativeError;
115151
result['length'] = arg['length'];
116152
117153
if (isRecord) {
@@ -136,9 +172,12 @@ class ClassMetaData {
136172
dartName: metadata['dartName'],
137173
isFunction: metadata['isFunction'],
138174
isRecord: metadata['isRecord'],
175+
isNativeError: metadata['isNativeError'],
139176
length: metadata['length'],
140177
);
141-
} on ChromeDebugException {
178+
} on ChromeDebugException catch (e, s) {
179+
_logger.fine(
180+
'Could not create class metadata for ${remoteObject.json}', e, s);
142181
return null;
143182
}
144183
}
@@ -162,4 +201,8 @@ class ClassMetaData {
162201

163202
/// True if this class refers to a record type.
164203
bool isRecord;
204+
205+
/// True is this class refers to a native JS type.
206+
/// i.e. inherits from NativeError.
207+
bool isNativeError;
165208
}

0 commit comments

Comments
 (0)