[dart2js] Emit a Rti universe

Use embedded names to keep the field names consistent.

Change-Id: Ic8986b38e4a30e9457221939c6512d0446fb32f1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106203
Commit-Queue: Stephen Adams <sra@google.com>
Reviewed-by: Mayank Patke <fishythefish@google.com>
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index b33324b..2ae2052 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -1870,6 +1870,10 @@
           js.string(TYPE_TO_INTERCEPTOR_MAP), js.LiteralNull()));
     }
 
+    if (_options.experimentNewRti) {
+      globals.add(js.Property(js.string(RTI_UNIVERSE), createRtiUniverse()));
+    }
+
     globals.add(emitMangledGlobalNames());
 
     // The [MANGLED_NAMES] table must contain the mapping for const symbols.
@@ -1906,6 +1910,22 @@
     return js.Block(statements);
   }
 
+  /// Returns an expression that creates the initial Rti Universe.
+  ///
+  /// This needs to be kept in sync with `_Universe.create` in `dart:_rti`.
+  js.Expression createRtiUniverse() {
+    List<js.Property> universeFields = [];
+    void initField(String name, String value) {
+      universeFields.add(js.Property(js.string(name), js.js(value)));
+    }
+
+    initField(RtiUniverseFieldNames.evalCache, 'new Map()');
+    initField(RtiUniverseFieldNames.unprocessedRules, '[]');
+    initField(RtiUniverseFieldNames.sharedEmptyArray, '[]');
+
+    return js.ObjectInitializer(universeFields);
+  }
+
   /// Emits data needed for native classes.
   ///
   /// We don't try to reduce the size of the native data, but rather build
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
index 78b6164..fd8db9b 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
@@ -24,6 +24,8 @@
         MANGLED_NAMES,
         METADATA,
         NATIVE_SUPERCLASS_TAG_NAME,
+        RTI_UNIVERSE,
+        RtiUniverseFieldNames,
         TYPE_TO_INTERCEPTOR_MAP,
         TYPES;
 
diff --git a/sdk/lib/_internal/js_runtime/lib/rti.dart b/sdk/lib/_internal/js_runtime/lib/rti.dart
index 5039386..1a12e60 100644
--- a/sdk/lib/_internal/js_runtime/lib/rti.dart
+++ b/sdk/lib/_internal/js_runtime/lib/rti.dart
@@ -5,9 +5,12 @@
 /// This library contains support for runtime type information.
 library rti;
 
-import 'dart:_foreign_helper' show JS, RAW_DART_FUNCTION_REF;
+import 'dart:_foreign_helper'
+    show JS, JS_EMBEDDED_GLOBAL, RAW_DART_FUNCTION_REF;
 import 'dart:_interceptors' show JSArray, JSUnmodifiableArray;
 
+import 'dart:_js_embedded_names' show RtiUniverseFieldNames, RTI_UNIVERSE;
+
 /// An Rti object represents both a type (e.g `Map<int, String>`) and a type
 /// environment (`Map<int, String>` binds `Map.K=int` and `Map.V=String`).
 ///
@@ -51,7 +54,10 @@
 
   /// Method called from generated code to evaluate a type environment recipe in
   /// `this` type environment.
-  Rti _eval(String recipe) => _rtiEval(this, recipe);
+  Rti _eval(String recipe) {
+    // TODO(sra): Clone the fast-path of _Universe.evalInEnvironment to here.
+    return _rtiEval(this, recipe);
+  }
 
   /// Method called from generated code to extend `this` type environment (an
   /// interface or binding Rti) with function type arguments (a singleton
@@ -201,19 +207,24 @@
   }
 }
 
