Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(562)

Unified Diff: sdk/lib/_internal/compiler/implementation/patch_parser.dart

Issue 11864010: Improve checking of patches. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rebased (again) Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | sdk/lib/_internal/compiler/implementation/resolution/members.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 998348915d45d10843b4f0881f4a99f8826a1714..5a0da01bbb926bf30876d077a08e3f4dab912e4a 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;
@@ -192,89 +193,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;
- }
- }
-
- 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;
- }
- 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);
- }
+ for (Element patch in patches) {
+ if (!isPatchElement(patch)) continue;
- 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);
+ Element origin = originClass.localLookup(patch.name);
+ patchElement(compiler, origin, patch);
}
- 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;
}
}
@@ -404,59 +330,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(popMetadataHack());
+ patch.addMetadata(popMetadataHack());
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);
}
}
@@ -510,3 +393,204 @@ class PatchMetadataAnnotation extends MetadataAnnotationX {
Token get beginToken => null;
Token get endToken => null;
}
+
+void patchElement(leg.DiagnosticListener listener,
+ Element origin,
+ Element patch) {
+ if (origin == null) {
+ listener.reportMessage(
+ listener.spanFromSpannable(patch),
+ leg.MessageKind.PATCH_NON_EXISTING.error({'name': patch.name}),
+ 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 if(patch.isFunction()) {
+ tryPatchFunction(listener, origin, patch);
+ } else {
+ listener.reportMessage(
+ listener.spanFromSpannable(patch),
+ leg.MessageKind.PATCH_NONPATCHABLE.error(),
+ api.Diagnostic.ERROR);
+ }
+}
+
+void tryPatchClass(leg.DiagnosticListener listener,
+ Element origin,
+ ClassElement patch) {
+ if (!origin.isClass()) {
+ listener.reportMessage(
+ listener.spanFromSpannable(origin),
+ leg.MessageKind.PATCH_NON_CLASS.error({'className': patch.name}),
+ api.Diagnostic.ERROR);
+ listener.reportMessage(
+ listener.spanFromSpannable(patch),
+ leg.MessageKind.PATCH_POINT_TO_CLASS.error({'className': patch.name}),
+ api.Diagnostic.INFO);
+ return;
+ }
+ patchClass(listener, origin, patch);
+}
+
+void patchClass(leg.DiagnosticListener listener,
+ ClassElement origin,
+ ClassElement patch) {
+ if (origin.isPatched) {
+ listener.internalErrorOnElement(
+ origin, "Patching the same class more than once.");
+ }
+ // TODO(johnniwinther): Change to functions on the ElementX class.
+ origin.patch = patch;
+ patch.origin = origin;
+}
+
+void tryPatchGetter(leg.DiagnosticListener listener,
+ Element origin,
+ FunctionElement patch) {
+ if (!origin.isAbstractField()) {
+ listener.reportMessage(
+ listener.spanFromSpannable(origin),
+ leg.MessageKind.PATCH_NON_GETTER.error({'name': origin.name}),
+ api.Diagnostic.ERROR);
+ listener.reportMessage(
+ listener.spanFromSpannable(patch),
+ leg.MessageKind.PATCH_POINT_TO_GETTER.error({'getterName': patch.name}),
+ api.Diagnostic.INFO);
+ return;
+ }
+ AbstractFieldElement originField = origin;
+ if (originField.getter == null) {
+ listener.reportMessage(
+ listener.spanFromSpannable(origin),
+ leg.MessageKind.PATCH_NO_GETTER.error({'getterName': patch.name}),
+ api.Diagnostic.ERROR);
+ listener.reportMessage(
+ listener.spanFromSpannable(patch),
+ leg.MessageKind.PATCH_POINT_TO_GETTER.error({'getterName': patch.name}),
+ api.Diagnostic.INFO);
+ return;
+ }
+ patchFunction(listener, originField.getter, patch);
+}
+
+void tryPatchSetter(leg.DiagnosticListener listener,
+ Element origin,
+ FunctionElement patch) {
+ if (!origin.isAbstractField()) {
+ listener.reportMessage(
+ listener.spanFromSpannable(origin),
+ leg.MessageKind.PATCH_NON_SETTER.error({'name': origin.name}),
+ api.Diagnostic.ERROR);
+ listener.reportMessage(
+ listener.spanFromSpannable(patch),
+ leg.MessageKind.PATCH_POINT_TO_SETTER.error({'setterName': patch.name}),
+ api.Diagnostic.INFO);
+ return;
+ }
+ AbstractFieldElement originField = origin;
+ if (originField.setter == null) {
+ listener.reportMessage(
+ listener.spanFromSpannable(origin),
+ leg.MessageKind.PATCH_NO_SETTER.error({'setterName': patch.name}),
+ api.Diagnostic.ERROR);
+ listener.reportMessage(
+ listener.spanFromSpannable(patch),
+ leg.MessageKind.PATCH_POINT_TO_SETTER.error({'setterName': patch.name}),
+ api.Diagnostic.INFO);
+ return;
+ }
+ patchFunction(listener, originField.setter, patch);
+}
+
+void tryPatchConstructor(leg.DiagnosticListener listener,
+ Element origin,
+ FunctionElement patch) {
+ if (!origin.isConstructor()) {
+ listener.reportMessage(
+ listener.spanFromSpannable(origin),
+ leg.MessageKind.PATCH_NON_CONSTRUCTOR.error(
+ {'constructorName': patch.name}),
+ api.Diagnostic.ERROR);
+ listener.reportMessage(
+ listener.spanFromSpannable(patch),
+ leg.MessageKind.PATCH_POINT_TO_CONSTRUCTOR.error(
+ {'constructorName': patch.name}),
+ api.Diagnostic.INFO);
+ return;
+ }
+ patchFunction(listener, origin, patch);
+}
+
+void tryPatchFunction(leg.DiagnosticListener listener,
+ Element origin,
+ FunctionElement patch) {
+ if (!origin.isFunction()) {
+ listener.reportMessage(
+ listener.spanFromSpannable(origin),
+ leg.MessageKind.PATCH_NON_FUNCTION.error({'functionName': patch.name}),
+ api.Diagnostic.ERROR);
+ listener.reportMessage(
+ listener.spanFromSpannable(patch),
+ leg.MessageKind.PATCH_POINT_TO_FUNCTION.error(
+ {'functionName': patch.name}),
+ api.Diagnostic.INFO);
+ return;
+ }
+ patchFunction(listener, origin, patch);
+}
+
+void patchFunction(leg.DiagnosticListener listener,
+ FunctionElement origin,
+ FunctionElement patch) {
+ if (!origin.modifiers.isExternal()) {
+ listener.reportMessage(
+ listener.spanFromSpannable(origin),
+ leg.MessageKind.PATCH_NON_EXTERNAL.error(),
+ api.Diagnostic.ERROR);
+ listener.reportMessage(
+ listener.spanFromSpannable(patch),
+ leg.MessageKind.PATCH_POINT_TO_FUNCTION.error(
+ {'functionName': 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.
+ // TODO(johnniwinther): Change to functions on the ElementX class.
+ origin.setPatch(patch);
+ patch.origin = origin;
+}
+
+// TODO(johnniwinther): Add unittest when patch is (real) metadata.
+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;
+ }
+ return false;
+}
« no previous file with comments | « no previous file | sdk/lib/_internal/compiler/implementation/resolution/members.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698