blob: 8cb3b96316f5333a22ae0faf79eb3783bd542e77 [file] [log] [blame]
// Copyright (c) 2024, 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 'constant.dart';
import 'helper.dart';
import 'identifier.dart';
import 'location.dart' show Location;
const _loadingUnitKey = 'loading_unit';
/// A reference to *something*.
///
/// The something might be a call or an instance, matching a [CallReference] or
/// an [InstanceReference].
/// All references have in common that they occur in a [loadingUnit], which we
/// record to be able to piece together which loading units are "related", for
/// example all needing the same asset.
sealed class Reference {
final String? loadingUnit;
final Location location;
const Reference({required this.loadingUnit, required this.location});
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is Reference &&
other.loadingUnit == loadingUnit &&
other.location == location;
}
@override
int get hashCode => Object.hash(loadingUnit, location);
Map<String, Object?> toJson(
Map<Constant, int> constants,
Map<Location, int> locations,
) => {_loadingUnitKey: loadingUnit, _locationKey: locations[location]};
}
const _locationKey = '@';
const _positionalKey = 'positional';
const _namedKey = 'named';
const _typeKey = 'type';
/// A reference to a call to some [Identifier].
///
/// This might be an actual call, in which case we record the arguments, or a
/// tear-off, in which case we can't record the arguments.
sealed class CallReference extends Reference {
const CallReference({required super.loadingUnit, required super.location});
static CallReference fromJson(
Map<String, Object?> json,
List<Constant> constants,
List<Location> locations,
) {
final loadingUnit = json[_loadingUnitKey] as String?;
final location = locations[json[_locationKey] as int];
return json[_typeKey] == 'tearoff'
? CallTearOff(loadingUnit: loadingUnit, location: location)
: CallWithArguments(
positionalArguments:
(json[_positionalKey] as List<dynamic>? ?? [])
.whereType<int?>()
.map((index) {
return index != null ? constants[index] : null;
})
.toList(),
namedArguments: (json[_namedKey] as Map<String, Object?>? ?? {}).map(
(key, value) =>
MapEntry(key, value != null ? constants[value as int] : null),
),
loadingUnit: loadingUnit,
location: location,
);
}
}
/// A reference to a call to some [Identifier] with [positionalArguments] and
/// [namedArguments].
final class CallWithArguments extends CallReference {
final List<Constant?> positionalArguments;
final Map<String, Constant?> namedArguments;
const CallWithArguments({
required this.positionalArguments,
required this.namedArguments,
required super.loadingUnit,
required super.location,
});
@override
Map<String, Object?> toJson(
Map<Constant, int> constants,
Map<Location, int> locations,
) {
final positionalJson =
positionalArguments.map((constant) => constants[constant]).toList();
final namedJson = namedArguments.map(
(name, constant) => MapEntry(name, constants[constant]),
);
return {
_typeKey: 'with_arguments',
if (positionalJson.isNotEmpty) _positionalKey: positionalJson,
if (namedJson.isNotEmpty) _namedKey: namedJson,
...super.toJson(constants, locations),
};
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (!(super == other)) return false;
return other is CallWithArguments &&
deepEquals(other.positionalArguments, positionalArguments) &&
deepEquals(other.namedArguments, namedArguments);
}
@override
int get hashCode => Object.hash(
deepHash(positionalArguments),
deepHash(namedArguments),
super.hashCode,
);
}
/// A reference to a tear-off use of the [Identifier]. This means that we can't
/// record the arguments possibly passed to the method somewhere else.
final class CallTearOff extends CallReference {
const CallTearOff({required super.loadingUnit, required super.location});
@override
Map<String, Object?> toJson(
Map<Constant, int> constants,
Map<Location, int> locations,
) {
return {_typeKey: 'tearoff', ...super.toJson(constants, locations)};
}
}
final class InstanceReference extends Reference {
final InstanceConstant instanceConstant;
const InstanceReference({
required this.instanceConstant,
required super.loadingUnit,
required super.location,
});
static const _constantKey = 'constant_index';
factory InstanceReference.fromJson(
Map<String, Object?> json,
List<Constant> constants,
List<Location> locations,
) {
return InstanceReference(
instanceConstant:
constants[json[_constantKey] as int] as InstanceConstant,
loadingUnit: json[_loadingUnitKey] as String?,
location: locations[json[_locationKey] as int],
);
}
@override
Map<String, Object?> toJson(
Map<Constant, int> constants,
Map<Location, int> locations,
) => {
_constantKey: constants[instanceConstant]!,
...super.toJson(constants, locations),
};
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (!(super == other)) return false;
return other is InstanceReference &&
other.instanceConstant == instanceConstant;
}
@override
int get hashCode => Object.hash(instanceConstant, super.hashCode);
}