Add @pragma('dart2js:disableFinal')

Change-Id: I9b3f878803f946d08979144028d08b1a4dc4612f
Reviewed-on: https://dart-review.googlesource.com/c/86924
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Auto-Submit: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/frontend_strategy.dart b/pkg/compiler/lib/src/frontend_strategy.dart
index 1508076..23a1cee 100644
--- a/pkg/compiler/lib/src/frontend_strategy.dart
+++ b/pkg/compiler/lib/src/frontend_strategy.dart
@@ -12,6 +12,7 @@
 import 'elements/entities.dart';
 import 'elements/types.dart';
 import 'enqueue.dart';
+import 'js_backend/annotations.dart';
 import 'js_backend/allocator_analysis.dart' show KAllocatorAnalysis;
 import 'js_backend/backend_usage.dart';
 import 'js_backend/interceptor_data.dart';
@@ -72,6 +73,7 @@
       KAllocatorAnalysis allocatorAnalysis,
       NativeResolutionEnqueuer nativeResolutionEnqueuer,
       NoSuchMethodRegistry noSuchMethodRegistry,
+      AnnotationsDataBuilder annotationsDataBuilder,
       SelectorConstraintsStrategy selectorConstraintsStrategy,
       ClassHierarchyBuilder classHierarchyBuilder,
       ClassQueries classQueries);
@@ -81,6 +83,7 @@
   WorkItemBuilder createResolutionWorkItemBuilder(
       NativeBasicData nativeBasicData,
       NativeDataBuilder nativeDataBuilder,
+      AnnotationsDataBuilder annotationsDataBuilder,
       ImpactTransformer impactTransformer,
       Map<Entity, WorldImpact> impactCache);
 
diff --git a/pkg/compiler/lib/src/ir/static_type.dart b/pkg/compiler/lib/src/ir/static_type.dart
index 3206a45..4003b5b 100644
--- a/pkg/compiler/lib/src/ir/static_type.dart
+++ b/pkg/compiler/lib/src/ir/static_type.dart
@@ -40,8 +40,14 @@
 
   Map<ir.Expression, ir.DartType> get cachedStaticTypes => _cache;
 
+  /// If `true`, the effect of executing assert statements is taken into account
+  /// when computing the static type.
   bool get useAsserts;
 
+  /// If `true`, the static type of an effectively final variable is inferred
+  /// from the static type of its initializer.
+  bool get inferEffectivelyFinalVariableTypes;
+
   VariableScopeModel get variableScopeModel;
 
   bool completes(ir.DartType type) => type != const ir.BottomType();
@@ -1155,7 +1161,8 @@
     _currentVariables.add(node);
     ir.DartType type = visitNode(node.initializer);
     if (node.initializer != null &&
-        variableScopeModel.isEffectivelyFinal(node)) {
+        variableScopeModel.isEffectivelyFinal(node) &&
+        inferEffectivelyFinalVariableTypes) {
       node.type = type;
     }
     handleVariableDeclaration(node);
diff --git a/pkg/compiler/lib/src/js_backend/annotations.dart b/pkg/compiler/lib/src/js_backend/annotations.dart
index bd195e7..96ed552 100644
--- a/pkg/compiler/lib/src/js_backend/annotations.dart
+++ b/pkg/compiler/lib/src/js_backend/annotations.dart
@@ -42,141 +42,159 @@
   return false;
 }
 
