Version 2.13.0-111.0.dev

Merge commit 'b683098fde9611cea472cb081da2cea7ba15bc8b' into 'dev'
diff --git a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
new file mode 100644
index 0000000..d1d72dc
--- /dev/null
+++ b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2021, 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.
+
+import 'package:kernel/ast.dart';
+import 'package:kernel/core_types.dart';
+import 'package:kernel/kernel.dart';
+
+/// Replaces js_util methods with inline calls to foreign_helper JS which
+/// emits the code as a JavaScript code fragment.
+class JsUtilOptimizer extends Transformer {
+  final Procedure _jsTarget;
+  final Procedure _getPropertyTarget;
+
+  JsUtilOptimizer(CoreTypes coreTypes)
+      : _jsTarget =
+            coreTypes.index.getTopLevelMember('dart:_foreign_helper', 'JS'),
+        _getPropertyTarget =
+            coreTypes.index.getTopLevelMember('dart:js_util', 'getProperty') {}
+
+  /// Replaces js_util method calls with lowering straight to JS fragment call.
+  ///
+  /// Lowers the following types of js_util calls:
+  ///  - `getProperty` for any argument types
+  @override
+  visitStaticInvocation(StaticInvocation node) {
+    if (node.target == _getPropertyTarget) {
+      node = _lowerGetProperty(node);
+    }
+    node.transformChildren(this);
+    return node;
+  }
+
+  /// Lowers the given js_util `getProperty` call to the foreign_helper JS call
+  /// for any argument type. Lowers `getProperty(o, name)` to
+  /// `JS('Object|Null', '#.#', o, name)`.
+  StaticInvocation _lowerGetProperty(StaticInvocation node) {
+    Arguments args = node.arguments;
+    assert(args.positional.length == 2);
+    return StaticInvocation(
+        _jsTarget,
+        Arguments(
+          [
+            StringLiteral("Object|Null"),
+            StringLiteral("#.#"),
+            args.positional.first,
+            args.positional.last
+          ],
+          // TODO(rileyporter): Copy type from getProperty when it's generic.
+          types: [DynamicType()],
+        ))
+      ..fileOffset = node.fileOffset;
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart b/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
index 56d2102..795eabe 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
@@ -106,18 +106,7 @@
     localVariableDistance,
     startsWithDollar,
     superMatches,
-  ], [
-    1.00, // contextType
-    1.00, // elementKind
-    0.50, // hasDeprecated
-    1.00, // inheritanceDistance
-    1.00, // isConstant
-    1.00, // isNoSuchMethod
-    1.00, // keyword
-    1.00, // localVariableDistance
-    0.50, // startsWithDollar
-    1.00, // superMatches
-  ]);
+  ], FeatureComputer.featureWeights);
   return (average + 1.0) / 2.0;
 }
 
@@ -138,6 +127,38 @@
 
 /// An object that computes the values of features.
 class FeatureComputer {
+  /// The names of features whose values are averaged.
+  static List<String> featureNames = [
+    'contextType',
+    'elementKind',
+    'hasDeprecated',
+    'inheritanceDistance',
+    'isConstant',
+    'isNoSuchMethod',
+    'keyword',
+    'localVariableDistance',
+    'startsWithDollar',
+    'superMatches',
+  ];
+
+  /// The values of the weights used to compute an average of feature values.
+  static List<double> featureWeights = defaultFeatureWeights;
+
+  /// The default values of the weights used to compute an average of feature
+  /// values.
+  static const List<double> defaultFeatureWeights = [
+    1.00, // contextType
+    1.00, // elementKind
+    0.50, // hasDeprecated
+    1.00, // inheritanceDistance
+    1.00, // isConstant
+    1.00, // isNoSuchMethod
+    1.00, // keyword
+    1.00, // localVariableDistance
+    0.50, // startsWithDollar
+    1.00, // superMatches
+  ];
+
   /// The type system used to perform operations on types.
   final TypeSystem typeSystem;
 
diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics.dart b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
index 2a9e75d..744cd50 100644
--- a/pkg/analysis_server/tool/code_completion/completion_metrics.dart
+++ b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
@@ -13,6 +13,7 @@
 import 'package:analysis_server/src/services/completion/completion_core.dart';
 import 'package:analysis_server/src/services/completion/completion_performance.dart';
 import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
+import 'package:analysis_server/src/services/completion/dart/feature_computer.dart';
 import 'package:analysis_server/src/services/completion/dart/probability_range.dart';
 import 'package:analysis_server/src/services/completion/dart/relevance_tables.g.dart';
 import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
@@ -600,6 +601,21 @@
 
   CompletionMetricsComputer(this.rootPath, this.options);
 
+  /// Compare the metrics when each feature is used in isolation.
+  void compareIndividualFeatures() {
+    var featureNames = FeatureComputer.featureNames;
+    var featureCount = featureNames.length;
+    for (var i = 0; i < featureCount; i++) {
+      var weights = List.filled(featureCount, 0.00);
+      weights[i] = 1.00;
+      targetMetrics.add(CompletionMetrics(featureNames[i], enableFunction: () {
+        FeatureComputer.featureWeights = weights;
+      }, disableFunction: () {
+        FeatureComputer.featureWeights = FeatureComputer.defaultFeatureWeights;
+      }));
+    }
+  }
+
   /// Compare the relevance [tables] to the default relevance tables.
   void compareRelevanceTables(List<RelevanceTables> tables) {
     assert(tables.isNotEmpty);
@@ -622,10 +638,14 @@
         enableFunction: null, disableFunction: null));
 
     // To compare two or more relevance tables, uncomment the line below and
