[dds] Remove copy of dap tool

Cq-Include-Trybots: luci.dart.try:flutter-linux-try,flutter-web-try
Bug: b/286184681
Change-Id: I408d36a10dde43e3e182d57685f7d8423124461b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/344021
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Alexander Thomas <athom@google.com>
This commit is contained in:
Alexander Thomas 2024-01-09 10:45:30 +00:00 committed by Commit Queue
parent bc260f4d73
commit e609c8232b
8 changed files with 0 additions and 5228 deletions

View file

@ -37,7 +37,6 @@ dependencies:
# best practice for packages is to specify their compatible version ranges.
# See also https://dart.dev/tools/pub/dependencies.
dev_dependencies:
http: any
lints: any
test: any
webdriver: any

View file

@ -1,199 +0,0 @@
# Debug Adapter Protocol
Dart includes support for debugging using [the Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/) as an alternative to using the [VM Service](https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md) directly, simplifying the integration for new editors.
The debug adapters are started with the `dart debug_adapter` command and are intended to be consumed by DAP-compliant tools such as Dart-specific extensions for editors, or configured by users whose editors include generic configurable DAP clients.
Two adapters are available:
- `dart debug_adapter`
- `dart debug_adapter --test`
The standard adapter will run scripts using `dart` while the `--test` adapter will cause scripts to be run using `dart test` and will emit custom `dart.testNotification` events (described below).
Because in the DAP protocol the client speaks first, running this command from the terminal will result in no output (nor will the process terminate). This is expected behaviour.
For details on the standard DAP functionality, see [the Debug Adapter Protocol Overview](https://microsoft.github.io/debug-adapter-protocol/) and [the Debug Adapter Protocol Specification](https://microsoft.github.io/debug-adapter-protocol/specification). Custom extensions are detailed below.
**Flutter**: Flutter apps should be run using the debug adapter in the `flutter` tool - [see this document](https://github.com/flutter/flutter/blob/master/packages/flutter_tools/lib/src/debug_adapters/README.md).
## Launch/Attach Arguments
Arguments common to both `launchRequest` and `attachRequest` are:
- `bool? debugExternalPackageLibraries` - whether to enable debugging for packages that are not inside the current workspace
- `bool? debugSdkLibraries` - whether to enable debugging for SDK libraries
- `bool? showGettersInDebugViews` - whether to include getters in Variables and Evaluation responses (inc. hovers/watch windows)
- `bool? evaluateGettersInDebugViews` - whether to eagerly evaluate getters in Variables and Evaluation responses (inc. hovers/watch windows) without needing user action. Implies `showGettersInDebugViews=true`.
- `bool? evaluateToStringInDebugViews` - whether to invoke `toString()` in expression evaluation requests (inc. hovers/watch windows)
- `bool? sendLogsToClient` - used to proxy all VM Service traffic back to the client in custom `dart.log` events (has performance implications, intended for troubleshooting)
- `int? vmServicePort` - the port to bind the VM Service too
- `List<String>? additionalProjectPaths` - paths of any projects (outside of `cwd`) that are open in the users workspace
- `String? cwd` - the working directory for the Dart process to be spawned in
- `Map<String, String>? env` - environment variables to be passed to any spawned process
- `bool? sendCustomProgressEvents` - whether to send custom `dart.progressStart`, `dart.progressUpdate`, `dart.progressEnd` progress events in place of standard DAP `progressStart`, `progressUpdate`, `progressEnd` events. When this is set, only standard events will not be sent regardless of the `supportsProgressReporting` capability.
- `bool? allowAnsiColorOutput` - whether to allow ansi color codes in Output events
Arguments specific to `launchRequest` are:
- `bool? noDebug` - whether to run in debug or noDebug mode (if not supplied, defaults to debug)
- `String program` - the path of the Dart program to run
- `List<String>? args` - arguments to be passed to the Dart program (after the `program` on the command line)
- `List<String>? toolArgs` - arguments passed after the tool that will run `program` (after `dart` for CLI scripts and after `dart run test:test` for test scripts)
- `List<String>? vmAdditionalArgs` - arguments passed directly to the Dart VM (after `dart` for both CLI scripts and test scripts)
- `String? console` - if set to `"terminal"` or `"externalTerminal"` will be run using the `runInTerminal` reverse-request; otherwise the debug adapter spawns the Dart process
- `String? customTool` - an optional tool to run instead of `dart` - the custom tool must be completely compatible with the tool/command it is replacing
- `int? customToolReplacesArgs` - the number of arguments to delete from the beginning of the argument list when invoking `customTool` - e.g. setting `customTool` to `dart_test` and
`customToolReplacesArgs` to `2` for a test run would invoke `dart_test foo_test.dart` instead of `dart run test:test foo_test.dart` (if larger than the number of computed arguments all arguments will be removed, if not supplied will default to `0`)
Arguments specific to `attachRequest` are:
- `String? vmServiceInfoFile` - the file to read the VM Service info from \*
- `String? vmServiceUri` - the VM Service URI to attach to \*
\* Exactly one of `vmServiceInfoFile` or `vmServiceUri` should be supplied.
## Expression Evaluation Format Specifiers
Special suffixes can be added to evaluation expressions (such as a Watch window or Debug Console) that will affect the formatting of variables:
- `,nq` - don't add quotes around strings
- `,h` - format integers as hex
- `,d` - format integers as decimal (base 10)
A format specifier overrides any other formatting (such as the `format` argument that can be supplied to `variablesRequest` and `evaluateRequest`). Format specifiers also carry down the variables tree, so adding `,h` to an expression that is a `List<int>` will cause the values inside the list (once expanded) to be rendered as hex. Multiple format specifiers can be comma-separated (`myVariable,nq,h` on a class will cause `String`s in child fields to be unquoted and `int`s to be formatted as hex).
## Custom Requests
Some custom requests are available for clients to call.
### `updateDebugOptions`
`updateDebugOptions` allows updating some debug options usually provided at launch/attach while the session is running. Any keys included in the request will overwrite the previously set values. To update only some values, include only those in the parameters.
```
{
"debugSdkLibraries": true
"debugExternalPackageLibraries": false
}
```
### `callService`
`callService` allows calling arbitrary services (for example service extensions that have been registered). The service RPC/method should be sent in the `method` field and `params` will depend on the service being called.
```
{
"method": "myFooService",
"params": {
// ...
}
}
```
### `hotReload`
`hotReload` calls the VM's `reloadSources` service for each active isolate, reloading all modified source files.
```
{
"method": "hotReload",
"params": null
}
```
## Custom Events
The debug adapter may emit several custom events that are useful to clients.
### `dart.debuggerUris`
When running in debug mode, a `dart.debuggerUris` event will be emitted containing the URI of the VM Service.
```
{
"type": "event",
"event": "dart.debuggerUris",
"body": {
"vmServiceUri": "ws://127.0.0.1:123/abdef123="
}
}
```
### `dart.log`
When `sendLogsToClient` in the launch/attach arguments is `true`, debug logging and all traffic to the VM Service will be proxied back to the client in `dart.log` events to aid troubleshooting.
```
{
"type": "event",
"event": "dart.log",
"body": {
"message": "<log message or json string>"
}
}
```
### `dart.serviceRegistered`
Emitted when a VM Service is registered.
```
{
"type": "event",
"event": "dart.serviceRegistered",
"body": {
"service": "ServiceName",
"method": "methodName"
}
}
```
### `dart.serviceUnregistered`
Emitted when a VM Service is unregistered.
```
{
"type": "event",
"event": "dart.serviceUnregistered",
"body": {
"service": "ServiceName",
"method": "methodName"
}
}
```
### `dart.serviceExtensionAdded`
Emitted when a VM Service Extension is added.
```
{
"type": "event",
"event": "dart.serviceExtensionAdded",
"body": {
"extensionRPC": "<extensionRPC to call>",
"isolateId": "<isolateId>"
}
}
```
### `dart.testNotification`
When running the `--test` debug adapter, `package:test` JSON messages will be passed back to the client in a `dart.testNotification` event. For details on this protocol, see the [package:test documentation](https://github.com/dart-lang/test/blob/master/pkgs/test/doc/json_reporter.md).
```
{
"type": "event",
"event": "dart.testNotification",
"body": {
"type": "testStart",
"test": {
"id": 1,
"name": "my test name",
// ...
}
}
}
```

View file

@ -1,699 +0,0 @@
// Copyright (c) 2021, 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 'dart:math';
import 'package:collection/collection.dart';
import 'json_schema.dart';
import 'json_schema_extensions.dart';
/// Generates Dart classes from the Debug Adapter Protocol's JSON Schema.
class CodeGenerator {
/// Writes all required Dart classes for the supplied DAP [schema].
void writeAll(IndentableStringBuffer buffer, JsonSchema schema) {
_writeDefinitionClasses(buffer, schema);
buffer.writeln();
_writeBodyClasses(buffer, schema);
buffer.writeln();
_writeEventTypeLookup(buffer, schema);
buffer.writeln();
_writeCommandArgumentTypeLookup(buffer, schema);
}
/// Maps a name used in the DAP spec to a valid name for use in Dart.
///
/// Reserved words like `default` will be mapped to a suitable alternative.
/// Prefixed underscores are removed to avoid making things private.
///
/// Underscores between words are swapped for camelCase.
String _dartSafeName(String name) {
const improvedName = {
'default': 'defaultValue',
};
return improvedName[name] ??
// Some types are prefixed with _ in the spec but that will make them
// private in Dart and inaccessible to the adapter so we strip it off.
name
.replaceAll(RegExp(r'^_+'), '')
// Also replace any other underscores to make camelCase
.replaceAllMapped(
RegExp(r'_(.)'), (m) => m.group(1)!.toUpperCase());
}
/// Re-wraps [lines] at [maxLength] to help keep comments for indented code
/// within 80 characters.
Iterable<String> _wrapLines(List<String> lines, int maxLength) sync* {
lines = lines.map((l) => l.trimRight()).toList();
for (var line in lines) {
while (true) {
if (line.length <= maxLength || line.startsWith('-')) {
yield line;
break;
} else {
var lastSpace = line.lastIndexOf(' ', max(maxLength, 0));
// If there was no valid place to wrap, yield the whole string.
if (lastSpace == -1) {
yield line;
break;
} else {
yield line.substring(0, lastSpace);
line = line.substring(lastSpace + 1);
}
}
}
}
}
/// For each Response/Event class in the spec, generate a specific class to
/// represent its body.
///
/// These classes are used to simplify sending responses/events from the
/// Debug Adapters by avoiding the need to construct the entire response/event
/// which requires additional fields (for example the corresponding requests
/// id/command and sequences):
///
/// this.sendResponse(FooBody(x: 1))
///
/// instead of
///
/// this.sendResponse(Response(
/// seq: seq++,
/// request_seq: request.seq,
/// command: request.command,
/// body: {
/// x: 1
/// ...
/// }
/// ))
void _writeBodyClasses(IndentableStringBuffer buffer, JsonSchema schema) {
for (final entry in schema.definitions.entries.sortedBy((e) => e.key)) {
final name = entry.key;
final type = entry.value;
final baseType = type.baseType;
if (baseType?.refName == 'Response' || baseType?.refName == 'Event') {
final baseClass = baseType?.refName == 'Event'
? JsonType.named(schema, 'EventBody')
: null;
final classProperties = schema.propertiesFor(type);
final bodyProperty = classProperties['body'];
var bodyPropertyProperties = bodyProperty?.properties;
_writeClass(
buffer,
bodyProperty ?? JsonType.empty(schema),
'${name}Body',
bodyPropertyProperties ?? {},
{},
baseClass,
null,
);
}
}
}
/// Writes a `canParse` function for a DAP spec class.
///
/// The function checks whether an Object? is a valid map that contains all
/// required fields and matches the types of the spec class.
///
/// This is used where the spec contains union classes and we need to decide
/// which of the allowed types a given value is.
void _writeCanParseMethod(
IndentableStringBuffer buffer,
JsonType type,
Map<String, JsonType> properties, {
required String? baseTypeRefName,
}) {
buffer
..writeIndentedln('static bool canParse(Object? obj) {')
..indent()
..writeIndentedln('if (obj is! Map<String, dynamic>) {')
..indent()
..writeIndentedln('return false;')
..outdent()
..writeIndentedln('}');
// In order to consider this valid for parsing, all fields that must not be
// undefined must be present and also type check for the correct type.
// Any fields that are optional but present, must still type check.
for (final entry in properties.entries.sortedBy((e) => e.key)) {
final propertyName = entry.key;
final propertyType = entry.value;
final isOptional = !type.requiresField(propertyName);
if (propertyType.isAny && isOptional) {
continue;
}
buffer.writeIndented('if (');
_writeTypeCheckCondition(buffer, propertyType, "obj['$propertyName']",
isOptional: isOptional, invert: true);
buffer
..writeln(') {')
..indent()
..writeIndentedln('return false;')
..outdent()
..writeIndentedln('}');
}
buffer
..writeIndentedln(
baseTypeRefName != null
? 'return $baseTypeRefName.canParse(obj);'
: 'return true;',
)
..outdent()
..writeIndentedln('}');
}
/// Writes the Dart class for [type].
void _writeClass(
IndentableStringBuffer buffer,
JsonType type,
String name,
Map<String, JsonType> classProperties,
Map<String, JsonType> baseProperties,
JsonType? baseType,
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.
final classOnlyProperties = {
for (final property in classProperties.entries)
if (!baseProperties.containsKey(property.key))
property.key: property.value,
};
buffer.write('class $name ');
if (baseType != null) {
buffer.write('extends ${baseType.refName} ');
}
buffer
..writeln('{')
..indent();
for (final val in additionalValues.entries) {
buffer
..writeIndentedln('@override')
..writeIndentedln("final ${val.key} = '${val.value}';");
}
_writeFields(buffer, type, classOnlyProperties);
buffer.writeln();
_writeFromJsonStaticMethod(buffer, name);
buffer.writeln();
_writeConstructor(buffer, name, type, classProperties, baseProperties,
classOnlyProperties,
baseType: resolvedBaseType);
buffer.writeln();
_writeFromMapConstructor(buffer, name, type, classOnlyProperties,
callSuper: resolvedBaseType != null);
buffer.writeln();
_writeCanParseMethod(buffer, type, classProperties,
baseTypeRefName: baseType?.refName);
buffer.writeln();
_writeToJsonMethod(buffer, name, type, classOnlyProperties,
callSuper: resolvedBaseType != null);
buffer
..outdent()
..writeln('}')
..writeln();
}
/// Write a map to look up the `command` for a given `RequestArguments` type
/// to simplify sending requests back to the client:
///
/// this.sendRequest(FooArguments(x: 1))
///
/// instead of
///
/// this.sendRequest(Request(
/// seq: seq++,
/// command: request.command,
/// arguments: {
/// x: 1
/// ...
/// }
/// ))
void _writeCommandArgumentTypeLookup(
IndentableStringBuffer buffer, JsonSchema schema) {
buffer
..writeln('const commandTypes = {')
..indent();
for (final entry in schema.definitions.entries.sortedBy((e) => e.key)) {
final type = entry.value;
final baseType = type.baseType;
if (baseType?.refName == 'Request') {
final classProperties = schema.propertiesFor(type);
final argumentsProperty = classProperties['arguments'];
final commandType = classProperties['command']?.literalValue;
if (argumentsProperty?.dollarRef != null && commandType != null) {
buffer.writeIndentedln(
"${argumentsProperty!.refName}: '$commandType',");
}
}
}
buffer
..writeln('};')
..outdent();
}
/// Writes a constructor for [type].
///
/// The constructor will have named arguments for all fields, with those that
/// are mandatory marked with `required`.
void _writeConstructor(
IndentableStringBuffer buffer,
String name,
JsonType type,
Map<String, JsonType> classProperties,
Map<String, JsonType> baseProperties,
Map<String, JsonType> classOnlyProperties, {
required JsonType? baseType,
}) {
buffer.writeIndented('$name(');
if (classProperties.isNotEmpty || baseProperties.isNotEmpty) {
buffer
..writeln('{')
..indent();
// Properties for this class are written as 'this.foo'.
for (final entry in classOnlyProperties.entries.sortedBy((e) => e.key)) {
final propertyName = entry.key;
final fieldName = _dartSafeName(propertyName);
final isOptional = !type.requiresField(propertyName);
buffer.writeIndented('');
if (!isOptional) {
buffer.write('required ');
}
buffer.writeln('this.$fieldName, ');
}
// Properties from the base class are standard arguments that will be
// passed to a super() call.
for (final entry in baseProperties.entries.sortedBy((e) => e.key)) {
final propertyName = entry.key;
// If this field is defined by the class and the base, prefer the
// class one as it may contain things like the literal values.
final propertyType = classProperties[propertyName] ?? entry.value;
final fieldName = _dartSafeName(propertyName);
if (propertyType.literalValue != null) {
continue;
}
final isOptional = !type.requiresField(propertyName);
final dartType = propertyType.asDartType(isOptional: isOptional);
buffer.writeIndented('');
if (!isOptional) {
buffer.write('required ');
}
buffer.writeln('$dartType $fieldName, ');
}
buffer
..outdent()
..writeIndented('}');
}
buffer.write(')');
if (baseType != null) {
buffer.write(': super(');
if (baseProperties.isNotEmpty) {
buffer
..writeln()
..indent();
for (final entry in baseProperties.entries) {
final propertyName = entry.key;
// Skip any properties that have literal values defined by the base
// as we won't need to supply them.
if (entry.value.literalValue != null) {
continue;
}
// If this field is defined by the class and the base, prefer the
// class one as it may contain things like the literal values.
final propertyType = classProperties[propertyName] ?? entry.value;
final fieldName = _dartSafeName(propertyName);
final literalValue = propertyType.literalValue;
final value = literalValue != null ? "'$literalValue'" : fieldName;
buffer.writeIndentedln('$fieldName: $value, ');
}
buffer
..outdent()
..writeIndented('');
}
buffer.write(')');
}
buffer.writeln(';');
}
/// Write a class for each item in the DAP spec.
///
/// Skips over the Request and Event sub-classes, as they are handled by the
/// simplified body classes written by [_writeBodyClasses]. Uses
/// [RequestArguments] as the base class for all argument classes.
void _writeDefinitionClasses(
IndentableStringBuffer buffer, JsonSchema schema) {
for (final entry in schema.definitions.entries.sortedBy((e) => e.key)) {
final name = entry.key;
final type = entry.value;
var baseType = type.baseType;
final resolvedBaseType =
baseType != null ? schema.typeFor(baseType) : null;
final classProperties = schema.propertiesFor(type, includeBase: false);
final baseProperties = resolvedBaseType != null
? schema.propertiesFor(resolvedBaseType)
: <String, JsonType>{};
// Skip creation of Request sub-classes, as we don't use these we just
// pass the arguments into the method directly.
if (name != 'Request' && name.endsWith('Request')) {
continue;
}
// Skip creation of Event sub-classes, as we don't use these we just
// pass the body into sendEvent directly.
if (name != 'Event' && name.endsWith('Event')) {
continue;
}
// Create a synthetic base class for arguments to provide type safety
// for sending requests.
if (baseType == null && name.endsWith('Arguments')) {
baseType = JsonType.named(schema, 'RequestArguments');
}
_writeClass(
buffer,
type,
name,
classProperties,
baseProperties,
baseType,
resolvedBaseType,
);
}
}
/// Writes a DartDoc comment, wrapped at 80 characters taking into account
/// the indentation.
void _writeDescription(IndentableStringBuffer buffer, String? description) {
final maxLength = 80 - buffer.totalIndent - 4;
if (description != null) {
for (final line in _wrapLines(description.split('\n'), maxLength)) {
buffer.writeIndentedln('/// $line');
}
}
}
/// Write a map to look up the `event` for a given `EventBody` type
/// to simplify sending events back to the client:
///
/// this.sendEvent(FooEvent(x: 1))
///
/// instead of
///
/// this.sendEvent(Event(
/// seq: seq++,
/// event: 'FooEvent',
/// arguments: {
/// x: 1
/// ...
/// }
/// ))
void _writeEventTypeLookup(IndentableStringBuffer buffer, JsonSchema schema) {
buffer
..writeln('const eventTypes = {')
..indent();
for (final entry in schema.definitions.entries.sortedBy((e) => e.key)) {
final name = entry.key;
final type = entry.value;
final baseType = type.baseType;
if (baseType?.refName == 'Event') {
final classProperties = schema.propertiesFor(type);
final eventType = classProperties['event']!.literalValue;
buffer.writeIndentedln("${name}Body: '$eventType',");
}
}
buffer
..writeln('};')
..outdent();
}
/// Writes Dart fields for [properties], taking into account whether they are
/// required for [type].
void _writeFields(IndentableStringBuffer buffer, JsonType type,
Map<String, JsonType> properties) {
for (final entry in properties.entries.sortedBy((e) => e.key)) {
final propertyName = entry.key;
final fieldName = _dartSafeName(propertyName);
final propertyType = entry.value;
final isOptional = !type.requiresField(propertyName);
final dartType = propertyType.asDartType(isOptional: isOptional);
_writeDescription(buffer, propertyType.description);
buffer.writeIndentedln('final $dartType $fieldName;');
}
}
/// Writes an expression to deserialize a [valueCode].
///
/// If [type] represents a spec type, it's `fromJson` function will be called.
/// If [type] is a [List], it will be mapped over this function again.
/// If [type] is an union, the appropriate `canParse` functions will be used to
/// determine which `fromJson` function to call.
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 (baseType.isAny || baseType.isSimple) {
buffer.write(valueCode);
if (dartType != 'Object?') {
buffer.write(' as $dartType');
}
} else if (type.isList) {
buffer.write('($valueCode as List$nullOp)$nullOp.map((item) => ');
_writeFromJsonExpression(buffer, type.items!, 'item');
buffer.write(').toList()');
} else if (type.isUnion) {
final types = type.unionTypes;
// Write a check against each type, e.g.:
// x is y ? new Either.tx(x) : (...)
for (var i = 0; i < types.length; i++) {
final isLast = i == types.length - 1;
// For the last item, if we're optional we won't wrap if in a check, as
// the constructor will only be called if canParse() returned true, so
// it'll the only remaining option.
if (!isLast || isOptional) {
_writeTypeCheckCondition(buffer, types[i], valueCode,
isOptional: false);
buffer.write(' ? ');
}
buffer.write('$dartTypeNotNullable.t${i + 1}(');
_writeFromJsonExpression(buffer, types[i], valueCode);
buffer.write(')');
if (!isLast) {
buffer.write(' : ');
} else if (isLast && isOptional) {
buffer.write(' : null');
}
}
} else if (type.isSpecType) {
if (isOptional) {
buffer.write('$valueCode == null ? null : ');
}
buffer.write(
'$dartTypeNotNullable.fromJson($valueCode as Map<String, Object?>)');
} else {
throw 'Unable to type check $valueCode against $type';
}
}
/// Writes a static `fromJson` method that converts an object into a spec type
/// by calling its fromMap constructor.
///
/// This is a helper method used as a tear-off since the constructor cannot be.
void _writeFromJsonStaticMethod(
IndentableStringBuffer buffer,
String name,
) =>
buffer.writeIndentedln(
'static $name fromJson(Map<String, Object?> obj) => $name.fromMap(obj);');
/// Writes a fromMap constructor to construct an object from a JSON map.
void _writeFromMapConstructor(
IndentableStringBuffer buffer,
String name,
JsonType type,
Map<String, JsonType> properties, {
bool callSuper = false,
}) {
buffer.writeIndented('$name.fromMap(Map<String, Object?> obj)');
if (properties.isNotEmpty || callSuper) {
buffer
..writeln(':')
..indent();
var isFirst = true;
for (final entry in properties.entries.sortedBy((e) => e.key)) {
if (isFirst) {
isFirst = false;
} else {
buffer.writeln(',');
}
final propertyName = entry.key;
final fieldName = _dartSafeName(propertyName);
final propertyType = entry.value;
final isOptional = !type.requiresField(propertyName);
buffer.writeIndented('$fieldName = ');
_writeFromJsonExpression(buffer, propertyType, "obj['$propertyName']",
isOptional: isOptional);
}
if (callSuper) {
if (!isFirst) {
buffer.writeln(',');
}
buffer.writeIndented('super.fromMap(obj)');
}
buffer.outdent();
}
buffer.writeln(';');
}
/// Writes a toJson method to construct a JSON map for this class, recursively
/// calling through base classes.
void _writeToJsonMethod(
IndentableStringBuffer buffer,
String name,
JsonType type,
Map<String, JsonType> properties, {
bool callSuper = false,
}) {
if (callSuper) {
buffer.writeIndentedln('@override');
}
buffer
..writeIndentedln('Map<String, Object?> toJson() => {')
..indent();
if (callSuper) {
buffer.writeIndentedln('...super.toJson(),');
}
for (final entry in properties.entries.sortedBy((e) => e.key)) {
final propertyName = entry.key;
final fieldName = _dartSafeName(propertyName);
final isOptional = !type.requiresField(propertyName);
buffer.writeIndented('');
if (isOptional) {
buffer.write('if ($fieldName != null) ');
}
buffer.writeln("'$propertyName': $fieldName, ");
}
buffer
..outdent()
..writeIndentedln('};');
}
/// Writes an expression that checks whether [valueCode] represents a [type].
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
// code is easier to read.
final opBang = invert ? '!' : '';
final opTrue = invert ? 'false' : 'true';
final opIs = invert ? 'is!' : 'is';
final opEquals = invert ? '!=' : '==';
final opAnd = invert ? '||' : '&&';
final opOr = invert ? '&&' : '||';
final opEvery = invert ? 'any' : 'every';
if (baseType.isAny) {
buffer.write(opTrue);
} else if (dartType == 'Null') {
buffer.write('$valueCode $opEquals null');
} else if (baseType.isSimple) {
buffer.write('$valueCode $opIs $dartType');
} else if (type.isList) {
buffer.write('($valueCode $opIs List');
buffer.write(' $opAnd ($valueCode.$opEvery((item) => ');
_writeTypeCheckCondition(buffer, type.items!, 'item',
isOptional: false, invert: invert);
buffer.write('))');
buffer.write(')');
} else if (type.isUnion) {
final types = type.unionTypes;
// To type check a union, we recursively check against each of its types.
buffer.write('(');
for (var i = 0; i < types.length; i++) {
if (i != 0) {
buffer.write(' $opOr ');
}
_writeTypeCheckCondition(buffer, types[i], valueCode,
isOptional: false, invert: invert);
}
if (isOptional) {
buffer.write(' $opOr $valueCode $opEquals null');
}
buffer.write(')');
} else if (type.isSpecType) {
buffer.write('$opBang${type.asDartType()}.canParse($valueCode)');
} else {
throw 'Unable to type check $valueCode against $type';
}
}
/// Writes the description for [type], looking at the base type from the
/// DAP spec if necessary.
void _writeTypeDescription(IndentableStringBuffer buffer, JsonType type) {
// In the DAP spec, many of the descriptions are on one of the allOf types
// rather than the type itself.
final description = type.description ??
type.allOf
?.firstWhereOrNull((element) => element.description != null)
?.description;
_writeDescription(buffer, description);
}
}
/// A [StringBuffer] with support for indenting.
class IndentableStringBuffer extends StringBuffer {
int _indentLevel = 0;
final int _indentSpaces = 2;
int get totalIndent => _indentLevel * _indentSpaces;
String get _indentString => ' ' * totalIndent;
void indent() => _indentLevel++;
void outdent() => _indentLevel--;
void writeIndented(Object obj) {
write(_indentString);
write(obj);
}
void writeIndentedln(Object obj) {
write(_indentString);
writeln(obj);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,23 +0,0 @@
debugAdapterProtocol.json is an unmodified copy of the DAP Specification,
downloaded from:
https://raw.githubusercontent.com/microsoft/debug-adapter-protocol/gh-pages/debugAdapterProtocol.json
The licence for this file is included below. This accompanying file is the
version of the specification that was used to generate a portion of the Dart
code used to support the protocol.
To regenerate the generated code, run the script in "tool/dap/generate_all.dart"
with no arguments. To download the latest version of the specification before
regenerating the code, run the same script with the "--download" argument.
---
Copyright (c) Microsoft Corporation.
All rights reserved.
Distributed under the following terms:
1. Documentation is licensed under the Creative Commons Attribution 3.0 United States License. Code is licensed under the MIT License.
2. This license does not grant you rights to use any trademarks or logos of Microsoft. For Microsofts general trademark guidelines, go to http://go.microsoft.com/fwlink/?LinkID=254653

View file

@ -1,97 +0,0 @@
// Copyright (c) 2021, 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 'dart:convert';
import 'dart:io';
import 'package:args/args.dart';
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as path;
import 'codegen.dart';
import 'json_schema.dart';
Future<void> main(List<String> arguments) async {
final args = argParser.parse(arguments);
if (args[argHelp]) {
print(argParser.usage);
return;
}
if (args[argDownload]) {
await downloadSpec();
}
final schemaContent = await File(specFile).readAsString();
final schemaJson = jsonDecode(schemaContent);
final schema = JsonSchema.fromJson(schemaJson);
final buffer = IndentableStringBuffer();
CodeGenerator().writeAll(buffer, schema);
final generatedCode = buffer.toString();
await File(generatedCodeFile)
.writeAsString('$codeFileHeader\n$generatedCode');
// Format the generated code.
await Process.run(Platform.resolvedExecutable, ['format', generatedCodeFile]);
}
const argDownload = 'download';
const argHelp = 'help';
const codeFileHeader = '''
// Copyright (c) 2021, 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.
// This code was auto-generated by tool/dap/generate_all.dart; do not hand-edit!
// ignore_for_file: prefer_void_to_null
import 'protocol_common.dart';
import 'protocol_special.dart';
''';
final argParser = ArgParser()
..addFlag(argHelp, hide: true)
..addFlag(argDownload,
negatable: false,
abbr: 'd',
help: 'Download latest version of the DAP spec before generating types');
final generatedCodeFile =
path.join(toolFolder, '../../lib/src/dap/protocol_generated.dart');
final licenseFile = path.join(specFolder, 'debugAdapterProtocol.license.txt');
final specFile = path.join(specFolder, 'debugAdapterProtocol.json');
final specFolder = path.join(toolFolder, 'external_dap_spec');
final specLicenseUri = Uri.parse(
'https://raw.githubusercontent.com/microsoft/debug-adapter-protocol/main/License.txt');
final specUri = Uri.parse(
'https://raw.githubusercontent.com/microsoft/debug-adapter-protocol/gh-pages/debugAdapterProtocol.json');
final toolFolder = path.dirname(Platform.script.toFilePath());
Future<void> downloadSpec() async {
final specResp = await http.get(specUri);
final licenseResp = await http.get(specLicenseUri);
assert(specResp.statusCode == 200);
assert(licenseResp.statusCode == 200);
final licenseHeader = '''
debugAdapterProtocol.json is an unmodified copy of the DAP Specification,
downloaded from:
$specUri
The licence for this file is included below. This accompanying file is the
version of the specification that was used to generate a portion of the Dart
code used to support the protocol.
To regenerate the generated code, run the script in "tool/dap/generate_all.dart"
with no arguments. To download the latest version of the specification before
regenerating the code, run the same script with the "--download" argument.
---
''';
await File(specFile).writeAsString(specResp.body);
await File(licenseFile).writeAsString('$licenseHeader\n${licenseResp.body}');
}

View file

@ -1,90 +0,0 @@
// Copyright (c) 2021, 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:dap/dap.dart';
class JsonSchema {
late final Uri dollarSchema;
late final Map<String, JsonType> definitions;
JsonSchema.fromJson(Map<String, Object?> json) {
dollarSchema = Uri.parse(json[r'$schema'] as String);
definitions = (json['definitions'] as Map<String, Object?>).map((key,
value) =>
MapEntry(key, JsonType.fromJson(this, value as Map<String, Object?>)));
}
}
class JsonType {
final JsonSchema root;
final List<JsonType>? allOf;
final List<JsonType>? oneOf;
final String? description;
final String? dollarRef;
final List<String>? enumValues;
final JsonType? items;
final Map<String, JsonType>? properties;
final List<String>? required;
final String? title;
final Either2<String, List<String>>? type;
JsonType.empty(this.root)
: allOf = null,
oneOf = null,
description = null,
dollarRef = null,
enumValues = null,
items = null,
properties = null,
required = null,
title = null,
type = null;
JsonType.fromJson(this.root, Map<String, Object?> json)
: allOf = json['allOf'] == null
? null
: (json['allOf'] as List<Object?>)
.cast<Map<String, Object?>>()
.map((item) => JsonType.fromJson(root, item))
.toList(),
description = json['description'] as String?,
dollarRef = json[r'$ref'] as String?,
enumValues = (json['enum'] as List<Object?>?)?.cast<String>(),
items = json['items'] == null
? null
: JsonType.fromJson(root, json['items'] as Map<String, Object?>),
oneOf = json['oneOf'] == null
? null
: (json['oneOf'] as List<Object?>)
.cast<Map<String, Object?>>()
.map((item) => JsonType.fromJson(root, item))
.toList(),
properties = json['properties'] == null
? null
: (json['properties'] as Map<String, Object?>).map((key, value) =>
MapEntry(key,
JsonType.fromJson(root, value as Map<String, Object?>))),
required = (json['required'] as List<Object?>?)?.cast<String>(),
title = json['title'] as String?,
type = json['type'] == null
? null
: json['type'] is String
? Either2<String, List<String>>.t1(json['type'] as String)
: Either2<String, List<String>>.t2(
(json['type'] as List<Object?>).cast<String>());
/// Creates a dummy type to represent a type that exists outside of the
/// generated code (in 'lib/src/dap/protocol_common.dart').
JsonType.named(this.root, String name)
: allOf = null,
oneOf = null,
description = null,
dollarRef = '#/definitions/$name',
enumValues = null,
items = null,
properties = null,
required = null,
title = null,
type = null;
}

View file

@ -1,172 +0,0 @@
// Copyright (c) 2021, 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:collection/collection.dart';
import 'json_schema.dart';
String _toDartType(String type) {
if (type.startsWith('#/definitions/')) {
return type.replaceAll('#/definitions/', '');
}
switch (type) {
case 'object':
return 'Map<String, Object?>';
case 'integer':
return 'int';
case 'number':
return 'num';
case 'string':
return 'String';
case 'boolean':
return 'bool';
case 'null':
return 'Null';
default:
return type;
}
}
String _toDartUnionType(List<String> types) {
const allLiteralTypes = {
'array',
'boolean',
'integer',
'null',
'number',
'object',
'string'
};
if (types.length == 7 && allLiteralTypes.containsAll(types)) {
return 'Object';
}
return 'Either${types.length}<${types.map(_toDartType).join(', ')}>';
}
extension JsonSchemaExtensions on JsonSchema {
JsonType typeFor(JsonType type) => type.dollarRef != null
// TODO(dantup): Do we need to support more than just refs to definitions?
? definitions[type.refName]!
: type;
Map<String, JsonType> propertiesFor(JsonType type,
{bool includeBase = true}) {
// Merge this types direct properties with anything from the included
// (allOf) types, but excluding those that come from the base class.
final baseType = type.baseType;
final includedBaseTypes =
(type.allOf ?? []).where((t) => includeBase || t != baseType);
final properties = {
for (final other in includedBaseTypes) ...propertiesFor(typeFor(other)),
...?type.properties,
};
return properties;
}
}
extension JsonTypeExtensions on JsonType {
String asDartType({bool isOptional = false}) {
final dartType = dollarRef != null
? _toDartType(dollarRef!)
: oneOf != null
? _toDartUnionType(oneOf!.map((item) => item.asDartType()).toList())
: type == null
? refName
: type!.valueEquals('array')
? 'List<${items!.asDartType()}>'
: type!.map(_toDartType, _toDartUnionType);
return isOptional ? '$dartType?' : dartType;
}
/// Whether this type can have any type of value (Object/dynamic/any).
bool get isAny => asDartType() == 'Object';
/// Whether this type represents a List.
bool get isList => type?.valueEquals('array') ?? false;
/// 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
/// deserialization (such as `String`, `bool`, `int`, `Map<String, Object?>`).
bool get isSimple {
const dartSimpleTypes = {
'bool',
'int',
'num',
'String',
'Map<String, Object?>',
'Null',
};
return type != null &&
dartSimpleTypes.contains(type!.map(_toDartType, _toDartUnionType));
}
/// Whether this type is a Union type using JSON schema's "oneOf" of where its
/// [type] is a list of types.
bool get isUnion =>
oneOf != null || type != null && type!.map((_) => false, (_) => true);
/// Whether this type is a reference to another spec type (using `dollarRef`).
bool get isSpecType => dollarRef != null;
/// Whether [propertyName] is a required for this type or its base types.
bool requiresField(String propertyName) {
if (required?.contains(propertyName) ?? false) {
return true;
}
if (allOf?.any((type) => root.typeFor(type).requiresField(propertyName)) ??
false) {
return true;
}
return false;
}
/// The name of the type that this one references.
String get refName => dollarRef!.replaceAll('#/definitions/', '');
/// The literal value of this type, if it can have only one.
///
/// These are represented in the spec using an enum with only a single value.
String? get literalValue => enumValues?.singleOrNull;
/// The base type for this type. Base types are inferred by a type using
/// allOf and the first listed type being a reference (dollarRef) to another
/// spec type.
JsonType? get baseType {
final all = allOf;
if (all != null && all.length > 1 && all.first.dollarRef != null) {
return all.first;
}
return null;
}
/// The list of possible types allowed by this union.
///
/// May be represented using `oneOf` or a list of types in `type`.
List<JsonType> get unionTypes {
final types = oneOf ??
// Fabricate a union for types where "type" is an array of literal types:
// ['a', 'b']
type!.map(
(_) => throw 'unexpected non-union in isUnion condition',
(types) =>
types.map((t) => JsonType.fromJson(root, {'type': t})).toList(),
)!;
return types;
}
}