blob: 095e5f355f0d7463acf46daca6fb2d01ae3e1ba7 [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 'dart:collection';
macro
class DataClass implements ClassDeclarationsMacro, ClassDefinitionMacro {
const DataClass();
FutureOr<void> buildDeclarationsForClass(ClassDeclaration clazz,
ClassMemberDeclarationBuilder builder) async {
Uri dartCore = Uri.parse('dart:core');
Identifier objectIdentifier =
await builder.resolveIdentifier(dartCore, 'Object');
Identifier boolIdentifier =
await builder.resolveIdentifier(dartCore, 'bool');
Identifier intIdentifier =
await builder.resolveIdentifier(dartCore, 'int');
Identifier stringIdentifier =
await builder.resolveIdentifier(dartCore, 'String');
List<FieldDeclaration> fields = await builder.fieldsOf(clazz);
List<Object> constructorParts = ['const ', clazz.identifier.name, '({'];
String comma = '';
for (FieldDeclaration field in fields) {
constructorParts.addAll([comma, 'required this.', field.identifier]);
comma = ', ';
}
constructorParts.add('});');
builder.declareInClass(new DeclarationCode.fromParts(constructorParts));
builder.declareInClass(new DeclarationCode.fromParts([
'external ', intIdentifier, ' get hashCode;']));
builder.declareInClass(new DeclarationCode.fromParts([
'external ',
boolIdentifier,
' operator ==(',
objectIdentifier,
' other);'
]));
builder.declareInClass(new DeclarationCode.fromParts([
'external ', stringIdentifier, ' toString();']));
}
FutureOr<void> buildDefinitionForClass(ClassDeclaration clazz,
ClassDefinitionBuilder builder) async {
Uri dartCore = Uri.parse('dart:core');
Identifier identicalIdentifier =
await builder.resolveIdentifier(dartCore, 'identical');
List<FieldDeclaration> fields = await builder.fieldsOf(clazz);
List<MethodDeclaration> methods = await builder.methodsOf(clazz);
FunctionDefinitionBuilder hashCodeBuilder = await builder.buildMethod(
methods
.firstWhere((e) => e.identifier.name == 'hashCode')
.identifier);
FunctionDefinitionBuilder equalsBuilder = await builder.buildMethod(
methods
.firstWhere((e) => e.identifier.name == '==')
.identifier);
FunctionDefinitionBuilder toStringBuilder = await builder.buildMethod(
methods
.firstWhere((e) => e.identifier.name == 'toString')
.identifier);
List<Object> hashCodeParts = ['''{
return '''
];
List<Object> equalsParts = ['''{
if (''', identicalIdentifier, '''(this, other)) return true;
return other is ${clazz.identifier.name}'''
];
List<Object> toStringParts = ['''{
return "${clazz.identifier.name}('''
];
bool first = true;
for (FieldDeclaration field in fields) {
if (!first) {
hashCodeParts.add(' ^ ');
toStringParts.add(',');
}
// TODO(johnniwinther): Generate different code for collection typed
// fields.
hashCodeParts.addAll([field.identifier, '.hashCode']);
equalsParts.addAll(
[' && ', field.identifier, ' == other.', field.identifier]);
toStringParts.addAll(
['${field.identifier.name}=\${', field.identifier, '}']);
first = false;
}
hashCodeParts.add(''';
}''');
equalsParts.add(''';
}''');
toStringParts.add(''')";
}''');
hashCodeBuilder.augment(new FunctionBodyCode.fromParts(hashCodeParts));
equalsBuilder.augment(new FunctionBodyCode.fromParts(equalsParts));
toStringBuilder.augment(new FunctionBodyCode.fromParts(toStringParts));
}
}