-/// Process backend specific annotations.
-// TODO(johnniwinther): Merge this with [AnnotationProcessor].
-AnnotationsData processAnnotations(
+enum PragmaAnnotation {
+  noInline,
+  tryInline,
+  disableFinal,
+  noThrows,
+  noSideEffects,
+  trustTypeAnnotations,
+  assumeDynamic,
+}
+
+Set<PragmaAnnotation> processMemberAnnotations(
     DiagnosticReporter reporter,
     KCommonElements commonElements,
     KElementEnvironment elementEnvironment,
-    Iterable<MemberEntity> processedMembers) {
-  AnnotationsDataBuilder annotationsDataBuilder = new AnnotationsDataBuilder();
+    AnnotationsDataBuilder annotationsDataBuilder,
+    MemberEntity element) {
+  Set<PragmaAnnotation> values = new Set<PragmaAnnotation>();
+  bool hasNoInline = false;
+  bool hasTryInline = false;
+  bool disableFinal = false;
 
-  void processMemberAnnotations(MemberEntity element) {
-    bool hasNoInline = false;
-    bool hasTryInline = false;
+  if (_trustTypeAnnotations(elementEnvironment, commonElements, element)) {
+    values.add(PragmaAnnotation.trustTypeAnnotations);
+    annotationsDataBuilder.registerTrustTypeAnnotations(element);
+  }
 
-    if (_trustTypeAnnotations(elementEnvironment, commonElements, element)) {
-      annotationsDataBuilder.registerTrustTypeAnnotations(element);
-    }
+  if (_assumeDynamic(elementEnvironment, commonElements, element)) {
+    values.add(PragmaAnnotation.assumeDynamic);
+    annotationsDataBuilder.registerAssumeDynamic(element);
+  }
 
-    if (_assumeDynamic(elementEnvironment, commonElements, element)) {
-      annotationsDataBuilder.registerAssumeDynamic(element);
-    }
+  // TODO(sra): Check for inappropriate annotations on fields.
+  if (element.isField) {
+    return values;
+  }
 
-    // TODO(sra): Check for inappropriate annotations on fields.
-    if (element.isField) return;
+  FunctionEntity method = element;
+  LibraryEntity library = element.library;
+  bool platformAnnotationsAllowed = library.canonicalUri.scheme == 'dart' ||
+      maybeEnableNative(library.canonicalUri);
 
-    FunctionEntity method = element;
-    LibraryEntity library = element.library;
-    bool platformAnnotationsAllowed = library.canonicalUri.scheme == 'dart' ||
-        maybeEnableNative(library.canonicalUri);
+  bool hasNoThrows = false;
+  bool hasNoSideEffects = false;
 
-    bool hasNoThrows = false;
-    bool hasNoSideEffects = false;
+  for (ConstantValue constantValue
+      in elementEnvironment.getMemberMetadata(method)) {
+    if (!constantValue.isConstructedObject) continue;
+    ConstructedConstantValue value = constantValue;
+    ClassEntity cls = value.type.element;
+    assert(cls != null); // Unresolved classes null.
 
-    for (ConstantValue constantValue
-        in elementEnvironment.getMemberMetadata(method)) {
-      if (!constantValue.isConstructedObject) continue;
-      ConstructedConstantValue value = constantValue;
-      ClassEntity cls = value.type.element;
-      assert(cls != null); // Unresolved classes null.
-
-      if (platformAnnotationsAllowed) {
-        if (cls == commonElements.forceInlineClass) {
-          hasTryInline = true;
-        } else if (cls == commonElements.noInlineClass) {
-          hasNoInline = true;
-        } else if (cls == commonElements.noThrowsClass) {
-          hasNoThrows = true;
-          bool isValid = true;
-          if (method.isTopLevel) {
-            isValid = true;
-          } else if (method.isStatic) {
-            isValid = true;
-          } else if (method is ConstructorEntity &&
-              method.isFactoryConstructor) {
-            isValid = true;
-          }
-          if (!isValid) {
-            reporter.internalError(
-                method,
-                "@NoThrows() is currently limited to top-level"
-                " or static functions and factory constructors.");
-          }
-          annotationsDataBuilder.registerCannotThrow(method);
-        } else if (cls == commonElements.noSideEffectsClass) {
-          hasNoSideEffects = true;
-          annotationsDataBuilder.registerSideEffectsFree(method);
-        }
-      }
-
-      if (cls == commonElements.expectNoInlineClass) {
-        hasNoInline = true;
-      } else if (cls == commonElements.metaNoInlineClass) {
-        hasNoInline = true;
-      } else if (cls == commonElements.metaTryInlineClass) {
+    if (platformAnnotationsAllowed) {
+      if (cls == commonElements.forceInlineClass) {
         hasTryInline = true;
-      } else if (cls == commonElements.pragmaClass) {
-        // Recognize:
-        //
-        //     @pragma('dart2js:noInline')
-        //     @pragma('dart2js:tryInline')
-        //
-        ConstantValue nameValue =
-            value.fields[commonElements.pragmaClassNameField];
-        if (nameValue == null || !nameValue.isString) continue;
-        String name = (nameValue as StringConstantValue).stringValue;
-        if (!name.startsWith('dart2js:')) continue;
-
-        ConstantValue optionsValue =
-            value.fields[commonElements.pragmaClassOptionsField];
-        if (name == 'dart2js:noInline') {
-          if (!optionsValue.isNull) {
-            reporter.reportErrorMessage(element, MessageKind.GENERIC,
-                {'text': "@pragma('$name') annotation does not take options"});
-          }
-          hasNoInline = true;
-        } else if (name == 'dart2js:tryInline') {
-          if (!optionsValue.isNull) {
-            reporter.reportErrorMessage(element, MessageKind.GENERIC,
-                {'text': "@pragma('$name') annotation does not take options"});
-          }
-          hasTryInline = true;
-        } else if (!platformAnnotationsAllowed) {
-          reporter.reportErrorMessage(element, MessageKind.GENERIC,
-              {'text': "Unknown dart2js pragma @pragma('$name')"});
-        } else {
-          // Handle platform-only `@pragma` annotations.
+      } else if (cls == commonElements.noInlineClass) {
+        hasNoInline = true;
+      } else if (cls == commonElements.noThrowsClass) {
+        hasNoThrows = true;
+        bool isValid = true;
+        if (method.isTopLevel) {
+          isValid = true;
+        } else if (method.isStatic) {
+          isValid = true;
+        } else if (method is ConstructorEntity && method.isFactoryConstructor) {
+          isValid = true;
         }
+        if (!isValid) {
+          reporter.internalError(
+              method,
+              "@NoThrows() is currently limited to top-level"
+              " or static functions and factory constructors.");
+        }
+        annotationsDataBuilder.registerCannotThrow(method);
+      } else if (cls == commonElements.noSideEffectsClass) {
+        hasNoSideEffects = true;
+        annotationsDataBuilder.registerSideEffectsFree(method);
       }
     }
 
-    if (hasTryInline && hasNoInline) {
-      reporter.reportErrorMessage(element, MessageKind.GENERIC,
-          {'text': '@tryInline must not be used with @noInline.'});
-      hasTryInline = false;
-    }
-    if (hasNoInline) {
-      annotationsDataBuilder.markAsNonInlinable(method);
-    }
-    if (hasTryInline) {
-      annotationsDataBuilder.markAsTryInline(method);
-    }
-    if (hasNoThrows && !hasNoInline) {
-      reporter.internalError(
-          method, "@NoThrows() should always be combined with @noInline.");
-    }
-    if (hasNoSideEffects && !hasNoInline) {
-      reporter.internalError(
-          method, "@NoSideEffects() should always be combined with @noInline.");
+    if (cls == commonElements.expectNoInlineClass) {
+      hasNoInline = true;
+    } else if (cls == commonElements.metaNoInlineClass) {
+      hasNoInline = true;
+    } else if (cls == commonElements.metaTryInlineClass) {
+      hasTryInline = true;
+    } else if (cls == commonElements.pragmaClass) {
+      // Recognize:
+      //
+      //     @pragma('dart2js:noInline')
+      //     @pragma('dart2js:tryInline')
+      //
+      ConstantValue nameValue =
+          value.fields[commonElements.pragmaClassNameField];
+      if (nameValue == null || !nameValue.isString) continue;
+      String name = (nameValue as StringConstantValue).stringValue;
+      if (!name.startsWith('dart2js:')) continue;
+
+      ConstantValue optionsValue =
+          value.fields[commonElements.pragmaClassOptionsField];
+      if (name == 'dart2js:noInline') {
+        if (!optionsValue.isNull) {
+          reporter.reportErrorMessage(element, MessageKind.GENERIC,
+              {'text': "@pragma('$name') annotation does not take options"});
+        }
+        hasNoInline = true;
+      } else if (name == 'dart2js:tryInline') {
+        if (!optionsValue.isNull) {
+          reporter.reportErrorMessage(element, MessageKind.GENERIC,
+              {'text': "@pragma('$name') annotation does not take options"});
+        }
+        hasTryInline = true;
+      } else if (!platformAnnotationsAllowed) {
+        reporter.reportErrorMessage(element, MessageKind.GENERIC,
+            {'text': "Unknown dart2js pragma @pragma('$name')"});
+      } else {
+        // Handle platform-only `@pragma` annotations.
+        if (name == 'dart2js:disableFinal') {
+          if (!optionsValue.isNull) {
+            reporter.reportErrorMessage(element, MessageKind.GENERIC,
+                {'text': "@pragma('$name') annotation does not take options"});
+          }
+          disableFinal = true;
+        }
+      }
     }
   }
 
-  for (MemberEntity entity in processedMembers) {
-    processMemberAnnotations(entity);
+  if (hasTryInline && hasNoInline) {
+    reporter.reportErrorMessage(element, MessageKind.GENERIC,
+        {'text': '@tryInline must not be used with @noInline.'});
+    hasTryInline = false;
   }
-
-  return annotationsDataBuilder;
+  if (hasNoInline) {
+    values.add(PragmaAnnotation.noInline);
+    annotationsDataBuilder.markAsNonInlinable(method);
+  }
+  if (hasTryInline) {
+    values.add(PragmaAnnotation.tryInline);
+    annotationsDataBuilder.markAsTryInline(method);
+  }
+  if (disableFinal) {
+    values.add(PragmaAnnotation.disableFinal);
+    annotationsDataBuilder.markAsDisableFinal(method);
+  }
+  if (hasNoThrows && !hasNoInline) {
+    reporter.internalError(
+        method, "@NoThrows() should always be combined with @noInline.");
+  }
+  if (hasNoSideEffects && !hasNoInline) {
+    reporter.internalError(
+        method, "@NoSideEffects() should always be combined with @noInline.");
+  }
+  return values;
 }
 
 abstract class AnnotationsData {
@@ -187,12 +205,17 @@
   /// Serializes this [AnnotationsData] to [sink].
   void writeToDataSink(DataSink sink);
 
-  /// Functions with a `@NoInline()` or `@noInline` annotation.
+  /// Functions with a `@NoInline()`, `@noInline`, or
+  /// `@pragma('dart2js:noInline')` annotation.
   Iterable<FunctionEntity> get nonInlinableFunctions;
 
-  /// Functions with a `@ForceInline()` or `@tryInline` annotation.
+  /// Functions with a `@ForceInline()`, `@tryInline`, or
+  /// `@pragma('dart2js:tryInline')` annotation.
   Iterable<FunctionEntity> get tryInlineFunctions;
 
+  /// Functions with a `@pragma('dart2js:disable-final')` annotation.
+  Iterable<FunctionEntity> get disableFinalFunctions;
+
   /// Functions with a `@NoThrows()` annotation.
   Iterable<FunctionEntity> get cannotThrowFunctions;
 
@@ -213,6 +236,7 @@
 
   final Iterable<FunctionEntity> nonInlinableFunctions;
   final Iterable<FunctionEntity> tryInlineFunctions;
+  final Iterable<FunctionEntity> disableFinalFunctions;
   final Iterable<FunctionEntity> cannotThrowFunctions;
   final Iterable<FunctionEntity> sideEffectFreeFunctions;
   final Iterable<MemberEntity> trustTypeAnnotationsMembers;
@@ -221,6 +245,7 @@
   AnnotationsDataImpl(
       this.nonInlinableFunctions,
       this.tryInlineFunctions,
+      this.disableFinalFunctions,
       this.cannotThrowFunctions,
       this.sideEffectFreeFunctions,
       this.trustTypeAnnotationsMembers,
@@ -228,16 +253,32 @@
 
   factory AnnotationsDataImpl.readFromDataSource(DataSource source) {
     source.begin(tag);
-    Iterable<FunctionEntity> nonInlinableFunctions = source.readMembers();
-    Iterable<FunctionEntity> tryInlineFunctions = source.readMembers();
-    Iterable<FunctionEntity> cannotThrowFunctions = source.readMembers();
-    Iterable<FunctionEntity> sideEffectFreeFunctions = source.readMembers();
-    Iterable<MemberEntity> trustTypeAnnotationsMembers = source.readMembers();
-    Iterable<MemberEntity> assumeDynamicMembers = source.readMembers();
+    Iterable<FunctionEntity> nonInlinableFunctions =
+        source.readMembers<FunctionEntity>(emptyAsNull: true) ??
+            const <FunctionEntity>[];
+    Iterable<FunctionEntity> tryInlineFunctions =
+        source.readMembers<FunctionEntity>(emptyAsNull: true) ??
+            const <FunctionEntity>[];
+    Iterable<FunctionEntity> disableFinalFunctions =
+        source.readMembers<FunctionEntity>(emptyAsNull: true) ??
+            const <FunctionEntity>[];
+    Iterable<FunctionEntity> cannotThrowFunctions =
+        source.readMembers<FunctionEntity>(emptyAsNull: true) ??
+            const <FunctionEntity>[];
+    Iterable<FunctionEntity> sideEffectFreeFunctions =
+        source.readMembers<FunctionEntity>(emptyAsNull: true) ??
+            const <FunctionEntity>[];
+    Iterable<MemberEntity> trustTypeAnnotationsMembers =
+        source.readMembers<MemberEntity>(emptyAsNull: true) ??
+            const <MemberEntity>[];
+    Iterable<MemberEntity> assumeDynamicMembers =
+        source.readMembers<MemberEntity>(emptyAsNull: true) ??
+            const <MemberEntity>[];
     source.end(tag);
     return new AnnotationsDataImpl(
         nonInlinableFunctions,
         tryInlineFunctions,
+        disableFinalFunctions,
         cannotThrowFunctions,
         sideEffectFreeFunctions,
         trustTypeAnnotationsMembers,
@@ -248,6 +289,7 @@
     sink.begin(tag);
     sink.writeMembers(nonInlinableFunctions);
     sink.writeMembers(tryInlineFunctions);
+    sink.writeMembers(disableFinalFunctions);
     sink.writeMembers(cannotThrowFunctions);
     sink.writeMembers(sideEffectFreeFunctions);
     sink.writeMembers(trustTypeAnnotationsMembers);
@@ -257,45 +299,63 @@
 }
 
 class AnnotationsDataBuilder implements AnnotationsData {
-  List<FunctionEntity> _nonInlinableFunctions = <FunctionEntity>[];
-  List<FunctionEntity> _tryInlinableFunctions = <FunctionEntity>[];
-  List<FunctionEntity> _cannotThrowFunctions = <FunctionEntity>[];
-  List<FunctionEntity> _sideEffectFreeFunctions = <FunctionEntity>[];
-  List<MemberEntity> _trustTypeAnnotationsMembers = <MemberEntity>[];
-  List<MemberEntity> _assumeDynamicMembers = <MemberEntity>[];
+  List<FunctionEntity> _nonInlinableFunctions;
+  List<FunctionEntity> _tryInlinableFunctions;
+  List<FunctionEntity> _disableFinalFunctions;
+  List<FunctionEntity> _cannotThrowFunctions;
+  List<FunctionEntity> _sideEffectFreeFunctions;
+  List<MemberEntity> _trustTypeAnnotationsMembers;
+  List<MemberEntity> _assumeDynamicMembers;
 
   void markAsNonInlinable(FunctionEntity function) {
+    _nonInlinableFunctions ??= <FunctionEntity>[];
     _nonInlinableFunctions.add(function);
   }
 
   void markAsTryInline(FunctionEntity function) {
+    _tryInlinableFunctions ??= <FunctionEntity>[];
     _tryInlinableFunctions.add(function);
   }
 
+  void markAsDisableFinal(FunctionEntity function) {
+    _disableFinalFunctions ??= <FunctionEntity>[];
+    _disableFinalFunctions.add(function);
+  }
+
   void registerCannotThrow(FunctionEntity function) {
+    _cannotThrowFunctions ??= <FunctionEntity>[];
     _cannotThrowFunctions.add(function);
   }
 
   void registerSideEffectsFree(FunctionEntity function) {
+    _sideEffectFreeFunctions ??= <FunctionEntity>[];
     _sideEffectFreeFunctions.add(function);
   }
 
   void registerTrustTypeAnnotations(MemberEntity member) {
+    _trustTypeAnnotationsMembers ??= <MemberEntity>[];
     _trustTypeAnnotationsMembers.add(member);
   }
 
   void registerAssumeDynamic(MemberEntity member) {
+    _assumeDynamicMembers ??= <MemberEntity>[];
     _assumeDynamicMembers.add(member);
   }
 
-  Iterable<FunctionEntity> get nonInlinableFunctions => _nonInlinableFunctions;
-  Iterable<FunctionEntity> get tryInlineFunctions => _tryInlinableFunctions;
-  Iterable<FunctionEntity> get cannotThrowFunctions => _cannotThrowFunctions;
+  Iterable<FunctionEntity> get nonInlinableFunctions =>
+      _nonInlinableFunctions ?? const <FunctionEntity>[];
+  Iterable<FunctionEntity> get tryInlineFunctions =>
+      _tryInlinableFunctions ?? const <FunctionEntity>[];
+  Iterable<FunctionEntity> get disableFinalFunctions =>
+      _disableFinalFunctions ?? const <FunctionEntity>[];
+  Iterable<FunctionEntity> get cannotThrowFunctions =>
+      _cannotThrowFunctions ?? const <FunctionEntity>[];
   Iterable<FunctionEntity> get sideEffectFreeFunctions =>
-      _sideEffectFreeFunctions;
+      _sideEffectFreeFunctions ?? const <FunctionEntity>[];
   Iterable<MemberEntity> get trustTypeAnnotationsMembers =>
-      _trustTypeAnnotationsMembers;
-  Iterable<MemberEntity> get assumeDynamicMembers => _assumeDynamicMembers;
+      _trustTypeAnnotationsMembers ?? const <MemberEntity>[];
+  Iterable<MemberEntity> get assumeDynamicMembers =>
+      _assumeDynamicMembers ?? const <MemberEntity>[];
 
   void writeToDataSink(DataSink sink) {
     throw new UnsupportedError('AnnotationsDataBuilder.writeToDataSink');
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index 22b77c3..1bdea15 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -574,6 +574,8 @@
     InterceptorDataBuilder interceptorDataBuilder =
         new InterceptorDataBuilderImpl(
             nativeBasicData, elementEnvironment, commonElements);
+    AnnotationsDataBuilder annotationsDataBuilder =
+        new AnnotationsDataBuilder();
     return new ResolutionEnqueuer(
         task,
         compiler.options,
@@ -600,12 +602,14 @@
             _allocatorResolutionAnalysis,
             _nativeResolutionEnqueuer,
             noSuchMethodRegistry,
+            annotationsDataBuilder,
             const StrongModeWorldStrategy(),
             classHierarchyBuilder,
             classQueries),
         compiler.frontendStrategy.createResolutionWorkItemBuilder(
             nativeBasicData,
             _nativeDataBuilder,
+            annotationsDataBuilder,
             impactTransformer,
             compiler.impactCache));
   }
diff --git a/pkg/compiler/lib/src/js_model/js_world_builder.dart b/pkg/compiler/lib/src/js_model/js_world_builder.dart
index 5110d31..b3ad9ca 100644
--- a/pkg/compiler/lib/src/js_model/js_world_builder.dart
+++ b/pkg/compiler/lib/src/js_model/js_world_builder.dart
@@ -198,6 +198,8 @@
         map.toBackendFunctionSet(
             closedWorld.annotationsData.tryInlineFunctions),
         map.toBackendFunctionSet(
+            closedWorld.annotationsData.disableFinalFunctions),
+        map.toBackendFunctionSet(
             closedWorld.annotationsData.cannotThrowFunctions),
         map.toBackendFunctionSet(
             closedWorld.annotationsData.sideEffectFreeFunctions),
diff --git a/pkg/compiler/lib/src/kernel/element_map_impl.dart b/pkg/compiler/lib/src/kernel/element_map_impl.dart
index 5ea5049..d15fb43 100644
--- a/pkg/compiler/lib/src/kernel/element_map_impl.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_impl.dart
@@ -35,6 +35,7 @@
 import '../ir/visitors.dart';
 import '../ir/util.dart';
 import '../js/js.dart' as js;
+import '../js_backend/annotations.dart';
 import '../js_backend/backend.dart' show JavaScriptBackend;
 import '../js_backend/constant_system_javascript.dart';
 import '../js_backend/namer.dart';
@@ -1337,11 +1338,13 @@
           commonElements, nativeBasicData, reporter, options);
 
   ResolutionImpact computeWorldImpact(
-      KMember member, VariableScopeModel variableScopeModel) {
+      KMember member,
+      VariableScopeModel variableScopeModel,
+      Set<PragmaAnnotation> annotations) {
     KMemberData memberData = members.getData(member);
     ir.Member node = memberData.node;
     KernelImpactBuilder builder = new KernelImpactBuilder(
-        this, member, reporter, options, variableScopeModel);
+        this, member, reporter, options, variableScopeModel, annotations);
     node.accept(builder);
     memberData.staticTypes = builder.cachedStaticTypes;
     return builder.impactBuilder;
diff --git a/pkg/compiler/lib/src/kernel/kernel_impact.dart b/pkg/compiler/lib/src/kernel/kernel_impact.dart
index 431c832..ee62aa6 100644
--- a/pkg/compiler/lib/src/kernel/kernel_impact.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_impact.dart
@@ -17,6 +17,7 @@
 import '../ir/scope.dart';
 import '../ir/static_type.dart';
 import '../ir/util.dart';
+import '../js_backend/annotations.dart';
 import '../js_backend/native_data.dart';
 import '../options.dart';
 import '../resolution/registry.dart' show ResolutionWorldImpactBuilder;
@@ -35,9 +36,10 @@
   final CompilerOptions _options;
   final MemberEntity currentMember;
   final VariableScopeModel variableScopeModel;
+  final Set<PragmaAnnotation> _annotations;
 
   KernelImpactBuilder(this.elementMap, this.currentMember, this.reporter,
-      this._options, this.variableScopeModel)
+      this._options, this.variableScopeModel, this._annotations)
       : this.impactBuilder =
             new ResolutionWorldImpactBuilder('${currentMember}'),
         super(elementMap.typeEnvironment);
@@ -48,6 +50,9 @@
 
   bool get useAsserts => _options.enableUserAssertions;
 
+  bool get inferEffectivelyFinalVariableTypes =>
+      !_annotations.contains(PragmaAnnotation.disableFinal);
+
   /// Add a checked-mode type use of [type] if it is not `dynamic`.
   DartType checkType(ir.DartType irType, TypeUseKind kind) {
     DartType type = elementMap.getDartType(irType);
diff --git a/pkg/compiler/lib/src/kernel/kernel_strategy.dart b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
index 31c810f..32d2c7a 100644
--- a/pkg/compiler/lib/src/kernel/kernel_strategy.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
@@ -19,6 +19,7 @@
 import '../frontend_strategy.dart';
 import '../ir/closure.dart' show ClosureScopeModel;
 import '../ir/scope.dart' show ScopeModel;
+import '../js_backend/annotations.dart';
 import '../js_backend/allocator_analysis.dart' show KAllocatorAnalysis;
 import '../js_backend/backend_usage.dart';
 import '../js_backend/interceptor_data.dart';
@@ -114,6 +115,7 @@
       KAllocatorAnalysis allocatorAnalysis,
       NativeResolutionEnqueuer nativeResolutionEnqueuer,
       NoSuchMethodRegistry noSuchMethodRegistry,
+      AnnotationsDataBuilder annotationsDataBuilder,
       SelectorConstraintsStrategy selectorConstraintsStrategy,
       ClassHierarchyBuilder classHierarchyBuilder,
       ClassQueries classQueries) {
@@ -131,6 +133,7 @@
         allocatorAnalysis,
         nativeResolutionEnqueuer,
         noSuchMethodRegistry,
+        annotationsDataBuilder,
         selectorConstraintsStrategy,
         classHierarchyBuilder,
         classQueries);
@@ -140,10 +143,18 @@
   WorkItemBuilder createResolutionWorkItemBuilder(
       NativeBasicData nativeBasicData,
       NativeDataBuilder nativeDataBuilder,
+      AnnotationsDataBuilder annotationsDataBuilder,
       ImpactTransformer impactTransformer,
       Map<Entity, WorldImpact> impactCache) {
-    return new KernelWorkItemBuilder(_compilerTask, elementMap, nativeBasicData,
-        nativeDataBuilder, impactTransformer, closureModels, impactCache);
+    return new KernelWorkItemBuilder(
+        _compilerTask,
+        elementMap,
+        nativeBasicData,
+        nativeDataBuilder,
+        annotationsDataBuilder,
+        impactTransformer,
+        closureModels,
+        impactCache);
   }
 
   ClassQueries createClassQueries() {
@@ -161,6 +172,7 @@
   final KernelToElementMapImpl _elementMap;
   final ImpactTransformer _impactTransformer;
   final NativeMemberResolver _nativeMemberResolver;
+  final AnnotationsDataBuilder _annotationsDataBuilder;
   final Map<MemberEntity, ClosureScopeModel> closureModels;
   final Map<Entity, WorldImpact> impactCache;
 
@@ -169,6 +181,7 @@
       this._elementMap,
       NativeBasicData nativeBasicData,
       NativeDataBuilder nativeDataBuilder,
+      this._annotationsDataBuilder,
       this._impactTransformer,
       this.closureModels,
       this.impactCache)
@@ -177,8 +190,15 @@
 
   @override
   WorkItem createWorkItem(MemberEntity entity) {
-    return new KernelWorkItem(_compilerTask, _elementMap, _impactTransformer,
-        _nativeMemberResolver, entity, closureModels, impactCache);
+    return new KernelWorkItem(
+        _compilerTask,
+        _elementMap,
+        _impactTransformer,
+        _nativeMemberResolver,
+        _annotationsDataBuilder,
+        entity,
+        closureModels,
+        impactCache);
   }
 }
 
@@ -187,6 +207,7 @@
   final KernelToElementMapImpl _elementMap;
   final ImpactTransformer _impactTransformer;
   final NativeMemberResolver _nativeMemberResolver;
+  final AnnotationsDataBuilder _annotationsDataBuilder;
   final MemberEntity element;
   final Map<MemberEntity, ClosureScopeModel> closureModels;
   final Map<Entity, WorldImpact> impactCache;
@@ -196,6 +217,7 @@
       this._elementMap,
       this._impactTransformer,
       this._nativeMemberResolver,
+      this._annotationsDataBuilder,
       this.element,
       this.closureModels,
       this.impactCache);
@@ -204,6 +226,12 @@
   WorldImpact run() {
     return _compilerTask.measure(() {
       _nativeMemberResolver.resolveNativeMember(element);
+      Set<PragmaAnnotation> annotations = processMemberAnnotations(
+          _elementMap.reporter,
+          _elementMap.commonElements,
+          _elementMap.elementEnvironment,
+          _annotationsDataBuilder,
+          element);
       ScopeModel scopeModel = _compilerTask.measureSubtask('closures', () {
         ScopeModel scopeModel = _elementMap.computeScopeModel(element);
         if (scopeModel?.closureScopeModel != null) {
@@ -213,7 +241,7 @@
       });
       return _compilerTask.measureSubtask('worldImpact', () {
         ResolutionImpact impact = _elementMap.computeWorldImpact(
-            element, scopeModel?.variableScopeModel);
+            element, scopeModel?.variableScopeModel, annotations);
         WorldImpact worldImpact =
             _impactTransformer.transformResolutionImpact(impact);
         if (impactCache != null) {
diff --git a/pkg/compiler/lib/src/serialization/member_data.dart b/pkg/compiler/lib/src/serialization/member_data.dart
index 69bb187..b4aeee12 100644
--- a/pkg/compiler/lib/src/serialization/member_data.dart
+++ b/pkg/compiler/lib/src/serialization/member_data.dart
@@ -21,7 +21,9 @@
         _libraryMap[library.importUri] = new _LibraryData(library);
       }
     }
-    return _libraryMap[canonicalUri];
+    _LibraryData data = _libraryMap[canonicalUri];
+    assert(data != null, "No library found for $canonicalUri.");
+    return data;
   }
 }
 
diff --git a/pkg/compiler/lib/src/universe/resolution_world_builder.dart b/pkg/compiler/lib/src/universe/resolution_world_builder.dart
index 39caad3..6e75f80 100644
--- a/pkg/compiler/lib/src/universe/resolution_world_builder.dart
+++ b/pkg/compiler/lib/src/universe/resolution_world_builder.dart
@@ -399,6 +399,7 @@
   final KAllocatorAnalysis _allocatorAnalysis;
   final NativeResolutionEnqueuer _nativeResolutionEnqueuer;
   final NoSuchMethodRegistry _noSuchMethodRegistry;
+  final AnnotationsDataBuilder _annotationsDataBuilder;
 
   final SelectorConstraintsStrategy _selectorConstraintsStrategy;
   final ClassHierarchyBuilder _classHierarchyBuilder;
@@ -435,6 +436,7 @@
       this._allocatorAnalysis,
       this._nativeResolutionEnqueuer,
       this._noSuchMethodRegistry,
+      this._annotationsDataBuilder,
       this._selectorConstraintsStrategy,
       this._classHierarchyBuilder,
       this._classQueries);
@@ -1024,9 +1026,6 @@
     BackendUsage backendUsage = _backendUsageBuilder.close();
     _closed = true;
 
-    AnnotationsData annotationsData = processAnnotations(
-        reporter, _commonElements, _elementEnvironment, _processedMembers);
-
     KClosedWorld closedWorld = new KClosedWorldImpl(_elementMap,
         options: _options,
         elementEnvironment: _elementEnvironment,
@@ -1047,7 +1046,7 @@
         mixinUses: _classHierarchyBuilder.mixinUses,
         typesImplementedBySubclasses: typesImplementedBySubclasses,
         classHierarchy: _classHierarchyBuilder.close(),
-        annotationsData: annotationsData);
+        annotationsData: _annotationsDataBuilder);
     if (retainDataForTesting) {
       _closedWorldCache = closedWorld;
     }
diff --git a/tests/compiler/dart2js/analyses/analysis_helper.dart b/tests/compiler/dart2js/analyses/analysis_helper.dart
index fec52d9..9fcd366 100644
--- a/tests/compiler/dart2js/analyses/analysis_helper.dart
+++ b/tests/compiler/dart2js/analyses/analysis_helper.dart
@@ -79,6 +79,9 @@
   bool get useAsserts => false;
 
   @override
+  bool get inferEffectivelyFinalVariableTypes => true;
+
+  @override
   Null visitProcedure(ir.Procedure node) {
     if (node.kind == ir.ProcedureKind.Factory) {
       if (node.function.body is ir.RedirectingFactoryBody) {
diff --git a/tests/compiler/dart2js/rti/data/as.dart b/tests/compiler/dart2js/rti/data/as.dart
index 6f2de47..b84f239 100644
--- a/tests/compiler/dart2js/rti/data/as.dart
+++ b/tests/compiler/dart2js/rti/data/as.dart
@@ -15,6 +15,7 @@
 /*class: Baz:explicit=[Baz<num>],needsArgs*/
 class Baz<T extends num> {}
 
+@pragma('dart2js:disableFinal')
 main() {
   test(new Foo(), Foo, expectTypeArguments: false);
   // ignore: unnecessary_cast
@@ -22,9 +23,6 @@
   Baz<num> b = new Baz();
   dynamic c = b;
   test(c as Baz<num>, Baz, expectTypeArguments: true);
-  // Makes sure that [c] is not effectively final, so that we don't infer the
-  // static type from the initializer.
-  c = null;
 }
 
 void test(dynamic object, Type type, {bool expectTypeArguments}) {
diff --git a/tests/compiler/dart2js_extra/35341_test.dart b/tests/compiler/dart2js_extra/35341_test.dart
index c19ca81..027182f 100644
--- a/tests/compiler/dart2js_extra/35341_test.dart
+++ b/tests/compiler/dart2js_extra/35341_test.dart
@@ -6,11 +6,9 @@
 
 import "dart:async";
 
+@pragma('dart2js:disableFinal')
 void main() {
   FutureOr<int> i = 0;
   i = new Future<int>.value(0);
   print(i.runtimeType);
-  // Ensure that [i] is not effectively final, so that we don't infer the
-  // static type from the initializer.
-  i = null;
 }
diff --git a/tests/compiler/dart2js_extra/881_test.dart b/tests/compiler/dart2js_extra/881_test.dart
index dfc9577..5be849c 100644
--- a/tests/compiler/dart2js_extra/881_test.dart
+++ b/tests/compiler/dart2js_extra/881_test.dart
@@ -4,10 +4,8 @@
 
 // Regression test for DartPad issue 881.
 
+@pragma('dart2js:disableFinal')
 void main() {
   String v = null;
   print('${v.hashCode}');
-  // Makes sure that [v] is not effectively final, so that we don't infer the
-  // static type from the initializer.
-  v = '';
 }
diff --git a/tests/compiler/dart2js_extra/generic_type_error_message_test.dart b/tests/compiler/dart2js_extra/generic_type_error_message_test.dart
index d331999..038fecf 100644
--- a/tests/compiler/dart2js_extra/generic_type_error_message_test.dart
+++ b/tests/compiler/dart2js_extra/generic_type_error_message_test.dart
@@ -12,15 +12,13 @@
 
 class Baz<T extends num> {}
 
+@pragma('dart2js:disableFinal')
 main() {
   test(new Foo(), Foo, expectTypeArguments: false);
   test(new Bar() as Bar<num>, Bar, expectTypeArguments: false);
   Baz<num> b = new Baz();
   dynamic c = b;
   test(c as Baz<num>, Baz, expectTypeArguments: true);
-  // Makes sure that [c] is not effectively final, so that we don't infer the
-  // static type from the initializer.
-  c = null;
 }
 
 void test(dynamic object, Type type, {bool expectTypeArguments}) {