[dart2js] make js_runtime a proper package

Change-Id: Ib5583f79abc0ab00a96ce6473282f4322da5143c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/180720
Reviewed-by: Stephen Adams <sra@google.com>
Commit-Queue: Sigmund Cherem <sigmund@google.com>
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index eb075b1..3daa83f 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -354,8 +354,9 @@
     },
     {
       "name": "js_runtime",
-      "rootUri": "../sdk/lib/_internal/js_runtime",
-      "packageUri": "lib/"
+      "rootUri": "../pkg/js_runtime",
+      "packageUri": "lib/",
+      "languageVersion": "2.12"
     },
     {
       "name": "json_rpc_2",
diff --git a/.packages b/.packages
index d31bc9b..badd487 100644
--- a/.packages
+++ b/.packages
@@ -54,7 +54,7 @@
 intl:third_party/pkg/intl/lib
 js:pkg/js/lib
 js_ast:pkg/js_ast/lib
-js_runtime:sdk/lib/_internal/js_runtime/lib
+js_runtime:pkg/js_runtime/lib
 json_rpc_2:third_party/pkg/json_rpc_2/lib
 kernel:pkg/kernel/lib
 linter:third_party/pkg/linter/lib
diff --git a/pkg/compiler/pubspec.yaml b/pkg/compiler/pubspec.yaml
index 8a61bf9..0f826c1 100644
--- a/pkg/compiler/pubspec.yaml
+++ b/pkg/compiler/pubspec.yaml
@@ -26,7 +26,7 @@
   js_ast:
     path: ../js_ast
   js_runtime:
-    path: ../../sdk/lib/_internal/js_runtime
+    path: ../js_runtime
 
 dev_dependencies:
   # Published packages - repo version ensured via dependency_overrides
diff --git a/pkg/js_runtime/README.md b/pkg/js_runtime/README.md
new file mode 100644
index 0000000..5f60952
--- /dev/null
+++ b/pkg/js_runtime/README.md
@@ -0,0 +1,9 @@
+# Package `js_runtime`:
+
+This package contains code that is shared between the dart2js compiler and the
+dart2js runtime libraries.
+
+*Important*: all code under the `lib/shared/` must be kept in sync with the
+runtime at all times (in `sdk/lib/_internal/js_runtime/lib/shared`). The
+`test/in_sync_test.dart` test verifies this.
+
diff --git a/pkg/js_runtime/lib/shared/async_await_error_codes.dart b/pkg/js_runtime/lib/shared/async_await_error_codes.dart
new file mode 100644
index 0000000..f87406b
--- /dev/null
+++ b/pkg/js_runtime/lib/shared/async_await_error_codes.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2015, 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.
+
+/// Contains error codes that transformed async/async* functions use to
+/// communicate with js_helper functions.
+
+const int SUCCESS = 0;
+const int ERROR = 1;
+const int STREAM_WAS_CANCELED = 2;
diff --git a/pkg/js_runtime/lib/shared/embedded_names.dart b/pkg/js_runtime/lib/shared/embedded_names.dart
new file mode 100644
index 0000000..e80fb6a
--- /dev/null
+++ b/pkg/js_runtime/lib/shared/embedded_names.dart
@@ -0,0 +1,281 @@
+// Copyright (c) 2014, 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.
+
+/// Contains the names of globals that are embedded into the output by the
+/// compiler.
+///
+/// Variables embedded this way should be access with `JS_EMBEDDED_GLOBAL` from
+/// the `_foreign_helper` library.
+///
+/// This library is shared between the compiler and the runtime system.
+library dart2js._embedded_names;
+
+/// The name of the property that is used to find the native superclass of
+/// an extended class.
+///
+/// Every class that extends a native class has this property set on its
+/// native class.
+const NATIVE_SUPERCLASS_TAG_NAME = r"$nativeSuperclassTag";
+
+/// The name of the static-function property name.
+///
+/// This property is set for all tear-offs of static functions, and provides
+/// the static function's unique (potentially minified) name.
+const STATIC_FUNCTION_NAME_PROPERTY_NAME = r'$static_name';
+
+/// The name of a property on the constructor function of Dart Object
+/// and interceptor types, used for caching Rti types.
+const CONSTRUCTOR_RTI_CACHE_PROPERTY_NAME = r'$ccache';
+
+/// The name of the embedded global for metadata.
+///
+/// Use [JsBuiltin.getMetadata] instead of directly accessing this embedded
+/// global.
+const METADATA = 'metadata';
+
+/// A list of types used in the program e.g. for reflection or encoding of
+/// function types.
+///
+/// Use [JsBuiltin.getType] instead of directly accessing this embedded global.
+const TYPES = 'types';
+
+/// Returns a function that maps a name of a class to its type.
+///
+/// This embedded global is used by the runtime when computing the internal
+/// runtime-type-information (rti) object.
+const GET_TYPE_FROM_NAME = 'getTypeFromName';
+
+/// A JS map from mangled global names to their unmangled names.
+///
+/// If the program does not use reflection, this embedded global may be empty
+/// (but not null or undefined).
+const MANGLED_GLOBAL_NAMES = 'mangledGlobalNames';
+
+/// A JS map from mangled instance names to their unmangled names.
+///
+/// This embedded global is mainly used for reflection, but is also used to
+/// map const-symbols (`const Symbol('x')`) to the mangled instance names.
+///
+/// This embedded global may be empty (but not null or undefined).
+const MANGLED_NAMES = 'mangledNames';
+
+/// A JS map from dispatch tags (usually constructor names of DOM classes) to
+/// interceptor class. This map is used to find the correct interceptor for
+/// native classes.
+///
+/// This embedded global is used for natives.
+const INTERCEPTORS_BY_TAG = 'interceptorsByTag';
+
+/// A JS map from dispatch tags (usually constructor names of DOM classes) to
+/// booleans. Every tag entry of [INTERCEPTORS_BY_TAG] has a corresponding
+/// entry in the leaf-tags map.
+///
+/// A tag-entry is true, when a class can be treated as leaf class in the
+/// hierarchy. That is, even though it might have subclasses, all subclasses
+/// have the same code for the used methods.
+///
+/// This embedded global is used for natives.
+const LEAF_TAGS = 'leafTags';
+
+/// A JS function that returns the isolate tag for a given name.
+///
+/// This function uses the [ISOLATE_TAG] (below) to construct a name that is
+/// unique per isolate.
+///
+/// This embedded global is used for natives.
+// TODO(floitsch): should we rename this variable to avoid confusion with
+//    [INTERCEPTORS_BY_TAG] and [LEAF_TAGS].
+const GET_ISOLATE_TAG = 'getIsolateTag';
+
+/// A string that is different for each running isolate.
+///
+/// When this embedded global is initialized a global variable is used to
+/// ensure that no other running isolate uses the same isolate-tag string.
+///
+/// This embedded global is used for natives.
+// TODO(floitsch): should we rename this variable to avoid confusion with
+//    [INTERCEPTORS_BY_TAG] and [LEAF_TAGS].
+const ISOLATE_TAG = 'isolateTag';
+
+/// An embedded global that contains the property used to store type information
+/// on JavaScript Array instances. This is a Symbol (except for IE11, where is
+/// is a String).
+const ARRAY_RTI_PROPERTY = 'arrayRti';
+
+/// This embedded global (a function) returns the isolate-specific dispatch-tag
+/// that is used to accelerate interceptor calls.
+const DISPATCH_PROPERTY_NAME = "dispatchPropertyName";
+
+/// An embedded global that maps a [Type] to the [Interceptor] and constructors
+/// for that type.
+///
+/// More documentation can be found in the interceptors library (close to its
+/// use).
+const TYPE_TO_INTERCEPTOR_MAP = "typeToInterceptorMap";
+
+/// The current script's URI when the program was loaded.
+///
+/// This embedded global is set at startup, just before invoking `main`.
+const CURRENT_SCRIPT = 'currentScript';
+
+/// Contains a map from load-ids to lists of part indexes.
+///
+/// To load the deferred library that is represented by the load-id, the runtime
+/// must load all associated URIs (named in DEFERRED_PART_URIS) and initialize
+/// all the loaded hunks (DEFERRED_PART_HASHES).
+///
+/// This embedded global is only used for deferred loading.
+const DEFERRED_LIBRARY_PARTS = 'deferredLibraryParts';
+
+/// Contains a list of URIs (Strings), indexed by part.
+///
+/// The lists in the DEFERRED_LIBRARY_PARTS map contain indexes into this list.
+///
+/// This embedded global is only used for deferred loading.
+const DEFERRED_PART_URIS = 'deferredPartUris';
+
+/// Contains a list of hashes, indexed by part.
+///
+/// The lists in the DEFERRED_LIBRARY_PARTS map contain indexes into this list.
+///
+/// The hashes are associated with the URIs of the load-ids (see
+/// [DEFERRED_PART_URIS]). They are SHA1 (or similar) hashes of the code that
+/// must be loaded. By using cryptographic hashes we can (1) handle loading in
+/// the same web page the parts from multiple Dart applications (2) avoid
+/// loading similar code multiple times.
+///
+/// This embedded global is only used for deferred loading.
+const DEFERRED_PART_HASHES = 'deferredPartHashes';
+
+/// Initialize a loaded hunk.
+///
+/// Once a hunk (the code from a deferred URI) has been loaded it must be
+/// initialized. Calling this function with the corresponding hash (see
+/// [DEFERRED_LIBRARY_HASHES]) initializes the code.
+///
+/// This embedded global is only used for deferred loading.
+const INITIALIZE_LOADED_HUNK = 'initializeLoadedHunk';
+
+/// Returns, whether a hunk (identified by its hash) has already been loaded.
+///
+/// This embedded global is only used for deferred loading.
+const IS_HUNK_LOADED = 'isHunkLoaded';
+
+/// Returns, whether a hunk (identified by its hash) has already been
+/// initialized.
+///
+/// This embedded global is only used for deferred loading.
+const IS_HUNK_INITIALIZED = 'isHunkInitialized';
+
+/// A set (implemented as map to booleans) of hunks (identified by hashes) that
+/// have already been initialized.
+///
+/// This embedded global is only used for deferred loading.
+///
+/// This global is an emitter-internal embedded global, and not used by the
+/// runtime. The constant remains in this file to make sure that other embedded
+/// 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';
+
+/// Names that are supported by [JS_GET_NAME].
+// TODO(herhut): Make entries lower case (as in fields) and find a better name.
+enum JsGetName {
+  GETTER_PREFIX,
+  SETTER_PREFIX,
+  CALL_PREFIX,
+  CALL_PREFIX0,
+  CALL_PREFIX1,
+  CALL_PREFIX2,
+  CALL_PREFIX3,
+  CALL_PREFIX4,
+  CALL_PREFIX5,
+  CALL_CATCH_ALL,
+  REQUIRED_PARAMETER_PROPERTY,
+  DEFAULT_VALUES_PROPERTY,
+  CALL_NAME_PROPERTY,
+  DEFERRED_ACTION_PROPERTY,
+
+  /// Prefix used for generated type argument substitutions on classes.
+  OPERATOR_AS_PREFIX,
+
+  /// Prefix used for generated type test property on classes.
+  OPERATOR_IS_PREFIX,
+
+  /// Name used for generated function types on classes and methods.
+  SIGNATURE_NAME,
+
+  /// Name of JavaScript property used to store runtime-type information on
+  /// instances of parameterized classes.
+  RTI_NAME,
+
+  /// String representation of the type of the Future class.
+  FUTURE_CLASS_TYPE_NAME,
+
+  /// Field name used for determining if an object or its interceptor has
+  /// JavaScript indexing behavior.
+  IS_INDEXABLE_FIELD_NAME,
+
+  /// String representation of the type of the null class.
+  NULL_CLASS_TYPE_NAME,
+
+  /// String representation of the type of the object class.
+  OBJECT_CLASS_TYPE_NAME,
+
+  /// Property name for Rti._as field.
+  RTI_FIELD_AS,
+
+  /// Property name for Rti._is field.
+  RTI_FIELD_IS,
+}
+
+enum JsBuiltin {
+  /// Returns the JavaScript constructor function for Dart's Object class.
+  /// This can be used for type tests, as in
+  ///
+  ///     var constructor = JS_BUILTIN('', JsBuiltin.dartObjectConstructor);
+  ///     if (JS('bool', '# instanceof #', obj, constructor))
+  ///       ...
+  dartObjectConstructor,
+
+  /// Returns the JavaScript constructor function for the runtime's Closure
+  /// class, the base class of all closure objects.  This can be used for type
+  /// tests, as in
+  ///
+  ///     var constructor = JS_BUILTIN('', JsBuiltin.dartClosureConstructor);
+  ///     if (JS('bool', '# instanceof #', obj, constructor))
+  ///       ...
+  dartClosureConstructor,
+
+  /// Returns true if the given type is a type argument of a js-interop class
+  /// or a supertype of a js-interop class.
+  ///
+  ///     JS_BUILTIN('bool', JsBuiltin.isJsInteropTypeArgument, o)
+  isJsInteropTypeArgument,
+
+  /// Returns the metadata of the given [index].
+  ///
+  ///     JS_BUILTIN('returns:var;effects:none;depends:none',
+  ///                JsBuiltin.getMetadata, index);
+  getMetadata,
+
+  /// Returns the type of the given [index].
+  ///
+  ///     JS_BUILTIN('returns:var;effects:none;depends:none',
+  ///                JsBuiltin.getType, index);
+  getType,
+}
+
+/// Names of fields of the Rti Universe object.
+class RtiUniverseFieldNames {
+  static String evalCache = 'eC';
+  static String typeRules = 'tR';
+  static String erasedTypes = 'eT';
+  static String typeParameterVariances = 'tPV';
+  static String sharedEmptyArray = 'sEA';
+}
diff --git a/pkg/js_runtime/lib/shared/recipe_syntax.dart b/pkg/js_runtime/lib/shared/recipe_syntax.dart
new file mode 100644
index 0000000..c183861
--- /dev/null
+++ b/pkg/js_runtime/lib/shared/recipe_syntax.dart
@@ -0,0 +1,237 @@
+// Copyright (c) 2019, 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.
+
+/// Constants and predicates used for encoding and decoding type recipes.
+///
+/// This library is shared between the compiler and the runtime system.
+library dart2js._recipe_syntax;
+
+abstract class Recipe {
+  Recipe._();
+
+  // Operators.
+
+  static const int separator = _comma;
+  static const String separatorString = _commaString;
+
+  static const int toType = _semicolon;
+  static const String toTypeString = _semicolonString;
+
+  static const int pushErased = _hash;
+  static const String pushErasedString = _hashString;
+  static const int pushDynamic = _at;
+  static const String pushDynamicString = _atString;
+  static const int pushVoid = _tilde;
+  static const String pushVoidString = _tildeString;
+
+  static const int wrapStar = _asterisk;
+  static const String wrapStarString = _asteriskString;
+  static const int wrapQuestion = _question;
+  static const String wrapQuestionString = _questionString;
+  static const int wrapFutureOr = _slash;
+  static const String wrapFutureOrString = _slashString;
+
+  static const int startTypeArguments = _lessThan;
+  static const String startTypeArgumentsString = _lessThanString;
+  static const int endTypeArguments = _greaterThan;
+  static const String endTypeArgumentsString = _greaterThanString;
+
+  static const int startFunctionArguments = _leftParen;
+  static const String startFunctionArgumentsString = _leftParenString;
+  static const int endFunctionArguments = _rightParen;
+  static const String endFunctionArgumentsString = _rightParenString;
+  static const int startOptionalGroup = _leftBracket;
+  static const String startOptionalGroupString = _leftBracketString;
+  static const int endOptionalGroup = _rightBracket;
+  static const String endOptionalGroupString = _rightBracketString;
+  static const int startNamedGroup = _leftBrace;
+  static const String startNamedGroupString = _leftBraceString;
+  static const int endNamedGroup = _rightBrace;
+  static const String endNamedGroupString = _rightBraceString;
+  static const int nameSeparator = _colon;
+  static const String nameSeparatorString = _colonString;
+  static const int requiredNameSeparator = _exclamation;
+  static const String requiredNameSeparatorString = _exclamationString;
+
+  static const int genericFunctionTypeParameterIndex = _circumflex;
+  static const String genericFunctionTypeParameterIndexString =
+      _circumflexString;
+
+  static const int extensionOp = _ampersand;
+  static const String extensionOpString = _ampersandString;
+  static const int pushNeverExtension = 0;
+  static const String pushNeverExtensionString = '$pushNeverExtension';
+  static const int pushAnyExtension = 1;
+  static const String pushAnyExtensionString = '$pushAnyExtension';
+
+  // Number and name components.
+
+  static bool isDigit(int code) => code >= _digit0 && code <= _digit9;
+  static int digitValue(int code) => code - _digit0;
+
+  static bool isIdentifierStart(int ch) =>
+      (((ch | 32) - _lowercaseA) & 0xffff) < 26 ||
+      (ch == _underscore) ||
+      (ch == _dollar);
+
+  static const int period = _period;
+
+  // Private names.
+
+  static const int _formfeed = 0x0C;
+  static const String _formfeedString = '\f';
+
+  static const int _space = 0x20;
+  static const String _spaceString = ' ';
+  static const int _exclamation = 0x21;
+  static const String _exclamationString = '!';
+  static const int _hash = 0x23;
+  static const String _hashString = '#';
+  static const int _dollar = 0x24;
+  static const String _dollarString = r'$';
+  static const int _percent = 0x25;
+  static const String _percentString = '%';
+  static const int _ampersand = 0x26;
+  static const String _ampersandString = '&';
+  static const int _apostrophe = 0x27;
+  static const String _apostropheString = "'";
+  static const int _leftParen = 0x28;
+  static const String _leftParenString = '(';
+  static const int _rightParen = 0x29;
+  static const String _rightParenString = ')';
+  static const int _asterisk = 0x2A;
+  static const String _asteriskString = '*';
+  static const int _plus = 0x2B;
+  static const String _plusString = '+';
+  static const int _comma = 0x2C;
+  static const String _commaString = ',';
+  static const int _minus = 0x2D;
+  static const String _minusString = '-';
+  static const int _period = 0x2E;
+  static const String _periodString = '.';
+  static const int _slash = 0x2F;
+  static const String _slashString = '/';
+
+  static const int _digit0 = 0x30;
+  static const int _digit9 = 0x39;
+
+  static const int _colon = 0x3A;
+  static const String _colonString = ':';
+  static const int _semicolon = 0x3B;
+  static const String _semicolonString = ';';
+  static const int _lessThan = 0x3C;
+  static const String _lessThanString = '<';
+  static const int _equals = 0x3D;
+  static const String _equalsString = '=';
+  static const int _greaterThan = 0x3E;
+  static const String _greaterThanString = '>';
+  static const int _question = 0x3F;
+  static const String _questionString = '?';
+  static const int _at = 0x40;
+  static const String _atString = '@';
+
+  static const int _uppercaseA = 0x41;
+  static const int _uppercaseZ = 0x5A;
+
+  static const int _leftBracket = 0x5B;
+  static const String _leftBracketString = '[';
+  static const int _backslash = 0x5C;
+  static const String _backslashString = r'\';
+  static const int _rightBracket = 0x5D;
+  static const String _rightBracketString = ']';
+  static const int _circumflex = 0x5E;
+  static const String _circumflexString = '^';
+  static const int _underscore = 0x5F;
+  static const String _underscoreString = '_';
+  static const int _backtick = 0x60;
+  static const String _backtickString = '`';
+
+  static const int _lowercaseA = 0x61;
+  static const int _lowercaseZ = 0x7A;
+
+  static const int _leftBrace = 0x7B;
+  static const String _leftBraceString = '{';
+  static const int _vertical = 0x7C;
+  static const String _verticalString = '|';
+  static const int _rightBrace = 0x7D;
+  static const String _rightBraceString = '}';
+  static const int _tilde = 0x7E;
+  static const String _tildeString = '~';
+
+  static void testEquivalence() {
+    void test(String label, int charCode, String str) {
+      if (String.fromCharCode(charCode) != str) {
+        throw StateError("$label: String.fromCharCode($charCode) != $str");
+      }
+    }
+
+    void testExtension(String label, int op, String str) {
+      if ('$op' != str) {
+        throw StateError("$label: $op.toString() != $str");
+      }
+    }
+
+    test("separator", separator, separatorString);
+    test("toType", toType, toTypeString);
+    test("pushErased", pushErased, pushErasedString);
+    test("pushDynamic", pushDynamic, pushDynamicString);
+    test("pushVoid", pushVoid, pushVoidString);
+    test("wrapStar", wrapStar, wrapStarString);
+    test("wrapQuestion", wrapQuestion, wrapQuestionString);
+    test("wrapFutureOr", wrapFutureOr, wrapFutureOrString);
+    test("startTypeArguments", startTypeArguments, startTypeArgumentsString);
+    test("endTypeArguments", endTypeArguments, endTypeArgumentsString);
+    test("startFunctionArguments", startFunctionArguments,
+        startFunctionArgumentsString);
+    test("endFunctionArguments", endFunctionArguments,
+        endFunctionArgumentsString);
+    test("startOptionalGroup", startOptionalGroup, startOptionalGroupString);
+    test("endOptionalGroup", endOptionalGroup, endOptionalGroupString);
+    test("startNamedGroup", startNamedGroup, startNamedGroupString);
+    test("endNamedGroup", endNamedGroup, endNamedGroupString);
+    test("nameSeparator", nameSeparator, nameSeparatorString);
+    test("requiredNameSeparator", requiredNameSeparator,
+        requiredNameSeparatorString);
+    test("genericFunctionTypeParameterIndex", genericFunctionTypeParameterIndex,
+        genericFunctionTypeParameterIndexString);
+    test("extensionOp", extensionOp, extensionOpString);
+    testExtension(
+        "pushNeverExtension", pushNeverExtension, pushNeverExtensionString);
+    testExtension("pushAnyExtension", pushAnyExtension, pushAnyExtensionString);
+
+    test("_formfeed", _formfeed, _formfeedString);
+    test("_space", _space, _spaceString);
+    test("_exclamation", _exclamation, _exclamationString);
+    test("_hash", _hash, _hashString);
+    test("_dollar", _dollar, _dollarString);
+    test("_percent", _percent, _percentString);
+    test("_ampersand", _ampersand, _ampersandString);
+    test("_apostrophe", _apostrophe, _apostropheString);
+    test("_leftParen", _leftParen, _leftParenString);
+    test("_rightParen", _rightParen, _rightParenString);
+    test("_asterisk", _asterisk, _asteriskString);
+    test("_plus", _plus, _plusString);
+    test("_comma", _comma, _commaString);
+    test("_minus", _minus, _minusString);
+    test("_period", _period, _periodString);
+    test("_slash", _slash, _slashString);
+    test("_colon", _colon, _colonString);
+    test("_semicolon", _semicolon, _semicolonString);
+    test("_lessThan", _lessThan, _lessThanString);
+    test("_equals", _equals, _equalsString);
+    test("_greaterThan", _greaterThan, _greaterThanString);
+    test("_question", _question, _questionString);
+    test("_at", _at, _atString);
+    test("_leftBracket", _leftBracket, _leftBracketString);
+    test("_backslash", _backslash, _backslashString);
+    test("_rightBracket", _rightBracket, _rightBracketString);
+    test("_circumflex", _circumflex, _circumflexString);
+    test("_underscore", _underscore, _underscoreString);
+    test("_backtick", _backtick, _backtickString);
+    test("_leftBrace", _leftBrace, _leftBraceString);
+    test("_vertical", _vertical, _verticalString);
+    test("_rightBrace", _rightBrace, _rightBraceString);
+    test("_tilde", _tilde, _tildeString);
+  }
+}
diff --git a/pkg/js_runtime/pubspec.yaml b/pkg/js_runtime/pubspec.yaml
new file mode 100644
index 0000000..e707d77
--- /dev/null
+++ b/pkg/js_runtime/pubspec.yaml
@@ -0,0 +1,12 @@
+name: js_runtime
+# This package is not intended for consumption on pub.dev. DO NOT publish.
+publish_to: none
+
+environment:
+  sdk: '>=2.12.0 <3.0.0'
+
+dev_dependencies:
+  expect:
+    path: ../expect
+  _fe_analyzer_shared:
+    path: ../_fe_analyzer_shared
diff --git a/pkg/js_runtime/test/in_sync_test.dart b/pkg/js_runtime/test/in_sync_test.dart
new file mode 100644
index 0000000..4dbef635
--- /dev/null
+++ b/pkg/js_runtime/test/in_sync_test.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2020, 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.
+
+// @dart = 2.9
+
+/// Test to verify that this package is in-sync with dart2js runtime libraries.
+import 'dart:io';
+
+import 'package:_fe_analyzer_shared/src/util/relativize.dart';
+import 'package:expect/expect.dart';
+
+void main(List<String> argv) {
+  var packageDir = Platform.script.resolve('../lib/shared/');
+  var sdkDir = Platform.script
+      .resolve('../../../sdk/lib/_internal/js_runtime/lib/shared/');
+  var rPackageDir =
+      relativizeUri(Directory.current.uri, packageDir, Platform.isWindows);
+  var rSdkDir =
+      relativizeUri(Directory.current.uri, sdkDir, Platform.isWindows);
+
+  for (var file in Directory.fromUri(sdkDir).listSync()) {
+    if (file is File) {
+      var filename = file.uri.pathSegments.last;
+      var packageFile = File.fromUri(packageDir.resolve(filename));
+      Expect.isTrue(
+          packageFile.existsSync(),
+          "$filename not in sync. Please update it by running:\n"
+          "  cp $rSdkDir$filename $rPackageDir$filename");
+      var original = file.readAsBytesSync();
+      var copy = packageFile.readAsBytesSync();
+      Expect.listEquals(
+          original,
+          copy,
+          "$filename not in sync. Please update it by running:\n"
+          "  cp $rSdkDir$filename $rPackageDir$filename");
+    }
+  }
+}
diff --git a/pkg/pkg.status b/pkg/pkg.status
index 6b93319..da7fa09 100644
--- a/pkg/pkg.status
+++ b/pkg/pkg.status
@@ -173,6 +173,7 @@
 dev_compiler/test/options/*: SkipByDesign
 front_end/test/hot_reload_e2e_test: Skip
 frontend_server/test/*: SkipByDesign # Only meant to run on vm
+js_runtime/test/*: SkipByDesign # Only meant to run on vm
 vm/test/*: SkipByDesign # Only meant to run on vm
 vm_service/test/*: SkipByDesign # Uses dart:io
 vm_snapshot_analysis/test/*: SkipByDesign # Only meant to run on vm
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 2bf93261..f3980bc 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -2205,6 +2205,13 @@
           ]
         },
         {
+          "name": "js_runtime unit tests",
+          "arguments": [
+            "-nunittest-asserts-no-sdk-linux",
+            "pkg//js_runtime/"
+          ]
+        },
+        {
           "name": "dart2js unit tests",
           "arguments": [
             "-nunittest-asserts-no-sdk-linux",
@@ -3218,7 +3225,7 @@
           "name": "package unit tests",
           "arguments": [
             "-nunittest-asserts-${mode}-${system}",
-            "pkg/pkg/(?!(analyzer*|analysis_server|compiler|front_end|kernel|nnbd_migration)/)"
+            "pkg/pkg/(?!(analyzer*|analysis_server|compiler|js_runtime|front_end|kernel|nnbd_migration)/)"
           ]
         },
         {
@@ -3256,7 +3263,7 @@
           "name": "package unit tests",
           "arguments": [
             "-nunittest-asserts-${mode}-${system}",
-            "pkg/pkg/(?!(analyzer*|analysis_server|compiler|front_end|kernel|nnbd_migration)/)"
+            "pkg/pkg/(?!(analyzer*|analysis_server|compiler|js_runtime|front_end|kernel|nnbd_migration)/)"
           ]
         },
         {