[pkg:js] Add proto argument to createStaticInteropMock

Adds optional argument proto to allow users to pass instanceof/is
checks with the generated forwarding mock.

Change-Id: If2dba386fb0e66cbe738105e58d9e5ec1f02d02a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/257162
Commit-Queue: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Riley Porter <rileyporter@google.com>
diff --git a/pkg/_js_interop_checks/lib/src/transformations/static_interop_mock_creator.dart b/pkg/_js_interop_checks/lib/src/transformations/static_interop_mock_creator.dart
index e80ee64..d88e799 100644
--- a/pkg/_js_interop_checks/lib/src/transformations/static_interop_mock_creator.dart
+++ b/pkg/_js_interop_checks/lib/src/transformations/static_interop_mock_creator.dart
@@ -314,7 +314,8 @@
       Map<ExtensionMemberDescriptor, Class> descriptorToClass,
       Map<String, Member> dartMemberMap) {
     var block = <Statement>[];
-    assert(node.arguments.positional.length == 1);
+    var argsLength = node.arguments.positional.length;
+    assert(argsLength == 1 || argsLength == 2);
     var interopType = node.arguments.types[0];
     var dartType = node.arguments.types[1];
 
@@ -330,22 +331,25 @@
         Arguments([StaticGet(_globalThis), StringLiteral('Object')],
             types: [_objectType]));
 
-    // Get a fresh object literal.
-    // TODO(srujzs): Add prototype option for instance checks.
-    StaticInvocation getLiteral() {
+    // Get a fresh object literal, using the proto to create it if one was
+    // given.
+    StaticInvocation getLiteral([Expression? proto]) {
       return StaticInvocation(
           _callMethod,
           Arguments([
             getObjectProperty(),
             StringLiteral('create'),
-            ListLiteral([NullLiteral()]),
+            ListLiteral([proto ?? NullLiteral()]),
           ], types: [
             _objectType
           ]));
     }
 
     var jsMock = VariableDeclaration('#jsMock',
-        initializer: AsExpression(getLiteral(), interopType), type: interopType)
+        initializer: AsExpression(
+            getLiteral(argsLength == 2 ? node.arguments.positional[1] : null),
+            interopType),
+        type: interopType)
       ..fileOffset = node.fileOffset
       ..parent = node.parent;
     block.add(jsMock);
diff --git a/sdk/lib/js_util/js_util.dart b/sdk/lib/js_util/js_util.dart
index d3a4510..c233e9b 100644
--- a/sdk/lib/js_util/js_util.dart
+++ b/sdk/lib/js_util/js_util.dart
@@ -166,6 +166,10 @@
 /// U that implements the external extension members of T, creates a forwarding
 /// mock.
 ///
+/// Optionally, you may provide a JS prototype object e.g. the JS value
+/// `Window.prototype` using [proto]. This allows instanceof and is checks with
+/// `@Native` types to pass with the returned forwarding mock.
+///
 /// When external extension members are called, they will forward to the
 /// corresponding implementing member in [dartMock]. If U does not implement all
 /// the external extension members of T, or if U does not properly override
@@ -194,4 +198,4 @@
 /// TODO(srujzs): Add more detail on how inherited extension members need to be
 /// implemented, as well as how conflicts are resolved (if they are resolvable).
 /// The semantics here tries to conform to the view type specification.
-external T createStaticInteropMock<T, U>(U dartMock);
+external T createStaticInteropMock<T, U>(U dartMock, [Object? proto = null]);
diff --git a/tests/lib/js/static_interop_test/mock/functional_test.dart b/tests/lib/js/static_interop_test/mock/functional_test.dart
index f44f0cb..a43df9a 100644
--- a/tests/lib/js/static_interop_test/mock/functional_test.dart
+++ b/tests/lib/js/static_interop_test/mock/functional_test.dart
@@ -2,135 +2,8 @@
 // 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.
 
