[dart2js] Add ability to check if RTIs are Object, Function, or Null.

Change-Id: Ia893c688fa619a87fd9ab97cf881f35763a48bdf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/107080
Commit-Queue: Mayank Patke <fishythefish@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
diff --git a/sdk/lib/_internal/js_runtime/lib/rti.dart b/sdk/lib/_internal/js_runtime/lib/rti.dart
index 5396d8a..17cb4d4 100644
--- a/sdk/lib/_internal/js_runtime/lib/rti.dart
+++ b/sdk/lib/_internal/js_runtime/lib/rti.dart
@@ -6,10 +6,11 @@
 library rti;
 
 import 'dart:_foreign_helper'
-    show JS, JS_EMBEDDED_GLOBAL, RAW_DART_FUNCTION_REF;
+    show JS, JS_EMBEDDED_GLOBAL, JS_GET_NAME, RAW_DART_FUNCTION_REF;
 import 'dart:_interceptors' show JSArray, JSUnmodifiableArray;
 
-import 'dart:_js_embedded_names' show RtiUniverseFieldNames, RTI_UNIVERSE;
+import 'dart:_js_embedded_names'
+    show JsGetName, RtiUniverseFieldNames, RTI_UNIVERSE;
 
 import 'dart:_recipe_syntax';
 
@@ -955,14 +956,13 @@
 
   if (isNullType(s)) return true;
 
