[dartdevc] Adding support for more Record nodes and core methods

- Supports: RecordGet, RecordIndexGet, RecordConstant
- Adds methods: hashCode, toString, ==

Change-Id: I512978a0ccf2eb58e24baf8462cc93ff98bd9105
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/269745
Reviewed-by: Nicholas Shahan <nshahan@google.com>
Commit-Queue: Mark Zhou <markzipan@google.com>
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index 32d5dcc..20f1241 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -4899,12 +4899,12 @@
 
   @override
   js_ast.Expression visitRecordIndexGet(RecordIndexGet node) {
-    return defaultExpression(node);
+    return _emitPropertyGet(node.receiver, null, '\$${node.index}');
   }
 
   @override
   js_ast.Expression visitRecordNameGet(RecordNameGet node) {
-    return defaultExpression(node);
+    return _emitPropertyGet(node.receiver, null, node.name);
   }
 
   @override
@@ -7020,8 +7020,20 @@
       node.typeArgument, node.entries.map(visitConstant).toList());
 
   @override
-  js_ast.Expression visitRecordConstant(RecordConstant node) =>
-      defaultConstant(node);
+  js_ast.Expression visitRecordConstant(RecordConstant node) {
+    var sortedNames = node.named.entries.toList().sortedBy((e) => e.key);
+    var names = sortedNames.map((e) => e.key);
+    var shape = '${node.positional.length} ${names.join(" ")}';
+    return runtimeCall('recordLiteral(#, #, #, [#])', [
+      js.string(shape),
+      js.number(node.positional.length),
+      names.isEmpty ? js.call('void 0') : js.stringArray(names),
+      [
+        ...node.positional.map(visitConstant),
+        ...sortedNames.map((e) => visitConstant(e.value))
+      ]
+    ]);
+  }
 
   @override
   js_ast.Expression visitInstanceConstant(InstanceConstant node) {
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
index 8f3f00f..9f3d9ec 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/types.dart
@@ -2215,18 +2215,56 @@
 }
 
 final _shape = JS('', 'Symbol("shape")');
-var Record = JS('!', '''class Record {}''');
+
 final Object _RecordImpl = JS(
     '!',
     '''
-class _RecordImpl extends # {
+class _RecordImpl {
   constructor(values) {
-    super();
     this.values = values;
   }
+
+  [#](other) {
+    if (!(other instanceof #)) return false;
+    if (this.# !== other.#) return false;
+    if (this.values.length !== other.values.length) return false;
+    for (let i = 0; i < this.values.length; i++)
+      if (!#(this.values[i], other.values[i]))
+        return false;
+    return true;
+  }
+
+  get [#]() {
+    return #([this.#].concat(this.values.map((v) => #(v))));
+  }
+
+  [#]() {
+    let shape = this.#;
+    let s = '(';
+    for (let i = 0; i < this.values.length; i++) {
+      if (i >= shape.positionals) {
+        s += shape.named[i - shape.positionals] + ': '
+      }
+      s += #(this.values[i]);
+      if (i < this.values.length - 1) s += ', ';
+    }
+    s += ')';
+    return s;
+  }
 }
 ''',
-    Record);
+    extensionSymbol('_equals'),
+    _RecordImpl,
+    _shape,
+    _shape,
+    equals,
+    extensionSymbol('hashCode'),
+    Object.hashAll,
+    _shape,
+    hashCode,
+    extensionSymbol('toString'),
+    _shape,
+    _toString);
 
 /// Cache for Record shapes. These are keyed by a distinct shape recipe,
 /// which consists of an integer followed by space-separated named labels.