| 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
|
| deleted file mode 100644
|
| index 0899be3419411bc66fd745de6ce41499c0ae0651..0000000000000000000000000000000000000000
|
| --- a/sdk/lib/_internal/compiler/implementation/patch_parser.dart
|
| +++ /dev/null
|
| @@ -1,513 +0,0 @@
|
| -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -/**
|
| - * This library contains the infrastructure to parse and integrate patch files.
|
| - *
|
| - * Three types of elements can be patched: [LibraryElement], [ClassElement],
|
| - * [FunctionElement]. Patches are introduced in patch libraries which are loaded
|
| - * together with the corresponding origin library. Which libraries that are
|
| - * patched is determined by the dart2jsPatchPath field of LibraryInfo found
|
| - * in [:lib/_internal/libraries.dart:].
|
| - *
|
| - * Patch libraries are parsed like regular library and thus provided with their
|
| - * own elements. These elements which are distinct from the elements from the
|
| - * patched library and the relation between patched and patch elements is
|
| - * established through the [:patch:] and [:origin:] fields found on
|
| - * [LibraryElement], [ClassElement] and [FunctionElement]. The [:patch:] fields
|
| - * are set on the patched elements to point to their corresponding patch
|
| - * element, and the [:origin:] elements are set on the patch elements to point
|
| - * their corresponding patched elements.
|
| - *
|
| - * The fields [Element.isPatched] and [Element.isPatch] can be used to determine
|
| - * whether the [:patch:] or [:origin:] field, respectively, has been set on an
|
| - * element, regardless of whether the element is one of the three patchable
|
| - * element types or not.
|
| - *
|
| - * ## Variants of classes and functions ##
|
| - *
|
| - * With patches there are four variants of classes and function:
|
| - *
|
| - * Regular: A class or function which is not declared in a patch library and
|
| - * which has no corresponding patch.
|
| - * Origin: A class or function which is not declared in a patch library and
|
| - * which has a corresponding patch. Origin functions must use the [:external:]
|
| - * modifier and can have no body. Origin classes and functions are also
|
| - * called 'patched'.
|
| - * Patch: A class or function which is declared in a patch library and which
|
| - * has a corresponding origin. Both patch classes and patch functions must use
|
| - * the [:patch:] modifier.
|
| - * Injected: A class or function (or even field) which is declared in a
|
| - * patch library and which has no corresponding origin. An injected element
|
| - * cannot use the [:patch:] modifier. Injected elements are never visible from
|
| - * outside the patch library in which they have been declared. For this
|
| - * reason, injected elements are often declared private and therefore called
|
| - * also called 'patch private'.
|
| - *
|
| - * Examples of the variants is shown in the code below:
|
| - *
|
| - * // In the origin library:
|
| - * class RegularClass { // A regular class.
|
| - * void regularMethod() {} // A regular method.
|
| - * }
|
| - * class PatchedClass { // An origin class.
|
| - * int regularField; // A regular field.
|
| - * void regularMethod() {} // A regular method.
|
| - * external void patchedMethod(); // An origin method.
|
| - * }
|
| - *
|
| - * // In the patch library:
|
| - * class _InjectedClass { // An injected class.
|
| - * void _injectedMethod() {} // An injected method.
|
| - * }
|
| - * @patch class PatchedClass { // A patch class.
|
| - * int _injectedField; { // An injected field.
|
| - * @patch void patchedMethod() {} // A patch method.
|
| - * }
|
| - *
|
| - *
|
| - * ## Declaration and implementation ##
|
| - *
|
| - * With patches we have two views on elements: as the 'declaration' which
|
| - * introduces the entity and defines its interface, and as the 'implementation'
|
| - * which defines the actual implementation of the entity.
|
| - *
|
| - * Every element has a 'declaration' and an 'implementation' element. For
|
| - * regular and injected elements these are the same. For origin elements the
|
| - * declaration is the element itself and the implementation is the patch element
|
| - * found through its [:patch:] field. For patch elements the implementation is
|
| - * the element itself and the declaration is the origin element found through
|
| - * its [:origin:] field. The declaration and implementation of any element is
|
| - * conveniently available through the [Element.declaration] and
|
| - * [Element.implementation] getters.
|
| - *
|
| - * Most patch-related invariants enforced through-out the compiler are defined
|
| - * in terms of 'declaration' and 'implementation', and tested through the
|
| - * predicate getters [Element.isDeclaration] and [Element.isImplementation].
|
| - * Patch invariants are stated both in comments and as assertions.
|
| - *
|
| - *
|
| - * ## General invariant guidelines ##
|
| - *
|
| - * For [LibraryElement] we always use declarations. This means the
|
| - * [Element.getLibrary] method will only return library declarations. Patch
|
| - * library implementations are only accessed through calls to
|
| - * [Element.getImplementationLibrary] which is used to setup the correct
|
| - * [Element.enclosingElement] relation between patch/injected elements and the
|
| - * patch library.
|
| - *
|
| - * For [ClassElement] and [FunctionElement] we use declarations for determining
|
| - * identity and implementations for work based on the AST nodes, such as
|
| - * resolution, type-checking, type inference, building SSA graphs, etc.
|
| - * - Worklist only contain declaration elements.
|
| - * - Most maps and sets use declarations exclusively, and their individual
|
| - * invariants are stated in the field comments.
|
| - * - [tree.TreeElements] only map to patch elements from inside a patch library.
|
| - * TODO(johnniwinther): Simplify this invariant to use only declarations in
|
| - * [tree.TreeElements].
|
| - * - Builders shift between declaration and implementation depending on usages.
|
| - * - Compile-time constants use constructor implementation exclusively.
|
| - * - Work on function parameters is performed on the declaration of the function
|
| - * element.
|
| - */
|
| -
|
| -library patchparser;
|
| -
|
| -import 'dart:async';
|
| -
|
| -import 'constants/values.dart' show ConstantValue;
|
| -import 'dart2jslib.dart'
|
| - show Compiler,
|
| - CompilerTask,
|
| - DiagnosticListener,
|
| - MessageKind,
|
| - Script;
|
| -import 'elements/elements.dart';
|
| -import 'elements/modelx.dart'
|
| - show LibraryElementX,
|
| - MetadataAnnotationX,
|
| - ClassElementX,
|
| - FunctionElementX;
|
| -import 'helpers/helpers.dart'; // Included for debug helpers.
|
| -import 'library_loader.dart' show LibraryLoader;
|
| -import 'scanner/scannerlib.dart'; // Scanner, Parsers, Listeners
|
| -import 'util/util.dart';
|
| -
|
| -class PatchParserTask extends CompilerTask {
|
| - PatchParserTask(Compiler compiler): super(compiler);
|
| - final String name = "Patching Parser";
|
| -
|
| - /**
|
| - * Scans a library patch file, applies the method patches and
|
| - * injections to the library, and returns a list of class
|
| - * patches.
|
| - */
|
| - Future patchLibrary(LibraryLoader loader,
|
| - Uri patchUri, LibraryElement originLibrary) {
|
| - return compiler.readScript(originLibrary, patchUri)
|
| - .then((Script script) {
|
| - var patchLibrary = new LibraryElementX(script, null, originLibrary);
|
| - return compiler.withCurrentElement(patchLibrary, () {
|
| - loader.registerNewLibrary(patchLibrary);
|
| - compiler.withCurrentElement(patchLibrary.entryCompilationUnit, () {
|
| - // This patches the elements of the patch library into [library].
|
| - // Injected elements are added directly under the compilation unit.
|
| - // Patch elements are stored on the patched functions or classes.
|
| - scanLibraryElements(patchLibrary.entryCompilationUnit);
|
| - });
|
| - return loader.processLibraryTags(patchLibrary);
|
| - });
|
| - });
|
| - }
|
| -
|
| - void scanLibraryElements(CompilationUnitElement compilationUnit) {
|
| - measure(() {
|
| - // TODO(johnniwinther): Test that parts and exports are handled correctly.
|
| - Script script = compilationUnit.script;
|
| - Token tokens = new Scanner(script.file).tokenize();
|
| - Function idGenerator = compiler.getNextFreeClassId;
|
| - Listener patchListener = new PatchElementListener(compiler,
|
| - compilationUnit,
|
| - idGenerator);
|
| - new PartialParser(patchListener).parseUnit(tokens);
|
| - });
|
| - }
|
| -
|
| - void parsePatchClassNode(PartialClassElement element) {
|
| - // Parse [PartialClassElement] using a "patch"-aware parser instead
|
| - // of calling its [parseNode] method.
|
| - if (element.cachedNode != null) return;
|
| -
|
| - measure(() => compiler.withCurrentElement(element, () {
|
| - MemberListener listener = new MemberListener(compiler, element);
|
| - Parser parser = new PatchClassElementParser(listener);
|
| - Token token = parser.parseTopLevelDeclaration(element.beginToken);
|
| - assert(identical(token, element.endToken.next));
|
| - element.cachedNode = listener.popNode();
|
| - assert(listener.nodes.isEmpty);
|
| -
|
| - Link<Element> patches = element.localMembers;
|
| - applyContainerPatch(element.origin, patches);
|
| - }));
|
| - }
|
| -
|
| - void applyContainerPatch(ClassElement originClass,
|
| - Link<Element> patches) {
|
| - for (Element patch in patches) {
|
| - if (!isPatchElement(compiler, patch)) continue;
|
| -
|
| - Element origin = originClass.localLookup(patch.name);
|
| - patchElement(compiler, origin, patch);
|
| - }
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Partial parser for patch files that also handles the members of class
|
| - * declarations.
|
| - */
|
| -class PatchClassElementParser extends PartialParser {
|
| - PatchClassElementParser(Listener listener) : super(listener);
|
| -
|
| - Token parseClassBody(Token token) => fullParseClassBody(token);
|
| -}
|
| -
|
| -/**
|
| - * Extension of [ElementListener] for parsing patch files.
|
| - */
|
| -class PatchElementListener extends ElementListener implements Listener {
|
| - final Compiler compiler;
|
| -
|
| - PatchElementListener(Compiler compiler,
|
| - CompilationUnitElement patchElement,
|
| - int idGenerator())
|
| - : this.compiler = compiler,
|
| - super(compiler, patchElement, idGenerator);
|
| -
|
| - void pushElement(Element patch) {
|
| - super.pushElement(patch);
|
| - if (isPatchElement(compiler, patch)) {
|
| - LibraryElement originLibrary = compilationUnitElement.library;
|
| - assert(originLibrary.isPatched);
|
| - Element origin = originLibrary.localLookup(patch.name);
|
| - patchElement(listener, origin, patch);
|
| - }
|
| - }
|
| -}
|
| -
|
| -void patchElement(Compiler compiler,
|
| - Element origin,
|
| - Element patch) {
|
| - if (origin == null) {
|
| - compiler.reportError(
|
| - patch, MessageKind.PATCH_NON_EXISTING, {'name': patch.name});
|
| - return;
|
| - }
|
| - if (!(origin.isClass ||
|
| - origin.isConstructor ||
|
| - origin.isFunction ||
|
| - origin.isAbstractField)) {
|
| - // TODO(ahe): Remove this error when the parser rejects all bad modifiers.
|
| - compiler.reportError(origin, MessageKind.PATCH_NONPATCHABLE);
|
| - return;
|
| - }
|
| - if (patch.isClass) {
|
| - tryPatchClass(compiler, origin, patch);
|
| - } else if (patch.isGetter) {
|
| - tryPatchGetter(compiler, origin, patch);
|
| - } else if (patch.isSetter) {
|
| - tryPatchSetter(compiler, origin, patch);
|
| - } else if (patch.isConstructor) {
|
| - tryPatchConstructor(compiler, origin, patch);
|
| - } else if(patch.isFunction) {
|
| - tryPatchFunction(compiler, origin, patch);
|
| - } else {
|
| - // TODO(ahe): Remove this error when the parser rejects all bad modifiers.
|
| - compiler.reportError(patch, MessageKind.PATCH_NONPATCHABLE);
|
| - }
|
| -}
|
| -
|
| -void tryPatchClass(Compiler compiler,
|
| - Element origin,
|
| - ClassElement patch) {
|
| - if (!origin.isClass) {
|
| - compiler.reportError(
|
| - origin, MessageKind.PATCH_NON_CLASS, {'className': patch.name});
|
| - compiler.reportInfo(
|
| - patch, MessageKind.PATCH_POINT_TO_CLASS, {'className': patch.name});
|
| - return;
|
| - }
|
| - patchClass(compiler, origin, patch);
|
| -}
|
| -
|
| -void patchClass(Compiler compiler,
|
| - ClassElementX origin,
|
| - ClassElementX patch) {
|
| - if (origin.isPatched) {
|
| - compiler.internalError(origin,
|
| - "Patching the same class more than once.");
|
| - }
|
| - origin.applyPatch(patch);
|
| - checkNativeAnnotation(compiler, patch);
|
| -}
|
| -
|
| -/// Check whether [cls] has a `@Native(...)` annotation, and if so, set its
|
| -/// native name from the annotation.
|
| -checkNativeAnnotation(Compiler compiler, ClassElement cls) {
|
| - EagerAnnotationHandler.checkAnnotation(compiler, cls,
|
| - const NativeAnnotationHandler());
|
| -}
|
| -
|
| -/// Abstract interface for pre-resolution detection of metadata.
|
| -///
|
| -/// The detection is handled in two steps:
|
| -/// - match the annotation syntactically and assume that the annotation is valid
|
| -/// if it looks correct,
|
| -/// - setup a deferred action to check that the annotation has a valid constant
|
| -/// value and report an internal error if not.
|
| -abstract class EagerAnnotationHandler {
|
| - /// Checks that [annotation] looks like a matching annotation and optionally
|
| - /// applies actions on [element]. Returns `true` if the annotation matched.
|
| - bool apply(Compiler compiler,
|
| - Element element,
|
| - MetadataAnnotation annotation);
|
| -
|
| - /// Checks that the annotation value is valid.
|
| - void validate(Compiler compiler,
|
| - Element element,
|
| - MetadataAnnotation annotation,
|
| - ConstantValue constant);
|
| -
|
| -
|
| - /// Checks [element] for metadata matching the [handler]. Return `true` if
|
| - /// matching metadata was found.
|
| - static bool checkAnnotation(Compiler compiler,
|
| - Element element,
|
| - EagerAnnotationHandler handler) {
|
| - for (Link<MetadataAnnotation> link = element.metadata;
|
| - !link.isEmpty;
|
| - link = link.tail) {
|
| - MetadataAnnotation annotation = link.head;
|
| - if (handler.apply(compiler, element, annotation)) {
|
| - // TODO(johnniwinther): Perform this check in
|
| - // [Compiler.onLibrariesLoaded].
|
| - compiler.enqueuer.resolution.addDeferredAction(element, () {
|
| - annotation.ensureResolved(compiler);
|
| - handler.validate(
|
| - compiler, element, annotation, annotation.constant.value);
|
| - });
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -}
|
| -
|
| -/// Annotation handler for pre-resolution detection of `@Native(...)`
|
| -/// annotations.
|
| -class NativeAnnotationHandler implements EagerAnnotationHandler {
|
| - const NativeAnnotationHandler();
|
| -
|
| - String getNativeAnnotation(MetadataAnnotation annotation) {
|
| - if (annotation.beginToken != null &&
|
| - annotation.beginToken.next.value == 'Native') {
|
| - // Skipping '@', 'Native', and '('.
|
| - Token argument = annotation.beginToken.next.next.next;
|
| - if (argument is StringToken) {
|
| - return argument.value;
|
| - }
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - bool apply(Compiler compiler,
|
| - Element element,
|
| - MetadataAnnotation annotation) {
|
| - if (element.isClass) {
|
| - String native = getNativeAnnotation(annotation);
|
| - if (native != null) {
|
| - ClassElementX declaration = element.declaration;
|
| - declaration.setNative(native);
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - void validate(Compiler compiler,
|
| - Element element,
|
| - MetadataAnnotation annotation,
|
| - ConstantValue constant) {
|
| - if (constant.computeType(compiler).element !=
|
| - compiler.nativeAnnotationClass) {
|
| - compiler.internalError(annotation, 'Invalid @Native(...) annotation.');
|
| - }
|
| - }
|
| -}
|
| -
|
| -/// Annotation handler for pre-resolution detection of `@patch` annotations.
|
| -class PatchAnnotationHandler implements EagerAnnotationHandler {
|
| - const PatchAnnotationHandler();
|
| -
|
| - bool isPatchAnnotation(MetadataAnnotation annotation) {
|
| - return annotation.beginToken != null &&
|
| - annotation.beginToken.next.value == 'patch';
|
| - }
|
| -
|
| - bool apply(Compiler compiler,
|
| - Element element,
|
| - MetadataAnnotation annotation) {
|
| - return isPatchAnnotation(annotation);
|
| - }
|
| -
|
| - void validate(Compiler compiler,
|
| - Element element,
|
| - MetadataAnnotation annotation,
|
| - ConstantValue constant) {
|
| - if (constant != compiler.patchConstant) {
|
| - compiler.internalError(annotation, 'Invalid patch annotation.');
|
| - }
|
| - }
|
| -}
|
| -
|
| -
|
| -void tryPatchGetter(DiagnosticListener listener,
|
| - Element origin,
|
| - FunctionElement patch) {
|
| - if (!origin.isAbstractField) {
|
| - listener.reportError(
|
| - origin, MessageKind.PATCH_NON_GETTER, {'name': origin.name});
|
| - listener.reportInfo(
|
| - patch,
|
| - MessageKind.PATCH_POINT_TO_GETTER, {'getterName': patch.name});
|
| - return;
|
| - }
|
| - AbstractFieldElement originField = origin;
|
| - if (originField.getter == null) {
|
| - listener.reportError(
|
| - origin, MessageKind.PATCH_NO_GETTER, {'getterName': patch.name});
|
| - listener.reportInfo(
|
| - patch,
|
| - MessageKind.PATCH_POINT_TO_GETTER, {'getterName': patch.name});
|
| - return;
|
| - }
|
| - patchFunction(listener, originField.getter, patch);
|
| -}
|
| -
|
| -void tryPatchSetter(DiagnosticListener listener,
|
| - Element origin,
|
| - FunctionElement patch) {
|
| - if (!origin.isAbstractField) {
|
| - listener.reportError(
|
| - origin, MessageKind.PATCH_NON_SETTER, {'name': origin.name});
|
| - listener.reportInfo(
|
| - patch,
|
| - MessageKind.PATCH_POINT_TO_SETTER, {'setterName': patch.name});
|
| - return;
|
| - }
|
| - AbstractFieldElement originField = origin;
|
| - if (originField.setter == null) {
|
| - listener.reportError(
|
| - origin, MessageKind.PATCH_NO_SETTER, {'setterName': patch.name});
|
| - listener.reportInfo(
|
| - patch,
|
| - MessageKind.PATCH_POINT_TO_SETTER, {'setterName': patch.name});
|
| - return;
|
| - }
|
| - patchFunction(listener, originField.setter, patch);
|
| -}
|
| -
|
| -void tryPatchConstructor(DiagnosticListener listener,
|
| - Element origin,
|
| - FunctionElement patch) {
|
| - if (!origin.isConstructor) {
|
| - listener.reportError(
|
| - origin,
|
| - MessageKind.PATCH_NON_CONSTRUCTOR, {'constructorName': patch.name});
|
| - listener.reportInfo(
|
| - patch,
|
| - MessageKind.PATCH_POINT_TO_CONSTRUCTOR,
|
| - {'constructorName': patch.name});
|
| - return;
|
| - }
|
| - patchFunction(listener, origin, patch);
|
| -}
|
| -
|
| -void tryPatchFunction(DiagnosticListener listener,
|
| - Element origin,
|
| - FunctionElement patch) {
|
| - if (!origin.isFunction) {
|
| - listener.reportError(
|
| - origin,
|
| - MessageKind.PATCH_NON_FUNCTION, {'functionName': patch.name});
|
| - listener.reportInfo(
|
| - patch,
|
| - MessageKind.PATCH_POINT_TO_FUNCTION, {'functionName': patch.name});
|
| - return;
|
| - }
|
| - patchFunction(listener, origin, patch);
|
| -}
|
| -
|
| -void patchFunction(DiagnosticListener listener,
|
| - FunctionElementX origin,
|
| - FunctionElementX patch) {
|
| - if (!origin.modifiers.isExternal) {
|
| - listener.reportError(origin, MessageKind.PATCH_NON_EXTERNAL);
|
| - listener.reportInfo(
|
| - patch,
|
| - MessageKind.PATCH_POINT_TO_FUNCTION, {'functionName': patch.name});
|
| - return;
|
| - }
|
| - if (origin.isPatched) {
|
| - listener.internalError(origin,
|
| - "Trying to patch a function more than once.");
|
| - }
|
| - origin.applyPatch(patch);
|
| -}
|
| -
|
| -// TODO(johnniwinther): Add unittest when patch is (real) metadata.
|
| -bool isPatchElement(Compiler compiler, Element element) {
|
| - return EagerAnnotationHandler.checkAnnotation(compiler, element,
|
| - const PatchAnnotationHandler());
|
| -}
|
|
|