-  if (isFunctionType(t)) {
+  if (isFunctionKind(t)) {
     // TODO(fishythefish): Check if s is a function subtype of t.
-    throw UnimplementedError("isFunctionType(t)");
+    throw UnimplementedError("isFunctionKind(t)");
   }
 
-  if (isFunctionType(s)) {
-    // TODO(fishythefish): Check if t is Function.
-    throw UnimplementedError("isFunctionType(s)");
+  if (isFunctionKind(s)) {
+    return isFunctionType(t);
   }
 
   if (isFutureOrType(t)) {
@@ -1023,19 +1023,23 @@
 bool isVoidType(Rti t) => Rti._getKind(t) == Rti.kindVoid;
 bool isJsInteropType(Rti t) => Rti._getKind(t) == Rti.kindAny;
 bool isFutureOrType(Rti t) => Rti._getKind(t) == Rti.kindFutureOr;
-bool isFunctionType(Rti t) => Rti._getKind(t) == Rti.kindFunction;
+bool isFunctionKind(Rti t) => Rti._getKind(t) == Rti.kindFunction;
 bool isGenericFunctionTypeParameter(Rti t) =>
     Rti._getKind(t) == Rti.kindGenericFunctionParameter;
 
-bool isObjectType(Rti t) {
-  // TODO(fishythefish): Look up Object in universe and compare.
-  return false;
-}
+bool isObjectType(Rti t) =>
+    Rti._getKind(t) == Rti.kindInterface &&
+    Rti._getInterfaceName(t) == JS_GET_NAME(JsGetName.OBJECT_CLASS_TYPE_NAME);
 
-bool isNullType(Rti t) {
-  // TODO(fishythefish): Look up Null in universe and compare.
-  return false;
-}
+// TODO(fishythefish): Which representation should we use for NNBD?
+// Do we also need to check for `Never?`, etc.?
+bool isNullType(Rti t) =>
+    Rti._getKind(t) == Rti.kindInterface &&
+    Rti._getInterfaceName(t) == JS_GET_NAME(JsGetName.NULL_CLASS_TYPE_NAME);
+
+bool isFunctionType(Rti t) =>
+    Rti._getKind(t) == Rti.kindInterface &&
+    Rti._getInterfaceName(t) == JS_GET_NAME(JsGetName.FUNCTION_CLASS_TYPE_NAME);
 
 /// Unchecked cast to Rti.
 Rti _castToRti(s) => JS('Rti', '#', s);
diff --git a/tests/compiler/dart2js_extra/rti/subtype_test.dart b/tests/compiler/dart2js_extra/rti/subtype_test.dart
index 0bf71dd..78753f7 100644
--- a/tests/compiler/dart2js_extra/rti/subtype_test.dart
+++ b/tests/compiler/dart2js_extra/rti/subtype_test.dart
@@ -2,10 +2,14 @@
 // 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 'dart:_foreign_helper' show JS;
+import 'dart:_foreign_helper' show JS, JS_GET_NAME;
+import 'dart:_js_embedded_names' show JsGetName;
 import 'dart:_rti' as rti;
 import "package:expect/expect.dart";
 
+final String objectName = JS_GET_NAME(JsGetName.OBJECT_CLASS_TYPE_NAME);
+final String nullName = JS_GET_NAME(JsGetName.NULL_CLASS_TYPE_NAME);
+
 const typeRulesJson = r'''
 {
   "int": {"num": []},
@@ -29,44 +33,51 @@
   strictSubtype('List<CodeUnits>', 'Iterable<List<int>>');
   strictSubtype('CodeUnits', 'Iterable<num>');
   strictSubtype('Iterable<int>', 'Iterable<num>');
+  strictSubtype('List<int>', objectName);
+  strictSubtype(nullName, 'int');
+  strictSubtype(nullName, 'Iterable<CodeUnits>');
+  strictSubtype(nullName, objectName);
   unrelated('int', 'CodeUnits');
+  equivalent(nullName, nullName);
   equivalent('double', 'double');
-  equivalent('Object', 'Object');
+  equivalent(objectName, objectName);
   equivalent('@', '@');
   equivalent('~', '~');
   equivalent('1&', '1&');
   equivalent('List<int>', 'List<int>');
-  //equivalent('Object', '@');
-  //equivalent('Object', '~');
-  //equivalent('Object', '1&');
+  equivalent(objectName, '@');
+  equivalent(objectName, '~');
+  equivalent(objectName, '1&');
   equivalent('@', '~');
   equivalent('@', '1&');
   equivalent('~', '1&');
-  //equivalent('List<Object>', 'List<@>');
-  //equivalent('List<Object>', 'List<~>');
-  //equivalent('List<Object>', 'List<1&>');
+  equivalent('List<$objectName>', 'List<@>');
+  equivalent('List<$objectName>', 'List<~>');
+  equivalent('List<$objectName>', 'List<1&>');
   equivalent('List<@>', 'List<~>');
   equivalent('List<@>', 'List<1&>');
   equivalent('List<~>', 'List<1&>');
 }
 
+String reason(String s, String t) => "$s <: $t";
+
 void strictSubtype(String s, String t) {
   var sRti = rti.testingUniverseEval(universe, s);
   var tRti = rti.testingUniverseEval(universe, t);
-  Expect.isTrue(rti.testingIsSubtype(universe, sRti, tRti));
-  Expect.isFalse(rti.testingIsSubtype(universe, tRti, sRti));
+  Expect.isTrue(rti.testingIsSubtype(universe, sRti, tRti), reason(s, t));
+  Expect.isFalse(rti.testingIsSubtype(universe, tRti, sRti), reason(t, s));
 }
 
 void unrelated(String s, String t) {
   var sRti = rti.testingUniverseEval(universe, s);
   var tRti = rti.testingUniverseEval(universe, t);
-  Expect.isFalse(rti.testingIsSubtype(universe, sRti, tRti));
-  Expect.isFalse(rti.testingIsSubtype(universe, tRti, sRti));
+  Expect.isFalse(rti.testingIsSubtype(universe, sRti, tRti), reason(s, t));
+  Expect.isFalse(rti.testingIsSubtype(universe, tRti, sRti), reason(t, s));
 }
 
 void equivalent(String s, String t) {
   var sRti = rti.testingUniverseEval(universe, s);
   var tRti = rti.testingUniverseEval(universe, t);
-  Expect.isTrue(rti.testingIsSubtype(universe, sRti, tRti));
-  Expect.isTrue(rti.testingIsSubtype(universe, tRti, sRti));
+  Expect.isTrue(rti.testingIsSubtype(universe, sRti, tRti), reason(s, t));
+  Expect.isTrue(rti.testingIsSubtype(universe, tRti, sRti), reason(t, s));
 }