-// Test basic functionality of `createStaticInteropMock`. Basic methods, fields
-// (final and not), getters, and setters are tested along with potential
-// renames.
-
-import 'package:expect/minitest.dart';
-import 'package:js/js.dart';
-import 'package:js/js_util.dart';
-
-@JS()
-@staticInterop
-class Methods {}
-
-extension on Methods {
-  external int add(int a, int b);
-  // Sanity-check that non-externals are unaffected.
-  int nonExternal() => 0;
-  @JS('_rename')
-  external int rename();
-  external int optionalAdd(int a, int b, [int c = 0, int? d]);
-}
-
-class MethodsDart {
-  int add(int a, int b) => a + b;
-  int nonExternal() => 1;
-  int rename() => 1;
-  int optionalAdd(int a, int b, [int? c, int? d]) =>
-      a + b + (c ?? 0) + (d ?? 0);
-}
-
-@JS()
-@staticInterop
-class Fields {}
-
-extension on Fields {
-  external int field;
-  external final int finalField;
-  @JS('_renamedField')
-  external int renamedField;
-  @JS('_renamedFinalField')
-  external final int renamedFinalField;
-}
-
-class FieldsDart {
-  int field = 1;
-  int finalField = 1;
-  int renamedField = 1;
-  final int renamedFinalField = 1;
-}
-
-@JS()
-@staticInterop
-class GetSet {}
-
-extension on GetSet {
-  external int get getSet;
-  external set getSet(int val);
-  @JS('_renamedGetSet')
-  external int get renamedGetSet;
-  @JS('_renamedGetSet')
-  external set renamedGetSet(int val);
-  @JS('_sameNameDifferentRenameGet')
-  external int get sameNameDifferentRename;
-  @JS('_sameNameDifferentRenameSet')
-  external set sameNameDifferentRename(int val);
-  @JS('_differentNameSameRename')
-  external int get differentNameSameRenameGet;
-  @JS('_differentNameSameRename')
-  external set differentNameSameRenameSet(int val);
-}
-
-class GetSetDart {
-  int getSet = 1;
-  int renamedGetSet = 1;
-  int sameNameDifferentRename = 1;
-  int differentNameSameRenameGet = 1;
-  int differentNameSameRenameSet = 1;
-}
+import 'functional_test_lib.dart';
 
 void main() {
-  var jsMethods = createStaticInteropMock<Methods, MethodsDart>(MethodsDart());
-  expect(jsMethods.add(1, 1), 2);
-  expect(jsMethods.nonExternal(), 0);
-  expect(jsMethods.rename(), 1);
-  expect(jsMethods.optionalAdd(1, 1), 2);
-  expect(jsMethods.optionalAdd(1, 1, 1), 3);
-  expect(jsMethods.optionalAdd(1, 1, 1, 1), 4);
-  var dartFields = FieldsDart();
-  var jsFields = createStaticInteropMock<Fields, FieldsDart>(dartFields);
-  expect(jsFields.field, 1);
-  expect(jsFields.finalField, 1);
-  expect(jsFields.renamedField, 1);
-  expect(jsFields.renamedFinalField, 1);
-  // Modify the JS mock and check for updates in the Dart mock.
-  jsFields.field = 2;
-  jsFields.renamedField = 2;
-  expect(dartFields.field, 2);
-  expect(dartFields.renamedField, 2);
-  // Modify the Dart mock and check for updates in the JS mock.
-  dartFields.field = 3;
-  dartFields.finalField = 3;
-  dartFields.renamedField = 3;
-  expect(jsFields.field, 3);
-  expect(jsFields.finalField, 3);
-  expect(jsFields.renamedField, 3);
-  var dartGetSet = GetSetDart();
-  var jsGetSet = createStaticInteropMock<GetSet, GetSetDart>(dartGetSet);
-  expect(jsGetSet.getSet, 1);
-  expect(jsGetSet.renamedGetSet, 1);
-  expect(jsGetSet.sameNameDifferentRename, 1);
-  expect(jsGetSet.differentNameSameRenameGet, 1);
-  // Modify the JS mock and check for updates in the Dart mock.
-  jsGetSet.getSet = 2;
-  jsGetSet.renamedGetSet = 2;
-  jsGetSet.sameNameDifferentRename = 2;
-  jsGetSet.differentNameSameRenameSet = 2;
-  expect(dartGetSet.getSet, 2);
-  expect(dartGetSet.renamedGetSet, 2);
-  expect(dartGetSet.sameNameDifferentRename, 2);
-  expect(dartGetSet.differentNameSameRenameGet, 1);
-  expect(dartGetSet.differentNameSameRenameSet, 2);
-  // Modify the Dart mock and check for updates in the JS mock.
-  dartGetSet.getSet = 3;
-  dartGetSet.renamedGetSet = 3;
-  dartGetSet.sameNameDifferentRename = 3;
-  // Use different values to disambiguate.
-  dartGetSet.differentNameSameRenameGet = 3;
-  dartGetSet.differentNameSameRenameSet = 4;
-  expect(jsGetSet.getSet, 3);
-  expect(jsGetSet.renamedGetSet, 3);
-  expect(jsGetSet.sameNameDifferentRename, 3);
-  expect(jsGetSet.differentNameSameRenameGet, 3);
+  test();
 }
