80c9d57250
This is a step towards split variables from their declarations as part of encoding variables and their usage more precisely. TEST=existing Change-Id: I4a0eeb2947bdebce3667afda4e6cfbfdf5d7de18 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/504201 Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Nicholas Shahan <nshahan@google.com> Reviewed-by: Chloe Stefantsova <cstefantsova@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com>
160 lines
5.2 KiB
Dart
160 lines
5.2 KiB
Dart
// 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 'package:kernel/ast.dart';
|
|
|
|
import 'reference_extensions.dart';
|
|
|
|
/// Information about optional parameters and their default values for a member
|
|
/// or a set of members belonging to the same override group.
|
|
class ParameterInfo {
|
|
final int typeParamCount;
|
|
|
|
/// Default values of optional positonal parameters. `positional[i] == null`
|
|
/// means positional parameter `i` is not optional.
|
|
final List<Constant?> positional;
|
|
|
|
/// Default values of named parameters. Similar to [positional], `null` means
|
|
/// the parameter is not optional.
|
|
final Map<String, Constant?> named;
|
|
|
|
final bool takesContextOrReceiver;
|
|
|
|
// Dispatch table builder updates `ParameterInfo`s, do not access late fields
|
|
// until the `ParameterInfo` is complete.
|
|
late final List<String> names = named.keys.toList()..sort();
|
|
|
|
late final Map<String, int> nameIndex = {
|
|
for (int i = 0; i < names.length; i++) names[i]: positional.length + i,
|
|
};
|
|
|
|
/// A special marker value to use for default parameter values to indicate
|
|
/// that different implementations within the same selector have different
|
|
/// default values.
|
|
static final Constant defaultValueSentinel = UnevaluatedConstant(
|
|
InvalidExpression("Default value sentinel"),
|
|
);
|
|
|
|
int get paramCount => positional.length + named.length;
|
|
|
|
static Constant? _defaultValue(Variable param) {
|
|
Expression? initializer = param.initializer;
|
|
if (initializer is ConstantExpression) {
|
|
return initializer.constant;
|
|
} else if (initializer == null) {
|
|
return null;
|
|
} else {
|
|
throw "Non-constant default value";
|
|
}
|
|
}
|
|
|
|
ParameterInfo._(
|
|
this.takesContextOrReceiver,
|
|
this.typeParamCount,
|
|
this.positional,
|
|
this.named,
|
|
);
|
|
|
|
factory ParameterInfo.fromMember(
|
|
Reference target,
|
|
bool useDefaultValueSentinel,
|
|
) {
|
|
final member = target.asMember; // Constructor, Field, or Procedure
|
|
assert(!member.isAbstract || useDefaultValueSentinel);
|
|
final function = member.function;
|
|
|
|
if (target.isTearOffReference) {
|
|
// Tear-off getters don't take type parameters even if the member is
|
|
// generic.
|
|
return ParameterInfo._(true, 0, [], {});
|
|
}
|
|
|
|
if (function != null) {
|
|
// Constructor, or static or instance method.
|
|
assert(member is Constructor || member is Procedure);
|
|
|
|
final typeParamCount =
|
|
(member is Constructor
|
|
? member.enclosingClass.typeParameters
|
|
: function.typeParameters)
|
|
.length;
|
|
|
|
final positional = List.generate(function.positionalParameters.length, (
|
|
i,
|
|
) {
|
|
// A required parameter has no default value.
|
|
if (i < function.requiredParameterCount) return null;
|
|
if (useDefaultValueSentinel) return defaultValueSentinel;
|
|
return _defaultValue(function.positionalParameters[i])!;
|
|
});
|
|
|
|
final named = {
|
|
for (Variable param in function.namedParameters)
|
|
if (param.isRequired)
|
|
param.name!: null
|
|
else
|
|
param.name!: useDefaultValueSentinel
|
|
? defaultValueSentinel
|
|
: _defaultValue(param)!,
|
|
};
|
|
|
|
return ParameterInfo._(
|
|
member.isInstanceMember,
|
|
typeParamCount,
|
|
positional,
|
|
named,
|
|
);
|
|
}
|
|
|
|
// A setter or getter. A setter parameter has no default value.
|
|
assert(member is Field);
|
|
return ParameterInfo._(true, 0, [if (target.isSetter) null], {});
|
|
}
|
|
|
|
factory ParameterInfo.fromLocalFunction(FunctionNode function) {
|
|
final typeParamCount = function.typeParameters.length;
|
|
final positional = List.generate(function.positionalParameters.length, (i) {
|
|
// A required parameter has no default value.
|
|
if (i < function.requiredParameterCount) return null;
|
|
return _defaultValue(function.positionalParameters[i]);
|
|
});
|
|
final named = {
|
|
for (Variable param in function.namedParameters)
|
|
param.name!: _defaultValue(param),
|
|
};
|
|
return ParameterInfo._(true, typeParamCount, positional, named);
|
|
}
|
|
|
|
void merge(ParameterInfo other) {
|
|
assert(typeParamCount == other.typeParamCount);
|
|
assert(takesContextOrReceiver == other.takesContextOrReceiver);
|
|
for (int i = 0; i < other.positional.length; i++) {
|
|
if (i >= positional.length) {
|
|
positional.add(other.positional[i]);
|
|
} else {
|
|
if (positional[i] == null) {
|
|
positional[i] = other.positional[i];
|
|
} else if (other.positional[i] != null) {
|
|
if (positional[i] != other.positional[i]) {
|
|
// Default value differs between implementations.
|
|
positional[i] = defaultValueSentinel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (String name in other.named.keys) {
|
|
Constant? value = named[name];
|
|
Constant? otherValue = other.named[name];
|
|
if (value == null) {
|
|
named[name] = otherValue;
|
|
} else if (otherValue != null) {
|
|
if (value != otherValue) {
|
|
// Default value differs between implementations.
|
|
named[name] = defaultValueSentinel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|