-    // add the `RelevanceTable`s to the list. The default relevance tables
+    // add the `RelevanceTables` to the list. The default relevance tables
     // should not be included in the list.
 //     compareRelevanceTables([]);
 
+    // To compare the relative benefit from each of the features, uncomment the
+    // line below.
+//    compareIndividualFeatures();
+
     final collection = AnalysisContextCollection(
       includedPaths: [rootPath],
       resourceProvider: PhysicalResourceProvider.INSTANCE,
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index 5103adc..60301f2 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -9,6 +9,7 @@
 import 'package:_fe_analyzer_shared/src/messages/codes.dart'
     show Message, LocatedMessage;
 import 'package:_js_interop_checks/js_interop_checks.dart';
+import 'package:_js_interop_checks/src/transformations/js_util_optimizer.dart';
 import 'package:kernel/ast.dart' as ir;
 import 'package:kernel/class_hierarchy.dart';
 import 'package:kernel/core_types.dart';
@@ -92,8 +93,10 @@
 
   @override
   List<String> get extraIndexedLibraries => const [
+        'dart:_foreign_helper',
         'dart:_interceptors',
         'dart:_js_helper',
+        'dart:js_util'
       ];
 
   @override
@@ -127,7 +130,11 @@
       {void logger(String msg),
       ChangedStructureNotifier changedStructureNotifier}) {
     _nativeClasses ??= JsInteropChecks.getNativeClasses(component);
+    var jsUtilOptimizer = JsUtilOptimizer(coreTypes);
     for (var library in libraries) {
+      // TODO (rileyporter): Merge js_util optimizations with other lowerings
+      // in the single pass in `transformations/lowering.dart`.
+      jsUtilOptimizer.visitLibrary(library);
       JsInteropChecks(
               coreTypes,
               diagnosticReporter as DiagnosticReporter<Message, LocatedMessage>,
diff --git a/pkg/dev_compiler/lib/src/kernel/target.dart b/pkg/dev_compiler/lib/src/kernel/target.dart
index 8318288..ffc64a4 100644
--- a/pkg/dev_compiler/lib/src/kernel/target.dart
+++ b/pkg/dev_compiler/lib/src/kernel/target.dart
@@ -16,6 +16,7 @@
 import 'package:kernel/target/targets.dart';
 import 'package:kernel/transformations/track_widget_constructor_locations.dart';
 import 'package:_js_interop_checks/js_interop_checks.dart';
+import 'package:_js_interop_checks/src/transformations/js_util_optimizer.dart';
 
 import 'constants.dart' show DevCompilerConstantsBackend;
 import 'kernel_helpers.dart';
@@ -92,11 +93,13 @@
         'dart:collection',
         'dart:html',
         'dart:indexed_db',
+        'dart:js_util',
         'dart:math',
         'dart:svg',
         'dart:web_audio',
         'dart:web_gl',
         'dart:web_sql',
+        'dart:_foreign_helper',
         'dart:_interceptors',
         'dart:_js_helper',
         'dart:_native_typed_data',
@@ -152,8 +155,10 @@
       {void Function(String msg) logger,
       ChangedStructureNotifier changedStructureNotifier}) {
     _nativeClasses ??= JsInteropChecks.getNativeClasses(component);
+    var jsUtilOptimizer = JsUtilOptimizer(coreTypes);
     for (var library in libraries) {
       _CovarianceTransformer(library).transform();
+      jsUtilOptimizer.visitLibrary(library);
       JsInteropChecks(
               coreTypes,
               diagnosticReporter as DiagnosticReporter<Message, LocatedMessage>,
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index 547cc29..a759519 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -989,6 +989,13 @@
     return result;
   }
 
+  /// Execute the statement using the [StatementConstantEvaluator].
+  Constant execute(Statement statement) {
+    StatementConstantEvaluator statementEvaluator =
+        new StatementConstantEvaluator(this);
+    return statement.accept(statementEvaluator);
+  }
+
   /// Create an error-constant indicating that an error has been detected during
   /// constant evaluation.
   AbortConstant createErrorConstant(TreeNode node, Message message,
@@ -2738,17 +2745,7 @@
         if (value is AbortConstant) return value;
         env.updateVariableValue(parameter, value);
       }
-      Statement body = function.body;
-      if (body is ReturnStatement) {
-        if (!enableConstFunctions) {
-          return createInvalidExpressionConstant(
-              node, "Return statements are not supported.");
-        }
-        return body.expression.accept(this);
-      } else {
-        return createInvalidExpressionConstant(
-            node, "Unsupported statement: ${body.runtimeType}.");
-      }
+      return execute(function.body);
     });
   }
 
@@ -3209,6 +3206,27 @@
   }
 }
 
