[vm/tfa] Infer concrete class of string literals in TFA

Depending on a value of a string literal, it is possible to determine
concrete class at compile time (either _OneByteString or _TwoByteString).

Change-Id: Ied696b328021e9a61f1ad14d02b88681a9ed4fed
Reviewed-on: https://dart-review.googlesource.com/76260
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart
index d2b11a3..3a1ae91 100644
--- a/pkg/kernel/lib/target/targets.dart
+++ b/pkg/kernel/lib/target/targets.dart
@@ -200,6 +200,8 @@
 
   Class concreteMapLiteralClass(CoreTypes coreTypes) => null;
   Class concreteConstMapLiteralClass(CoreTypes coreTypes) => null;
+
+  Class concreteStringLiteralClass(CoreTypes coreTypes, String value) => null;
 }
 
 class NoneTarget extends Target {
diff --git a/pkg/vm/lib/target/vm.dart b/pkg/vm/lib/target/vm.dart
index 8b75174..a62c3c5 100644
--- a/pkg/vm/lib/target/vm.dart
+++ b/pkg/vm/lib/target/vm.dart
@@ -24,6 +24,8 @@
   Class _immutableList;
   Class _internalLinkedHashMap;
   Class _immutableMap;
+  Class _oneByteString;
+  Class _twoByteString;
 
   VmTarget(this.flags);
 
@@ -321,4 +323,17 @@
     return _immutableMap ??=
         coreTypes.index.getClass('dart:core', '_ImmutableMap');
   }
+
+  @override
+  Class concreteStringLiteralClass(CoreTypes coreTypes, String value) {
+    const int maxLatin1 = 0xff;
+    for (int i = 0; i < value.length; ++i) {
+      if (value.codeUnitAt(i) > maxLatin1) {
+        return _twoByteString ??=
+            coreTypes.index.getClass('dart:core', '_TwoByteString');
+      }
+    }
+    return _oneByteString ??=
+        coreTypes.index.getClass('dart:core', '_OneByteString');
+  }
 }
diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
index 984521d..72b616b 100644
--- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart
+++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
@@ -554,6 +554,14 @@
 
   Class get _superclass => _environment.thisType.classNode.superclass;
 
+  Type _stringLiteralType(String value) {
+    Class concreteClass =
+        target.concreteStringLiteralClass(_environment.coreTypes, value);
+    return concreteClass != null
+        ? _entryPointsListener.addAllocatedClass(concreteClass)
+        : _stringType;
+  }
+
   void _handleNestedFunctionNode(FunctionNode node) {
     var oldReturn = _returnValue;
     var oldVariables = _variables;
@@ -892,7 +900,7 @@
 
   @override
   TypeExpr visitStringLiteral(StringLiteral node) {
-    return _stringType;
+    return _stringLiteralType(node.value);
   }
 
   @override
@@ -1244,7 +1252,7 @@
 
   @override
   Type visitStringConstant(StringConstant constant) {
-    return summaryCollector._stringType;
+    return summaryCollector._stringLiteralType(constant.value);
   }
 
   @override
diff --git a/pkg/vm/testcases/transformations/type_flow/summary_collector/hello.dart.expect b/pkg/vm/testcases/transformations/type_flow/summary_collector/hello.dart.expect
index 35081b4..770e1df 100644
--- a/pkg/vm/testcases/transformations/type_flow/summary_collector/hello.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/summary_collector/hello.dart.expect
@@ -1,4 +1,4 @@
 ------------ #lib::main ------------
 %args = _Parameter #0 [_T (dart.core::List<dynamic>)+?]
-t1 = _Call direct [dart.core::print] (_T (dart.core::String)+)
+t1 = _Call direct [dart.core::print] (_T (dart.core::_OneByteString))
 RESULT: _T {}?
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/bench_vector.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/bench_vector.dart.expect
index e158e50..e25e859 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/bench_vector.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/bench_vector.dart.expect
@@ -12,7 +12,7 @@
     ;
   operator [](core::int i) → core::double
     return [@vm.direct-call.metadata=dart.typed_data::_Float64List::[]] [@vm.inferred-type.metadata=dart.core::_Double] [@vm.direct-call.metadata=#lib::_Vector::_elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements}.{core::List::[]}([@vm.direct-call.metadata=dart.core::_IntegerImplementation::+??] [@vm.inferred-type.metadata=int?] i.{core::num::+}([@vm.direct-call.metadata=#lib::_Vector::_offset] [@vm.inferred-type.metadata=int] this.{self::_Vector::_offset}));
-  operator []=([@vm.inferred-type.metadata=!] core::int i, core::double value) → void {
+  operator []=([@vm.inferred-type.metadata=dart.core::_OneByteString] core::int i, core::double value) → void {
     let dynamic #t1 = [@vm.direct-call.metadata=#lib::_Vector::_elements] [@vm.inferred-type.metadata=dart.typed_data::_Float64List] this.{self::_Vector::_elements} in let dynamic #t2 = i in let dynamic #t3 = [@vm.direct-call.metadata=#lib::_Vector::_offset] [@vm.inferred-type.metadata=int] this.{self::_Vector::_offset} in throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
   }
   operator *([@vm.inferred-type.metadata=#lib::_Vector?] self::_Vector a) → core::double {
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/devirt.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/devirt.dart.expect
index 0e43e97..aebe06c 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/devirt.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/devirt.dart.expect
@@ -48,7 +48,7 @@
 static method callerA4([@vm.inferred-type.metadata=#lib::D?] self::A aa) → void {
   [@vm.direct-call.metadata=#lib::C::foo??] aa.{self::A::foo}();
 }
-static method callerE1([@vm.inferred-type.metadata=!] dynamic x) → void {
+static method callerE1([@vm.inferred-type.metadata=dart.core::_OneByteString] dynamic x) → void {
   [@vm.direct-call.metadata=dart.core::_StringBase::toString] x.{core::Object::toString}();
 }
 static method callerE2([@vm.inferred-type.metadata=#lib::E?] dynamic x) → void {