| Index: lib/compiler/implementation/compiler.dart
|
| diff --git a/lib/compiler/implementation/compiler.dart b/lib/compiler/implementation/compiler.dart
|
| index 4c078eb2884bb28109d19d276b27e8ea7ca8d82b..91520736ca3814c2482509e954b2381e265210cd 100644
|
| --- a/lib/compiler/implementation/compiler.dart
|
| +++ b/lib/compiler/implementation/compiler.dart
|
| @@ -521,11 +521,39 @@ class Compiler implements DiagnosticListener {
|
|
|
| void applyLibraryPatch(LibraryElement original, LibraryElement patch) {
|
| Link<Element> patches = patch.topLevelElements;
|
| + applyContainerPatch(original, patches, original.findLocal);
|
|
|
| - // Copy/patch top-level elements.
|
| + // Copy imports from patch to original library.
|
| + Map<String, LibraryElement> delayedPatches = <LibraryElement>{};
|
| + Uri patchBase = patch.script.uri;
|
| + for (ScriptTag tag in patch.tags.reverse()) {
|
| + if (tag.isImport()) {
|
| + StringNode argument = tag.argument;
|
| + Uri resolved = patchBase.resolve(argument.dartString.slowToString());
|
| + LibraryElement importedLibrary =
|
| + scanner.loadLibrary(resolved, argument);
|
| + scanner.importLibrary(original, importedLibrary, tag, patch);
|
| + if (resolved.scheme == "dart") {
|
| + delayedPatches[resolved.path] = importedLibrary;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Mark library as already patched.
|
| + original.patch = patch;
|
| +
|
| + // We patch imported libraries after marking the current library as
|
| + // patched, to avoid problems with cyclic dependencies.
|
| + delayedPatches.forEach((String path, LibraryElement importedLibrary) {
|
| + patchDartLibrary(importedLibrary, path);
|
| + });
|
| + }
|
| +
|
| + void applyContainerPatch(ContainerElement original, Link<Element> patches,
|
| + Element lookup(SourceString name)) {
|
| while (!patches.isEmpty()) {
|
| Element patchElement = patches.head;
|
| - Element originalElement = original.elements[patchElement.name];
|
| + Element originalElement = lookup(patchElement.name);
|
| // Getters and setters are kept inside a synthetic field.
|
| if (patchElement.kind === ElementKind.ABSTRACT_FIELD) {
|
| if (originalElement !== null &&
|
| @@ -537,50 +565,37 @@ class Compiler implements DiagnosticListener {
|
| AbstractFieldElement originalField = originalElement;
|
| if (patchField.getter !== null) {
|
| if (originalField === null || originalField.getter === null) {
|
| - original.addGetterOrSetter(clonePatch(patchField.getter));
|
| + original.addGetterOrSetter(clonePatch(patchField.getter),
|
| + originalField,
|
| + this);
|
| + if (originalField === null && patchField.setter !== null) {
|
| + // It exists now, so find it for the setter patching.
|
| + originalField = lookup(patchElement.name);
|
| + }
|
| } else {
|
| patchMember(originalField.getter, patchField.getter);
|
| }
|
| }
|
| if (patchField.setter !== null) {
|
| if (originalField === null || originalField.setter === null) {
|
| - original.addGetterOrSetter(clonePatch(patchField.setter));
|
| + original.addGetterOrSetter(clonePatch(patchField.setter),
|
| + originalField,
|
| + this);
|
| } else {
|
| patchMember(originalField.setter, patchField.setter);
|
| }
|
| }
|
| } else if (originalElement === null) {
|
| - original.addMember(clonePatch(patchElement));
|
| + if (isPatchElement(patchElement)) {
|
| + internalError("Cannot patch non-existing member '"
|
| + "${patchElement.name.slowToString()}'.");
|
| + }
|
| + original.addMember(clonePatch(patchElement), this);
|
| } else {
|
| patchMember(originalElement, patchElement);
|
| }
|
| patches = patches.tail;
|
| }
|
| -
|
| - // Copy imports.
|
| - Map<String, LibraryElement> delayedPatches = <LibraryElement>{};
|
| - Uri patchBase = patch.script.uri;
|
| - for (ScriptTag tag in patch.tags.reverse()) {
|
| - if (tag.isImport()) {
|
| - StringNode argument = tag.argument;
|
| - Uri resolved = patchBase.resolve(argument.dartString.slowToString());
|
| - LibraryElement importedLibrary =
|
| - scanner.loadLibrary(resolved, argument);
|
| - scanner.importLibrary(original, importedLibrary, tag, patch);
|
| - if (resolved.scheme == "dart") {
|
| - delayedPatches[resolved.path] = importedLibrary;
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Mark library as already patched.
|
| - original.patch = patch;
|
| -
|
| - // We patch imported libraries after marking the current library as
|
| - // patched, to avoid problems with cyclic dependencies.
|
| - delayedPatches.forEach((String path, LibraryElement importedLibrary) {
|
| - patchDartLibrary(importedLibrary, path);
|
| - });
|
| }
|
|
|
| bool isPatchElement(Element element) {
|
| @@ -594,22 +609,16 @@ class Compiler implements DiagnosticListener {
|
| // as the patch library element.
|
| // In this case, the patch library element must not be marked as "patch",
|
| // and its name must make it private.
|
| - if (isPatchElement(patchElement)) {
|
| - internalError("Cannot patch non-existing member '"
|
| - "${patchElement.name.slowToString()}'.");
|
| -
|
| - }
|
| if (!patchElement.name.isPrivate()) {
|
| internalError("Cannot add non-private member '"
|
| "${patchElement.name.slowToString()}' from patch.");
|
| }
|
| - // TODO(lrn): Create a copy of patchElement that isn't added to anything,
|
| - // but which takes its source from patchElement.
|
| + // TODO(lrn): Create a copy of patchElement that isn't added to any
|
| + // object/library yet, but which takes its source from patchElement.
|
| throw "Adding members from patch is unsupported";
|
| }
|
|
|
| - void patchMember(Element originalElement,
|
| - Element patchElement) {
|
| + 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
|
| @@ -618,18 +627,30 @@ class Compiler implements DiagnosticListener {
|
| internalError("Cannot overwrite existing '"
|
| "${originalElement.name.slowToString()}' with non-patch.");
|
| }
|
| + if (originalElement is PartialClassElement) {
|
| + // Only happens when patching a library. Dart does not, yet, have nested
|
| + // classes.
|
| + if (patchElement is! PartialClassElement) {
|
| + internalError("Trying to patch class with non-class",
|
| + element:originalElement);
|
| + }
|
| + applyClassPatch(originalElement, patchElement);
|
| + return;
|
| + }
|
| if (originalElement is! FunctionElement) {
|
| // TODO(lrn): Handle class declarations too.
|
| internalError("Can only patch functions", element: originalElement);
|
| }
|
| - // TODO(lrn): Abort if the original isn't marked external, when
|
| - // that is added to the language.
|
| + FunctionElement original = originalElement;
|
| + if (!original.modifiers.isExternal()) {
|
| + internalError("Can only patch external functions.", element: original);
|
| + }
|
| if (patchElement is! FunctionElement ||
|
| - !patchSignatureMatches(originalElement, patchElement)) {
|
| + !patchSignatureMatches(original, patchElement)) {
|
| internalError("Can only patch functions with matching signatures",
|
| - element: originalElement);
|
| + element: original);
|
| }
|
| - applyFunctionPatch(originalElement, patchElement);
|
| + applyFunctionPatch(original, patchElement);
|
| }
|
|
|
| bool patchSignatureMatches(FunctionElement original, FunctionElement patch) {
|
| @@ -640,7 +661,6 @@ class Compiler implements DiagnosticListener {
|
|
|
| void applyFunctionPatch(FunctionElement element,
|
| FunctionElement patchElement) {
|
| - // Don't just assign the patch field. This also updates the cachedNode.
|
| if (element.isPatched) {
|
| internalError("Trying to patch a function more than once.",
|
| element: element);
|
| @@ -649,9 +669,27 @@ class Compiler implements DiagnosticListener {
|
| 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);
|
| }
|
|
|
| + void applyClassPatch(PartialClassElement original,
|
| + PartialClassElement patch) {
|
| + // Eagerly parse the class so we can patch it.
|
| + // TODO(lrn): Perhaps find a way to delay parsing until the class is needed,
|
| + // i.e., until [parseNode] is called on [original].
|
| + ClassNode node = original.parseNode(this);
|
| + // Parse patch class with "patch" parser.
|
| + ClassNode patchNode = patchParser.parsePatchClassNode(patch);
|
| + Link<Element> patches = patch.members;
|
| + Element lookupMemberOrConstructor(SourceString name) {
|
| + Element result = original.lookupLocalMember(name);
|
| + if (result !== null) return result;
|
| + return original.lookupConstructor(name);
|
| + }
|
| + applyContainerPatch(original, patches, lookupMemberOrConstructor);
|
| + }
|
| +
|
| /**
|
| * Get an [Uri] pointing to a patch for the dart: library with
|
| * the given path. Returns null if there is no patch.
|
|
|