blob: 8ffe56de11ce12194e62142e8621e2c91b1efc97 [file] [log] [blame]
// Copyright (c) 2012, 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.
part of dart.core;
/// Representation of the invocation of a member on an object.
///
/// This is the type of objects passed to [Object.noSuchMethod] when
/// an object doesn't support the member invocation that was attempted
/// on it.
abstract class Invocation {
Invocation();
/// Creates an invocation corresponding to a method invocation.
///
/// The method invocation has no type arguments.
/// If the named arguments are omitted, they default to no named arguments.
factory Invocation.method(
Symbol memberName, Iterable<Object?>? positionalArguments,
[Map<Symbol, Object?>? namedArguments]) =>
_Invocation.method(memberName, null, positionalArguments, namedArguments);
/// Creates an invocation corresponding to a generic method invocation.
///
/// If [typeArguments] is `null` or empty, the constructor is equivalent to
/// calling [Invocation.method] with the remaining arguments.
/// All the individual type arguments must be non-null.
///
/// If the named arguments are omitted, they default to no named arguments.
factory Invocation.genericMethod(Symbol memberName,
Iterable<Type>? typeArguments, Iterable<Object?>? positionalArguments,
[Map<Symbol, Object?>? namedArguments]) =>
_Invocation.method(
memberName, typeArguments, positionalArguments, namedArguments);
/// Creates an invocation corresponding to a getter invocation.
factory Invocation.getter(Symbol name) = _Invocation.getter;
/// Creates an invocation corresponding to a setter invocation.
///
/// This constructor accepts any [Symbol] as [memberName], but remember that
/// *actual setter names* end in `=`, so the invocation corresponding
/// to `object.member = value` is
/// ```dart
/// Invocation.setter(const Symbol("member="), value)
/// ```
factory Invocation.setter(Symbol memberName, Object? argument) =
_Invocation.setter;
/// The name of the invoked member.
Symbol get memberName;
/// An unmodifiable view of the type arguments of the call.
///
/// If the member is a getter, setter or operator,
/// the type argument list is always empty.
List<Type> get typeArguments => const <Type>[];
/// An unmodifiable view of the positional arguments of the call.
///
/// If the member is a getter, the positional arguments list is
/// always empty.
List<dynamic> get positionalArguments;
/// An unmodifiable view of the named arguments of the call.
///
/// If the member is a getter, setter or operator,
/// the named arguments map is always empty.
Map<Symbol, dynamic> get namedArguments;
/// Whether the invocation was a method call.
bool get isMethod;
/// Whether the invocation was a getter call.
/// If so, all three types of arguments lists are empty.
bool get isGetter;
/// Whether the invocation was a setter call.
///
/// If so, [positionalArguments] has exactly one positional
/// argument, [namedArguments] is empty, and typeArguments is
/// empty.
bool get isSetter;
/// Whether the invocation was a getter or a setter call.
bool get isAccessor => isGetter || isSetter;
}
/// Implementation of [Invocation] used by its factory constructors.
class _Invocation implements Invocation {
final Symbol memberName;
final List<Type> typeArguments;
// Positional arguments is `null` for getters only.
final List<Object?>? _positional;
// Named arguments is `null` for accessors only.
final Map<Symbol, Object?>? _named;
_Invocation.method(this.memberName, Iterable<Type>? types,
Iterable<Object?>? positional, Map<Symbol, Object?>? named)
: typeArguments = _ensureNonNullTypes(types),
_positional = positional == null
? const <Object?>[]
: List<Object?>.unmodifiable(positional),
_named = (named == null || named.isEmpty)
? const <Symbol, Object?>{}
: Map<Symbol, Object?>.unmodifiable(named);
_Invocation.getter(this.memberName)
: typeArguments = const <Type>[],
_positional = null,
_named = null;
_Invocation.setter(this.memberName, Object? argument)
: typeArguments = const <Type>[],
_positional = List<Object?>.unmodifiable([argument]),
_named = null;
List<dynamic> get positionalArguments => _positional ?? const <Object>[];
Map<Symbol, dynamic> get namedArguments => _named ?? const <Symbol, Object>{};
bool get isMethod => _named != null;
bool get isGetter => _positional == null;
bool get isSetter => _positional != null && _named == null;
bool get isAccessor => _named == null;
/// Checks that the elements of [types] are not null.
static List<Type> _ensureNonNullTypes(Iterable<Type>? types) {
if (types == null) return const <Type>[];
List<Type> typeArguments = List<Type>.unmodifiable(types);
for (int i = 0; i < typeArguments.length; i++) {
if (typeArguments[i] == null) {
throw ArgumentError.value(types, "types",
"Type arguments must be non-null, was null at index $i.");
}
}
return typeArguments;
}
}