Use less static types, just check names and library URIs of declarations.

Also adds some additional "parallelizatoin" of async tasks.

Overall speedup of >50%, definitions phase in particular is about 75% faster.

Before:

[time: 16043 ms]
(name: <scheduler>, count: 0, elapsed: 0:00:00.000000, elapsedSelf: -0:00:15.823859)
  (name: analyzeFile, count: 104, elapsed: 0:00:15.823859, elapsedSelf: 0:00:01.428470)
    (name: libraryContext, count: 104, elapsed: 0:00:14.395389, elapsedSelf: 0:00:00.272344)(bytesPut: 5386317, cycleCount: 9, libraryCount: 115)
      (name: link, count: 9, elapsed: 0:00:11.027694, elapsedSelf: 0:00:00.529548)
        (name: computeLibraryScopes, count: 9, elapsed: 0:00:00.305524, elapsedSelf: 0:00:00.126633)
          (name: buildMacroApplier, count: 9, elapsed: 0:00:00.178642, elapsedSelf: 0:00:00.178642)
          (name: executeMacroTypesPhase, count: 9, elapsed: 0:00:00.000249, elapsedSelf: 0:00:00.000249)
        (name: executeMacroDeclarationsPhase, count: 9, elapsed: 0:00:00.367608, elapsedSelf: 0:00:00.367608)(constructorsOf: 100, methodsOf: 100)
        (name: executeMacroDefinitionsPhase, count: 9, elapsed: 0:00:09.226594, elapsedSelf: 0:00:00.002483)
          (name: executeDefinitionsPhase, count: 315, elapsed: 0:00:08.188795, elapsedSelf: 0:00:08.188795)(constructorsOf: 300, methodsOf: 300, resolve: 42000, typeDeclarationOf: 1200)
          (name: addMacroResults, count: 200, elapsed: 0:00:01.035316, elapsedSelf: 0:00:01.035316)
        (name: mergeMacroAugmentations, count: 9, elapsed: 0:00:00.598420, elapsedSelf: 0:00:00.598420)
      (name: macroCompileKernel, count: 1, elapsed: 0:00:03.095351, elapsedSelf: 0:00:03.095351)

After:

[time: 7774 ms]
(name: <scheduler>, count: 0, elapsed: 0:00:00.000000, elapsedSelf: -0:00:07.582758)
  (name: analyzeFile, count: 104, elapsed: 0:00:07.582758, elapsedSelf: 0:00:01.049135)
    (name: libraryContext, count: 104, elapsed: 0:00:06.533623, elapsedSelf: 0:00:00.232088)(bytesPut: 5385998, cycleCount: 9, libraryCount: 115)
      (name: link, count: 9, elapsed: 0:00:03.198967, elapsedSelf: 0:00:00.309712)
        (name: computeLibraryScopes, count: 9, elapsed: 0:00:00.184852, elapsedSelf: 0:00:00.079394)
          (name: buildMacroApplier, count: 9, elapsed: 0:00:00.105297, elapsedSelf: 0:00:00.105297)
          (name: executeMacroTypesPhase, count: 9, elapsed: 0:00:00.000161, elapsedSelf: 0:00:00.000161)
        (name: executeMacroDeclarationsPhase, count: 9, elapsed: 0:00:00.301573, elapsedSelf: 0:00:00.301573)(constructorsOf: 100, methodsOf: 100)
        (name: executeMacroDefinitionsPhase, count: 9, elapsed: 0:00:01.823845, elapsedSelf: 0:00:00.001162)
          (name: executeDefinitionsPhase, count: 315, elapsed: 0:00:01.101564, elapsedSelf: 0:00:01.101564)(constructorsOf: 300, methodsOf: 300, resolve: 400, typeDeclarationOf: 41200)
          (name: addMacroResults, count: 200, elapsed: 0:00:00.721119, elapsedSelf: 0:00:00.721119)
        (name: mergeMacroAugmentations, count: 9, elapsed: 0:00:00.578985, elapsedSelf: 0:00:00.578985)
      (name: macroCompileKernel, count: 1, elapsed: 0:00:03.102568, elapsedSelf: 0:00:03.102568)

Change-Id: I88f390ec01469e96ca8a43a49897e3e5b173b731
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/362041
Commit-Queue: Jake Macdonald <jakemac@google.com>
Auto-Submit: Jake Macdonald <jakemac@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Jake Macdonald 2024-04-09 19:56:01 +00:00 committed by Commit Queue
parent c2f3418251
commit ddca3d2386

View file

