| 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;
|
| +}
|
|
|