+class StatementConstantEvaluator extends StatementVisitor<Constant> {
+  ConstantEvaluator exprEvaluator;
+
+  StatementConstantEvaluator(this.exprEvaluator) {
+    if (!exprEvaluator.enableConstFunctions) {
+      throw new UnsupportedError("Const functions feature is not enabled.");
+    }
+  }
+
+  /// Evaluate the expression using the [ConstantEvaluator].
+  Constant evaluate(Expression expr) => expr.accept(exprEvaluator);
+
+  @override
+  Constant defaultStatement(Statement node) => throw new UnsupportedError(
+      'Statement constant evaluation does not support ${node.runtimeType}.');
+
+  @override
+  Constant visitReturnStatement(ReturnStatement node) =>
+      evaluate(node.expression);
+}
+
 class ConstantCoverage {
   final Map<Uri, Set<Reference>> constructorCoverage;
 
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 8fe1b6b..ba25ffb 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -389,6 +389,7 @@
 established
 estimate
 eval
+execute
 exhausted
 existence
 existentially
diff --git a/tests/language/const_functions/const_functions_disabled_simple_invocations.dart b/tests/language/const_functions/const_functions_disabled_simple_invocations_test.dart
similarity index 100%
rename from tests/language/const_functions/const_functions_disabled_simple_invocations.dart
rename to tests/language/const_functions/const_functions_disabled_simple_invocations_test.dart
diff --git a/tests/language/const_functions/const_functions_simple_invocations.dart b/tests/language/const_functions/const_functions_simple_invocations_test.dart
similarity index 100%
rename from tests/language/const_functions/const_functions_simple_invocations.dart
rename to tests/language/const_functions/const_functions_simple_invocations_test.dart
diff --git a/tests/lib/js/js_util/properties_test.dart b/tests/lib/js/js_util/properties_test.dart
index 986c431..ab0b844 100644
--- a/tests/lib/js/js_util/properties_test.dart
+++ b/tests/lib/js/js_util/properties_test.dart
@@ -31,6 +31,7 @@
   external num get a;
   external num bar();
   external Object get objectProperty;
+  external List get list;
 }
 
 @JS('Foo')
@@ -213,6 +214,11 @@
           js_util.callMethod(
               js_util.getProperty(f, 'fnList')[0], 'apply', [f, []]),
           equals(42));
+      expect(js_util.getProperty(f.list, "0"), equals(2));
+      var index = 0;
+      expect(js_util.getProperty(f.list, index++), equals(2));
+      expect(index, equals(1));
+      expect(js_util.getProperty(f.list, index), equals(4));
 
       // Accessing nested object properites.
       var objectProperty = js_util.getProperty(f, 'objectProperty');
