[dart2js] Fix deferred load URI when baseUrl has a single path segment

This fixes https://github.com/dart-lang/sdk/issues/48848

When the base URI is just a filename, then base was empty, and we accidentally
added a / in the first position.  This made the deferred URI absolute by
mistake.

Change-Id: I4d6a773f6ef8bfefbbf61417bfe7c005aa5e63ea
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/241990
Reviewed-by: Stephen Adams <sra@google.com>
Commit-Queue: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index 9430808..a5172df 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -43,9 +43,11 @@
   RegExp(r'(?<!generated_)tests/web/native'),
   RegExp(r'(?<!generated_)tests/web/internal'),
   'generated_tests/web/native/native_test',
+  'generated_tests/web/internal/deferred_url_test',
   RegExp(r'(?<!generated_)tests/web_2/native'),
   RegExp(r'(?<!generated_)tests/web_2/internal'),
   'generated_tests/web_2/native/native_test',
+  'generated_tests/web_2/internal/deferred_url_test',
 ];
 
 bool allowedNativeTest(Uri uri) {
diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
index cd6018f..ae676e0 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
@@ -2768,7 +2768,7 @@
 
 String _computeBaseUrl() {
   String script = thisScript!;
-  return JS('', '#.substring(0, #.lastIndexOf("/"))', script, script);
+  return JS('', '#.substring(0, #.lastIndexOf("/") + 1)', script, script);
 }
 
 /// Trusted Type policy [1] for generating validated URLs for scripts for
@@ -2810,11 +2810,14 @@
 Object _getBasedScriptUrl(String component) {
   final base = _thisScriptBaseUrl;
   final encodedComponent = _encodeURIComponent(component);
-  final url = '$base/$encodedComponent';
+  final url = '$base$encodedComponent';
   final policy = _deferredLoadingTrustedTypesPolicy;
   return JS('', '#.createScriptURL(#)', policy, url);
 }
 
+Object getBasedScriptUrlForTesting(String component) =>
+    _getBasedScriptUrl(component);
+
 String _encodeURIComponent(String component) {
   return JS('', 'self.encodeURIComponent(#)', component);
 }
diff --git a/tests/web/internal/deferred_url_test.dart b/tests/web/internal/deferred_url_test.dart
new file mode 100644
index 0000000..3624bef
--- /dev/null
+++ b/tests/web/internal/deferred_url_test.dart
@@ -0,0 +1,33 @@
+// 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.
+
+import 'dart:_js_helper';
+import 'dart:_foreign_helper';
+import 'dart:_js_embedded_names';
+
+import 'package:expect/expect.dart';
+
+main() {
+  String currentScriptUrl = 'x/main.dart.js';
+  String expectedPartUrl = 'x/part.js';
+  // We make use of the multi-tests format to test multiple scenarios, that's
+  // because internally dart:_js_helper computes what the current script
+  // location is and caches the result in a final variable. As a result, we
+  // can only set it once per test.
+  currentScriptUrl = 'x/y/main.dart.js'; //# 01: ok
+  expectedPartUrl = 'x/y/part.js'; //# 01: continued
+
+  currentScriptUrl = 'main.dart.js'; //# 02: ok
+  expectedPartUrl = 'part.js'; //# 02: continued
+
+  currentScriptUrl = '/main.dart.js'; //# 03: ok
+  expectedPartUrl = '/part.js'; //# 03: continued
+
+  // Override the currentScript. This depends on the internal implementation
+  // of [thisScript].
+  JS('', '# = {"src": #}', JS_EMBEDDED_GLOBAL('', CURRENT_SCRIPT),
+      currentScriptUrl);
+  Object url = getBasedScriptUrlForTesting("part.js");
+  Expect.equals(expectedPartUrl, JS('', '#.toString()', url));
+}
diff --git a/tests/web/web.status b/tests/web/web.status
index d20e136..9613b8b 100644
--- a/tests/web/web.status
+++ b/tests/web/web.status
@@ -4,6 +4,7 @@
 
 [ $compiler != dart2js ]
 dummy_compiler_test: SkipByDesign # Issue 30773. Test should be migrated as a unit test of dart2js, is only intended to test self-hosting.
+internal/deferred_url_test: SkipByDesign # test specific for the dart2js runtime
 
 [ $compiler != dart2wasm ]
 wasm/*: SkipByDesign
diff --git a/tests/web_2/internal/deferred_url_test.dart b/tests/web_2/internal/deferred_url_test.dart
new file mode 100644
index 0000000..813fa2a
--- /dev/null
+++ b/tests/web_2/internal/deferred_url_test.dart
@@ -0,0 +1,34 @@
+// 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.
+
+// @dart = 2.11
+import 'dart:_js_helper';
+import 'dart:_foreign_helper';
+import 'dart:_js_embedded_names';
+
+import 'package:expect/expect.dart';
+
+main() {
+  String currentScriptUrl = 'x/main.dart.js';
+  String expectedPartUrl = 'x/part.js';
+  // We make use of the multi-tests format to test multiple scenarios, that's
+  // because internally dart:_js_helper computes what the current script
+  // location is and caches the result in a final variable. As a result, we
+  // can only set it once per test.
+  currentScriptUrl = 'x/y/main.dart.js'; //# 01: ok
+  expectedPartUrl = 'x/y/part.js'; //# 01: continued
+
+  currentScriptUrl = 'main.dart.js'; //# 02: ok
+  expectedPartUrl = 'part.js'; //# 02: continued
+
+  currentScriptUrl = '/main.dart.js'; //# 03: ok
+  expectedPartUrl = '/part.js'; //# 03: continued
+
+  // Override the currentScript. This depends on the internal implementation
+  // of [thisScript].
+  JS('', '# = {"src": #}', JS_EMBEDDED_GLOBAL('', CURRENT_SCRIPT),
+      currentScriptUrl);
+  Object url = getBasedScriptUrlForTesting("part.js");
+  Expect.equals(expectedPartUrl, JS('', '#.toString()', url));
+}
diff --git a/tests/web_2/web_2.status b/tests/web_2/web_2.status
index c54cd80..6d902367 100644
--- a/tests/web_2/web_2.status
+++ b/tests/web_2/web_2.status
@@ -4,6 +4,7 @@
 
 [ $compiler != dart2js ]
 dummy_compiler_test: SkipByDesign # Issue 30773. Test should be migrated as a unit test of dart2js, is only intended to test self-hosting.
+internal/deferred_url_test: SkipByDesign # test specific for the dart2js runtime
 
 [ $runtime == jsshell ]
 deferred/load_in_correct_order_test: SkipByDesign # jsshell preamble does not support this test.