| 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 541091c93cefc3c1c537b79b81ff67ea5c3aa7f4..22c2d9d9151dafd1c7a4de6243ea7d4207461a2e 100644
|
| --- a/sdk/lib/_internal/compiler/implementation/patch_parser.dart
|
| +++ b/sdk/lib/_internal/compiler/implementation/patch_parser.dart
|
| @@ -230,11 +230,11 @@ class PatchElementListener extends ElementListener implements Listener {
|
| }
|
| }
|
|
|
| -void patchElement(leg.DiagnosticListener listener,
|
| +void patchElement(leg.Compiler compiler,
|
| Element origin,
|
| Element patch) {
|
| if (origin == null) {
|
| - listener.reportError(
|
| + compiler.reportError(
|
| patch, leg.MessageKind.PATCH_NON_EXISTING, {'name': patch.name});
|
| return;
|
| }
|
| @@ -243,48 +243,168 @@ void patchElement(leg.DiagnosticListener listener,
|
| origin.isFunction ||
|
| origin.isAbstractField)) {
|
| // TODO(ahe): Remove this error when the parser rejects all bad modifiers.
|
| - listener.reportError(origin, leg.MessageKind.PATCH_NONPATCHABLE);
|
| + compiler.reportError(origin, leg.MessageKind.PATCH_NONPATCHABLE);
|
| return;
|
| }
|
| if (patch.isClass) {
|
| - tryPatchClass(listener, origin, patch);
|
| + tryPatchClass(compiler, origin, patch);
|
| } else if (patch.isGetter) {
|
| - tryPatchGetter(listener, origin, patch);
|
| + tryPatchGetter(compiler, origin, patch);
|
| } else if (patch.isSetter) {
|
| - tryPatchSetter(listener, origin, patch);
|
| + tryPatchSetter(compiler, origin, patch);
|
| } else if (patch.isConstructor) {
|
| - tryPatchConstructor(listener, origin, patch);
|
| + tryPatchConstructor(compiler, origin, patch);
|
| } else if(patch.isFunction) {
|
| - tryPatchFunction(listener, origin, patch);
|
| + tryPatchFunction(compiler, origin, patch);
|
| } else {
|
| // TODO(ahe): Remove this error when the parser rejects all bad modifiers.
|
| - listener.reportError(patch, leg.MessageKind.PATCH_NONPATCHABLE);
|
| + compiler.reportError(patch, leg.MessageKind.PATCH_NONPATCHABLE);
|
| }
|
| }
|
|
|
| -void tryPatchClass(leg.DiagnosticListener listener,
|
| - Element origin,
|
| - ClassElement patch) {
|
| +void tryPatchClass(leg.Compiler compiler,
|
| + Element origin,
|
| + ClassElement patch) {
|
| if (!origin.isClass) {
|
| - listener.reportError(
|
| + compiler.reportError(
|
| origin, leg.MessageKind.PATCH_NON_CLASS, {'className': patch.name});
|
| - listener.reportInfo(
|
| + compiler.reportInfo(
|
| patch, leg.MessageKind.PATCH_POINT_TO_CLASS, {'className': patch.name});
|
| return;
|
| }
|
| - patchClass(listener, origin, patch);
|
| + patchClass(compiler, origin, patch);
|
| }
|
|
|
| -void patchClass(leg.DiagnosticListener listener,
|
| +void patchClass(leg.Compiler compiler,
|
| ClassElementX origin,
|
| ClassElementX patch) {
|
| if (origin.isPatched) {
|
| - listener.internalError(origin,
|
| + 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(leg.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(leg.Compiler compiler,
|
| + Element element,
|
| + MetadataAnnotation annotation);
|
| +
|
| + /// Checks that the annotation value is valid.
|
| + void validate(leg.Compiler compiler,
|
| + Element element,
|
| + MetadataAnnotation annotation,
|
| + leg.Constant constant);
|
| +
|
| +
|
| + /// Checks [element] for metadata matching the [handler]. Return `true` if
|
| + /// matching metadata was found.
|
| + static bool checkAnnotation(leg.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.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(leg.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(leg.Compiler compiler,
|
| + Element element,
|
| + MetadataAnnotation annotation,
|
| + leg.Constant 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(leg.Compiler compiler,
|
| + Element element,
|
| + MetadataAnnotation annotation) {
|
| + return isPatchAnnotation(annotation);
|
| + }
|
| +
|
| + void validate(leg.Compiler compiler,
|
| + Element element,
|
| + MetadataAnnotation annotation,
|
| + leg.Constant constant) {
|
| + if (constant != compiler.patchConstant) {
|
| + compiler.internalError(annotation, 'Invalid patch annotation.');
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| void tryPatchGetter(leg.DiagnosticListener listener,
|
| Element origin,
|
| FunctionElement patch) {
|
| @@ -381,24 +501,6 @@ void patchFunction(leg.DiagnosticListener listener,
|
|
|
| // TODO(johnniwinther): Add unittest when patch is (real) metadata.
|
| bool isPatchElement(leg.Compiler compiler, 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<MetadataAnnotation> link = element.metadata;
|
| - !link.isEmpty;
|
| - link = link.tail) {
|
| - MetadataAnnotation annotation = link.head;
|
| - if (annotation.beginToken != null &&
|
| - annotation.beginToken.next.value == 'patch') {
|
| - // TODO(johnniwinther): Perform this check in
|
| - // [Compiler.onLibrariesLoaded].
|
| - compiler.enqueuer.resolution.addDeferredAction(element, () {
|
| - annotation.ensureResolved(compiler);
|
| - if (annotation.value != compiler.patchConstant) {
|
| - compiler.internalError(annotation, 'Invalid patch annotation.');
|
| - }
|
| - });
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| + return EagerAnnotationHandler.checkAnnotation(compiler, element,
|
| + const PatchAnnotationHandler());
|
| }
|
|
|