@@ -222,6 +228,13 @@
       expect(
           js_util.getProperty(objectProperty, 'functionProperty') is Function,
           isTrue);
+      // Using nested getProperty calls.
+      expect(
+          js_util.getProperty(
+              js_util.getProperty(
+                  js_util.getProperty(f, 'objectProperty'), 'list'),
+              1),
+          equals(20));
 
       // Using a variable for the property name.
       String propertyName = 'a';
@@ -289,6 +302,10 @@
       expect(js_util.getProperty(f.objectProperty, 'c'), equals('new val'));
       js_util.setProperty(f.objectProperty, 'list', [1, 2, 3]);
       expect(js_util.getProperty(f.objectProperty, 'list')[1], equals(2));
+      // Using a nested getProperty call.
+      js_util.setProperty(
+          js_util.getProperty(f, 'objectProperty'), 'c', 'nested val');
+      expect(js_util.getProperty(f.objectProperty, 'c'), equals('nested val'));
 
       // Using a variable for the property name.
       String propertyName = 'bar';
@@ -317,6 +334,11 @@
       // Call method on a nested function property.
       expect(js_util.callMethod(f.objectProperty, 'functionProperty', []),
           equals('Function Property'));
+      // Using a nested getProperty call.
+      expect(
+          js_util.callMethod(
+              js_util.getProperty(f, 'objectProperty'), 'functionProperty', []),
+          equals('Function Property'));
 
       // Call method with different args.
       expect(
diff --git a/tests/lib_2/js/js_util/properties_test.dart b/tests/lib_2/js/js_util/properties_test.dart
index 986c431..ab0b844 100644
--- a/tests/lib_2/js/js_util/properties_test.dart
+++ b/tests/lib_2/js/js_util/properties_test.dart
@@ -31,6 +31,7 @@
   external num get a;
   external num bar();
   external Object get objectProperty;
+  external List get list;
 }
 
 @JS('Foo')
@@ -213,6 +214,11 @@
           js_util.callMethod(
               js_util.getProperty(f, 'fnList')[0], 'apply', [f, []]),
           equals(42));
+      expect(js_util.getProperty(f.list, "0"), equals(2));
+      var index = 0;
+      expect(js_util.getProperty(f.list, index++), equals(2));
+      expect(index, equals(1));
+      expect(js_util.getProperty(f.list, index), equals(4));
 
       // Accessing nested object properites.
       var objectProperty = js_util.getProperty(f, 'objectProperty');
@@ -222,6 +228,13 @@
       expect(
           js_util.getProperty(objectProperty, 'functionProperty') is Function,
           isTrue);
+      // Using nested getProperty calls.
+      expect(
+          js_util.getProperty(
+              js_util.getProperty(
+                  js_util.getProperty(f, 'objectProperty'), 'list'),
+              1),
+          equals(20));
 
       // Using a variable for the property name.
       String propertyName = 'a';
@@ -289,6 +302,10 @@
       expect(js_util.getProperty(f.objectProperty, 'c'), equals('new val'));
       js_util.setProperty(f.objectProperty, 'list', [1, 2, 3]);
       expect(js_util.getProperty(f.objectProperty, 'list')[1], equals(2));
+      // Using a nested getProperty call.
+      js_util.setProperty(
+          js_util.getProperty(f, 'objectProperty'), 'c', 'nested val');
+      expect(js_util.getProperty(f.objectProperty, 'c'), equals('nested val'));
 
       // Using a variable for the property name.
       String propertyName = 'bar';
@@ -317,6 +334,11 @@
       // Call method on a nested function property.
       expect(js_util.callMethod(f.objectProperty, 'functionProperty', []),
           equals('Function Property'));
+      // Using a nested getProperty call.
+      expect(
+          js_util.callMethod(
+              js_util.getProperty(f, 'objectProperty'), 'functionProperty', []),
+          equals('Function Property'));
 
       // Call method with different args.
       expect(
diff --git a/tools/VERSION b/tools/VERSION
index 283b038..24f1ba5 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 110
+PRERELEASE 111
 PRERELEASE_PATCH 0
\ No newline at end of file