+Object _theUniverse() => JS_EMBEDDED_GLOBAL('', RTI_UNIVERSE);
+
 Rti _rtiEval(Rti environment, String recipe) {
-  // TODO(sra): return _Universe.eval(the-universe, environment, recipe);
-  throw UnimplementedError('_rtiEval');
+  return _Universe.evalInEnvironment(_theUniverse(), environment, recipe);
 }
 
 Rti _rtiBind1(Rti environment, Rti types) {
-  // TODO(sra): return _Universe.bind1(the-universe, environment, types);
-  throw UnimplementedError('_rtiBind1');
+  return _Universe.bind1(_theUniverse(), environment, types);
 }
 
-Rti _rtiBind(Rti environment, Rti typeTuple) {
-  // TODO(sra): return _Universe.bind(the-universe, environment, types);
-  throw UnimplementedError('_rtiBind');
+Rti _rtiBind(Rti environment, Rti types) {
+  return _Universe.bind(_theUniverse(), environment, types);
+}
+
+/// Evaluate a ground-term type.
+/// Called from generated code.
+Rti rtiTypeEval(String recipe) {
+  _Universe.eval(_theUniverse(), recipe);
 }
 
 Type getRuntimeType(object) {
@@ -322,26 +333,32 @@
 
   @pragma('dart2js:noInline')
   static Object create() {
-    // TODO(sra): For consistency, this expression should be a JS_BUILTIN that
-    // uses the same template as emitted by the emitter.
+    // This needs to be kept in sync with `FragmentEmitter.createRtiUniverse` in
+    // `fragment_emtter.dart`.
     return JS(
         '',
         '{'
-            'evalCache: new Map(),'
-            'unprocessedRules:[],'
-            'a0:[],' // shared empty array.
-            '}');
+            '#: new Map(),'
+            '#: [],'
+            '#: [],' // shared empty array.
+            '}',
+        RtiUniverseFieldNames.evalCache,
+        RtiUniverseFieldNames.unprocessedRules,
+        RtiUniverseFieldNames.sharedEmptyArray);
   }
 
   // Field accessors.
 
-  static evalCache(universe) => JS('', '#.evalCache', universe);
+  static evalCache(universe) =>
+      JS('', '#.#', universe, RtiUniverseFieldNames.evalCache);
 
   static void addRules(universe, String rules) {
-    JS('', '#.unprocessedRules.push(#)', universe, rules);
+    JS('', '#.#.push(#)', universe, RtiUniverseFieldNames.unprocessedRules,
+        rules);
   }
 
-  static Object sharedEmptyArray(universe) => JS('JSArray', '#.a0', universe);
+  static Object sharedEmptyArray(universe) =>
+      JS('JSArray', '#.#', universe, RtiUniverseFieldNames.sharedEmptyArray);
 
   /// Evaluates [recipe] in the global environment.
   static Rti eval(Object universe, String recipe) {
@@ -388,6 +405,10 @@
     return rti;
   }
 
+  static Rti bind1(Object universe, Rti environment, Rti argumentsRti) {
+    throw UnimplementedError('_Universe.bind1');
+  }
+
   static Rti evalTypeVariable(Object universe, Rti environment, String name) {
     throw UnimplementedError('_Universe.evalTypeVariable("$name")');
   }
diff --git a/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart b/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart
index bc10677..28658e4 100644
--- a/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart
+++ b/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart
@@ -192,6 +192,11 @@
 /// globals don't clash with it.
 const DEFERRED_INITIALIZED = 'deferredInitialized';
 
+/// A 'Universe' object used by 'dart:_rti'.
+///
+/// This embedded global is used for --experiment-new-rti.
+const RTI_UNIVERSE = 'typeUniverse';
+
 /// Returns a function that creates all precompiled functions (in particular
 /// constructors).
 ///
@@ -411,3 +416,10 @@
   ///                JsBuiltin.getType, index);
   getType,
 }
+
+/// Names of fields of the Rti Universe object.
+class RtiUniverseFieldNames {
+  static String evalCache = 'eC';
+  static String unprocessedRules = 'uR';
+  static String sharedEmptyArray = 'sEA';
+}