blob: 277e29ba4ebff0c3bb79503fdde9fa7b1bd2102a [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;
import 'syntax.g.dart';
/// 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,
) => _toSyntax(constants, locations).json;
JsonObjectSyntax _toSyntax(
Map<Constant, int> constants,
Map<Location, int> locations,
);
}
/// 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,
) => _fromSyntax(CallSyntax.fromJson(json), constants, locations);
static CallReference _fromSyntax(
CallSyntax syntax,
List<Constant> constants,
List<Location> locations,
) {
final locationIndex = syntax.at;
final location = locationIndex == null ? null : locations[locationIndex];
return switch (syntax) {
TearoffCallSyntax() => CallTearOff(
loadingUnit: syntax.loadingUnit,
location: location,
),
WithArgumentsCallSyntax(
:final named,
:final positional,
:final loadingUnit,
) =>
CallWithArguments(
positionalArguments: (positional ?? [])
.map(
(constantsIndex) =>
constantsIndex != null ? constants[constantsIndex] : null,
)
.toList(),
namedArguments: (named ?? {}).map(
(name, constantsIndex) => MapEntry(name, constants[constantsIndex]),
),
loadingUnit: loadingUnit,
location: location,
),
_ => throw UnimplementedError('Unknown CallSyntax type'),
};
}
@override
CallSyntax _toSyntax(
Map<Constant, int> constants,
Map<Location, int> locations,
);
}
/// 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
WithArgumentsCallSyntax _toSyntax(
Map<Constant, int> constants,
Map<Location, int> locations,
) {
final namedArgs = <String, int>{};
for (final entry in namedArguments.entries) {
if (entry.value != null) {
final index = constants[entry.value!];
if (index != null) {
namedArgs[entry.key] = index;
}
}
}
return WithArgumentsCallSyntax(
at: locations[location]!,
loadingUnit: loadingUnit!,
named: namedArgs.isNotEmpty ? namedArgs : null,
positional: positionalArguments.isEmpty
? null
: positionalArguments.map((constant) => constants[constant]).toList(),
);
}
@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
TearoffCallSyntax _toSyntax(
Map<Constant, int> constants,
Map<Location, int> locations,
) => TearoffCallSyntax(at: locations[location]!, loadingUnit: loadingUnit!);
}
final class InstanceReference extends Reference {
final InstanceConstant instanceConstant;
const InstanceReference({
required this.instanceConstant,
required super.loadingUnit,
required super.location,
});
factory InstanceReference.fromJson(
Map<String, Object?> json,
List<Constant> constants,
List<Location> locations,
) => _fromSyntax(InstanceSyntax.fromJson(json), constants, locations);
static InstanceReference _fromSyntax(
InstanceSyntax syntax,
List<Constant> constants,
List<Location> locations,
) {
final locationIndex = syntax.at;
final location = locationIndex == null ? null : locations[locationIndex];
return InstanceReference(
instanceConstant: constants[syntax.constantIndex] as InstanceConstant,
loadingUnit: syntax.loadingUnit,
location: location,
);
}
@override
InstanceSyntax _toSyntax(
Map<Constant, int> constants,
Map<Location, int> locations,
) => InstanceSyntax(
at: locations[location]!,
constantIndex: constants[instanceConstant]!,
loadingUnit: loadingUnit!,
);
@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);
}
/// Package private (protected) methods for [CallReference].
///
/// This avoids bloating the public API and public API docs and prevents
/// internal types from leaking from the API.
extension CallReferenceProtected on CallReference {
CallSyntax toSyntax(
Map<Constant, int> constants,
Map<Location, int> locations,
) => _toSyntax(constants, locations);
static CallReference fromSyntax(
CallSyntax syntax,
List<Constant> constants,
List<Location> locations,
) => CallReference._fromSyntax(syntax, constants, locations);
}
/// Package private (protected) methods for [InstanceReference].
///
/// This avoids bloating the public API and public API docs and prevents
/// internal types from leaking from the API.
extension InstanceReferenceProtected on InstanceReference {
InstanceSyntax toSyntax(
Map<Constant, int> constants,
Map<Location, int> locations,
) => _toSyntax(constants, locations);
static InstanceReference fromSyntax(
InstanceSyntax syntax,
List<Constant> constants,
List<Location> locations,
) => InstanceReference._fromSyntax(syntax, constants, locations);
}