Index: pkg/compiler/lib/src/resolution/members.dart |
diff --git a/pkg/compiler/lib/src/resolution/members.dart b/pkg/compiler/lib/src/resolution/members.dart |
index fb0d246658580d7499c3d75516d40ca3d644c2e8..27dda38d8150b044756529368e0c2db331daabea 100644 |
--- a/pkg/compiler/lib/src/resolution/members.dart |
+++ b/pkg/compiler/lib/src/resolution/members.dart |
@@ -813,7 +813,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
} |
/// Compute the [AccessSemantics] corresponding to a super access of [target]. |
- AccessSemantics computeSuperAccess(Spannable node, Element target) { |
+ AccessSemantics computeSuperAccessSemantics(Spannable node, Element target) { |
if (target.isErroneous) { |
return new StaticAccess.unresolvedSuper(target); |
} else if (target.isGetter) { |
@@ -821,7 +821,11 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
} else if (target.isSetter) { |
return new StaticAccess.superSetter(target); |
} else if (target.isField) { |
- return new StaticAccess.superField(target); |
+ if (target.isFinal) { |
+ return new StaticAccess.superFinalField(target); |
+ } else { |
+ return new StaticAccess.superField(target); |
+ } |
} else { |
assert(invariant(node, target.isFunction, |
message: "Unexpected super target '$target'.")); |
@@ -829,6 +833,78 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
} |
} |
+ /// Compute the [AccessSemantics] corresponding to a local access of [target]. |
+ AccessSemantics computeLocalAccessSemantics(Spannable node, |
+ LocalElement target) { |
+ if (target.isParameter) { |
+ if (target.isFinal || target.isConst) { |
+ return new StaticAccess.finalParameter(target); |
+ } else { |
+ return new StaticAccess.parameter(target); |
+ } |
+ } else if (target.isVariable) { |
+ if (target.isFinal || target.isConst) { |
+ return new StaticAccess.finalLocalVariable(target); |
+ } else { |
+ return new StaticAccess.localVariable(target); |
+ } |
+ } else { |
+ assert(invariant(node, target.isFunction, |
+ message: "Unexpected local target '$target'.")); |
+ return new StaticAccess.localFunction(target); |
+ } |
+ } |
+ |
+ /// Compute the [AccessSemantics] corresponding to a static or toplevel access |
+ /// of [target]. |
+ AccessSemantics computeStaticOrTopLevelAccessSemantics( |
+ Spannable node, |
+ Element target) { |
+ |
+ target = target.declaration; |
+ if (target.isErroneous) { |
+ // This handles elements with parser errors. |
+ // TODO(johnniwinther): Elements with parse error should not set |
+ // [isErroneous] to `true`. |
+ return new StaticAccess.unresolved(target); |
+ } |
+ if (target.isStatic) { |
+ if (target.isGetter) { |
+ return new StaticAccess.staticGetter(target); |
+ } else if (target.isSetter) { |
+ return new StaticAccess.staticSetter(target); |
+ } else if (target.isField) { |
+ if (target.isFinal || target.isConst) { |
+ return new StaticAccess.finalStaticField(target); |
+ } else { |
+ return new StaticAccess.staticField(target); |
+ } |
+ } else { |
+ assert(invariant(node, target.isFunction, |
+ message: "Unexpected static target '$target'.")); |
+ return new StaticAccess.staticMethod(target); |
+ } |
+ } else { |
+ assert(invariant(node, target.isTopLevel, |
+ message: "Unexpected statically resolved target '$target'.")); |
+ if (target.isGetter) { |
+ return new StaticAccess.topLevelGetter(target); |
+ } else if (target.isSetter) { |
+ return new StaticAccess.topLevelSetter(target); |
+ } else if (target.isField) { |
+ if (target.isFinal) { |
+ return new StaticAccess.finalTopLevelField(target); |
+ } else { |
+ return new StaticAccess.topLevelField(target); |
+ } |
+ } else { |
+ assert(invariant(node, target.isFunction, |
+ message: "Unexpected top level target '$target'.")); |
+ return new StaticAccess.topLevelMethod(target); |
+ } |
+ } |
+ } |
+ |
/// Compute the [AccessSemantics] for accessing the name of [selector] on the |
/// super class. |
/// |
@@ -845,9 +921,11 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
/// foo => super.name; // Access to the setter. |
/// } |
/// |
- AccessSemantics computeSuperSemantics(Spannable node, |
- Selector selector, |
- {Name alternateName}) { |
+ AccessSemantics computeSuperAccessSemanticsForSelector( |
+ Spannable node, |
+ Selector selector, |
+ {Name alternateName}) { |
+ |
Name name = selector.memberName; |
// TODO(johnniwinther): Ensure correct behavior if currentClass is a |
// patch. |
@@ -869,7 +947,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
registry.registerDynamicInvocation(selector); |
registry.registerSuperNoSuchMethod(); |
} |
- return computeSuperAccess(node, target); |
+ return computeSuperAccessSemantics(node, target); |
} |
/// Resolve [node] as subexpression that is _not_ the prefix of a member |
@@ -948,7 +1026,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
AccessSemantics semantics; |
if (node.isSuperCall) { |
if (checkSuperAccess(node)) { |
- semantics = computeSuperSemantics(node, selector); |
+ semantics = computeSuperAccessSemanticsForSelector(node, selector); |
// TODO(johnniwinther): Add information to [AccessSemantics] about |
// whether it is erroneous. |
if (semantics.kind == AccessKind.SUPER_METHOD) { |
@@ -1122,7 +1200,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
if (node.isSuperCall) { |
if (checkSuperAccess(node)) { |
- semantics = computeSuperSemantics(node, selector); |
+ semantics = computeSuperAccessSemanticsForSelector(node, selector); |
// TODO(johnniwinther): Add information to [AccessSemantics] about |
// whether it is erroneous. |
if (semantics.kind == AccessKind.SUPER_METHOD) { |
@@ -1362,7 +1440,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
selector = new Selector(SelectorKind.GETTER, name, callStructure); |
} |
if (checkSuperAccess(node)) { |
- AccessSemantics semantics = computeSuperSemantics( |
+ AccessSemantics semantics = computeSuperAccessSemanticsForSelector( |
node, selector, alternateName: name.setter); |
if (node.isCall) { |
bool isIncompatibleInvoke = false; |
@@ -1380,6 +1458,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
} |
break; |
case AccessKind.SUPER_FIELD: |
+ case AccessKind.SUPER_FINAL_FIELD: |
case AccessKind.SUPER_GETTER: |
registry.registerStaticUse(semantics.element); |
selector = callStructure.callSelector; |
@@ -1405,6 +1484,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
registry.registerStaticUse(semantics.element); |
break; |
case AccessKind.SUPER_FIELD: |
+ case AccessKind.SUPER_FINAL_FIELD: |
case AccessKind.SUPER_GETTER: |
registry.registerStaticUse(semantics.element); |
break; |
@@ -1514,6 +1594,12 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
// TODO(johnniwinther): Support unresolved top level access as an |
// [AccessSemantics]. |
AccessSemantics accessSemantics = new StaticAccess.unresolved(element); |
+ return handleErroneousAccess(node, name, element, accessSemantics); |
+ } |
+ |
+ /// Handle erroneous access of [element] of the given [accessSemantics]. |
+ ResolutionResult handleErroneousAccess( |
+ Send node, Name name, Element element, AccessSemantics accessSemantics) { |
SendStructure sendStructure; |
Selector selector; |
if (node.isCall) { |
@@ -1536,6 +1622,247 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
return const NoneResult(); |
} |
+ /// Handle access to an ambiguous element, that is, a name imported twice. |
+ ResolutionResult handleAmbiguousSend( |
+ Send node, |
+ Name name, |
+ AmbiguousElement element) { |
+ |
+ compiler.reportError( |
+ node, element.messageKind, element.messageArguments); |
+ element.diagnose(enclosingElement, compiler); |
+ |
+ ErroneousElement error = new ErroneousElementX( |
+ element.messageKind, |
+ element.messageArguments, |
+ name.text, |
+ enclosingElement); |
+ |
+ // TODO(johnniwinther): Support ambiguous access as an [AccessSemantics]. |
+ AccessSemantics accessSemantics = new StaticAccess.unresolved(error); |
+ return handleErroneousAccess(node, name, error, accessSemantics); |
+ } |
+ |
+ /// Handle access of an instance [member] from a non-instance context. |
+ ResolutionResult handleStaticInstanceSend( |
+ Send node, Name name, MemberElement member) { |
+ compiler.reportError( |
+ node, MessageKind.NO_INSTANCE_AVAILABLE, {'name': member.name}); |
+ ErroneousElement error = new ErroneousElementX( |
+ MessageKind.NO_INSTANCE_AVAILABLE, |
+ {'name': name}, |
+ name.text, |
+ enclosingElement); |
+ |
+ // TODO(johnniwinther): Support static instance access as an |
+ // [AccessSemantics]. |
+ AccessSemantics accessSemantics = new StaticAccess.unresolved(error); |
+ return handleErroneousAccess(node, name, error, accessSemantics); |
+ } |
+ |
+ /// Handle access of a parameter, local variable or local function. |
+ ResolutionResult handleLocalAccess(Send node, Name name, Element element) { |
+ AccessSemantics semantics = computeLocalAccessSemantics(node, element); |
+ 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); |
+ } |
+ if (node.isCall) { |
+ bool isIncompatibleInvoke = false; |
+ switch (semantics.kind) { |
+ case AccessKind.LOCAL_FUNCTION: |
+ LocalFunctionElementX function = semantics.element; |
+ function.computeSignature(compiler); |
+ if (!callStructure.signatureApplies(function)) { |
+ registry.registerThrowNoSuchMethod(); |
+ registry.registerDynamicInvocation(selector); |
+ isIncompatibleInvoke = true; |
+ } |
+ break; |
+ case AccessKind.PARAMETER: |
+ case AccessKind.FINAL_PARAMETER: |
+ case AccessKind.LOCAL_VARIABLE: |
+ case AccessKind.FINAL_LOCAL_VARIABLE: |
+ selector = callStructure.callSelector; |
+ registry.registerDynamicInvocation(selector); |
+ break; |
+ default: |
+ internalError(node, |
+ "Unexpected local access $semantics."); |
+ break; |
+ } |
+ registry.registerSendStructure(node, |
+ isIncompatibleInvoke |
+ ? new IncompatibleInvokeStructure(semantics, selector) |
+ : new InvokeStructure(semantics, selector)); |
+ } else { |
+ registry.registerSendStructure(node, |
+ new GetStructure(semantics, selector)); |
+ } |
+ |
+ // TODO(johnniwinther): Remove these when all information goes through |
+ // the [SendStructure]. |
+ registry.useElement(node, element); |
+ registry.setSelector(node, selector); |
+ |
+ registerPotentialAccessInClosure(node, element); |
+ |
+ return node.isPropertyAccess |
+ ? new ElementResult(element) : const NoneResult(); |
+ } |
+ |
+ /// Handle access of a static or top level [element]. |
+ ResolutionResult handleStaticOrTopLevelAccess( |
+ Send node, Name name, Element element) { |
+ |
+ if (element.isAbstractField) { |
+ AbstractFieldElement abstractField = element; |
+ if (abstractField.getter != null) { |
+ element = abstractField.getter; |
+ } else { |
+ element = abstractField.setter; |
+ } |
+ } |
+ // TODO(johnniwinther): Needed to provoke a parsing and with it discovery |
+ // of parse errors to make [element] erroneous. Fix this! |
+ element.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, element); |
+ if (node.isCall) { |
+ bool isIncompatibleInvoke = false; |
+ switch (semantics.kind) { |
+ case AccessKind.STATIC_METHOD: |
+ case AccessKind.TOPLEVEL_METHOD: |
+ MethodElementX method = semantics.element; |
+ method.computeSignature(compiler); |
+ if (!callStructure.signatureApplies(method)) { |
+ registry.registerThrowNoSuchMethod(); |
+ registry.registerDynamicInvocation(selector); |
+ isIncompatibleInvoke = true; |
+ } else { |
+ registry.registerStaticUse(semantics.element); |
+ handleForeignCall(node, semantics.element, selector); |
+ } |
+ break; |
+ case AccessKind.STATIC_FIELD: |
+ case AccessKind.FINAL_STATIC_FIELD: |
+ case AccessKind.STATIC_GETTER: |
+ case AccessKind.TOPLEVEL_FIELD: |
+ case AccessKind.FINAL_TOPLEVEL_FIELD: |
+ case AccessKind.TOPLEVEL_GETTER: |
+ registry.registerStaticUse(semantics.element); |
+ selector = callStructure.callSelector; |
+ registry.registerDynamicInvocation(selector); |
+ break; |
+ case AccessKind.STATIC_SETTER: |
+ case AccessKind.TOPLEVEL_SETTER: |
+ case AccessKind.UNRESOLVED: |
+ registry.registerThrowNoSuchMethod(); |
+ element = reportAndCreateErroneousElement( |
+ node.selector, name.text, |
+ MessageKind.CANNOT_RESOLVE_GETTER, const {}); |
+ break; |
+ default: |
+ internalError(node, |
+ "Unexpected statically resolved access $semantics."); |
+ break; |
+ } |
+ registry.registerSendStructure(node, |
+ isIncompatibleInvoke |
+ ? new IncompatibleInvokeStructure(semantics, selector) |
+ : new InvokeStructure(semantics, selector)); |
+ } else { |
+ switch (semantics.kind) { |
+ case AccessKind.STATIC_METHOD: |
+ case AccessKind.TOPLEVEL_METHOD: |
+ // TODO(johnniwinther): Method this should be registered as a |
+ // closurization. |
+ registry.registerStaticUse(semantics.element); |
+ registry.registerGetOfStaticFunction(semantics.element); |
+ break; |
+ case AccessKind.STATIC_FIELD: |
+ case AccessKind.FINAL_STATIC_FIELD: |
+ case AccessKind.STATIC_GETTER: |
+ case AccessKind.TOPLEVEL_FIELD: |
+ case AccessKind.FINAL_TOPLEVEL_FIELD: |
+ case AccessKind.TOPLEVEL_GETTER: |
+ registry.registerStaticUse(semantics.element); |
+ break; |
+ case AccessKind.STATIC_SETTER: |
+ case AccessKind.TOPLEVEL_SETTER: |
+ case AccessKind.UNRESOLVED: |
+ registry.registerThrowNoSuchMethod(); |
+ element = reportAndCreateErroneousElement( |
+ node.selector, name.text, |
+ MessageKind.CANNOT_RESOLVE_GETTER, const {}); |
+ break; |
+ default: |
+ internalError(node, |
+ "Unexpected statically resolved access $semantics."); |
+ break; |
+ } |
+ registry.registerSendStructure(node, |
+ new GetStructure(semantics, selector)); |
+ } |
+ |
+ // TODO(johnniwinther): Remove these when all information goes through |
+ // the [SendStructure]. |
+ registry.useElement(node, element); |
+ registry.setSelector(node, selector); |
+ |
+ return node.isPropertyAccess |
+ ? new ElementResult(element) : const NoneResult(); |
+ } |
+ |
+ /// Handle access to resolved [element]. |
+ ResolutionResult handleResolvedSend(Send node, Name name, Element element) { |
+ if (element.isAmbiguous) { |
+ return handleAmbiguousSend(node, name, element); |
+ } |
+ if (element.isErroneous) { |
+ // This handles elements with parser errors. |
+ // TODO(johnniwinther): Elements with parse error should not set |
+ // [isErroneous] to `true`. |
+ assert(invariant(node, element is! ErroneousElement, |
+ message: "Unexpected erroneous element $element.")); |
+ return handleErroneousAccess(node, name, element, |
+ new StaticAccess.unresolved(element)); |
+ } |
+ if (element.isInstanceMember) { |
+ if (inInstanceContext) { |
+ // TODO(johnniwinther): Maybe use the found [element]. |
+ return handleThisPropertyAccess(node, name); |
+ } else { |
+ return handleStaticInstanceSend(node, name, element); |
+ } |
+ } |
+ if (element.isClass || element.isTypedef) { |
+ return oldVisitSend(node); |
+ } else if (element.isTypeVariable) { |
+ return oldVisitSend(node); |
+ } else if (element.isPrefix) { |
+ return oldVisitSend(node); |
+ } else if (element.isLocal) { |
+ return handleLocalAccess(node, name, element); |
+ } else if (element.isStatic || element.isTopLevel) { |
+ return handleStaticOrTopLevelAccess(node, name, element); |
+ } |
+ return internalError(node, "Unexpected resolved send: $element"); |
+ } |
+ |
/// Handle an unqualified [Send], that is where the `node.receiver` is null, |
/// like `a`, `a()`, `this()`, `assert()`, and `(){}()`. |
ResolutionResult handleUnqualifiedSend(Send node) { |
@@ -1568,19 +1895,33 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
ErroneousElement error = reportCannotResolve(node, text); |
return handleUnresolvedAccess(node, name, error); |
} |
+ } else { |
+ return handleResolvedSend(node, name, element); |
} |
- return oldVisitSend(node); |
} |
ResolutionResult visitSend(Send node) { |
if (node.isOperator) { |
+ // `a && b`, `a + b`, `-a`, or `a is T`. |
return handleOperatorSend(node); |
} else if (node.receiver != null) { |
+ // `a.b`. |
return handleQualifiedSend(node); |
} else { |
+ // `a`. |
return handleUnqualifiedSend(node); |
} |
- return oldVisitSend(node); |
+ } |
+ |
+ /// Regigster read access of [target] inside a closure. |
+ void registerPotentialAccessInClosure(Send node, Element target) { |
+ if (isPotentiallyMutableTarget(target)) { |
+ if (enclosingElement != target.enclosingElement) { |
+ for (Node scope in promotionScope) { |
+ registry.setAccessedByClosureIn(scope, target, node); |
+ } |
+ } |
+ } |
} |
ResolutionResult oldVisitSend(Send node) { |
@@ -1628,13 +1969,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
} else if (target.impliesType && (!sendIsMemberAccess || node.isCall)) { |
registerTypeLiteralAccess(node, target); |
} |
- if (isPotentiallyMutableTarget(target)) { |
- if (enclosingElement != target.enclosingElement) { |
- for (Node scope in promotionScope) { |
- registry.setAccessedByClosureIn(scope, target, node); |
- } |
- } |
- } |
+ registerPotentialAccessInClosure(node, target); |
} |
bool resolvedArguments = false; |
@@ -1672,23 +2007,7 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
} |
} |
- if (target != null && compiler.backend.isForeign(target)) { |
- if (selector.name == 'JS') { |
- registry.registerJsCall(node, this); |
- } else if (selector.name == 'JS_EMBEDDED_GLOBAL') { |
- registry.registerJsEmbeddedGlobalCall(node, this); |
- } else if (selector.name == 'JS_BUILTIN') { |
- registry.registerJsBuiltinCall(node, this); |
- } else if (selector.name == 'JS_INTERCEPTOR_CONSTANT') { |
- if (!node.argumentsNode.isEmpty) { |
- Node argument = node.argumentsNode.nodes.head; |
- if (argumentsToJsInterceptorConstant == null) { |
- argumentsToJsInterceptorConstant = new Set<Node>(); |
- } |
- argumentsToJsInterceptorConstant.add(argument); |
- } |
- } |
- } |
+ handleForeignCall(node, target, selector); |
} |
registry.useElement(node, target); |
@@ -1700,6 +2019,27 @@ class ResolverVisitor extends MappingVisitor<ResolutionResult> { |
? new ResolutionResult.forElement(target) : const NoneResult(); |
} |
+ // TODO(johnniwinther): Move this to the backend resolution callbacks. |
+ void handleForeignCall(Send node, Element target, Selector selector) { |
+ if (target != null && compiler.backend.isForeign(target)) { |
+ if (selector.name == 'JS') { |
+ registry.registerJsCall(node, this); |
+ } else if (selector.name == 'JS_EMBEDDED_GLOBAL') { |
+ registry.registerJsEmbeddedGlobalCall(node, this); |
+ } else if (selector.name == 'JS_BUILTIN') { |
+ registry.registerJsBuiltinCall(node, this); |
+ } else if (selector.name == 'JS_INTERCEPTOR_CONSTANT') { |
+ if (!node.argumentsNode.isEmpty) { |
+ Node argument = node.argumentsNode.nodes.head; |
+ if (argumentsToJsInterceptorConstant == null) { |
+ argumentsToJsInterceptorConstant = new Set<Node>(); |
+ } |
+ argumentsToJsInterceptorConstant.add(argument); |
+ } |
+ } |
+ } |
+ } |
+ |
/// Callback for native enqueuer to parse a type. Returns [:null:] on error. |
DartType resolveTypeFromString(Node node, String typeName) { |
Element element = lookupInScope(compiler, node, scope, typeName); |