mirror of
https://github.com/dart-lang/sdk
synced 2024-07-08 20:16:39 +00:00
Compute constant constructors in resolution.
BUG= R=sigurdm@google.com Review URL: https://codereview.chromium.org//1218793002.
This commit is contained in:
parent
3b34c17909
commit
74d04a6a48
|
@ -18,6 +18,7 @@ import '../elements/elements.dart' show
|
|||
import '../resolution/operators.dart';
|
||||
import '../tree/tree.dart' show DartString;
|
||||
import '../universe/universe.dart' show CallStructure;
|
||||
import '../util/util.dart';
|
||||
import 'values.dart';
|
||||
|
||||
enum ConstantExpressionKind {
|
||||
|
@ -141,6 +142,23 @@ class GenerativeConstantConstructor implements ConstantConstructor{
|
|||
return appliedFieldMap;
|
||||
}
|
||||
|
||||
int get hashCode {
|
||||
int hash = Hashing.objectHash(type);
|
||||
hash = Hashing.mapHash(defaultValues, hash);
|
||||
hash = Hashing.mapHash(fieldMap, hash);
|
||||
return Hashing.objectHash(superConstructorInvocation, hash);
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! GenerativeConstantConstructor) return false;
|
||||
return
|
||||
type == other.type &&
|
||||
superConstructorInvocation == other.superConstructorInvocation &&
|
||||
mapEquals(defaultValues, other.defaultValues) &&
|
||||
mapEquals(fieldMap, other.fieldMap);
|
||||
}
|
||||
|
||||
String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.write("{'type': $type");
|
||||
|
@ -157,6 +175,16 @@ class GenerativeConstantConstructor implements ConstantConstructor{
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
static bool mapEquals(Map map1, Map map2) {
|
||||
if (map1.length != map1.length) return false;
|
||||
for (var key in map1.keys) {
|
||||
if (map1[key] != map2[key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Creates the field-to-constant map from applying [args] to
|
||||
/// [constructorInvocation]. If [constructorInvocation] is `null`, an empty
|
||||
/// map is created.
|
||||
|
@ -205,6 +233,20 @@ class RedirectingGenerativeConstantConstructor implements ConstantConstructor {
|
|||
return appliedFieldMap;
|
||||
}
|
||||
|
||||
int get hashCode {
|
||||
int hash = Hashing.objectHash(thisConstructorInvocation);
|
||||
return Hashing.mapHash(defaultValues, hash);
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! RedirectingGenerativeConstantConstructor) return false;
|
||||
return
|
||||
thisConstructorInvocation == other.thisConstructorInvocation &&
|
||||
GenerativeConstantConstructor.mapEquals(
|
||||
defaultValues, other.defaultValues);
|
||||
}
|
||||
|
||||
String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.write("{'type': ${thisConstructorInvocation.type}");
|
||||
|
@ -240,6 +282,16 @@ class RedirectingFactoryConstantConstructor implements ConstantConstructor {
|
|||
return constantConstructor.computeInstanceFields(arguments, callStructure);
|
||||
}
|
||||
|
||||
int get hashCode {
|
||||
return Hashing.objectHash(targetConstructorInvocation);
|
||||
}
|
||||
|
||||
bool operator ==(other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! RedirectingFactoryConstantConstructor) return false;
|
||||
return targetConstructorInvocation == other.targetConstructorInvocation;
|
||||
}
|
||||
|
||||
String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.write("{");
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'common.dart';
|
|||
import 'elements.dart';
|
||||
import '../constants/expressions.dart';
|
||||
import '../constants/constructors.dart';
|
||||
import '../helpers/helpers.dart';
|
||||
import '../tree/tree.dart';
|
||||
import '../util/util.dart';
|
||||
import '../resolution/resolution.dart';
|
||||
|
@ -1248,16 +1249,16 @@ class VariableList implements DeclarationSite {
|
|||
}
|
||||
|
||||
abstract class ConstantVariableMixin implements VariableElement {
|
||||
ConstantExpression _constant;
|
||||
ConstantExpression constantCache;
|
||||
|
||||
ConstantExpression get constant {
|
||||
if (isPatch) {
|
||||
ConstantVariableMixin originVariable = origin;
|
||||
return originVariable.constant;
|
||||
}
|
||||
assert(invariant(this, _constant != null,
|
||||
assert(invariant(this, constantCache != null,
|
||||
message: "Constant has not been computed for $this."));
|
||||
return _constant;
|
||||
return constantCache;
|
||||
}
|
||||
|
||||
void set constant(ConstantExpression value) {
|
||||
|
@ -1266,9 +1267,9 @@ abstract class ConstantVariableMixin implements VariableElement {
|
|||
originVariable.constant = value;
|
||||
return null;
|
||||
}
|
||||
assert(invariant(this, _constant == null || _constant == value,
|
||||
assert(invariant(this, constantCache == null || constantCache == value,
|
||||
message: "Constant has already been computed for $this."));
|
||||
_constant = value;
|
||||
constantCache = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1966,6 +1967,25 @@ abstract class ConstantConstructorMixin implements ConstructorElement {
|
|||
return _constantConstructor;
|
||||
}
|
||||
|
||||
void set constantConstructor(ConstantConstructor value) {
|
||||
if (isPatch) {
|
||||
ConstantConstructorMixin originConstructor = origin;
|
||||
originConstructor.constantConstructor = value;
|
||||
} else {
|
||||
assert(invariant(this, isConst,
|
||||
message: "Constant constructor set on non-constant "
|
||||
"constructor $this."));
|
||||
assert(invariant(this, !isFromEnvironmentConstructor,
|
||||
message: "Constant constructor set on fromEnvironment "
|
||||
"constructor: $this."));
|
||||
assert(invariant(this,
|
||||
_constantConstructor == null || _constantConstructor == value,
|
||||
message: "Constant constructor already computed for $this:"
|
||||
"Existing: $_constantConstructor, new: $value"));
|
||||
_constantConstructor = value;
|
||||
}
|
||||
}
|
||||
|
||||
bool get isFromEnvironmentConstructor {
|
||||
return name == 'fromEnvironment' &&
|
||||
library.isDartCore &&
|
||||
|
|
|
@ -6,12 +6,18 @@ part of resolution;
|
|||
|
||||
class InitializerResolver {
|
||||
final ResolverVisitor visitor;
|
||||
final Map<Element, Node> initialized;
|
||||
final ConstructorElementX constructor;
|
||||
final FunctionExpression functionNode;
|
||||
final Map<FieldElement, Node> initialized = <FieldElement, Node>{};
|
||||
final Map<FieldElement, ConstantExpression> fieldInitializers =
|
||||
<FieldElement, ConstantExpression>{};
|
||||
Link<Node> initializers;
|
||||
bool hasSuper;
|
||||
bool hasSuper = false;
|
||||
bool isValidAsConstant = true;
|
||||
|
||||
InitializerResolver(this.visitor)
|
||||
: initialized = new Map<Element, Node>(), hasSuper = false;
|
||||
bool get isConst => constructor.isConst;
|
||||
|
||||
InitializerResolver(this.visitor, this.constructor, this.functionNode);
|
||||
|
||||
ResolutionRegistry get registry => visitor.registry;
|
||||
|
||||
|
@ -37,6 +43,7 @@ class InitializerResolver {
|
|||
visitor.compiler.reportInfo(
|
||||
existing,
|
||||
MessageKind.ALREADY_INITIALIZED, {'fieldName': field.name});
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
|
||||
void checkForDuplicateInitializers(FieldElementX field, Node init) {
|
||||
|
@ -54,12 +61,13 @@ class InitializerResolver {
|
|||
initialized[field] = init;
|
||||
}
|
||||
|
||||
void resolveFieldInitializer(FunctionElement constructor, SendSet init) {
|
||||
void resolveFieldInitializer(SendSet init) {
|
||||
// init is of the form [this.]field = value.
|
||||
final Node selector = init.selector;
|
||||
final String name = selector.asIdentifier().source;
|
||||
// Lookup target field.
|
||||
Element target;
|
||||
FieldElement field;
|
||||
if (isFieldInitializer(init)) {
|
||||
target = constructor.enclosingClass.lookupLocalMember(name);
|
||||
if (target == null) {
|
||||
|
@ -72,6 +80,8 @@ class InitializerResolver {
|
|||
selector.asIdentifier(), constructor.enclosingClass);
|
||||
} else if (!target.isInstanceMember) {
|
||||
error(selector, MessageKind.INIT_STATIC_FIELD, {'fieldName': name});
|
||||
} else {
|
||||
field = target;
|
||||
}
|
||||
} else {
|
||||
error(init, MessageKind.INVALID_RECEIVER_IN_INITIALIZER);
|
||||
|
@ -80,38 +90,44 @@ class InitializerResolver {
|
|||
registry.registerStaticUse(target);
|
||||
checkForDuplicateInitializers(target, init);
|
||||
// Resolve initializing value.
|
||||
visitor.visitInStaticContext(init.arguments.head);
|
||||
}
|
||||
|
||||
ClassElement getSuperOrThisLookupTarget(FunctionElement constructor,
|
||||
bool isSuperCall,
|
||||
Node diagnosticNode) {
|
||||
ClassElement lookupTarget = constructor.enclosingClass;
|
||||
if (isSuperCall) {
|
||||
// Calculate correct lookup target and constructor name.
|
||||
if (identical(lookupTarget, visitor.compiler.objectClass)) {
|
||||
error(diagnosticNode, MessageKind.SUPER_INITIALIZER_IN_OBJECT);
|
||||
ResolutionResult result = visitor.visitInStaticContext(
|
||||
init.arguments.head,
|
||||
inConstantInitializer: isConst);
|
||||
if (isConst) {
|
||||
if (result.isConstant && field != null) {
|
||||
// TODO(johnniwinther): Report error if `result.constant` is `null`.
|
||||
fieldInitializers[field] = result.constant;
|
||||
} else {
|
||||
return lookupTarget.supertype.element;
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
}
|
||||
return lookupTarget;
|
||||
}
|
||||
|
||||
Element resolveSuperOrThisForSend(FunctionElement constructor,
|
||||
FunctionExpression functionNode,
|
||||
Send call) {
|
||||
// Resolve the selector and the arguments.
|
||||
visitor.inStaticContext(() {
|
||||
visitor.resolveSelector(call, null);
|
||||
visitor.resolveArguments(call.argumentsNode);
|
||||
});
|
||||
Selector selector = registry.getSelector(call);
|
||||
bool isSuperCall = Initializers.isSuperConstructorCall(call);
|
||||
InterfaceType getSuperOrThisLookupTarget(Node diagnosticNode,
|
||||
{bool isSuperCall}) {
|
||||
if (isSuperCall) {
|
||||
// Calculate correct lookup target and constructor name.
|
||||
if (identical(constructor.enclosingClass, visitor.compiler.objectClass)) {
|
||||
error(diagnosticNode, MessageKind.SUPER_INITIALIZER_IN_OBJECT);
|
||||
isValidAsConstant = false;
|
||||
} else {
|
||||
return constructor.enclosingClass.supertype;
|
||||
}
|
||||
}
|
||||
return constructor.enclosingClass.thisType;
|
||||
}
|
||||
|
||||
ClassElement lookupTarget = getSuperOrThisLookupTarget(constructor,
|
||||
isSuperCall,
|
||||
call);
|
||||
ResolutionResult resolveSuperOrThisForSend(Send call) {
|
||||
// Resolve the selector and the arguments.
|
||||
ArgumentsResult argumentsResult = visitor.inStaticContext(() {
|
||||
visitor.resolveSelector(call, null);
|
||||
return visitor.resolveArguments(call.argumentsNode);
|
||||
}, inConstantInitializer: isConst);
|
||||
|
||||
bool isSuperCall = Initializers.isSuperConstructorCall(call);
|
||||
InterfaceType targetType =
|
||||
getSuperOrThisLookupTarget(call, isSuperCall: isSuperCall);
|
||||
ClassElement lookupTarget = targetType.element;
|
||||
Selector constructorSelector =
|
||||
visitor.getRedirectingThisOrSuperConstructorSelector(call);
|
||||
FunctionElement calledConstructor =
|
||||
|
@ -119,9 +135,8 @@ class InitializerResolver {
|
|||
|
||||
final bool isImplicitSuperCall = false;
|
||||
final String className = lookupTarget.name;
|
||||
verifyThatConstructorMatchesCall(constructor,
|
||||
calledConstructor,
|
||||
selector.callStructure,
|
||||
verifyThatConstructorMatchesCall(calledConstructor,
|
||||
argumentsResult.callStructure,
|
||||
isImplicitSuperCall,
|
||||
call,
|
||||
className,
|
||||
|
@ -129,11 +144,28 @@ class InitializerResolver {
|
|||
|
||||
registry.useElement(call, calledConstructor);
|
||||
registry.registerStaticUse(calledConstructor);
|
||||
return calledConstructor;
|
||||
if (isConst) {
|
||||
if (isValidAsConstant &&
|
||||
calledConstructor.isConst &&
|
||||
argumentsResult.isValidAsConstant) {
|
||||
CallStructure callStructure = argumentsResult.callStructure;
|
||||
List<ConstantExpression> arguments = argumentsResult.constantArguments;
|
||||
return new ConstantResult(
|
||||
call,
|
||||
new ConstructedConstantExpression(
|
||||
targetType,
|
||||
calledConstructor,
|
||||
callStructure,
|
||||
arguments),
|
||||
element: calledConstructor);
|
||||
} else {
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
}
|
||||
return new ResolutionResult.forElement(calledConstructor);
|
||||
}
|
||||
|
||||
void resolveImplicitSuperConstructorSend(FunctionElement constructor,
|
||||
FunctionExpression functionNode) {
|
||||
ConstructedConstantExpression resolveImplicitSuperConstructorSend() {
|
||||
// If the class has a super resolve the implicit super call.
|
||||
ClassElement classElement = constructor.enclosingClass;
|
||||
ClassElement superClass = classElement.superclass;
|
||||
|
@ -141,18 +173,16 @@ class InitializerResolver {
|
|||
assert(superClass != null);
|
||||
assert(superClass.isResolved);
|
||||
|
||||
final bool isSuperCall = true;
|
||||
ClassElement lookupTarget = getSuperOrThisLookupTarget(constructor,
|
||||
isSuperCall,
|
||||
functionNode);
|
||||
InterfaceType targetType =
|
||||
getSuperOrThisLookupTarget(functionNode, isSuperCall: true);
|
||||
ClassElement lookupTarget = targetType.element;
|
||||
Selector constructorSelector = new Selector.callDefaultConstructor();
|
||||
Element calledConstructor = lookupTarget.lookupConstructor(
|
||||
constructorSelector.name);
|
||||
|
||||
final String className = lookupTarget.name;
|
||||
final bool isImplicitSuperCall = true;
|
||||
verifyThatConstructorMatchesCall(constructor,
|
||||
calledConstructor,
|
||||
verifyThatConstructorMatchesCall(calledConstructor,
|
||||
CallStructure.NO_ARGS,
|
||||
isImplicitSuperCall,
|
||||
functionNode,
|
||||
|
@ -160,11 +190,19 @@ class InitializerResolver {
|
|||
constructorSelector);
|
||||
registry.registerImplicitSuperCall(calledConstructor);
|
||||
registry.registerStaticUse(calledConstructor);
|
||||
|
||||
if (isConst && isValidAsConstant) {
|
||||
return new ConstructedConstantExpression(
|
||||
targetType,
|
||||
calledConstructor,
|
||||
CallStructure.NO_ARGS,
|
||||
const <ConstantExpression>[]);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void verifyThatConstructorMatchesCall(
|
||||
FunctionElement caller,
|
||||
ConstructorElementX lookedupConstructor,
|
||||
CallStructure call,
|
||||
bool isImplicitSuperCall,
|
||||
|
@ -181,6 +219,7 @@ class InitializerResolver {
|
|||
: MessageKind.CANNOT_RESOLVE_CONSTRUCTOR;
|
||||
visitor.compiler.reportError(
|
||||
diagnosticNode, kind, {'constructorName': fullConstructorName});
|
||||
isValidAsConstant = false;
|
||||
} else {
|
||||
lookedupConstructor.computeSignature(visitor.compiler);
|
||||
if (!call.signatureApplies(lookedupConstructor.functionSignature)) {
|
||||
|
@ -188,12 +227,14 @@ class InitializerResolver {
|
|||
? MessageKind.NO_MATCHING_CONSTRUCTOR_FOR_IMPLICIT
|
||||
: MessageKind.NO_MATCHING_CONSTRUCTOR;
|
||||
visitor.compiler.reportError(diagnosticNode, kind);
|
||||
} else if (caller.isConst
|
||||
isValidAsConstant = false;
|
||||
} else if (constructor.isConst
|
||||
&& !lookedupConstructor.isConst) {
|
||||
MessageKind kind = isImplicitSuperCall
|
||||
? MessageKind.CONST_CALLS_NON_CONST_FOR_IMPLICIT
|
||||
: MessageKind.CONST_CALLS_NON_CONST;
|
||||
visitor.compiler.reportError(diagnosticNode, kind);
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,16 +243,50 @@ class InitializerResolver {
|
|||
* Resolve all initializers of this constructor. In the case of a redirecting
|
||||
* constructor, the resolved constructor's function element is returned.
|
||||
*/
|
||||
ConstructorElement resolveInitializers(ConstructorElementX constructor,
|
||||
FunctionExpression functionNode) {
|
||||
ConstructorElement resolveInitializers() {
|
||||
Map<dynamic/*String|int*/, ConstantExpression> defaultValues =
|
||||
<dynamic/*String|int*/, ConstantExpression>{};
|
||||
ConstructedConstantExpression constructorInvocation;
|
||||
// Keep track of all "this.param" parameters specified for constructor so
|
||||
// that we can ensure that fields are initialized only once.
|
||||
FunctionSignature functionParameters = constructor.functionSignature;
|
||||
functionParameters.forEachParameter((ParameterElement element) {
|
||||
functionParameters.forEachParameter((ParameterElementX element) {
|
||||
if (isConst) {
|
||||
if (element.isOptional) {
|
||||
if (element.constantCache == null) {
|
||||
// TODO(johnniwinther): Remove this when all constant expressions
|
||||
// can be computed during resolution.
|
||||
isValidAsConstant = false;
|
||||
} else {
|
||||
ConstantExpression defaultValue = element.constant;
|
||||
if (defaultValue != null) {
|
||||
if (element.isNamed) {
|
||||
defaultValues[element.name] = defaultValue;
|
||||
} else {
|
||||
int index =
|
||||
element.functionDeclaration.parameters.indexOf(element);
|
||||
defaultValues[index] = defaultValue;
|
||||
}
|
||||
} else {
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (element.isInitializingFormal) {
|
||||
InitializingFormalElement initializingFormal = element;
|
||||
checkForDuplicateInitializers(initializingFormal.fieldElement,
|
||||
element.initializer);
|
||||
InitializingFormalElementX initializingFormal = element;
|
||||
FieldElement field = initializingFormal.fieldElement;
|
||||
checkForDuplicateInitializers(field, element.initializer);
|
||||
if (isConst) {
|
||||
if (element.isNamed) {
|
||||
fieldInitializers[field] = new NamedArgumentReference(element.name);
|
||||
} else {
|
||||
int index = element.functionDeclaration.parameters.indexOf(element);
|
||||
fieldInitializers[field] = new PositionalArgumentReference(index);
|
||||
}
|
||||
} else {
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -224,7 +299,7 @@ class InitializerResolver {
|
|||
for (Link<Node> link = initializers; !link.isEmpty; link = link.tail) {
|
||||
if (link.head.asSendSet() != null) {
|
||||
final SendSet init = link.head.asSendSet();
|
||||
resolveFieldInitializer(constructor, init);
|
||||
resolveFieldInitializer(init);
|
||||
} else if (link.head.asSend() != null) {
|
||||
final Send call = link.head.asSend();
|
||||
if (call.argumentsNode == null) {
|
||||
|
@ -235,7 +310,14 @@ class InitializerResolver {
|
|||
if (resolvedSuper) {
|
||||
error(call, MessageKind.DUPLICATE_SUPER_INITIALIZER);
|
||||
}
|
||||
resolveSuperOrThisForSend(constructor, functionNode, call);
|
||||
ResolutionResult result = resolveSuperOrThisForSend(call);
|
||||
if (isConst) {
|
||||
if (result.isConstant) {
|
||||
constructorInvocation = result.constant;
|
||||
} else {
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
}
|
||||
resolvedSuper = true;
|
||||
} else if (Initializers.isConstructorRedirect(call)) {
|
||||
// Check that there is no body (Language specification 7.5.1). If the
|
||||
|
@ -256,9 +338,24 @@ class InitializerResolver {
|
|||
if (parameter.isInitializingFormal) {
|
||||
Node node = parameter.node;
|
||||
error(node, MessageKind.INITIALIZING_FORMAL_NOT_ALLOWED);
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
});
|
||||
return resolveSuperOrThisForSend(constructor, functionNode, call);
|
||||
ResolutionResult result = resolveSuperOrThisForSend(call);
|
||||
if (isConst) {
|
||||
if (result.isConstant) {
|
||||
constructorInvocation = result.constant;
|
||||
} else {
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
if (isConst && isValidAsConstant) {
|
||||
constructor.constantConstructor =
|
||||
new RedirectingGenerativeConstantConstructor(
|
||||
defaultValues,
|
||||
constructorInvocation);
|
||||
}
|
||||
}
|
||||
return result.element;
|
||||
} else {
|
||||
visitor.error(call, MessageKind.CONSTRUCTOR_CALL_EXPECTED);
|
||||
return null;
|
||||
|
@ -268,7 +365,14 @@ class InitializerResolver {
|
|||
}
|
||||
}
|
||||
if (!resolvedSuper) {
|
||||
resolveImplicitSuperConstructorSend(constructor, functionNode);
|
||||
constructorInvocation = resolveImplicitSuperConstructorSend();
|
||||
}
|
||||
if (isConst && isValidAsConstant) {
|
||||
constructor.constantConstructor = new GenerativeConstantConstructor(
|
||||
constructor.enclosingClass.thisType,
|
||||
defaultValues,
|
||||
fieldInitializers,
|
||||
constructorInvocation);
|
||||
}
|
||||
return null; // If there was no redirection always return null.
|
||||
}
|
||||
|
|
|
@ -4,6 +4,24 @@
|
|||
|
||||
part of resolution;
|
||||
|
||||
/// The state of constants in resolutions.
|
||||
enum ConstantState {
|
||||
/// Expressions are not required to be constants.
|
||||
NON_CONSTANT,
|
||||
|
||||
/// Expressions are required to be constants.
|
||||
///
|
||||
/// For instance the values of a constant list literal.
|
||||
CONSTANT,
|
||||
|
||||
/// Expressions are required to be constants and parameter references are
|
||||
/// also considered constant.
|
||||
///
|
||||
/// This is used for resolving constructor initializers of constant
|
||||
/// constructors.
|
||||
CONSTANT_INITIALIZER,
|
||||
}
|
||||
|
||||
/**
|
||||
* Core implementation of resolution.
|
||||
*
|
||||
|
@ -23,6 +41,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
bool inInstanceContext;
|
||||
bool inCheckContext;
|
||||
bool inCatchBlock;
|
||||
ConstantState constantState;
|
||||
|
||||
Scope scope;
|
||||
ClassElement currentClass;
|
||||
|
@ -100,6 +119,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
!element.isTypedef &&
|
||||
!element.enclosingElement.isTypedef,
|
||||
inCatchBlock = false,
|
||||
constantState = element.isConst
|
||||
? ConstantState.CONSTANT : ConstantState.NON_CONSTANT,
|
||||
super(compiler, registry);
|
||||
|
||||
CoreTypes get coreTypes => compiler.coreTypes;
|
||||
|
@ -153,14 +174,6 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
return result;
|
||||
}
|
||||
|
||||
inStaticContext(action()) {
|
||||
bool wasInstanceContext = inInstanceContext;
|
||||
inInstanceContext = false;
|
||||
var result = action();
|
||||
inInstanceContext = wasInstanceContext;
|
||||
return result;
|
||||
}
|
||||
|
||||
doInPromotionScope(Node node, action()) {
|
||||
promotionScope = promotionScope.prepend(node);
|
||||
var result = action();
|
||||
|
@ -168,8 +181,47 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
return result;
|
||||
}
|
||||
|
||||
ResolutionResult visitInStaticContext(Node node) {
|
||||
return inStaticContext(() => visit(node));
|
||||
inStaticContext(action(),
|
||||
{bool inConstantInitializer: false}) {
|
||||
bool wasInstanceContext = inInstanceContext;
|
||||
ConstantState oldConstantState = constantState;
|
||||
constantState = inConstantInitializer
|
||||
? ConstantState.CONSTANT_INITIALIZER
|
||||
: constantState;
|
||||
inInstanceContext = false;
|
||||
var result = action();
|
||||
inInstanceContext = wasInstanceContext;
|
||||
constantState = oldConstantState;
|
||||
return result;
|
||||
}
|
||||
|
||||
ResolutionResult visitInStaticContext(Node node,
|
||||
{bool inConstantInitializer: false}) {
|
||||
return inStaticContext(
|
||||
() => visit(node),
|
||||
inConstantInitializer: inConstantInitializer);
|
||||
}
|
||||
|
||||
/// Execute [action] where the constant state is `ConstantState.CONSTANT` if
|
||||
/// not already `ConstantState.CONSTANT_INITIALIZER`.
|
||||
inConstantContext(action()) {
|
||||
ConstantState oldConstantState = constantState;
|
||||
if (constantState != ConstantState.CONSTANT_INITIALIZER) {
|
||||
constantState = ConstantState.CONSTANT;
|
||||
}
|
||||
var result = action();
|
||||
constantState = oldConstantState;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Visit [node] where the constant state is `ConstantState.CONSTANT` if
|
||||
/// not already `ConstantState.CONSTANT_INITIALIZER`.
|
||||
ResolutionResult visitInConstantContext(Node node) {
|
||||
ResolutionResult result = inConstantContext(() => visit(node));
|
||||
assert(invariant(node, result != null,
|
||||
message: "No resolution result for $node."));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ErroneousElement reportAndCreateErroneousElement(
|
||||
|
@ -324,7 +376,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
FunctionSignature functionParameters = function.functionSignature;
|
||||
Link<Node> parameterNodes = (node.parameters == null)
|
||||
? const Link<Node>() : node.parameters.nodes;
|
||||
functionParameters.forEachParameter((ParameterElement element) {
|
||||
functionParameters.forEachParameter((ParameterElementX element) {
|
||||
// TODO(karlklose): should be a list of [FormalElement]s, but the actual
|
||||
// implementation uses [Element].
|
||||
List<Element> optionals = functionParameters.optionalParameters;
|
||||
|
@ -332,7 +384,16 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
NodeList nodes = parameterNodes.head;
|
||||
parameterNodes = nodes.nodes;
|
||||
}
|
||||
visit(element.initializer);
|
||||
if (element.isOptional) {
|
||||
if (element.initializer != null) {
|
||||
ResolutionResult result = visitInConstantContext(element.initializer);
|
||||
if (result.isConstant) {
|
||||
element.constant = result.constant;
|
||||
}
|
||||
} else {
|
||||
element.constant = new NullConstantExpression();
|
||||
}
|
||||
}
|
||||
VariableDefinitions variableDefinitions = parameterNodes.head;
|
||||
Node parameterNode = variableDefinitions.definitions.nodes.head;
|
||||
// Field parameters (this.x) are not visible inside the constructor. The
|
||||
|
@ -340,7 +401,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
if (element.isInitializingFormal) {
|
||||
registry.useElement(parameterNode, element);
|
||||
} else {
|
||||
LocalParameterElement parameterElement = element;
|
||||
LocalParameterElementX parameterElement = element;
|
||||
defineLocalVariable(parameterNode, parameterElement);
|
||||
addToScope(parameterElement);
|
||||
}
|
||||
|
@ -715,8 +776,10 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
return selector;
|
||||
}
|
||||
|
||||
CallStructure resolveArguments(NodeList list) {
|
||||
ArgumentsResult resolveArguments(NodeList list) {
|
||||
if (list == null) return null;
|
||||
bool isValidAsConstant = true;
|
||||
List<ResolutionResult> argumentResults = <ResolutionResult>[];
|
||||
bool oldSendIsMemberAccess = sendIsMemberAccess;
|
||||
sendIsMemberAccess = false;
|
||||
Map<String, Node> seenNamedArguments = new Map<String, Node>();
|
||||
|
@ -724,7 +787,12 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
List<String> namedArguments = <String>[];
|
||||
for (Link<Node> link = list.nodes; !link.isEmpty; link = link.tail) {
|
||||
Expression argument = link.head;
|
||||
visit(argument);
|
||||
ResolutionResult result = visit(argument);
|
||||
if (!result.isConstant) {
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
argumentResults.add(result);
|
||||
|
||||
NamedArgument namedArgument = argument.asNamedArgument();
|
||||
if (namedArgument != null) {
|
||||
String source = namedArgument.name.source;
|
||||
|
@ -734,16 +802,21 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
source,
|
||||
argument,
|
||||
seenNamedArguments[source]);
|
||||
isValidAsConstant = false;
|
||||
} else {
|
||||
seenNamedArguments[source] = namedArgument;
|
||||
}
|
||||
} else if (!seenNamedArguments.isEmpty) {
|
||||
error(argument, MessageKind.INVALID_ARGUMENT_AFTER_NAMED);
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
argumentCount++;
|
||||
}
|
||||
sendIsMemberAccess = oldSendIsMemberAccess;
|
||||
return new CallStructure(argumentCount, namedArguments);
|
||||
return new ArgumentsResult(
|
||||
new CallStructure(argumentCount, namedArguments),
|
||||
argumentResults,
|
||||
isValidAsConstant: isValidAsConstant);
|
||||
}
|
||||
|
||||
void registerTypeLiteralAccess(Send node, Element target) {
|
||||
|
@ -1380,7 +1453,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
message: "Unexpected expression: $node"));
|
||||
Node expression = node.selector;
|
||||
visitExpression(expression);
|
||||
CallStructure callStructure = resolveArguments(node.argumentsNode);
|
||||
CallStructure callStructure =
|
||||
resolveArguments(node.argumentsNode).callStructure;
|
||||
Selector selector = callStructure.callSelector;
|
||||
// TODO(johnniwinther): Remove this when all information goes through the
|
||||
// [SendStructure].
|
||||
|
@ -1398,7 +1472,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
// If this send is of the form "assert(expr);", then
|
||||
// this is an assertion.
|
||||
|
||||
CallStructure callStructure = resolveArguments(node.argumentsNode);
|
||||
CallStructure callStructure =
|
||||
resolveArguments(node.argumentsNode).callStructure;
|
||||
SendStructure sendStructure = const AssertStructure();
|
||||
if (callStructure.argumentCount != 1) {
|
||||
compiler.reportError(
|
||||
|
@ -1430,7 +1505,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
ResolutionResult handleThisAccess(Send node) {
|
||||
AccessSemantics accessSemantics = new AccessSemantics.thisAccess();
|
||||
if (node.isCall) {
|
||||
CallStructure callStructure = resolveArguments(node.argumentsNode);
|
||||
CallStructure callStructure =
|
||||
resolveArguments(node.argumentsNode).callStructure;
|
||||
Selector selector = callStructure.callSelector;
|
||||
// TODO(johnniwinther): Handle invalid this access as an
|
||||
// [AccessSemantics].
|
||||
|
@ -1456,7 +1532,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
Selector selector;
|
||||
CallStructure callStructure = CallStructure.NO_ARGS;
|
||||
if (node.isCall) {
|
||||
callStructure = resolveArguments(node.argumentsNode);
|
||||
callStructure =
|
||||
resolveArguments(node.argumentsNode).callStructure;
|
||||
selector = new Selector(SelectorKind.CALL, name, callStructure);
|
||||
} else {
|
||||
selector = new Selector(SelectorKind.GETTER, name, callStructure);
|
||||
|
@ -1711,7 +1788,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
SendStructure sendStructure;
|
||||
Selector selector;
|
||||
if (node.isCall) {
|
||||
CallStructure callStructure = resolveArguments(node.argumentsNode);
|
||||
CallStructure callStructure =
|
||||
resolveArguments(node.argumentsNode).callStructure;
|
||||
selector = new Selector(SelectorKind.CALL, name, callStructure);
|
||||
registry.registerDynamicInvocation(
|
||||
new UniverseSelector(selector, null));
|
||||
|
@ -1803,7 +1881,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
SendStructure sendStructure;
|
||||
Selector selector;
|
||||
if (node.isCall) {
|
||||
CallStructure callStructure = resolveArguments(node.argumentsNode);
|
||||
CallStructure callStructure =
|
||||
resolveArguments(node.argumentsNode).callStructure;
|
||||
selector = new Selector(SelectorKind.CALL, name, callStructure);
|
||||
registry.registerDynamicInvocation(
|
||||
new UniverseSelector(selector, null));
|
||||
|
@ -1864,16 +1943,13 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
|
||||
/// Handle access of a parameter, local variable or local function.
|
||||
ResolutionResult handleLocalAccess(Send node, Name name, Element element) {
|
||||
ResolutionResult result = const NoneResult();
|
||||
AccessSemantics semantics = computeLocalAccessSemantics(node, element);
|
||||
Selector selector;
|
||||
CallStructure callStructure = CallStructure.NO_ARGS;
|
||||
if (node.isCall) {
|
||||
callStructure = resolveArguments(node.argumentsNode);
|
||||
CallStructure callStructure =
|
||||
resolveArguments(node.argumentsNode).callStructure;
|
||||
selector = new Selector(SelectorKind.CALL, name, callStructure);
|
||||
} else {
|
||||
selector = new Selector(SelectorKind.GETTER, name, callStructure);
|
||||
}
|
||||
if (node.isCall) {
|
||||
bool isIncompatibleInvoke = false;
|
||||
switch (semantics.kind) {
|
||||
case AccessKind.LOCAL_FUNCTION:
|
||||
|
@ -1904,6 +1980,48 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
? new IncompatibleInvokeStructure(semantics, selector)
|
||||
: new InvokeStructure(semantics, selector));
|
||||
} else {
|
||||
switch (semantics.kind) {
|
||||
case AccessKind.LOCAL_VARIABLE:
|
||||
case AccessKind.LOCAL_FUNCTION:
|
||||
result = new ElementResult(element);
|
||||
break;
|
||||
case AccessKind.PARAMETER:
|
||||
case AccessKind.FINAL_PARAMETER:
|
||||
if (constantState == ConstantState.CONSTANT_INITIALIZER) {
|
||||
ParameterElement parameter = element;
|
||||
if (parameter.isNamed) {
|
||||
result = new ConstantResult(
|
||||
node,
|
||||
new NamedArgumentReference(parameter.name),
|
||||
element: element);
|
||||
} else {
|
||||
result = new ConstantResult(
|
||||
node,
|
||||
new PositionalArgumentReference(
|
||||
parameter.functionDeclaration.parameters.indexOf(
|
||||
parameter)),
|
||||
element: element);
|
||||
}
|
||||
} else {
|
||||
result = new ElementResult(element);
|
||||
}
|
||||
break;
|
||||
case AccessKind.FINAL_LOCAL_VARIABLE:
|
||||
if (element.isConst) {
|
||||
result = new ConstantResult(
|
||||
node,
|
||||
new VariableConstantExpression(element),
|
||||
element: element);
|
||||
} else {
|
||||
result = new ElementResult(element);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
internalError(node,
|
||||
"Unexpected local access $semantics.");
|
||||
break;
|
||||
}
|
||||
selector = new Selector(SelectorKind.GETTER, name, CallStructure.NO_ARGS);
|
||||
registry.registerSendStructure(node,
|
||||
new GetStructure(semantics, selector));
|
||||
}
|
||||
|
@ -1915,14 +2033,13 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
|
||||
registerPotentialAccessInClosure(node, element);
|
||||
|
||||
return node.isPropertyAccess
|
||||
? new ElementResult(element) : const NoneResult();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Handle access of a static or top level [element].
|
||||
ResolutionResult handleStaticOrTopLevelAccess(
|
||||
Send node, Name name, Element element) {
|
||||
|
||||
ResolutionResult result = const NoneResult();
|
||||
MemberElement member;
|
||||
if (element.isAbstractField) {
|
||||
AbstractFieldElement abstractField = element;
|
||||
|
@ -1939,16 +2056,14 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
member.computeType(compiler);
|
||||
|
||||
Selector selector;
|
||||
CallStructure callStructure = CallStructure.NO_ARGS;
|
||||
if (node.isCall) {
|
||||
callStructure = resolveArguments(node.argumentsNode);
|
||||
selector = new Selector(SelectorKind.CALL, name, callStructure);
|
||||
} else {
|
||||
selector = new Selector(SelectorKind.GETTER, name, callStructure);
|
||||
}
|
||||
AccessSemantics semantics =
|
||||
computeStaticOrTopLevelAccessSemantics(node, member);
|
||||
if (node.isCall) {
|
||||
ArgumentsResult argumentsResult =
|
||||
resolveArguments(node.argumentsNode);
|
||||
CallStructure callStructure = argumentsResult.callStructure;
|
||||
selector = new Selector(SelectorKind.CALL, name, callStructure);
|
||||
|
||||
bool isIncompatibleInvoke = false;
|
||||
switch (semantics.kind) {
|
||||
case AccessKind.STATIC_METHOD:
|
||||
|
@ -1963,6 +2078,13 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
} else {
|
||||
registry.registerStaticUse(semantics.element);
|
||||
handleForeignCall(node, semantics.element, selector);
|
||||
if (method == compiler.identicalFunction &&
|
||||
argumentsResult.isValidAsConstant) {
|
||||
result = new ConstantResult(node,
|
||||
new IdenticalConstantExpression(
|
||||
argumentsResult.argumentResults[0].constant,
|
||||
argumentsResult.argumentResults[1].constant));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AccessKind.STATIC_FIELD:
|
||||
|
@ -1994,6 +2116,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
? new IncompatibleInvokeStructure(semantics, selector)
|
||||
: new InvokeStructure(semantics, selector));
|
||||
} else {
|
||||
selector = new Selector(SelectorKind.GETTER, name, CallStructure.NO_ARGS);
|
||||
switch (semantics.kind) {
|
||||
case AccessKind.STATIC_METHOD:
|
||||
case AccessKind.TOPLEVEL_METHOD:
|
||||
|
@ -2025,6 +2148,13 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
}
|
||||
registry.registerSendStructure(node,
|
||||
new GetStructure(semantics, selector));
|
||||
if (member.isConst) {
|
||||
FieldElement field = member;
|
||||
result = new ConstantResult(
|
||||
node, new VariableConstantExpression(field), element: field);
|
||||
} else {
|
||||
result = new ElementResult(member);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(johnniwinther): Remove these when all information goes through
|
||||
|
@ -2032,8 +2162,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
registry.useElement(node, member);
|
||||
registry.setSelector(node, selector);
|
||||
|
||||
return node.isPropertyAccess
|
||||
? new ElementResult(member) : const NoneResult();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Handle access to resolved [element].
|
||||
|
@ -2535,6 +2664,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
}
|
||||
ConstructorElementX constructor = enclosingElement;
|
||||
bool isConstConstructor = constructor.isConst;
|
||||
bool isValidAsConstant = isConstConstructor;
|
||||
ConstructorElement redirectionTarget = resolveRedirectingFactory(
|
||||
node, inConstContext: isConstConstructor);
|
||||
constructor.immediateRedirectionTarget = redirectionTarget;
|
||||
|
@ -2554,9 +2684,13 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
if (isConstConstructor &&
|
||||
!redirectionTarget.isConst) {
|
||||
compiler.reportError(node, MessageKind.CONSTRUCTOR_IS_NOT_CONST);
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
if (redirectionTarget == constructor) {
|
||||
compiler.reportError(node, MessageKind.CYCLIC_REDIRECTING_FACTORY);
|
||||
// TODO(johnniwinther): Create constant constructor for this case and
|
||||
// let evaluation detect the cyclicity.
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2571,6 +2705,8 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
if (!isSubtype) {
|
||||
warning(node, MessageKind.NOT_ASSIGNABLE,
|
||||
{'fromType': targetType, 'toType': constructorType});
|
||||
// TODO(johnniwinther): Handle this (potentially) erroneous case.
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
|
||||
redirectionTarget.computeType(compiler);
|
||||
|
@ -2580,6 +2716,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
if (!targetSignature.isCompatibleWith(constructorSignature)) {
|
||||
assert(!isSubtype);
|
||||
registry.registerThrowNoSuchMethod();
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
|
||||
// Register a post process to check for cycles in the redirection chain and
|
||||
|
@ -2595,6 +2732,30 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
if (isSymbolConstructor) {
|
||||
registry.registerSymbolConstructor();
|
||||
}
|
||||
if (isValidAsConstant) {
|
||||
List<String> names = <String>[];
|
||||
List<ConstantExpression> arguments = <ConstantExpression>[];
|
||||
int index = 0;
|
||||
constructorSignature.forEachParameter((ParameterElement parameter) {
|
||||
if (parameter.isNamed) {
|
||||
String name = parameter.name;
|
||||
names.add(name);
|
||||
arguments.add(new NamedArgumentReference(name));
|
||||
} else {
|
||||
arguments.add(new PositionalArgumentReference(index));
|
||||
}
|
||||
index++;
|
||||
});
|
||||
CallStructure callStructure =
|
||||
new CallStructure(constructorSignature.parameterCount, names);
|
||||
constructor.constantConstructor =
|
||||
new RedirectingFactoryConstantConstructor(
|
||||
new ConstructedConstantExpression(
|
||||
type,
|
||||
redirectionTarget,
|
||||
callStructure,
|
||||
arguments));
|
||||
}
|
||||
return const NoneResult();
|
||||
}
|
||||
|
||||
|
@ -2682,12 +2843,19 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
}
|
||||
|
||||
ResolutionResult visitNewExpression(NewExpression node) {
|
||||
bool isValidAsConstant = true;
|
||||
FunctionElement constructor = resolveConstructor(node);
|
||||
final bool isSymbolConstructor = constructor == compiler.symbolConstructor;
|
||||
final bool isMirrorsUsedConstant =
|
||||
node.isConst && (constructor == compiler.mirrorsUsedConstructor);
|
||||
Selector callSelector = resolveSelector(node.send, constructor);
|
||||
resolveArguments(node.send.argumentsNode);
|
||||
ArgumentsResult argumentsResult;
|
||||
if (node.isConst) {
|
||||
argumentsResult =
|
||||
inConstantContext(() => resolveArguments(node.send.argumentsNode));
|
||||
} else {
|
||||
argumentsResult = resolveArguments(node.send.argumentsNode);
|
||||
}
|
||||
registry.useElement(node.send, constructor);
|
||||
if (Elements.isUnresolved(constructor)) {
|
||||
return new ResolutionResult.forElement(constructor);
|
||||
|
@ -2705,12 +2873,14 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
compiler.reportError(node,
|
||||
MessageKind.CANNOT_INSTANTIATE_ENUM,
|
||||
{'enumName': cls.name});
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
|
||||
InterfaceType type = registry.getType(node);
|
||||
if (node.isConst && type.containsTypeVariables) {
|
||||
compiler.reportError(node.send.selector,
|
||||
MessageKind.TYPE_VARIABLE_IN_CONSTANT);
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
// TODO(johniwinther): Avoid registration of `type` in face of redirecting
|
||||
// factory constructors.
|
||||
|
@ -2718,6 +2888,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
if (constructor.isGenerativeConstructor && cls.isAbstract) {
|
||||
warning(node, MessageKind.ABSTRACT_CLASS_INSTANTIATION);
|
||||
registry.registerAbstractClassInstantiation();
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
|
||||
if (isSymbolConstructor) {
|
||||
|
@ -2752,6 +2923,19 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
}
|
||||
if (node.isConst) {
|
||||
analyzeConstantDeferred(node);
|
||||
if (isValidAsConstant &&
|
||||
constructor.isConst &&
|
||||
argumentsResult.isValidAsConstant) {
|
||||
CallStructure callStructure = argumentsResult.callStructure;
|
||||
List<ConstantExpression> arguments = argumentsResult.constantArguments;
|
||||
ConstructedConstantExpression constant =
|
||||
new ConstructedConstantExpression(
|
||||
type,
|
||||
constructor,
|
||||
callStructure,
|
||||
arguments);
|
||||
return new ConstantResult(node, constant);
|
||||
}
|
||||
}
|
||||
|
||||
return const NoneResult();
|
||||
|
@ -2898,14 +3082,16 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
registry.registerRequiredType(listType, enclosingElement);
|
||||
if (node.isConst) {
|
||||
List<ConstantExpression> constantExpressions = <ConstantExpression>[];
|
||||
for (Node element in node.elements) {
|
||||
ResolutionResult elementResult = visit(element);
|
||||
if (isValidAsConstant && elementResult.isConstant) {
|
||||
constantExpressions.add(elementResult.constant);
|
||||
} else {
|
||||
isValidAsConstant = false;
|
||||
inConstantContext(() {
|
||||
for (Node element in node.elements) {
|
||||
ResolutionResult elementResult = visit(element);
|
||||
if (isValidAsConstant && elementResult.isConstant) {
|
||||
constantExpressions.add(elementResult.constant);
|
||||
} else {
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
analyzeConstantDeferred(node);
|
||||
sendIsMemberAccess = false;
|
||||
if (isValidAsConstant) {
|
||||
|
@ -3197,20 +3383,23 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> {
|
|||
registry.registerMapLiteral(node, mapType, node.isConst);
|
||||
registry.registerRequiredType(mapType, enclosingElement);
|
||||
if (node.isConst) {
|
||||
|
||||
List<ConstantExpression> keyExpressions = <ConstantExpression>[];
|
||||
List<ConstantExpression> valueExpressions = <ConstantExpression>[];
|
||||
for (LiteralMapEntry entry in node.entries) {
|
||||
ResolutionResult keyResult = visit(entry.key);
|
||||
ResolutionResult valueResult = visit(entry.value);
|
||||
if (isValidAsConstant &&
|
||||
keyResult.isConstant &&
|
||||
valueResult.isConstant) {
|
||||
keyExpressions.add(keyResult.constant);
|
||||
valueExpressions.add(valueResult.constant);
|
||||
} else {
|
||||
isValidAsConstant = false;
|
||||
inConstantContext(() {
|
||||
for (LiteralMapEntry entry in node.entries) {
|
||||
ResolutionResult keyResult = visit(entry.key);
|
||||
ResolutionResult valueResult = visit(entry.value);
|
||||
if (isValidAsConstant &&
|
||||
keyResult.isConstant &&
|
||||
valueResult.isConstant) {
|
||||
keyExpressions.add(keyResult.constant);
|
||||
valueExpressions.add(valueResult.constant);
|
||||
} else {
|
||||
isValidAsConstant = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
analyzeConstantDeferred(node);
|
||||
sendIsMemberAccess = false;
|
||||
if (isValidAsConstant) {
|
||||
|
|
|
@ -160,9 +160,9 @@ class ResolverTask extends CompilerTask {
|
|||
if (element.isGenerativeConstructor) {
|
||||
// Even if there is no initializer list we still have to do the
|
||||
// resolution in case there is an implicit super constructor call.
|
||||
InitializerResolver resolver = new InitializerResolver(visitor);
|
||||
FunctionElement redirection =
|
||||
resolver.resolveInitializers(element, tree);
|
||||
InitializerResolver resolver =
|
||||
new InitializerResolver(visitor, element, tree);
|
||||
FunctionElement redirection = resolver.resolveInitializers();
|
||||
if (redirection != null) {
|
||||
resolveRedirectingConstructor(resolver, tree, element, redirection);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ abstract class ResolutionResult {
|
|||
}
|
||||
|
||||
ResultKind get kind;
|
||||
Node get node => null;
|
||||
Element get element => null;
|
||||
DartType get type => null;
|
||||
ConstantExpression get constant => null;
|
||||
|
@ -64,11 +65,16 @@ class AssertResult extends ResolutionResult {
|
|||
String toString() => 'AssertResult()';
|
||||
}
|
||||
|
||||
/// The result for resolving a constant expression.
|
||||
class ConstantResult extends ResolutionResult {
|
||||
final Node node;
|
||||
final ConstantExpression constant;
|
||||
final Element element;
|
||||
|
||||
ConstantResult(this.node, this.constant);
|
||||
/// Creates a result for the [constant] expression. [node] is provided for
|
||||
/// error reporting on the constant and [element] is provided if the
|
||||
/// expression additionally serves an [Element] like [ElementResult].
|
||||
ConstantResult(this.node, this.constant, {this.element});
|
||||
|
||||
bool get isConstant => true;
|
||||
|
||||
|
@ -83,4 +89,31 @@ class NoneResult extends ResolutionResult {
|
|||
ResultKind get kind => ResultKind.NONE;
|
||||
|
||||
String toString() => 'NoneResult()';
|
||||
}
|
||||
|
||||
/// The result of resolving a list of arguments.
|
||||
class ArgumentsResult {
|
||||
/// The call structure of the arguments.
|
||||
final CallStructure callStructure;
|
||||
|
||||
/// The resolutions results for each argument.
|
||||
final List<ResolutionResult> argumentResults;
|
||||
|
||||
/// `true` if the arguments are valid as arguments to a constructed constant
|
||||
/// expression.
|
||||
final bool isValidAsConstant;
|
||||
|
||||
ArgumentsResult(
|
||||
this.callStructure,
|
||||
this.argumentResults,
|
||||
{this.isValidAsConstant});
|
||||
|
||||
/// Returns the list of [ConstantExpression]s for each of the arguments. If
|
||||
/// [isValidAsConstant] is `false`, `null` is returned.
|
||||
List<ConstantExpression> get constantArguments {
|
||||
if (!isValidAsConstant) return null;
|
||||
return argumentResults.map((ResolutionResult result) {
|
||||
return result.constant;
|
||||
}).toList();
|
||||
}
|
||||
}
|
|
@ -686,7 +686,7 @@ Future resolveConstructor(
|
|||
new ResolverVisitor(compiler, element,
|
||||
new ResolutionRegistry.internal(compiler,
|
||||
new CollectingTreeElements(element)));
|
||||
new InitializerResolver(visitor).resolveInitializers(element, tree);
|
||||
new InitializerResolver(visitor, element, tree).resolveInitializers();
|
||||
visitor.visit(tree.body);
|
||||
Expect.equals(expectedElementCount, map(visitor).length,
|
||||
"${map(visitor).values} for '$statement' in context of `$script`");
|
||||
|
|
Loading…
Reference in New Issue
Block a user