blob: 40a5d33c84fb844cbd935a52919b390ad5df41f8 [file] [log] [blame]
// Copyright (c) 2022, 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:async';
import 'package:_fe_analyzer_shared/src/macros/api.dart';
import 'introspect_shared.dart';
const introspectMacro = IntrospectDeclarationsPhaseMacro();
const introspectMacroX = IntrospectDeclarationsPhaseMacro(
withDetailsFor: {'X'},
);
/*macro*/ class IntrospectDeclarationsPhaseMacro
implements ClassDeclarationsMacro {
final Set<Object?> withDetailsFor;
const IntrospectDeclarationsPhaseMacro({
this.withDetailsFor = const {},
});
@override
Future<void> buildDeclarationsForClass(
IntrospectableClassDeclaration declaration,
ClassMemberDeclarationBuilder builder,
) async {
final printer = _DeclarationPrinter(
withDetailsFor: withDetailsFor.cast(),
typeIntrospector: builder,
identifierResolver: builder,
typeDeclarationResolver: builder,
typeResolver: builder,
);
await printer.writeClassDeclaration(declaration);
final text = printer._sink.toString();
final resultName = 'introspect_${declaration.identifier.name}';
builder.declareInLibrary(
DeclarationCode.fromString(
'const $resultName = r"""$text""";',
),
);
}
}
class _DeclarationPrinter {
final Set<String> withDetailsFor;
final TypeIntrospector typeIntrospector;
final IdentifierResolver identifierResolver;
final TypeDeclarationResolver typeDeclarationResolver;
final TypeResolver typeResolver;
final StringBuffer _sink = StringBuffer();
String _indent = '';
Identifier? _enclosingDeclarationIdentifier;
_DeclarationPrinter({
required this.withDetailsFor,
required this.typeIntrospector,
required this.identifierResolver,
required this.typeDeclarationResolver,
required this.typeResolver,
});
Future<void> writeClassDeclaration(IntrospectableClassDeclaration e) async {
_sink.write(_indent);
_writeIf(e.isAbstract, 'abstract ');
_writeIf(e.isExternal, 'external ');
_writeln('class ${e.identifier.name}');
if (!_shouldWriteDetailsFor(e)) {
return;
}
await _withIndent(() async {
final superclass = e.superclass == null
? null
: await typeDeclarationResolver.declarationOf(
e.superclass!.identifier) as IntrospectableClassDeclaration;
if (superclass != null) {
_writelnWithIndent('superclass');
await _withIndent(() => writeClassDeclaration(superclass));
}
await _writeTypeParameters(e.typeParameters);
await _writeTypeAnnotations('mixins', e.mixins);
await _writeTypeAnnotations('interfaces', e.interfaces);
_enclosingDeclarationIdentifier = e.identifier;
await _writeElements<FieldDeclaration>(
'fields',
await typeIntrospector.fieldsOf(e),
_writeField,
);
});
}
void _assertEnclosingClass(ClassMemberDeclaration e) {
if (e.definingClass != _enclosingDeclarationIdentifier) {
throw StateError('Mismatch: definingClass');
}
}
bool _shouldWriteDetailsFor(Declaration declaration) {
return withDetailsFor.isEmpty ||
withDetailsFor.contains(declaration.identifier.name);
}
Future<void> _withIndent(Future<void> Function() f) async {
final savedIndent = _indent;
_indent = '$savedIndent ';
await f();
_indent = savedIndent;
}
Future<void> _writeElements<T>(
String name,
Iterable<T> elements,
Future<void> Function(T) f,
) async {
if (elements.isNotEmpty) {
_writelnWithIndent(name);
await _withIndent(() async {
for (final element in elements) {
await f(element);
}
});
}
}
Future<void> _writeField(FieldDeclaration e) async {
_assertEnclosingClass(e);
_writeIndentedLine(() {
_writeIf(e.isStatic, 'static ');
_writeIf(e.isExternal, 'external ');
_writeIf(e.isLate, 'late ');
_writeIf(e.isFinal, 'final ');
_writeName(e);
});
await _withIndent(() async {
_writeTypeAnnotation('type', e.type);
});
}
void _writeIf(bool flag, String str) {
if (flag) {
_sink.write(str);
}
}
void _writeIndentedLine(void Function() f) {
_sink.write(_indent);
f();
_sink.writeln();
}
void _writeln(String line) {
_sink.writeln(line);
}
void _writelnWithIndent(String line) {
_sink.write(_indent);
_sink.writeln(line);
}
void _writeName(Declaration e) {
_sink.write(e.identifier.name);
}
void _writeTypeAnnotation(String name, TypeAnnotation? type) {
_sink.write(_indent);
_sink.write('$name: ');
if (type != null) {
_writeln(type.asString);
} else {
_writeln('null');
}
}
Future<void> _writeTypeAnnotationLine(TypeAnnotation type) async {
_writelnWithIndent(type.asString);
}
Future<void> _writeTypeAnnotations(
String name,
Iterable<TypeAnnotation> elements,
) async {
await _writeElements(name, elements, _writeTypeAnnotationLine);
}
Future<void> _writeTypeParameter(TypeParameterDeclaration e) async {
_writelnWithIndent(e.identifier.name);
await _withIndent(() async {
final bound = e.bound;
if (bound != null) {
_writeTypeAnnotation('bound', bound);
}
});
}
Future<void> _writeTypeParameters(
Iterable<TypeParameterDeclaration> elements,
) async {
await _writeElements('typeParameters', elements, _writeTypeParameter);
}
}