[dds] Fix code generation for type references to simple types

Fixes https://github.com/dart-lang/sdk/issues/49268.

Change-Id: Iaeda8271d8cb922511958a71cf1300ba64df16da
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/248605
Commit-Queue: Ben Konyi <bkonyi@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
This commit is contained in:
Danny Tuppeny 2022-06-17 18:26:09 +00:00 committed by Commit Bot
parent 65e8411498
commit 51eed0cffd
5 changed files with 94 additions and 160 deletions

View file

@ -641,8 +641,7 @@ class Capabilities {
supportTerminateDebuggee = obj['supportTerminateDebuggee'] as bool?,
supportedChecksumAlgorithms =
(obj['supportedChecksumAlgorithms'] as List?)
?.map((item) =>
ChecksumAlgorithm.fromJson(item as Map<String, Object?>))
?.map((item) => item as ChecksumAlgorithm)
.toList(),
supportsBreakpointLocationsRequest =
obj['supportsBreakpointLocationsRequest'] as bool?,
@ -716,7 +715,7 @@ class Capabilities {
}
if ((obj['supportedChecksumAlgorithms'] is! List ||
(obj['supportedChecksumAlgorithms']
.any((item) => !ChecksumAlgorithm.canParse(item))))) {
.any((item) => item is! ChecksumAlgorithm)))) {
return false;
}
if (obj['supportsBreakpointLocationsRequest'] is! bool?) {
@ -909,15 +908,14 @@ class Checksum {
});
Checksum.fromMap(Map<String, Object?> obj)
: algorithm = ChecksumAlgorithm.fromJson(
obj['algorithm'] as Map<String, Object?>),
: algorithm = obj['algorithm'] as ChecksumAlgorithm,
checksum = obj['checksum'] as String;
static bool canParse(Object? obj) {
if (obj is! Map<String, dynamic>) {
return false;
}
if (!ChecksumAlgorithm.canParse(obj['algorithm'])) {
if (obj['algorithm'] is! ChecksumAlgorithm) {
return false;
}
if (obj['checksum'] is! String) {
@ -933,23 +931,7 @@ class Checksum {
}
/// Names of checksum algorithms that may be supported by a debug adapter.
class ChecksumAlgorithm {
static ChecksumAlgorithm fromJson(Map<String, Object?> obj) =>
ChecksumAlgorithm.fromMap(obj);
ChecksumAlgorithm();
ChecksumAlgorithm.fromMap(Map<String, Object?> obj);
static bool canParse(Object? obj) {
if (obj is! Map<String, dynamic>) {
return false;
}
return true;
}
Map<String, Object?> toJson() => {};
}
typedef ChecksumAlgorithm = String;
/// A ColumnDescriptor specifies what module attribute to show in a column of
/// the ModulesView, how to format it,
@ -1085,9 +1067,7 @@ class CompletionItem {
sortText = obj['sortText'] as String?,
start = obj['start'] as int?,
text = obj['text'] as String?,
type = obj['type'] == null
? null
: CompletionItemType.fromJson(obj['type'] as Map<String, Object?>);
type = obj['type'] as CompletionItemType?;
static bool canParse(Object? obj) {
if (obj is! Map<String, dynamic>) {
@ -1114,7 +1094,7 @@ class CompletionItem {
if (obj['text'] is! String?) {
return false;
}
if (!CompletionItemType.canParse(obj['type'])) {
if (obj['type'] is! CompletionItemType?) {
return false;
}
return true;
@ -1134,23 +1114,7 @@ class CompletionItem {
/// Some predefined types for the CompletionItem. Please note that not all
/// clients have specific icons for all of them.
class CompletionItemType {
static CompletionItemType fromJson(Map<String, Object?> obj) =>
CompletionItemType.fromMap(obj);
CompletionItemType();
CompletionItemType.fromMap(Map<String, Object?> obj);
static bool canParse(Object? obj) {
if (obj is! Map<String, dynamic>) {
return false;
}
return true;
}
Map<String, Object?> toJson() => {};
}
typedef CompletionItemType = String;
/// Arguments for 'completions' request.
class CompletionsArguments extends RequestArguments {
@ -1408,10 +1372,7 @@ class DataBreakpoint {
});
DataBreakpoint.fromMap(Map<String, Object?> obj)
: accessType = obj['accessType'] == null
? null
: DataBreakpointAccessType.fromJson(
obj['accessType'] as Map<String, Object?>),
: accessType = obj['accessType'] as DataBreakpointAccessType?,
condition = obj['condition'] as String?,
dataId = obj['dataId'] as String,
hitCondition = obj['hitCondition'] as String?;
@ -1420,7 +1381,7 @@ class DataBreakpoint {
if (obj is! Map<String, dynamic>) {
return false;
}
if (!DataBreakpointAccessType.canParse(obj['accessType'])) {
if (obj['accessType'] is! DataBreakpointAccessType?) {
return false;
}
if (obj['condition'] is! String?) {
@ -1444,23 +1405,7 @@ class DataBreakpoint {
}
/// This enumeration defines all possible access types for data breakpoints.
class DataBreakpointAccessType {
static DataBreakpointAccessType fromJson(Map<String, Object?> obj) =>
DataBreakpointAccessType.fromMap(obj);
DataBreakpointAccessType();
DataBreakpointAccessType.fromMap(Map<String, Object?> obj);
static bool canParse(Object? obj) {
if (obj is! Map<String, dynamic>) {
return false;
}
return true;
}
Map<String, Object?> toJson() => {};
}
typedef DataBreakpointAccessType = String;
/// Arguments for 'dataBreakpointInfo' request.
class DataBreakpointInfoArguments extends RequestArguments {
@ -2056,23 +2001,7 @@ class Event extends ProtocolMessage {
/// always: always breaks,
/// unhandled: breaks when exception unhandled,
/// userUnhandled: breaks if the exception is not handled by user code.
class ExceptionBreakMode {
static ExceptionBreakMode fromJson(Map<String, Object?> obj) =>
ExceptionBreakMode.fromMap(obj);
ExceptionBreakMode();
ExceptionBreakMode.fromMap(Map<String, Object?> obj);
static bool canParse(Object? obj) {
if (obj is! Map<String, dynamic>) {
return false;
}
return true;
}
Map<String, Object?> toJson() => {};
}
typedef ExceptionBreakMode = String;
/// An ExceptionBreakpointsFilter is shown in the UI as an filter option for
/// configuring how exceptions are dealt with.
@ -2370,8 +2299,7 @@ class ExceptionOptions {
});
ExceptionOptions.fromMap(Map<String, Object?> obj)
: breakMode = ExceptionBreakMode.fromJson(
obj['breakMode'] as Map<String, Object?>),
: breakMode = obj['breakMode'] as ExceptionBreakMode,
path = (obj['path'] as List?)
?.map((item) =>
ExceptionPathSegment.fromJson(item as Map<String, Object?>))
@ -2381,7 +2309,7 @@ class ExceptionOptions {
if (obj is! Map<String, dynamic>) {
return false;
}
if (!ExceptionBreakMode.canParse(obj['breakMode'])) {
if (obj['breakMode'] is! ExceptionBreakMode) {
return false;
}
if ((obj['path'] is! List ||
@ -3002,23 +2930,7 @@ class InstructionBreakpoint {
}
/// Logical areas that can be invalidated by the 'invalidated' event.
class InvalidatedAreas {
static InvalidatedAreas fromJson(Map<String, Object?> obj) =>
InvalidatedAreas.fromMap(obj);
InvalidatedAreas();
InvalidatedAreas.fromMap(Map<String, Object?> obj);
static bool canParse(Object? obj) {
if (obj is! Map<String, dynamic>) {
return false;
}
return true;
}
Map<String, Object?> toJson() => {};
}
typedef InvalidatedAreas = String;
/// Arguments for 'launch' request. Additional attributes are implementation
/// specific.
@ -3508,17 +3420,14 @@ class NextArguments extends RequestArguments {
});
NextArguments.fromMap(Map<String, Object?> obj)
: granularity = obj['granularity'] == null
? null
: SteppingGranularity.fromJson(
obj['granularity'] as Map<String, Object?>),
: granularity = obj['granularity'] as SteppingGranularity?,
threadId = obj['threadId'] as int;
static bool canParse(Object? obj) {
if (obj is! Map<String, dynamic>) {
return false;
}
if (!SteppingGranularity.canParse(obj['granularity'])) {
if (obj['granularity'] is! SteppingGranularity?) {
return false;
}
if (obj['threadId'] is! int) {
@ -5686,17 +5595,14 @@ class StepBackArguments extends RequestArguments {
});
StepBackArguments.fromMap(Map<String, Object?> obj)
: granularity = obj['granularity'] == null
? null
: SteppingGranularity.fromJson(
obj['granularity'] as Map<String, Object?>),
: granularity = obj['granularity'] as SteppingGranularity?,
threadId = obj['threadId'] as int;
static bool canParse(Object? obj) {
if (obj is! Map<String, dynamic>) {
return false;
}
if (!SteppingGranularity.canParse(obj['granularity'])) {
if (obj['granularity'] is! SteppingGranularity?) {
return false;
}
if (obj['threadId'] is! int) {
@ -5770,10 +5676,7 @@ class StepInArguments extends RequestArguments {
});
StepInArguments.fromMap(Map<String, Object?> obj)
: granularity = obj['granularity'] == null
? null
: SteppingGranularity.fromJson(
obj['granularity'] as Map<String, Object?>),
: granularity = obj['granularity'] as SteppingGranularity?,
targetId = obj['targetId'] as int?,
threadId = obj['threadId'] as int;
@ -5781,7 +5684,7 @@ class StepInArguments extends RequestArguments {
if (obj is! Map<String, dynamic>) {
return false;
}
if (!SteppingGranularity.canParse(obj['granularity'])) {
if (obj['granularity'] is! SteppingGranularity?) {
return false;
}
if (obj['targetId'] is! int?) {
@ -5964,17 +5867,14 @@ class StepOutArguments extends RequestArguments {
});
StepOutArguments.fromMap(Map<String, Object?> obj)
: granularity = obj['granularity'] == null
? null
: SteppingGranularity.fromJson(
obj['granularity'] as Map<String, Object?>),
: granularity = obj['granularity'] as SteppingGranularity?,
threadId = obj['threadId'] as int;
static bool canParse(Object? obj) {
if (obj is! Map<String, dynamic>) {
return false;
}
if (!SteppingGranularity.canParse(obj['granularity'])) {
if (obj['granularity'] is! SteppingGranularity?) {
return false;
}
if (obj['threadId'] is! int) {
@ -6028,23 +5928,7 @@ class StepOutResponse extends Response {
/// The granularity of one 'step' in the stepping requests 'next', 'stepIn',
/// 'stepOut', and 'stepBack'.
class SteppingGranularity {
static SteppingGranularity fromJson(Map<String, Object?> obj) =>
SteppingGranularity.fromMap(obj);
SteppingGranularity();
SteppingGranularity.fromMap(Map<String, Object?> obj);
static bool canParse(Object? obj) {
if (obj is! Map<String, dynamic>) {
return false;
}
return true;
}
Map<String, Object?> toJson() => {};
}
typedef SteppingGranularity = String;
/// Arguments for 'terminate' request.
class TerminateArguments extends RequestArguments {
@ -6887,8 +6771,7 @@ class DataBreakpointInfoResponseBody {
DataBreakpointInfoResponseBody.fromMap(Map<String, Object?> obj)
: accessTypes = (obj['accessTypes'] as List?)
?.map((item) =>
DataBreakpointAccessType.fromJson(item as Map<String, Object?>))
?.map((item) => item as DataBreakpointAccessType)
.toList(),
canPersist = obj['canPersist'] as bool?,
dataId = obj['dataId'] is String
@ -6902,7 +6785,7 @@ class DataBreakpointInfoResponseBody {
}
if ((obj['accessTypes'] is! List ||
(obj['accessTypes']
.any((item) => !DataBreakpointAccessType.canParse(item))))) {
.any((item) => item is! DataBreakpointAccessType)))) {
return false;
}
if (obj['canPersist'] is! bool?) {
@ -7138,8 +7021,7 @@ class ExceptionInfoResponseBody {
});
ExceptionInfoResponseBody.fromMap(Map<String, Object?> obj)
: breakMode = ExceptionBreakMode.fromJson(
obj['breakMode'] as Map<String, Object?>),
: breakMode = obj['breakMode'] as ExceptionBreakMode,
description = obj['description'] as String?,
details = obj['details'] == null
? null
@ -7150,7 +7032,7 @@ class ExceptionInfoResponseBody {
if (obj is! Map<String, dynamic>) {
return false;
}
if (!ExceptionBreakMode.canParse(obj['breakMode'])) {
if (obj['breakMode'] is! ExceptionBreakMode) {
return false;
}
if (obj['description'] is! String?) {
@ -7319,8 +7201,7 @@ class InvalidatedEventBody extends EventBody {
InvalidatedEventBody.fromMap(Map<String, Object?> obj)
: areas = (obj['areas'] as List?)
?.map((item) =>
InvalidatedAreas.fromJson(item as Map<String, Object?>))
?.map((item) => item as InvalidatedAreas)
.toList(),
stackFrameId = obj['stackFrameId'] as int?,
threadId = obj['threadId'] as int?;
@ -7330,7 +7211,7 @@ class InvalidatedEventBody extends EventBody {
return false;
}
if ((obj['areas'] is! List ||
(obj['areas'].any((item) => !InvalidatedAreas.canParse(item))))) {
(obj['areas'].any((item) => item is! InvalidatedAreas)))) {
return false;
}
if (obj['stackFrameId'] is! int?) {

View file

@ -74,6 +74,28 @@ void main(List<String> args) async {
], eagerError: true);
});
test(
'stops at a line breakpoint and can step over (next) '
'when stepping granularity was included', () async {
final testFile = dap.createTestFile('''
void main(List<String> args) async {
print('Hello!'); $breakpointMarker
print('Hello!'); $stepMarker
}
''');
final breakpointLine = lineWith(testFile, breakpointMarker);
final stepLine = lineWith(testFile, stepMarker);
// Hit the initial breakpoint.
final stop = await dap.client.hitBreakpoint(testFile, breakpointLine);
// Step and expect stopping on the next line with a 'step' stop type.
await Future.wait([
dap.client.expectStop('step', file: testFile, line: stepLine),
dap.client.next(stop.threadId!, granularity: 'statement'),
], eagerError: true);
});
test(
'stops at a line breakpoint and can step over (next) an async boundary',
() async {

View file

@ -261,10 +261,14 @@ class DapTestClient {
/// Sends a next (step over) request for the given thread.
///
/// [granularity] is always ignored because the Dart debugger does not support
/// it (indicated in its capabilities), but it is used by tests to ensure the
/// adapter does not crash on the presence of it.
///
/// Returns a Future that completes when the server returns a corresponding
/// response.
Future<Response> next(int threadId) =>
sendRequest(NextArguments(threadId: threadId));
Future<Response> next(int threadId, {SteppingGranularity? granularity}) =>
sendRequest(NextArguments(threadId: threadId, granularity: granularity));
/// Sends a request to the server for variables scopes available for a given
/// stack frame.

View file

@ -178,6 +178,15 @@ class CodeGenerator {
JsonType? resolvedBaseType, {
Map<String, String> additionalValues = const {},
}) {
_writeTypeDescription(buffer, type);
// Types that are just aliases to simple value types should be written as
// typedefs.
if (type.isSimpleValue) {
buffer.writeln('typedef $name = ${type.asDartType()};');
return;
}
// Some properties are defined in both the base and the class, because the
// type may be narrowed, but sometimes we only want those that are defined
// only in this class.
@ -186,7 +195,6 @@ class CodeGenerator {
if (!baseProperties.containsKey(property.key))
property.key: property.value,
};
_writeTypeDescription(buffer, type);
buffer.write('class $name ');
if (baseType != null) {
buffer.write('extends ${baseType.refName} ');
@ -466,11 +474,12 @@ class CodeGenerator {
void _writeFromJsonExpression(
IndentableStringBuffer buffer, JsonType type, String valueCode,
{bool isOptional = false}) {
final baseType = type.aliasFor ?? type;
final dartType = type.asDartType(isOptional: isOptional);
final dartTypeNotNullable = type.asDartType();
final nullOp = isOptional ? '?' : '';
if (type.isAny || type.isSimple) {
if (baseType.isAny || baseType.isSimple) {
buffer.write('$valueCode');
if (dartType != 'Object?') {
buffer.write(' as $dartType');
@ -605,6 +614,7 @@ class CodeGenerator {
void _writeTypeCheckCondition(
IndentableStringBuffer buffer, JsonType type, String valueCode,
{required bool isOptional, bool invert = false}) {
final baseType = type.aliasFor ?? type;
final dartType = type.asDartType(isOptional: isOptional);
// When the expression is inverted, invert the operators so the generated
@ -617,11 +627,11 @@ class CodeGenerator {
final opOr = invert ? '&&' : '||';
final opEvery = invert ? 'any' : 'every';
if (type.isAny) {
if (baseType.isAny) {
buffer.write(opTrue);
} else if (dartType == 'Null') {
buffer.write('$valueCode $opEquals null');
} else if (type.isSimple) {
} else if (baseType.isSimple) {
buffer.write('$valueCode $opIs $dartType');
} else if (type.isList) {
buffer.write('($valueCode $opIs List');

View file

@ -72,9 +72,11 @@ extension JsonTypeExtensions on JsonType {
? _toDartType(dollarRef!)
: oneOf != null
? _toDartUnionType(oneOf!.map((item) => item.asDartType()).toList())
: type!.valueEquals('array')
? 'List<${items!.asDartType()}>'
: type!.map(_toDartType, _toDartUnionType);
: type == null
? refName
: type!.valueEquals('array')
? 'List<${items!.asDartType()}>'
: type!.map(_toDartType, _toDartUnionType);
return isOptional ? '$dartType?' : dartType;
}
@ -85,7 +87,21 @@ extension JsonTypeExtensions on JsonType {
/// Whether this type represents a List.
bool get isList => type?.valueEquals('array') ?? false;
/// Whether this type is a simple value that does not need any special handling.
/// Whether this type is a simple value like `String`, `bool`, `int`.
bool get isSimpleValue => isSimple && asDartType() != 'Map<String, Object?>';
/// If this type is an alias to a simple value type, returns that type.
/// Otherwise, returns `null`.
JsonType? get aliasFor {
final targetType = dollarRef != null ? root.typeFor(this) : null;
if (targetType == null) {
return null;
}
return targetType.isSimpleValue ? targetType : null;
}
/// Whether this type is a simple type that needs no special handling for
/// deserialisation (such as `String`, `bool`, `int`, `Map<String, Object?>`).
bool get isSimple {
const _dartSimpleTypes = {
'bool',
@ -95,7 +111,8 @@ extension JsonTypeExtensions on JsonType {
'Map<String, Object?>',
'Null',
};
return _dartSimpleTypes.contains(asDartType());
return type != null &&
_dartSimpleTypes.contains(type!.map(_toDartType, _toDartUnionType));
}
/// Whether this type is a Union type using JSON schema's "oneOf" of where its