Index: sdk/lib/_internal/compiler/implementation/patch_parser.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/patch_parser.dart b/sdk/lib/_internal/compiler/implementation/patch_parser.dart |
index af1ae5f919b455178b2daf90570ec0b0a900f5c7..41e937f2a0747653f0b4ae790d29e8ca303e3de0 100644 |
--- a/sdk/lib/_internal/compiler/implementation/patch_parser.dart |
+++ b/sdk/lib/_internal/compiler/implementation/patch_parser.dart |
@@ -118,6 +118,7 @@ import "dart:uri"; |
import "tree/tree.dart" as tree; |
import "dart2jslib.dart" as leg; // CompilerTask, Compiler. |
import "apiimpl.dart"; |
+import "../compiler.dart" as api; |
import "scanner/scannerlib.dart"; // Scanner, Parsers, Listeners |
import "elements/elements.dart"; |
import "elements/modelx.dart" show LibraryElementX, MetadataAnnotationX; |
@@ -189,89 +190,14 @@ class PatchParserTask extends leg.CompilerTask { |
})); |
} |
- void applyContainerPatch(ScopeContainerElement original, |
+ void applyContainerPatch(ClassElement originClass, |
Link<Element> patches) { |
- while (!patches.isEmpty) { |
- Element patchElement = patches.head; |
- Element originalElement = original.localLookup(patchElement.name); |
- if (patchElement.isAccessor() && originalElement != null) { |
- if (!identical(originalElement.kind, ElementKind.ABSTRACT_FIELD)) { |
- compiler.internalError( |
- "Cannot patch non-getter/setter with getter/setter", |
- element: originalElement); |
- } |
- AbstractFieldElement originalField = originalElement; |
- if (patchElement.isGetter()) { |
- originalElement = originalField.getter; |
- } else { |
- originalElement = originalField.setter; |
- } |
- } |
- if (originalElement == null) { |
- if (isPatchElement(patchElement)) { |
- compiler.internalError("Cannot patch non-existing member '" |
- "${patchElement.name.slowToString()}'."); |
- } |
- } else { |
- patchMember(originalElement, patchElement); |
- } |
- patches = patches.tail; |
- } |
- } |
+ for (Element patch in patches) { |
+ if (!_isPatchElement(patch)) continue; |
ngeoffray
2013/01/11 11:46:12
How can that be?
ahe
2013/01/21 10:53:35
This would be an injected member, right? I assume
|
- bool isPatchElement(Element element) { |
- // TODO(lrn): More checks needed if we introduce metadata for real. |
- // In that case, it must have the identifier "native" as metadata. |
- for (Link link = element.metadata; !link.isEmpty; link = link.tail) { |
- if (link.head is PatchMetadataAnnotation) return true; |
+ Element origin = originClass.localLookup(patch.name); |
+ _patchElement(compiler, origin, patch); |
} |
- return false; |
- } |
- |
- void patchMember(Element originalElement, Element patchElement) { |
- // The original library has an element with the same name as the patch |
- // library element. |
- // In this case, the patch library element must be a function marked as |
- // "patch" and it must have the same signature as the function it patches. |
- if (!isPatchElement(patchElement)) { |
- compiler.internalError("Cannot overwrite existing '" |
- "${originalElement.name.slowToString()}' with non-patch."); |
- } |
- if (originalElement is! FunctionElement) { |
- // TODO(lrn): Handle class declarations too. |
- compiler.internalError("Can only patch functions", element: originalElement); |
- } |
- FunctionElement original = originalElement; |
- if (!original.modifiers.isExternal()) { |
- compiler.internalError("Can only patch external functions.", element: original); |
- } |
- if (patchElement is! FunctionElement || |
- !patchSignatureMatches(original, patchElement)) { |
- compiler.internalError("Can only patch functions with matching signatures", |
- element: original); |
- } |
- applyFunctionPatch(original, patchElement); |
- } |
- |
- bool patchSignatureMatches(FunctionElement original, FunctionElement patch) { |
- // TODO(lrn): Check that patches actually match the signature of |
- // the function it's patching. |
- return true; |
- } |
- |
- void applyFunctionPatch(FunctionElement element, |
- FunctionElement patchElement) { |
- if (element.isPatched) { |
- compiler.internalError("Trying to patch a function more than once.", |
- element: element); |
- } |
- if (element.cachedNode != null) { |
- compiler.internalError("Trying to patch an already compiled function.", |
- element: element); |
- } |
- // Don't just assign the patch field. This also updates the cachedNode. |
- element.setPatch(patchElement); |
- patchElement.origin = element; |
} |
} |
@@ -401,59 +327,16 @@ class PatchElementListener extends ElementListener implements PatchListener { |
imports.addLast(tag); |
} |
- void pushElement(Element element) { |
- if (isMemberPatch || (isClassPatch && element is ClassElement)) { |
+ void pushElement(Element patch) { |
+ if (isMemberPatch || (isClassPatch && patch is ClassElement)) { |
// Apply patch. |
- element.addMetadata(popMetadata()); |
+ patch.addMetadata(popMetadata()); |
LibraryElement originLibrary = compilationUnitElement.getLibrary(); |
assert(originLibrary.isPatched); |
- Element existing = originLibrary.localLookup(element.name); |
- if (isMemberPatch) { |
- if (element is! FunctionElement) { |
- listener.internalErrorOnElement(element, |
- "Member patch is not a function."); |
- } |
- FunctionElement functionElement = element; |
- if (identical(existing.kind, ElementKind.ABSTRACT_FIELD)) { |
- if (!element.isAccessor()) { |
- listener.internalErrorOnElement( |
- functionElement, "Patching non-accessor with accessor"); |
- } |
- AbstractFieldElement field = existing; |
- if (functionElement.isGetter()) { |
- existing = field.getter; |
- } else { |
- existing = field.setter; |
- } |
- } |
- if (existing is! FunctionElement) { |
- listener.internalErrorOnElement(functionElement, |
- "No corresponding method for patch."); |
- } |
- FunctionElement existingFunction = existing; |
- if (existingFunction.isPatched) { |
- listener.internalErrorOnElement( |
- functionElement, "Patching the same function more than once."); |
- } |
- existingFunction.patch = functionElement; |
- functionElement.origin = existingFunction; |
- } else { |
- assert(leg.invariant(element, element is ClassElement)); |
- ClassElement classElement = element; |
- if (existing is! ClassElement) { |
- listener.internalErrorOnElement( |
- classElement, "Patching a non-class with a class patch."); |
- } |
- ClassElement existingClass = existing; |
- if (existingClass.isPatched) { |
- listener.internalErrorOnElement( |
- classElement, "Patching the same class more than once."); |
- } |
- existingClass.patch = classElement; |
- classElement.origin = existingClass; |
- } |
+ Element origin = originLibrary.localLookup(patch.name); |
+ _patchElement(listener, origin, patch); |
} |
- super.pushElement(element); |
+ super.pushElement(patch); |
} |
} |
@@ -506,3 +389,203 @@ class PatchMetadataAnnotation extends MetadataAnnotationX { |
Token get beginToken => null; |
} |
+ |
+void _patchElement(leg.DiagnosticListener listener, |
ahe
2013/01/21 10:53:35
I'd like to see a unit test of this method.
Johnni Winther
2013/01/29 09:30:56
It's tested by all methods in patch_test.
|
+ Element origin, |
+ Element patch) { |
+ if (origin == null) { |
+ listener.reportMessage( |
+ listener.spanFromSpannable(patch), |
+ leg.MessageKind.PATCH_NON_EXISTING.error([patch.name]), |
+ api.Diagnostic.ERROR); |
+ return; |
+ } |
ngeoffray
2013/01/11 11:46:12
indentation
Johnni Winther
2013/01/29 09:30:56
Done.
|
+ if (!(patch.isClass() || |
ahe
2013/01/21 10:53:35
Let me suggest a shorter alternative version of th
Johnni Winther
2013/01/29 09:30:56
Almost done. Test for patchable origin must remain
|
+ patch.isConstructor() || |
+ patch.isFunction() || |
+ patch.isAccessor())) { |
+ listener.reportMessage( |
+ listener.spanFromSpannable(patch), |
+ leg.MessageKind.PATCH_NONPATCHABLE.error(), |
+ api.Diagnostic.ERROR); |
+ return; |
+ } |
+ if (!(origin.isClass() || |
+ origin.isConstructor() || |
+ origin.isFunction() || |
+ origin.isAbstractField())) { |
+ listener.reportMessage( |
+ listener.spanFromSpannable(origin), |
+ leg.MessageKind.PATCH_NONPATCHABLE.error(), |
+ api.Diagnostic.ERROR); |
+ return; |
+ } |
+ if (patch.isClass()) { |
+ _tryPatchClass(listener, origin, patch); |
+ } else if (patch.isGetter()) { |
+ _tryPatchGetter(listener, origin, patch); |
+ } else if (patch.isSetter()) { |
+ _tryPatchSetter(listener, origin, patch); |
+ } else if (patch.isConstructor()) { |
+ _tryPatchConstructor(listener, origin, patch); |
+ } else { |
+ assert(patch.isFunction()); |
+ _tryPatchFunction(listener, origin, patch); |
+ } |
+} |
+ |
+void _tryPatchClass(leg.DiagnosticListener listener, |
ahe
2013/01/21 10:53:35
I'd like to see a unit test of this method.
Johnni Winther
2013/01/29 09:30:56
It's tested by several methods in patch_test.
|
+ Element origin, |
+ ClassElement patch) { |
+ if (!origin.isClass()) { |
+ listener.reportMessage( |
+ listener.spanFromSpannable(origin), |
+ leg.MessageKind.PATCH_NON_CLASS.error([patch.name]), |
+ api.Diagnostic.ERROR); |
+ listener.reportMessage( |
+ listener.spanFromSpannable(patch), |
+ leg.MessageKind.PATCH_POINT_TO_CLASS.error([patch.name]), |
+ api.Diagnostic.INFO); |
+ return; |
+ } |
+ _patchClass(listener, origin, patch); |
+} |
+ |
+void _patchClass(leg.DiagnosticListener listener, |
ahe
2013/01/21 10:53:35
I'd like to see a unit test of this method.
Johnni Winther
2013/01/29 09:30:56
It's tested by several methods in patch_test.
|
+ ClassElement origin, |
+ ClassElement patch) { |
+ if (origin.isPatched) { |
+ listener.internalErrorOnElement( |
+ origin, "Patching the same class more than once."); |
+ } |
+ origin.patch = patch; |
ngeoffray
2013/01/11 11:46:12
Use setPatch to hide implementation details?
Johnni Winther
2013/01/29 09:30:56
Added a TODO.
|
+ patch.origin = origin; |
+} |
+ |
+void _tryPatchGetter(leg.DiagnosticListener listener, |
ahe
2013/01/21 10:53:35
I'd like to see a unit test of this method.
Johnni Winther
2013/01/29 09:30:56
It's tested by some methods in patch_test.
|
+ Element origin, |
+ FunctionElement patch) { |
+ if (!origin.isAbstractField()) { |
ahe
2013/01/21 10:53:35
I'd share this check between tryPatchGetter and tr
Johnni Winther
2013/01/29 09:30:56
The granularity has already been made and it takes
|
+ listener.reportMessage( |
+ listener.spanFromSpannable(origin), |
+ leg.MessageKind.PATCH_NON_GETTER.error([patch.name]), |
+ api.Diagnostic.ERROR); |
+ listener.reportMessage( |
+ listener.spanFromSpannable(patch), |
+ leg.MessageKind.PATCH_POINT_TO_GETTER.error([patch.name]), |
+ api.Diagnostic.INFO); |
+ return; |
+ } |
+ AbstractFieldElement originField = origin; |
+ if (originField.getter == null) { |
+ listener.reportMessage( |
+ listener.spanFromSpannable(origin), |
+ leg.MessageKind.PATCH_NO_GETTER.error([patch.name]), |
+ api.Diagnostic.ERROR); |
+ listener.reportMessage( |
+ listener.spanFromSpannable(patch), |
+ leg.MessageKind.PATCH_POINT_TO_GETTER.error([patch.name]), |
+ api.Diagnostic.INFO); |
+ return; |
+ } |
+ _patchFunction(listener, originField.getter, patch); |
+} |
+ |
+void _tryPatchSetter(leg.DiagnosticListener listener, |
ahe
2013/01/21 10:53:35
I'd like to see a unit test of this method.
Johnni Winther
2013/01/29 09:30:56
It's tested by a method in patch_test.
|
+ Element origin, |
+ FunctionElement patch) { |
+ if (!origin.isAbstractField()) { |
+ listener.reportMessage( |
+ listener.spanFromSpannable(origin), |
+ leg.MessageKind.PATCH_NON_SETTER.error([patch.name]), |
+ api.Diagnostic.ERROR); |
+ listener.reportMessage( |
+ listener.spanFromSpannable(patch), |
+ leg.MessageKind.PATCH_POINT_TO_SETTER.error([patch.name]), |
+ api.Diagnostic.INFO); |
+ return; |
+ } |
+ AbstractFieldElement originField = origin; |
+ if (originField.setter == null) { |
+ listener.reportMessage( |
+ listener.spanFromSpannable(origin), |
+ leg.MessageKind.PATCH_NO_SETTER.error([patch.name]), |
+ api.Diagnostic.ERROR); |
+ listener.reportMessage( |
+ listener.spanFromSpannable(patch), |
+ leg.MessageKind.PATCH_POINT_TO_SETTER.error([patch.name]), |
+ api.Diagnostic.INFO); |
+ return; |
+ } |
+ _patchFunction(listener, originField.setter, patch); |
+} |
+ |
+void _tryPatchConstructor(leg.DiagnosticListener listener, |
ahe
2013/01/21 10:53:35
I'd like to see a unit test of this method.
Johnni Winther
2013/01/29 09:30:56
It's tested by a method in patch_test.
|
+ Element origin, |
+ FunctionElement patch) { |
+ if (!origin.isConstructor()) { |
+ listener.reportMessage( |
+ listener.spanFromSpannable(origin), |
+ leg.MessageKind.PATCH_NON_CONSTRUCTOR.error([patch.name]), |
+ api.Diagnostic.ERROR); |
+ listener.reportMessage( |
+ listener.spanFromSpannable(patch), |
+ leg.MessageKind.PATCH_POINT_TO_CONSTRUCTOR.error([patch.name]), |
+ api.Diagnostic.INFO); |
+ return; |
+ } |
+ _patchFunction(listener, origin, patch); |
+} |
+ |
+void _tryPatchFunction(leg.DiagnosticListener listener, |
ahe
2013/01/21 10:53:35
I'd like to see a unit test of this method.
Johnni Winther
2013/01/29 09:30:56
It's tested by some methods in patch_test.
|
+ Element origin, |
+ FunctionElement patch) { |
+ if (!origin.isFunction()) { |
+ listener.reportMessage( |
+ listener.spanFromSpannable(origin), |
+ leg.MessageKind.PATCH_NON_FUNCTION.error([patch.name]), |
+ api.Diagnostic.ERROR); |
+ listener.reportMessage( |
+ listener.spanFromSpannable(patch), |
+ leg.MessageKind.PATCH_POINT_TO_FUNCTION.error([patch.name]), |
+ api.Diagnostic.INFO); |
+ return; |
+ } |
+ _patchFunction(listener, origin, patch); |
+} |
+ |
+void _patchFunction(leg.DiagnosticListener listener, |
ahe
2013/01/21 10:53:35
I'd like to see a unit test of this method.
Johnni Winther
2013/01/29 09:30:56
It's tested by some methods in patch_test.
|
+ FunctionElement origin, |
+ FunctionElement patch) { |
+ if (!origin.modifiers.isExternal()) { |
+ listener.reportMessage( |
+ listener.spanFromSpannable(origin), |
+ leg.MessageKind.PATCH_NON_EXTERNAL.error([patch.name]), |
+ api.Diagnostic.ERROR); |
+ listener.reportMessage( |
+ listener.spanFromSpannable(patch), |
+ leg.MessageKind.PATCH_POINT_TO_FUNCTION.error([patch.name]), |
+ api.Diagnostic.INFO); |
+ return; |
+ } |
+ if (origin.isPatched) { |
+ listener.internalErrorOnElement(origin, |
+ "Trying to patch a function more than once."); |
+ } |
+ if (origin.cachedNode != null) { |
+ listener.internalErrorOnElement(origin, |
+ "Trying to patch an already compiled function."); |
+ } |
+ // Don't just assign the patch field. This also updates the cachedNode. |
+ origin.setPatch(patch); |
+ patch.origin = origin; |
ngeoffray
2013/01/11 11:46:12
Add a setOrigin?
Johnni Winther
2013/01/29 09:30:56
Added a TODO.
|
+} |
+ |
+bool _isPatchElement(Element element) { |
ahe
2013/01/21 10:53:35
I'd like to see a unit test of this method.
Johnni Winther
2013/01/29 09:30:56
Added a TODO.
|
+ // TODO(lrn): More checks needed if we introduce metadata for real. |
+ // In that case, it must have the identifier "native" as metadata. |
+ for (Link link = element.metadata; !link.isEmpty; link = link.tail) { |
+ if (link.head is PatchMetadataAnnotation) return true; |
+ } |
+ return false; |
+} |