dart-sdk/pkg/dart2wasm/lib/param_info.dart
Aske Simon Christensen 35e35f5786 [dart2wasm] Support different default values within the same selector
Default parameter values are implemented in dart2wasm via caller-side
substitution. When the same parameter has different default values
across different implementations within the same selector, a
special sentinel value is passed by the caller, and the callee
checks for this value and substitutes the actual default value.

Change-Id: I8235145f93c2aee7e9ef603456380253b836fcef
Cq-Include-Trybots: luci.dart.try:dart2wasm-linux-x64-d8-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/259040
Commit-Queue: Aske Simon Christensen <askesc@google.com>
Reviewed-by: Joshua Litt <joshualitt@google.com>
2022-09-14 13:43:52 +00:00

111 lines
3.9 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:dart2wasm/reference_extensions.dart';
import 'package:kernel/ast.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 Member? member;
int typeParamCount = 0;
late final List<Constant?> positional;
late final Map<String, Constant?> named;
// Do not access these until the info 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(VariableDeclaration 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.fromMember(Reference target) : member = target.asMember {
FunctionNode? function = member!.function;
if (target.isTearOffReference) {
positional = [];
named = {};
} else if (function != null) {
typeParamCount = (member is Constructor
? member!.enclosingClass!.typeParameters
: function.typeParameters)
.length;
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]);
});
named = {
for (VariableDeclaration param in function.namedParameters)
param.name!: defaultValue(param)
};
} else {
// A setter parameter has no default value.
positional = [if (target.isSetter) null];
named = {};
}
}
ParameterInfo.fromLocalFunction(FunctionNode function) : member = null {
typeParamCount = function.typeParameters.length;
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]);
});
named = {
for (VariableDeclaration param in function.namedParameters)
param.name!: defaultValue(param)
};
}
void merge(ParameterInfo other) {
assert(typeParamCount == other.typeParamCount);
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;
}
}
}
}
}