diff --git a/tests/lib/js/static_interop_test/mock/functional_test_lib.dart b/tests/lib/js/static_interop_test/mock/functional_test_lib.dart
new file mode 100644
index 0000000..69d5ec9
--- /dev/null
+++ b/tests/lib/js/static_interop_test/mock/functional_test_lib.dart
@@ -0,0 +1,137 @@
+// Copyright (c) 2022, 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.
+
+// Test basic functionality of `createStaticInteropMock`. Basic methods, fields
+// (final and not), getters, and setters are tested along with potential
+// renames.
+
+import 'package:expect/minitest.dart';
+import 'package:js/js.dart';
+import 'package:js/js_util.dart';
+
+@JS()
+@staticInterop
+class Methods {}
+
+extension on Methods {
+  external int add(int a, int b);
+  // Sanity-check that non-externals are unaffected.
+  int nonExternal() => 0;
+  @JS('_rename')
+  external int rename();
+  external int optionalAdd(int a, int b, [int c = 0, int? d]);
+}
+
+class MethodsDart {
+  int add(int a, int b) => a + b;
+  int nonExternal() => 1;
+  int rename() => 1;
+  int optionalAdd(int a, int b, [int? c, int? d]) =>
+      a + b + (c ?? 0) + (d ?? 0);
+}
+
+@JS()
+@staticInterop
+class Fields {}
+
+extension on Fields {
+  external int field;
+  external final int finalField;
+  @JS('_renamedField')
+  external int renamedField;
+  @JS('_renamedFinalField')
+  external final int renamedFinalField;
+}
+
+class FieldsDart {
+  int field = 1;
+  int finalField = 1;
+  int renamedField = 1;
+  final int renamedFinalField = 1;
+}
+
+@JS()
+@staticInterop
+class GetSet {}
+
+extension on GetSet {
+  external int get getSet;
+  external set getSet(int val);
+  @JS('_renamedGetSet')
+  external int get renamedGetSet;
+  @JS('_renamedGetSet')
+  external set renamedGetSet(int val);
+  @JS('_sameNameDifferentRenameGet')
+  external int get sameNameDifferentRename;
+  @JS('_sameNameDifferentRenameSet')
+  external set sameNameDifferentRename(int val);
+  @JS('_differentNameSameRename')
+  external int get differentNameSameRenameGet;
+  @JS('_differentNameSameRename')
+  external set differentNameSameRenameSet(int val);
+}
+
+class GetSetDart {
+  int getSet = 1;
+  int renamedGetSet = 1;
+  int sameNameDifferentRename = 1;
+  int differentNameSameRenameGet = 1;
+  int differentNameSameRenameSet = 1;
+}
+
+void test([Object? proto]) {
+  var jsMethods =
+      createStaticInteropMock<Methods, MethodsDart>(MethodsDart(), proto);
+  expect(jsMethods.add(1, 1), 2);
+  expect(jsMethods.nonExternal(), 0);
+  expect(jsMethods.rename(), 1);
+  expect(jsMethods.optionalAdd(1, 1), 2);
+  expect(jsMethods.optionalAdd(1, 1, 1), 3);
+  expect(jsMethods.optionalAdd(1, 1, 1, 1), 4);
+  var dartFields = FieldsDart();
+  var jsFields = createStaticInteropMock<Fields, FieldsDart>(dartFields, proto);
+  expect(jsFields.field, 1);
+  expect(jsFields.finalField, 1);
+  expect(jsFields.renamedField, 1);
+  expect(jsFields.renamedFinalField, 1);
+  // Modify the JS mock and check for updates in the Dart mock.
+  jsFields.field = 2;
+  jsFields.renamedField = 2;
+  expect(dartFields.field, 2);
+  expect(dartFields.renamedField, 2);
+  // Modify the Dart mock and check for updates in the JS mock.
+  dartFields.field = 3;
+  dartFields.finalField = 3;
+  dartFields.renamedField = 3;
+  expect(jsFields.field, 3);
+  expect(jsFields.finalField, 3);
+  expect(jsFields.renamedField, 3);
+  var dartGetSet = GetSetDart();
+  var jsGetSet = createStaticInteropMock<GetSet, GetSetDart>(dartGetSet, proto);
+  expect(jsGetSet.getSet, 1);
+  expect(jsGetSet.renamedGetSet, 1);
+  expect(jsGetSet.sameNameDifferentRename, 1);
+  expect(jsGetSet.differentNameSameRenameGet, 1);
+  // Modify the JS mock and check for updates in the Dart mock.
+  jsGetSet.getSet = 2;
+  jsGetSet.renamedGetSet = 2;
+  jsGetSet.sameNameDifferentRename = 2;
+  jsGetSet.differentNameSameRenameSet = 2;
+  expect(dartGetSet.getSet, 2);
+  expect(dartGetSet.renamedGetSet, 2);
+  expect(dartGetSet.sameNameDifferentRename, 2);
+  expect(dartGetSet.differentNameSameRenameGet, 1);
+  expect(dartGetSet.differentNameSameRenameSet, 2);
+  // Modify the Dart mock and check for updates in the JS mock.
+  dartGetSet.getSet = 3;
+  dartGetSet.renamedGetSet = 3;
+  dartGetSet.sameNameDifferentRename = 3;
+  // Use different values to disambiguate.
+  dartGetSet.differentNameSameRenameGet = 3;
+  dartGetSet.differentNameSameRenameSet = 4;
+  expect(jsGetSet.getSet, 3);
+  expect(jsGetSet.renamedGetSet, 3);
+  expect(jsGetSet.sameNameDifferentRename, 3);
+  expect(jsGetSet.differentNameSameRenameGet, 3);
+}
diff --git a/tests/lib/js/static_interop_test/mock/proto_test.dart b/tests/lib/js/static_interop_test/mock/proto_test.dart
new file mode 100644
index 0000000..647cd58
--- /dev/null
+++ b/tests/lib/js/static_interop_test/mock/proto_test.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2022, 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.
+
+// Test that providing a proto object to createStaticInteropMock does not break
+// any functionality, and allows instanceof/is checks to pass.
+
+@JS()
+library proto_test;
+
+import 'dart:html';
+
+import 'package:expect/minitest.dart';
+import 'package:js/js.dart';
+import 'package:js/js_util.dart';
+
+import 'functional_test_lib.dart' as functional_test;
+
+@JS('Window.prototype')
+external Object get windowProto;
+
+@JS('Window')
+external Object get windowType;
+
+@JS()
+@staticInterop
+class JSWindow {}
+
+class DartWindow {}
+
+void main() {
+  // Test that everything still works the same.
+  functional_test.test(windowProto);
+  // Test instanceof/is checks.
+  var jsMock =
+      createStaticInteropMock<JSWindow, DartWindow>(DartWindow(), windowProto);
+  expect(jsMock is Window, true);
+  expect(instanceof(jsMock, windowType), true);
+}
diff --git a/tests/lib/lib.status b/tests/lib/lib.status
index c68cbad..921086b 100644
--- a/tests/lib/lib.status
+++ b/tests/lib/lib.status
@@ -15,6 +15,7 @@
 js/js_util/javascriptobject_extensions_test: SkipByDesign # Uses dart:html.
 js/static_interop_test/constants_test: SkipByDesign # Uses dart:html.
 js/static_interop_test/futurevaluetype_test: SkipByDesign # Uses dart:html.