@ -38,13 +38,15 @@ macro class JsonSerializable implements ClassDeclarationsMacro {
Severity.error));
}
var map = await builder.resolveIdentifier(_dartCore, 'Map');
var string = NamedTypeAnnotationCode(
name: await builder.resolveIdentifier(_dartCore, 'String'));
var object = NamedTypeAnnotationCode(
name: await builder.resolveIdentifier(_dartCore, 'Object'));
var mapStringObject = NamedTypeAnnotationCode(
name: map, typeArguments: [string, object.asNullable]);
var [map, string, object] = await Future.wait([
builder.resolveIdentifier(_dartCore, 'Map'),
builder.resolveIdentifier(_dartCore, 'String'),
builder.resolveIdentifier(_dartCore, 'Object'),
]);
var mapStringObject = NamedTypeAnnotationCode(name: map, typeArguments: [
NamedTypeAnnotationCode(name: string),
NamedTypeAnnotationCode(name: object).asNullable
]);
var jsonSerializableUri = clazz.jsonSerializableUri;
@ -84,39 +86,40 @@ macro class FromJson implements ConstructorDefinitionMacro {
as ClassDeclaration;
var superclass = clazz.superclass;
var superclassHasFromJson = false;
if (superclass != null &&
!await (await builder
.resolve(NamedTypeAnnotationCode(name: superclass.identifier)))
.isExactly(fromJsonData.objectType)) {
if (superclass != null) {
var superclassDeclaration =
await builder.typeDeclarationOf(superclass.identifier);
var superclassConstructors =
await builder.constructorsOf(superclassDeclaration);
for (var superConstructor in superclassConstructors) {
if (superConstructor.identifier.name == 'fromJson') {
await _checkValidFromJson(superConstructor, fromJsonData, builder);
superclassHasFromJson = true;
break;
if (!superclassDeclaration.isExactly('Object', _dartCore)) {
var superclassConstructors =
await builder.constructorsOf(superclassDeclaration);
for (var superConstructor in superclassConstructors) {
if (superConstructor.identifier.name == 'fromJson') {
await _checkValidFromJson(superConstructor, fromJsonData, builder);
superclassHasFromJson = true;
break;
}
}
if (!superclassHasFromJson) {
throw new DiagnosticException(Diagnostic(
DiagnosticMessage(
'Serialization of classes that extend other classes is only '
'supported if those classes have a valid '
'`fromJson(Map<String, Object?> json)` constructor.',
target: superclass.asDiagnosticTarget),
Severity.error));
}
}
if (!superclassHasFromJson) {
throw new DiagnosticException(Diagnostic(
DiagnosticMessage(
'Serialization of classes that extend other classes is only '
'supported if those classes have a valid '
'`fromJson(Map<String, Object?> json)` constructor.',
target: superclass.asDiagnosticTarget),
Severity.error));
}
}
var fields = await builder.fieldsOf(clazz);
var jsonParam = constructor.positionalParameters.single.identifier;
var initializers = <Code>[];
for (var field in fields) {
var config = await field.readConfig(builder);
Future<Code> _initializerForField(FieldDeclaration field) async {
var config = field.metadata.isEmpty
? _FieldConfig(field, null)
: await field.readConfig(builder);
var defaultValue = config.defaultValue;
initializers.add(RawCode.fromParts([
return RawCode.fromParts([
field.identifier,
' = ',
if (defaultValue != null) ...[
@ -139,8 +142,9 @@ macro class FromJson implements ConstructorDefinitionMacro {
' : ',
defaultValue,
],
]));
]);
}
var initializers = await Future.wait(fields.map(_initializerForField));
if (superclassHasFromJson) {
initializers.add(RawCode.fromParts([
@ -205,13 +209,7 @@ macro class FromJson implements ConstructorDefinitionMacro {
"throw 'Unable to deserialize type ${type.code.debugString}'");
}
// The static type of the expected type, without any type arguments.
var typeDeclType = await builder.resolve(
NamedTypeAnnotationCode(name: typeDecl.identifier, typeArguments: [
for (var typeParam in typeDecl.typeParameters)
typeParam.bound?.code ?? fromJsonData.objectCode.asNullable,
]));
if (await typeDeclType.isExactly(fromJsonData.listType)) {
if (typeDecl.isExactly('List', _dartCore)) {
return RawCode.fromParts([
'[ for (var item in ',
jsonReference,
@ -222,7 +220,7 @@ macro class FromJson implements ConstructorDefinitionMacro {
RawCode.fromString('item'), builder, fromJsonData),
']',
]);
} else if (await typeDeclType.isExactly(fromJsonData.setType)) {
} else if (typeDecl.isExactly('Set', _dartCore)) {
return RawCode.fromParts([
'{ for (var item in ',
jsonReference,
@ -233,7 +231,7 @@ macro class FromJson implements ConstructorDefinitionMacro {
RawCode.fromString('item'), builder, fromJsonData),
'}',
]);
} else if (await typeDeclType.isExactly(fromJsonData.mapType)) {
} else if (typeDecl.isExactly('Map', _dartCore)) {
return RawCode.fromParts([
'{ for (var entry in ',
jsonReference,
@ -348,30 +346,21 @@ final class _FromJsonData {
final NamedTypeAnnotationCode jsonListCode;
final NamedTypeAnnotationCode jsonMapCode;
final StaticType jsonMapType;
final StaticType listType;
final StaticType mapType;
final NamedTypeAnnotationCode objectCode;
final StaticType objectType;
final StaticType setType;
_FromJsonData({
required this.jsonListCode,
required this.jsonMapCode,
required this.jsonMapType,
required this.listType,
required this.mapType,
required this.objectCode,
required this.objectType,
required this.setType,
});
static Future<_FromJsonData> build(
ConstructorDefinitionBuilder builder) async {
var [list, map, object, set, string] = await Future.wait([
var [list, map, object, string] = await Future.wait([
builder.resolveIdentifier(_dartCore, 'List'),
builder.resolveIdentifier(_dartCore, 'Map'),
builder.resolveIdentifier(_dartCore, 'Object'),
builder.resolveIdentifier(_dartCore, 'Set'),
builder.resolveIdentifier(_dartCore, 'String'),
]);
var objectCode = NamedTypeAnnotationCode(name: object);
@ -383,27 +372,13 @@ final class _FromJsonData {
NamedTypeAnnotationCode(name: string),
nullableObjectCode,
]);
var [jsonMapType, listType, mapType, objectType, setType] =
await Future.wait([
builder.resolve(jsonMapCode),
builder.resolve(NamedTypeAnnotationCode(
name: list, typeArguments: [nullableObjectCode])),
builder.resolve(NamedTypeAnnotationCode(
name: map, typeArguments: [nullableObjectCode, nullableObjectCode])),
builder.resolve(objectCode),
builder.resolve(NamedTypeAnnotationCode(
name: set, typeArguments: [nullableObjectCode])),
]);
var jsonMapType = await builder.resolve(jsonMapCode);
return _FromJsonData(
jsonListCode: jsonListCode,
jsonMapCode: jsonMapCode,
jsonMapType: jsonMapType,
listType: listType,
mapType: mapType,
objectCode: objectCode,
objectType: objectType,
setType: setType,
);
}
}
@ -424,31 +399,30 @@ macro class ToJson implements MethodDefinitionMacro {
as ClassDeclaration;
var superclass = clazz.superclass;
var superclassHasToJson = false;
if (superclass != null &&
!await (await builder
.resolve(NamedTypeAnnotationCode(name: superclass.identifier)))
.isExactly(toJsonData.objectType)) {
if (superclass != null) {
var superclassDeclaration =
await builder.typeDeclarationOf(superclass.identifier);
var superclassMethods = await builder.methodsOf(superclassDeclaration);
for (var superMethod in superclassMethods) {
if (superMethod.identifier.name == 'toJson') {
if (!(await _checkValidToJson(superMethod, toJsonData, builder))) {
return;
if (!superclassDeclaration.isExactly('Object', _dartCore)) {
var superclassMethods = await builder.methodsOf(superclassDeclaration);
for (var superMethod in superclassMethods) {
if (superMethod.identifier.name == 'toJson') {
if (!(await _checkValidToJson(superMethod, toJsonData, builder))) {
return;
}
superclassHasToJson = true;
break;
}
superclassHasToJson = true;
break;
}
}
if (!superclassHasToJson) {
builder.report(Diagnostic(
DiagnosticMessage(
'Serialization of classes that extend other classes is only '
'supported if those classes have a valid '
'`Map<String, Object?> toJson()` method.',
target: superclass.asDiagnosticTarget),
Severity.error));
return;
if (!superclassHasToJson) {
builder.report(Diagnostic(
DiagnosticMessage(
'Serialization of classes that extend other classes is only '
'supported if those classes have a valid '
'`Map<String, Object?> toJson()` method.',
target: superclass.asDiagnosticTarget),
Severity.error));
return;
}
}
}
@ -466,8 +440,12 @@ macro class ToJson implements MethodDefinitionMacro {
],
';\n '
];
for (var field in fields) {
var config = await field.readConfig(builder);
Future<Code> _addEntryForField(FieldDeclaration field) async {
var parts = <Object>[];
var config = field.metadata.isEmpty
? _FieldConfig(field, null)
: await field.readConfig(builder);
var doNullCheck = !config.includeIfNull && field.type.isNullable;
if (doNullCheck) {
// TODO: Compare == `null` instead, once we can resolve `null`.
@ -490,7 +468,10 @@ macro class ToJson implements MethodDefinitionMacro {
if (doNullCheck) {
parts.add(' }\n');
}
return RawCode.fromParts(parts);
}
parts.addAll(await Future.wait(fields.map(_addEntryForField)));
parts.add(' return json;\n }');
builder.augment(FunctionBodyCode.fromParts(parts));
@ -547,14 +528,9 @@ macro class ToJson implements MethodDefinitionMacro {
"throw 'Unable to serialize type ${type.code.debugString}'");
}
var typeDeclType = await builder.resolve(
NamedTypeAnnotationCode(name: typeDecl.identifier, typeArguments: [
for (var typeParam in typeDecl.typeParameters)
typeParam.bound?.code ?? toJsonData.objectCode.asNullable,
]));
// If it is a List/Set type, serialize it as a JSON list.
if (await typeDeclType.isExactly(toJsonData.listType) ||
await typeDeclType.isExactly(toJsonData.setType)) {
if (typeDecl.isExactly('List', _dartCore) ||
typeDecl.isExactly('Set', _dartCore)) {
return RawCode.fromParts([
'[ for (var item in ',
valueReference,
@ -564,7 +540,7 @@ macro class ToJson implements MethodDefinitionMacro {
']',
]);
// If it is a Map type, serialize it as a JSON map.
} else if (await typeDeclType.isExactly(toJsonData.mapType)) {
} else if (typeDecl.isExactly('Map', _dartCore)) {
return RawCode.fromParts([
'{ for (var entry in ',
valueReference,
@ -595,60 +571,37 @@ macro class ToJson implements MethodDefinitionMacro {
final class _ToJsonData {
final StaticType jsonMapType;
final StaticType listType;
final StaticType mapType;
final Identifier nullIdentifier;
final NamedTypeAnnotationCode objectCode;
final StaticType objectType;
final StaticType setType;
final NamedTypeAnnotationCode stringCode;
_ToJsonData({
required this.jsonMapType,
required this.listType,
required this.mapType,
required this.nullIdentifier,
required this.objectCode,
required this.objectType,
required this.setType,
required this.stringCode,
});
static Future<_ToJsonData> build(FunctionDefinitionBuilder builder) async {
var [list, map, nullIdentifier, object, set, string] = await Future.wait([
builder.resolveIdentifier(_dartCore, 'List'),
var [map, nullIdentifier, object, string] = await Future.wait([
builder.resolveIdentifier(_dartCore, 'Map'),
builder.resolveIdentifier(_dartCore, 'Null'),
builder.resolveIdentifier(_dartCore, 'Object'),
builder.resolveIdentifier(_dartCore, 'Set'),
builder.resolveIdentifier(_dartCore, 'String'),
]);
var objectCode = NamedTypeAnnotationCode(name: object);
var stringCode = NamedTypeAnnotationCode(name: string);
var nullableObjectCode = objectCode.asNullable;
var [jsonMapType, listType, mapType, objectType, setType] =
await Future.wait([
builder.resolve(NamedTypeAnnotationCode(name: map, typeArguments: [
stringCode,
nullableObjectCode,
])),
builder.resolve(NamedTypeAnnotationCode(
name: list, typeArguments: [nullableObjectCode])),
builder.resolve(NamedTypeAnnotationCode(
name: map, typeArguments: [nullableObjectCode, nullableObjectCode])),
builder.resolve(objectCode),
builder.resolve(NamedTypeAnnotationCode(
name: set, typeArguments: [nullableObjectCode])),
]);
var jsonMapType = await builder
.resolve(NamedTypeAnnotationCode(name: map, typeArguments: [
stringCode,
nullableObjectCode,
]));
return _ToJsonData(
jsonMapType: jsonMapType,
listType: listType,
mapType: mapType,
nullIdentifier: nullIdentifier,
objectCode: objectCode,
objectType: objectType,
setType: setType,
stringCode: stringCode,
);
}
@ -693,3 +646,8 @@ extension _RelativeUris on Declaration {
Uri get jsonSerializableUri => library.uri.resolve('json_serializable.dart');
}
extension _IsExactly on TypeDeclaration {
bool isExactly(String name, Uri library) =>
identifier.name == name && this.library.uri == library;
}