+js/static_interop_test/mock/proto_test: SkipByDesign # Uses dart:html.
 js/static_interop_test/supertype_transform_test: SkipByDesign # Uses dart:html.
 
 [ $runtime == dart_precompiled ]
diff --git a/tests/lib_2/js/static_interop_test/mock/functional_test.dart b/tests/lib_2/js/static_interop_test/mock/functional_test.dart
index f44f0cb..a43df9a 100644
--- a/tests/lib_2/js/static_interop_test/mock/functional_test.dart
+++ b/tests/lib_2/js/static_interop_test/mock/functional_test.dart
@@ -2,135 +2,8 @@
 // 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.
 
-// Test basic functionality of `createStaticInteropMock`. Basic methods, fields
-// (final and not), getters, and setters are tested along with potential
-// renames.
-
-import 'package:expect/minitest.dart';
-import 'package:js/js.dart';
-import 'package:js/js_util.dart';
-
-@JS()
-@staticInterop
-class Methods {}
-
-extension on Methods {
-  external int add(int a, int b);
-  // Sanity-check that non-externals are unaffected.
-  int nonExternal() => 0;
-  @JS('_rename')
-  external int rename();
-  external int optionalAdd(int a, int b, [int c = 0, int? d]);
-}
-
-class MethodsDart {
-  int add(int a, int b) => a + b;
-  int nonExternal() => 1;
-  int rename() => 1;
-  int optionalAdd(int a, int b, [int? c, int? d]) =>
-      a + b + (c ?? 0) + (d ?? 0);
-}
-
-@JS()
-@staticInterop
-class Fields {}
-
-extension on Fields {
-  external int field;
-  external final int finalField;
-  @JS('_renamedField')
-  external int renamedField;
-  @JS('_renamedFinalField')
-  external final int renamedFinalField;
-}
-
-class FieldsDart {
-  int field = 1;
-  int finalField = 1;
-  int renamedField = 1;
-  final int renamedFinalField = 1;
-}
-
-@JS()
-@staticInterop
-class GetSet {}
-
-extension on GetSet {
-  external int get getSet;
-  external set getSet(int val);
-  @JS('_renamedGetSet')
-  external int get renamedGetSet;
-  @JS('_renamedGetSet')
-  external set renamedGetSet(int val);
-  @JS('_sameNameDifferentRenameGet')
-  external int get sameNameDifferentRename;
-  @JS('_sameNameDifferentRenameSet')
-  external set sameNameDifferentRename(int val);
-  @JS('_differentNameSameRename')
-  external int get differentNameSameRenameGet;
-  @JS('_differentNameSameRename')
-  external set differentNameSameRenameSet(int val);
-}
-
-class GetSetDart {
-  int getSet = 1;
-  int renamedGetSet = 1;
-  int sameNameDifferentRename = 1;
-  int differentNameSameRenameGet = 1;
-  int differentNameSameRenameSet = 1;
-}
+import 'functional_test_lib.dart';
 
 void main() {
-  var jsMethods = createStaticInteropMock<Methods, MethodsDart>(MethodsDart());
-  expect(jsMethods.add(1, 1), 2);
-  expect(jsMethods.nonExternal(), 0);
-  expect(jsMethods.rename(), 1);
-  expect(jsMethods.optionalAdd(1, 1), 2);
-  expect(jsMethods.optionalAdd(1, 1, 1), 3);
-  expect(jsMethods.optionalAdd(1, 1, 1, 1), 4);
-  var dartFields = FieldsDart();
-  var jsFields = createStaticInteropMock<Fields, FieldsDart>(dartFields);
-  expect(jsFields.field, 1);
-  expect(jsFields.finalField, 1);
-  expect(jsFields.renamedField, 1);
-  expect(jsFields.renamedFinalField, 1);
-  // Modify the JS mock and check for updates in the Dart mock.
-  jsFields.field = 2;
-  jsFields.renamedField = 2;
-  expect(dartFields.field, 2);
-  expect(dartFields.renamedField, 2);
-  // Modify the Dart mock and check for updates in the JS mock.
-  dartFields.field = 3;
-  dartFields.finalField = 3;
-  dartFields.renamedField = 3;
-  expect(jsFields.field, 3);
-  expect(jsFields.finalField, 3);
-  expect(jsFields.renamedField, 3);
-  var dartGetSet = GetSetDart();
-  var jsGetSet = createStaticInteropMock<GetSet, GetSetDart>(dartGetSet);
-  expect(jsGetSet.getSet, 1);
-  expect(jsGetSet.renamedGetSet, 1);
-  expect(jsGetSet.sameNameDifferentRename, 1);
-  expect(jsGetSet.differentNameSameRenameGet, 1);
-  // Modify the JS mock and check for updates in the Dart mock.
-  jsGetSet.getSet = 2;
-  jsGetSet.renamedGetSet = 2;
-  jsGetSet.sameNameDifferentRename = 2;
-  jsGetSet.differentNameSameRenameSet = 2;
-  expect(dartGetSet.getSet, 2);
-  expect(dartGetSet.renamedGetSet, 2);
-  expect(dartGetSet.sameNameDifferentRename, 2);
-  expect(dartGetSet.differentNameSameRenameGet, 1);
-  expect(dartGetSet.differentNameSameRenameSet, 2);
-  // Modify the Dart mock and check for updates in the JS mock.
-  dartGetSet.getSet = 3;
-  dartGetSet.renamedGetSet = 3;
-  dartGetSet.sameNameDifferentRename = 3;
-  // Use different values to disambiguate.
-  dartGetSet.differentNameSameRenameGet = 3;
-  dartGetSet.differentNameSameRenameSet = 4;
-  expect(jsGetSet.getSet, 3);
-  expect(jsGetSet.renamedGetSet, 3);
-  expect(jsGetSet.sameNameDifferentRename, 3);
-  expect(jsGetSet.differentNameSameRenameGet, 3);
+  test();
 }
diff --git a/tests/lib_2/js/static_interop_test/mock/functional_test_lib.dart b/tests/lib_2/js/static_interop_test/mock/functional_test_lib.dart
new file mode 100644
index 0000000..69d5ec9
--- /dev/null
+++ b/tests/lib_2/js/static_interop_test/mock/functional_test_lib.dart
@@ -0,0 +1,137 @@
+// Copyright (c) 2022, 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.
+
+// Test basic functionality of `createStaticInteropMock`. Basic methods, fields
+// (final and not), getters, and setters are tested along with potential
+// renames.
+
+import 'package:expect/minitest.dart';
+import 'package:js/js.dart';
+import 'package:js/js_util.dart';
+
+@JS()
+@staticInterop
+class Methods {}
+
+extension on Methods {
+  external int add(int a, int b);
+  // Sanity-check that non-externals are unaffected.
+  int nonExternal() => 0;
+  @JS('_rename')
+  external int rename();
+  external int optionalAdd(int a, int b, [int c = 0, int? d]);
+}
+
+class MethodsDart {
+  int add(int a, int b) => a + b;
+  int nonExternal() => 1;
+  int rename() => 1;
+  int optionalAdd(int a, int b, [int? c, int? d]) =>
+      a + b + (c ?? 0) + (d ?? 0);
+}
+
+@JS()
+@staticInterop
+class Fields {}
+
+extension on Fields {
+  external int field;
+  external final int finalField;
+  @JS('_renamedField')
+  external int renamedField;
+  @JS('_renamedFinalField')
+  external final int renamedFinalField;
+}
+
+class FieldsDart {
+  int field = 1;
+  int finalField = 1;
+  int renamedField = 1;
+  final int renamedFinalField = 1;
+}
+
+@JS()
+@staticInterop
+class GetSet {}
+
+extension on GetSet {
+  external int get getSet;
+  external set getSet(int val);
+  @JS('_renamedGetSet')
+  external int get renamedGetSet;
+  @JS('_renamedGetSet')
+  external set renamedGetSet(int val);
+  @JS('_sameNameDifferentRenameGet')
+  external int get sameNameDifferentRename;
+  @JS('_sameNameDifferentRenameSet')
+  external set sameNameDifferentRename(int val);
+  @JS('_differentNameSameRename')
+  external int get differentNameSameRenameGet;
+  @JS('_differentNameSameRename')
+  external set differentNameSameRenameSet(int val);
+}
+
+class GetSetDart {
+  int getSet = 1;
+  int renamedGetSet = 1;
+  int sameNameDifferentRename = 1;
+  int differentNameSameRenameGet = 1;
+  int differentNameSameRenameSet = 1;
+}
+
+void test([Object? proto]) {
+  var jsMethods =
+      createStaticInteropMock<Methods, MethodsDart>(MethodsDart(), proto);
+  expect(jsMethods.add(1, 1), 2);
+  expect(jsMethods.nonExternal(), 0);
+  expect(jsMethods.rename(), 1);
+  expect(jsMethods.optionalAdd(1, 1), 2);
+  expect(jsMethods.optionalAdd(1, 1, 1), 3);
+  expect(jsMethods.optionalAdd(1, 1, 1, 1), 4);
+  var dartFields = FieldsDart();
+  var jsFields = createStaticInteropMock<Fields, FieldsDart>(dartFields, proto);
+  expect(jsFields.field, 1);
+  expect(jsFields.finalField, 1);
+  expect(jsFields.renamedField, 1);
+  expect(jsFields.renamedFinalField, 1);
+  // Modify the JS mock and check for updates in the Dart mock.
+  jsFields.field = 2;
+  jsFields.renamedField = 2;
+  expect(dartFields.field, 2);
+  expect(dartFields.renamedField, 2);
+  // Modify the Dart mock and check for updates in the JS mock.
+  dartFields.field = 3;
+  dartFields.finalField = 3;
+  dartFields.renamedField = 3;
+  expect(jsFields.field, 3);
+  expect(jsFields.finalField, 3);
+  expect(jsFields.renamedField, 3);
+  var dartGetSet = GetSetDart();
+  var jsGetSet = createStaticInteropMock<GetSet, GetSetDart>(dartGetSet, proto);
+  expect(jsGetSet.getSet, 1);
+  expect(jsGetSet.renamedGetSet, 1);
+  expect(jsGetSet.sameNameDifferentRename, 1);
+  expect(jsGetSet.differentNameSameRenameGet, 1);
+  // Modify the JS mock and check for updates in the Dart mock.
+  jsGetSet.getSet = 2;
+  jsGetSet.renamedGetSet = 2;
+  jsGetSet.sameNameDifferentRename = 2;
+  jsGetSet.differentNameSameRenameSet = 2;
+  expect(dartGetSet.getSet, 2);
+  expect(dartGetSet.renamedGetSet, 2);
+  expect(dartGetSet.sameNameDifferentRename, 2);
+  expect(dartGetSet.differentNameSameRenameGet, 1);
+  expect(dartGetSet.differentNameSameRenameSet, 2);
+  // Modify the Dart mock and check for updates in the JS mock.
+  dartGetSet.getSet = 3;
+  dartGetSet.renamedGetSet = 3;
+  dartGetSet.sameNameDifferentRename = 3;
+  // Use different values to disambiguate.
+  dartGetSet.differentNameSameRenameGet = 3;
+  dartGetSet.differentNameSameRenameSet = 4;
+  expect(jsGetSet.getSet, 3);
+  expect(jsGetSet.renamedGetSet, 3);
+  expect(jsGetSet.sameNameDifferentRename, 3);
+  expect(jsGetSet.differentNameSameRenameGet, 3);
+}
diff --git a/tests/lib_2/js/static_interop_test/mock/proto_test.dart b/tests/lib_2/js/static_interop_test/mock/proto_test.dart
new file mode 100644
index 0000000..647cd58
--- /dev/null
+++ b/tests/lib_2/js/static_interop_test/mock/proto_test.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2022, 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.
+
+// Test that providing a proto object to createStaticInteropMock does not break
+// any functionality, and allows instanceof/is checks to pass.
+
+@JS()
+library proto_test;
+
+import 'dart:html';
+
+import 'package:expect/minitest.dart';
+import 'package:js/js.dart';
+import 'package:js/js_util.dart';
+
+import 'functional_test_lib.dart' as functional_test;
+
+@JS('Window.prototype')
+external Object get windowProto;
+
+@JS('Window')
+external Object get windowType;
+
+@JS()
+@staticInterop
+class JSWindow {}
+
+class DartWindow {}
+
+void main() {
+  // Test that everything still works the same.
+  functional_test.test(windowProto);
+  // Test instanceof/is checks.
+  var jsMock =
+      createStaticInteropMock<JSWindow, DartWindow>(DartWindow(), windowProto);
+  expect(jsMock is Window, true);
+  expect(instanceof(jsMock, windowType), true);
+}
diff --git a/tests/lib_2/lib_2.status b/tests/lib_2/lib_2.status
index 1688cf9..15d4e48 100644
--- a/tests/lib_2/lib_2.status
+++ b/tests/lib_2/lib_2.status
@@ -15,6 +15,7 @@
 js/js_util/javascriptobject_extensions_test: SkipByDesign # Uses dart:html.
 js/static_interop_test/constants_test: SkipByDesign # Uses dart:html.
 js/static_interop_test/futurevaluetype_test: SkipByDesign # Uses dart:html.
+js/static_interop_test/mock/proto_test: SkipByDesign # Uses dart:html.
 js/static_interop_test/supertype_transform_test: SkipByDesign # Uses dart:html.
 
 [ $runtime == dart_precompiled ]