diff --git a/runtime/bin/gen_snapshot.cc b/runtime/bin/gen_snapshot.cc index 568fa5f570c..eb4012246fa 100644 --- a/runtime/bin/gen_snapshot.cc +++ b/runtime/bin/gen_snapshot.cc @@ -670,7 +670,7 @@ static int CreateIsolateAndSnapshot(const CommandLineOptions& inputs) { } auto isolate_group_data = std::unique_ptr( - new IsolateGroupData(nullptr, nullptr, nullptr, nullptr, false)); + new IsolateGroupData(nullptr, nullptr, nullptr, false)); Dart_Isolate isolate; char* error = NULL; diff --git a/runtime/bin/isolate_data.cc b/runtime/bin/isolate_data.cc index 3bf497c7e63..154797507aa 100644 --- a/runtime/bin/isolate_data.cc +++ b/runtime/bin/isolate_data.cc @@ -10,21 +10,16 @@ namespace dart { namespace bin { IsolateGroupData::IsolateGroupData(const char* url, - const char* package_root, const char* packages_file, AppSnapshot* app_snapshot, bool isolate_run_app_snapshot) : script_url((url != NULL) ? strdup(url) : NULL), - package_root(NULL), app_snapshot_(app_snapshot), resolved_packages_config_(NULL), kernel_buffer_(NULL), kernel_buffer_size_(0), isolate_run_app_snapshot_(isolate_run_app_snapshot) { - if (package_root != NULL) { - ASSERT(packages_file == NULL); - package_root = strdup(package_root); - } else if (packages_file != NULL) { + if (packages_file != NULL) { packages_file_ = strdup(packages_file); } } @@ -32,8 +27,6 @@ IsolateGroupData::IsolateGroupData(const char* url, IsolateGroupData::~IsolateGroupData() { free(script_url); script_url = NULL; - free(package_root); - package_root = NULL; free(packages_file_); packages_file_ = NULL; free(resolved_packages_config_); diff --git a/runtime/bin/isolate_data.h b/runtime/bin/isolate_data.h index c4523a1d742..6dae87a3842 100644 --- a/runtime/bin/isolate_data.h +++ b/runtime/bin/isolate_data.h @@ -34,14 +34,12 @@ class Loader; class IsolateGroupData { public: IsolateGroupData(const char* url, - const char* package_root, const char* packages_file, AppSnapshot* app_snapshot, bool isolate_run_app_snapshot); ~IsolateGroupData(); char* script_url; - char* package_root; const std::shared_ptr& kernel_buffer() const { return kernel_buffer_; diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc index ac3baa542ae..06b91091176 100644 --- a/runtime/bin/main.cc +++ b/runtime/bin/main.cc @@ -180,7 +180,7 @@ static Dart_Handle SetupCoreLibraries(Dart_Isolate isolate, // Prepare builtin and other core libraries for use to resolve URIs. // Set up various closures, e.g: printing, timers etc. - // Set up 'package root' for URI resolution. + // Set up package configuration for URI resolution. result = DartUtils::PrepareForScriptLoading(false, Options::trace_loading()); if (Dart_IsError(result)) return result; @@ -417,7 +417,6 @@ static Dart_Isolate IsolateSetupHelper(Dart_Isolate isolate, // For now we only support the kernel isolate coming up from an // application snapshot or from a .dill file. static Dart_Isolate CreateAndSetupKernelIsolate(const char* script_uri, - const char* package_root, const char* packages_config, Dart_IsolateFlags* flags, char** error, @@ -459,9 +458,8 @@ static Dart_Isolate CreateAndSetupKernelIsolate(const char* script_uri, app_snapshot->SetBuffers( &ignore_vm_snapshot_data, &ignore_vm_snapshot_instructions, &isolate_snapshot_data, &isolate_snapshot_instructions); - isolate_group_data = - new IsolateGroupData(uri, package_root, packages_config, app_snapshot, - isolate_run_app_snapshot); + isolate_group_data = new IsolateGroupData( + uri, packages_config, app_snapshot, isolate_run_app_snapshot); isolate_data = new IsolateData(isolate_group_data); isolate = Dart_CreateIsolateGroup( DART_KERNEL_ISOLATE_NAME, DART_KERNEL_ISOLATE_NAME, @@ -479,8 +477,8 @@ static Dart_Isolate CreateAndSetupKernelIsolate(const char* script_uri, intptr_t kernel_service_buffer_size = 0; dfe.LoadKernelService(&kernel_service_buffer, &kernel_service_buffer_size); ASSERT(kernel_service_buffer != NULL); - isolate_group_data = new IsolateGroupData( - uri, package_root, packages_config, nullptr, isolate_run_app_snapshot); + isolate_group_data = new IsolateGroupData(uri, packages_config, nullptr, + isolate_run_app_snapshot); isolate_group_data->SetKernelBufferUnowned( const_cast(kernel_service_buffer), kernel_service_buffer_size); @@ -508,7 +506,6 @@ static Dart_Isolate CreateAndSetupKernelIsolate(const char* script_uri, // For now we only support the service isolate coming up from sources // which are compiled by the VM parser. static Dart_Isolate CreateAndSetupServiceIsolate(const char* script_uri, - const char* package_root, const char* packages_config, Dart_IsolateFlags* flags, char** error, @@ -516,8 +513,8 @@ static Dart_Isolate CreateAndSetupServiceIsolate(const char* script_uri, #if !defined(PRODUCT) ASSERT(script_uri != NULL); Dart_Isolate isolate = NULL; - auto isolate_group_data = new IsolateGroupData( - script_uri, package_root, packages_config, nullptr, false); + auto isolate_group_data = + new IsolateGroupData(script_uri, packages_config, nullptr, false); #if defined(DART_PRECOMPILED_RUNTIME) // AOT: All isolates start from the app snapshot. @@ -580,7 +577,6 @@ static Dart_Isolate CreateIsolateGroupAndSetupHelper( bool is_main_isolate, const char* script_uri, const char* name, - const char* package_root, const char* packages_config, Dart_IsolateFlags* flags, void* callback_data, @@ -637,9 +633,8 @@ static Dart_Isolate CreateIsolateGroupAndSetupHelper( } #endif // !defined(DART_PRECOMPILED_RUNTIME) - auto isolate_group_data = - new IsolateGroupData(script_uri, package_root, packages_config, - app_snapshot, isolate_run_app_snapshot); + auto isolate_group_data = new IsolateGroupData( + script_uri, packages_config, app_snapshot, isolate_run_app_snapshot); if (kernel_buffer != NULL) { if (parent_kernel_buffer) { isolate_group_data->SetKernelBufferAlreadyOwned( @@ -716,28 +711,22 @@ static Dart_Isolate CreateIsolateGroupAndSetup(const char* script_uri, // The VM should never call the isolate helper with a NULL flags. ASSERT(flags != NULL); ASSERT(flags->version == DART_FLAGS_CURRENT_VERSION); - if ((package_root != NULL) && (package_config != NULL)) { - *error = strdup( - "Invalid arguments - Cannot simultaneously specify " - "package root and package map."); - return NULL; - } - + ASSERT(package_root == nullptr); int exit_code = 0; #if !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) if (strcmp(script_uri, DART_KERNEL_ISOLATE_NAME) == 0) { - return CreateAndSetupKernelIsolate(script_uri, package_root, package_config, - flags, error, &exit_code); + return CreateAndSetupKernelIsolate(script_uri, package_config, flags, error, + &exit_code); } #endif // !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) if (strcmp(script_uri, DART_VM_SERVICE_ISOLATE_NAME) == 0) { - return CreateAndSetupServiceIsolate( - script_uri, package_root, package_config, flags, error, &exit_code); + return CreateAndSetupServiceIsolate(script_uri, package_config, flags, + error, &exit_code); } bool is_main_isolate = false; return CreateIsolateGroupAndSetupHelper(is_main_isolate, script_uri, main, - package_root, package_config, flags, - callback_data, error, &exit_code); + package_config, flags, callback_data, + error, &exit_code); } static void OnIsolateShutdown(void* isolate_group_data, void* isolate_data) { @@ -845,10 +834,15 @@ bool RunMainIsolate(const char* script_name, CommandLineOptions* dart_options) { Dart_IsolateFlags flags; Dart_IsolateFlagsInitialize(&flags); + if (Options::package_root() != nullptr) { + Syslog::PrintErr( + "Warning: The --package-root option is deprecated (was: %s)\n", + Options::package_root()); + } + Dart_Isolate isolate = CreateIsolateGroupAndSetupHelper( - is_main_isolate, script_name, "main", Options::package_root(), - Options::packages_file(), &flags, NULL /* callback_data */, &error, - &exit_code); + is_main_isolate, script_name, "main", Options::packages_file(), &flags, + NULL /* callback_data */, &error, &exit_code); if (isolate == NULL) { Syslog::PrintErr("%s\n", error); diff --git a/runtime/bin/run_vm_tests.cc b/runtime/bin/run_vm_tests.cc index 5e44275b9d5..c7b4df64f88 100644 --- a/runtime/bin/run_vm_tests.cc +++ b/runtime/bin/run_vm_tests.cc @@ -101,7 +101,6 @@ static void PrintUsage() { } static Dart_Isolate CreateAndSetupServiceIsolate(const char* script_uri, - const char* package_root, const char* packages_config, Dart_IsolateFlags* flags, char** error) { @@ -117,7 +116,7 @@ static Dart_Isolate CreateAndSetupServiceIsolate(const char* script_uri, ASSERT(script_uri != nullptr); Dart_Isolate isolate = nullptr; auto isolate_group_data = new bin::IsolateGroupData( - script_uri, package_root, packages_config, /*app_snapshot=*/nullptr, + script_uri, packages_config, /*app_snapshot=*/nullptr, /*isolate_run_app_snapshot=*/false); const uint8_t* kernel_buffer = nullptr; @@ -169,9 +168,10 @@ static Dart_Isolate CreateIsolateAndSetup(const char* script_uri, void* data, char** error) { ASSERT(script_uri != nullptr); + ASSERT(package_root == nullptr); if (strcmp(script_uri, DART_VM_SERVICE_ISOLATE_NAME) == 0) { - return CreateAndSetupServiceIsolate(script_uri, package_root, - packages_config, flags, error); + return CreateAndSetupServiceIsolate(script_uri, packages_config, flags, + error); } const bool is_kernel_isolate = strcmp(script_uri, DART_KERNEL_ISOLATE_NAME) == 0; @@ -200,9 +200,8 @@ static Dart_Isolate CreateIsolateAndSetup(const char* script_uri, app_snapshot->SetBuffers( &ignore_vm_snapshot_data, &ignore_vm_snapshot_instructions, &isolate_snapshot_data, &isolate_snapshot_instructions); - isolate_group_data = - new bin::IsolateGroupData(script_uri, package_root, packages_config, - app_snapshot, app_snapshot != nullptr); + isolate_group_data = new bin::IsolateGroupData( + script_uri, packages_config, app_snapshot, app_snapshot != nullptr); isolate = Dart_CreateIsolateGroup( DART_KERNEL_ISOLATE_NAME, DART_KERNEL_ISOLATE_NAME, isolate_snapshot_data, isolate_snapshot_instructions, flags, @@ -229,8 +228,8 @@ static Dart_Isolate CreateIsolateAndSetup(const char* script_uri, bin::dfe.LoadKernelService(&kernel_service_buffer, &kernel_service_buffer_size); ASSERT(kernel_service_buffer != nullptr); - isolate_group_data = new bin::IsolateGroupData( - script_uri, package_root, packages_config, nullptr, false); + isolate_group_data = + new bin::IsolateGroupData(script_uri, packages_config, nullptr, false); isolate_group_data->SetKernelBufferUnowned( const_cast(kernel_service_buffer), kernel_service_buffer_size); diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h index b01cedcf5f4..c8fd5f81283 100644 --- a/runtime/include/dart_api.h +++ b/runtime/include/dart_api.h @@ -571,10 +571,7 @@ DART_EXPORT void Dart_IsolateFlagsInitialize(Dart_IsolateFlags* flags); * eventually run. This is provided for advisory purposes only to * improve debugging messages. The main function is not invoked by * this function. - * \param package_root The package root path for this isolate to resolve - * package imports against. Only one of package_root and package_map - * parameters is non-NULL. If neither parameter is passed the package - * resolution of the parent isolate should be used. + * \param package_root Ignored. * \param package_map The package map for this isolate to resolve package * imports against. The array contains alternating keys and values, * terminated by a NULL key. Only one of package_root and package_map diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc index 4ef798d5e43..059299d51e0 100644 --- a/runtime/lib/isolate.cc +++ b/runtime/lib/isolate.cc @@ -390,7 +390,7 @@ static const char* String2UTF8(const String& str) { return result; } -DEFINE_NATIVE_ENTRY(Isolate_spawnFunction, 0, 11) { +DEFINE_NATIVE_ENTRY(Isolate_spawnFunction, 0, 10) { GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0)); GET_NON_NULL_NATIVE_ARGUMENT(String, script_uri, arguments->NativeArgAt(1)); GET_NON_NULL_NATIVE_ARGUMENT(Instance, closure, arguments->NativeArgAt(2)); @@ -399,9 +399,8 @@ DEFINE_NATIVE_ENTRY(Isolate_spawnFunction, 0, 11) { GET_NATIVE_ARGUMENT(Bool, fatalErrors, arguments->NativeArgAt(5)); GET_NATIVE_ARGUMENT(SendPort, onExit, arguments->NativeArgAt(6)); GET_NATIVE_ARGUMENT(SendPort, onError, arguments->NativeArgAt(7)); - GET_NATIVE_ARGUMENT(String, packageRoot, arguments->NativeArgAt(8)); - GET_NATIVE_ARGUMENT(String, packageConfig, arguments->NativeArgAt(9)); - GET_NATIVE_ARGUMENT(String, debugName, arguments->NativeArgAt(10)); + GET_NATIVE_ARGUMENT(String, packageConfig, arguments->NativeArgAt(8)); + GET_NATIVE_ARGUMENT(String, debugName, arguments->NativeArgAt(9)); if (closure.IsClosure()) { Function& func = Function::Handle(); @@ -482,26 +481,19 @@ static const char* CanonicalizeUri(Thread* thread, return result; } -DEFINE_NATIVE_ENTRY(Isolate_spawnUri, 0, 13) { +DEFINE_NATIVE_ENTRY(Isolate_spawnUri, 0, 12) { GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0)); GET_NON_NULL_NATIVE_ARGUMENT(String, uri, arguments->NativeArgAt(1)); - GET_NON_NULL_NATIVE_ARGUMENT(Instance, args, arguments->NativeArgAt(2)); GET_NON_NULL_NATIVE_ARGUMENT(Instance, message, arguments->NativeArgAt(3)); - GET_NON_NULL_NATIVE_ARGUMENT(Bool, paused, arguments->NativeArgAt(4)); GET_NATIVE_ARGUMENT(SendPort, onExit, arguments->NativeArgAt(5)); GET_NATIVE_ARGUMENT(SendPort, onError, arguments->NativeArgAt(6)); - GET_NATIVE_ARGUMENT(Bool, fatalErrors, arguments->NativeArgAt(7)); GET_NATIVE_ARGUMENT(Bool, checked, arguments->NativeArgAt(8)); - GET_NATIVE_ARGUMENT(Array, environment, arguments->NativeArgAt(9)); - - GET_NATIVE_ARGUMENT(String, packageRoot, arguments->NativeArgAt(10)); - GET_NATIVE_ARGUMENT(String, packageConfig, arguments->NativeArgAt(11)); - - GET_NATIVE_ARGUMENT(String, debugName, arguments->NativeArgAt(12)); + GET_NATIVE_ARGUMENT(String, packageConfig, arguments->NativeArgAt(10)); + GET_NATIVE_ARGUMENT(String, debugName, arguments->NativeArgAt(11)); if (Dart::vm_snapshot_kind() == Snapshot::kFullAOT) { const Array& args = Array::Handle(Array::New(1)); diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h index 65e0dae8864..8340655e060 100644 --- a/runtime/vm/bootstrap_natives.h +++ b/runtime/vm/bootstrap_natives.h @@ -317,8 +317,8 @@ namespace dart { V(Int32x4_setFlagZ, 2) \ V(Int32x4_setFlagW, 2) \ V(Int32x4_select, 3) \ - V(Isolate_spawnFunction, 11) \ - V(Isolate_spawnUri, 13) \ + V(Isolate_spawnFunction, 10) \ + V(Isolate_spawnUri, 12) \ V(Isolate_getPortAndCapabilitiesOfCurrentIsolate, 0) \ V(Isolate_getCurrentRootUriStr, 0) \ V(Isolate_sendOOB, 2) \ diff --git a/sdk/lib/_internal/vm/bin/builtin.dart b/sdk/lib/_internal/vm/bin/builtin.dart index b79755ef7ec..12fcc14ae9f 100644 --- a/sdk/lib/_internal/vm/bin/builtin.dart +++ b/sdk/lib/_internal/vm/bin/builtin.dart @@ -11,6 +11,7 @@ import 'dart:async'; import 'dart:collection' hide LinkedList, LinkedListEntry; import 'dart:_internal' hide Symbol; import 'dart:io'; +import 'dart:convert'; import 'dart:isolate'; import 'dart:typed_data'; @@ -130,11 +131,12 @@ Uri _resolvePackageUri(Uri uri) { _log('Resolving package with uri path: ${uri.path}'); } var resolvedUri; - if (_packageError != null) { + final error = _packageError; + if (error != null) { if (_traceLoading) { - _log("Resolving package with pending resolution error: $_packageError"); + _log("Resolving package with pending resolution error: $error"); } - throw _packageError; + throw error; } else { if (packageNameEnd < 0) { // Package URIs must have a path after the package name, even if it's @@ -430,9 +432,67 @@ _parsePackagesFile(bool traceLoading, Uri packagesFile, List data) { return result; } +_loadPackageConfigFile(bool traceLoading, Uri packageConfig) { + try { + final Uint8List data = File.fromUri(packageConfig).readAsBytesSync(); + if (traceLoading) { + _log("Loaded package config file from $packageConfig."); + } + return _parsePackageConfig(traceLoading, packageConfig, data); + } catch (e, s) { + if (traceLoading) { + _log("Error loading packages: $e\n$s"); + } + return "Uncaught error ($e) loading packages file."; + } +} + +// The .dart_tool/package_config.json format is described in +// +// https://github.com/dart-lang/language/blob/master/accepted/future-releases/language-versioning/package-config-file-v2.md +// +// The returned list has the format: +// +// [0] Location of package_config.json file. +// [1] null +// [n*2] Name of n-th package +// [n*2 + 1] Location of n-th package's sources (as a String) +// +List _parsePackageConfig( + bool traceLoading, Uri packageConfig, Uint8List bytes) { + final Map packageJson = json.decode(utf8.decode(bytes)); + final version = packageJson['configVersion']; + if (version != 2) { + throw 'The package configuration file has an unsupported version.'; + } + // The first entry contains the location of the identified + // .dart_tool/package_config.json file instead of a mapping. + final result = [packageConfig.toString(), null]; + final List packages = packageJson['packages'] ?? []; + for (final Map package in packages) { + final String name = package['name']; + final String rootUri = package['rootUri']; + final String packageUri = package['packageUri']; + final Uri resolvedRootUri = packageConfig.resolve(rootUri); + final Uri resolvedPackageUri = packageUri != null + ? resolvedRootUri.resolve(packageUri) + : resolvedRootUri; + if (packageUri != null && + !'$resolvedPackageUri'.contains('$resolvedRootUri')) { + throw 'The resolved "packageUri" is not a subdirectory of the "rootUri".'; + } + result.add(name); + result.add(resolvedPackageUri.toString()); + if (traceLoading) { + _log('Resolved package $name to be at $resolvedPackageUri'); + } + } + return result; +} + _loadPackagesFile(bool traceLoading, Uri packagesFile) { try { - var data = new File.fromUri(packagesFile).readAsBytesSync(); + final Uint8List data = File.fromUri(packagesFile).readAsBytesSync(); if (traceLoading) { _log("Loaded packages file from $packagesFile:\n" "${new String.fromCharCodes(data)}"); @@ -446,39 +506,49 @@ _loadPackagesFile(bool traceLoading, Uri packagesFile) { } } -_findPackagesFile(bool traceLoading, Uri base) { +_findPackagesConfiguration(bool traceLoading, Uri base) { try { - // Walk up the directory hierarchy to check for the existence of - // .packages files in parent directories and for the existence of a - // packages/ directory on the first iteration. - var dir = new File.fromUri(base).parent; - var prev = null; - // Keep searching until we reach the root. - while ((prev == null) || (prev.path != dir.path)) { - // Check for the existence of a .packages file and if it exists try to - // load and parse it. - var dirUri = dir.uri; - var packagesFile = dirUri.resolve(".packages"); + // Walk up the directory hierarchy to check for the existence of either one + // of + // - .dart_tool/package_config.json + // - .packages + var currentDir = new File.fromUri(base).parent; + while (true) { + final dirUri = currentDir.uri; + + // We prefer using `.dart_tool/package_config.json` over `.packages`. + final packageConfig = dirUri.resolve(".dart_tool/package_config.json"); + if (traceLoading) { + _log("Checking for $packageConfig file."); + } + bool exists = File.fromUri(packageConfig).existsSync(); + if (traceLoading) { + _log("$packageConfig exists: $exists"); + } + if (exists) { + return _loadPackageConfigFile(traceLoading, packageConfig); + } + + final packagesFile = dirUri.resolve(".packages"); if (traceLoading) { _log("Checking for $packagesFile file."); } - var exists = new File.fromUri(packagesFile).existsSync(); + exists = File.fromUri(packagesFile).existsSync(); if (traceLoading) { _log("$packagesFile exists: $exists"); } if (exists) { return _loadPackagesFile(traceLoading, packagesFile); } - // Move up one level. - prev = dir; - dir = dir.parent; + final parentDir = currentDir.parent; + if (currentDir == parentDir) break; + currentDir = parentDir; } - // No .packages file was found. if (traceLoading) { - _log("Could not resolve a package location from $base"); + _log("Could not resolve a package configuration from $base"); } - return "Could not resolve a package location for base at $base"; + return "Could not resolve a package configuration for base at $base"; } catch (e, s) { if (traceLoading) { _log("Error loading packages: $e\n$s"); @@ -509,7 +579,7 @@ _handlePackagesRequest(bool traceLoading, int tag, Uri resource) { try { if (tag == -1) { if (resource.scheme == '' || resource.scheme == 'file') { - return _findPackagesFile(traceLoading, resource); + return _findPackagesConfiguration(traceLoading, resource); } else { return "Unsupported scheme used to locate .packages file:'$resource'."; } @@ -580,6 +650,9 @@ void _setWorkingDirectory(String cwd) { } // Embedder Entrypoint: +// The embedder calls this method with the value of the --packages command line +// option. It can point to a ".packages" or a ".dart_tool/package_config.json" +// file. @pragma("vm:entry-point") String _setPackagesMap(String packagesParam) { if (!_setupCompleted) { diff --git a/sdk/lib/_internal/vm/lib/internal_patch.dart b/sdk/lib/_internal/vm/lib/internal_patch.dart index 6accfc3d598..76a8a88bb0b 100644 --- a/sdk/lib/_internal/vm/lib/internal_patch.dart +++ b/sdk/lib/_internal/vm/lib/internal_patch.dart @@ -48,7 +48,6 @@ class VMLibraryHooks { // Implementation of package root/map provision. static var packageRootString; static var packageConfigString; - static var packageRootUriFuture; static var packageConfigUriFuture; static var resolvePackageUriFuture; diff --git a/sdk/lib/_internal/vm/lib/isolate_patch.dart b/sdk/lib/_internal/vm/lib/isolate_patch.dart index e17c1de0323..e357b8a0874 100644 --- a/sdk/lib/_internal/vm/lib/isolate_patch.dart +++ b/sdk/lib/_internal/vm/lib/isolate_patch.dart @@ -267,21 +267,21 @@ void _startIsolate( // The control port (aka the main isolate port) does not handle any messages. if (controlPort != null) { controlPort.handler = (_) {}; // Nobody home on the control port. - } - if (parentPort != null) { - // Build a message to our parent isolate providing access to the - // current isolate's control port and capabilities. - // - // TODO(floitsch): Send an error message if we can't find the entry point. - var readyMessage = new List(2); - readyMessage[0] = controlPort.sendPort; - readyMessage[1] = capabilities; + if (parentPort != null) { + // Build a message to our parent isolate providing access to the + // current isolate's control port and capabilities. + // + // TODO(floitsch): Send an error message if we can't find the entry point. + final readyMessage = List(2); + readyMessage[0] = controlPort.sendPort; + readyMessage[1] = capabilities; - // Out of an excess of paranoia we clear the capabilities from the - // stack. Not really necessary. - capabilities = null; - parentPort.send(readyMessage); + // Out of an excess of paranoia we clear the capabilities from the + // stack. Not really necessary. + capabilities = null; + parentPort.send(readyMessage); + } } assert(capabilities == null); @@ -343,7 +343,6 @@ class Isolate { } static bool _packageSupported() => - (VMLibraryHooks.packageRootUriFuture != null) && (VMLibraryHooks.packageConfigUriFuture != null) && (VMLibraryHooks.resolvePackageUriFuture != null); @@ -355,46 +354,33 @@ class Isolate { SendPort onError, String debugName}) async { // `paused` isn't handled yet. - RawReceivePort readyPort; + // Check for the type of `entryPoint` on the spawning isolate to make + // error-handling easier. + if (entryPoint is! _UnaryFunction) { + throw new ArgumentError(entryPoint); + } + // The VM will invoke [_startIsolate] with entryPoint as argument. + + // We do not inherit the package config settings from the parent isolate, + // instead we use the values that were set on the command line. + var packageConfig = VMLibraryHooks.packageConfigString; + var script = VMLibraryHooks.platformScript; + if (script == null) { + // We do not have enough information to support spawning the new + // isolate. + throw new UnsupportedError("Isolate.spawn"); + } + if (script.isScheme("package")) { + script = await Isolate.resolvePackageUri(script); + } + + final RawReceivePort readyPort = new RawReceivePort(); try { - // Check for the type of `entryPoint` on the spawning isolate to make - // error-handling easier. - if (entryPoint is! _UnaryFunction) { - throw new ArgumentError(entryPoint); - } - // The VM will invoke [_startIsolate] with entryPoint as argument. - readyPort = new RawReceivePort(); - - // We do not inherit the package config settings from the parent isolate, - // instead we use the values that were set on the command line. - var packageConfig = VMLibraryHooks.packageConfigString; - var script = VMLibraryHooks.platformScript; - if (script == null) { - // We do not have enough information to support spawning the new - // isolate. - throw new UnsupportedError("Isolate.spawn"); - } - if (script.scheme == "package") { - script = await Isolate.resolvePackageUri(script); - } - - _spawnFunction( - readyPort.sendPort, - script.toString(), - entryPoint, - message, - paused, - errorsAreFatal, - onExit, - onError, - null, - packageConfig, - debugName); + _spawnFunction(readyPort.sendPort, script.toString(), entryPoint, message, + paused, errorsAreFatal, onExit, onError, packageConfig, debugName); return await _spawnCommon(readyPort); } catch (e, st) { - if (readyPort != null) { - readyPort.close(); - } + readyPort.close(); return await new Future.error(e, st); } } @@ -411,7 +397,6 @@ class Isolate { Uri packageConfig, bool automaticPackageResolution: false, String debugName}) async { - RawReceivePort readyPort; if (environment != null) { throw new UnimplementedError("environment"); } @@ -434,38 +419,30 @@ class Isolate { "packageRoot and a packageConfig."); } } + // Resolve the uri against the current isolate's root Uri first. + final Uri spawnedUri = _rootUri.resolveUri(uri); + + // Inherit this isolate's package resolution setup if not overridden. + if (!automaticPackageResolution && packageConfig == null) { + if (Isolate._packageSupported()) { + packageConfig = await Isolate.packageConfig; + } + } + + // Ensure to resolve package: URIs being handed in as parameters. + if (packageConfig != null) { + // Avoid calling resolvePackageUri if not strictly necessary in case + // the API is not supported. + if (packageConfig.isScheme("package")) { + packageConfig = await Isolate.resolvePackageUri(packageConfig); + } + } + + // The VM will invoke [_startIsolate] and not `main`. + final packageConfigString = packageConfig?.toString(); + + final RawReceivePort readyPort = new RawReceivePort(); try { - // Resolve the uri against the current isolate's root Uri first. - var spawnedUri = _rootUri.resolveUri(uri); - - // Inherit this isolate's package resolution setup if not overridden. - if (!automaticPackageResolution && - (packageRoot == null) && - (packageConfig == null)) { - if (Isolate._packageSupported()) { - packageRoot = await Isolate.packageRoot; - packageConfig = await Isolate.packageConfig; - } - } - - // Ensure to resolve package: URIs being handed in as parameters. - if (packageRoot != null) { - // `packages/` directory is no longer supported. Force it null. - // TODO(mfairhurst) Should this throw an exception? - packageRoot = null; - } else if (packageConfig != null) { - // Avoid calling resolvePackageUri if not strictly necessary in case - // the API is not supported. - if (packageConfig.scheme == "package") { - packageConfig = await Isolate.resolvePackageUri(packageConfig); - } - } - - // The VM will invoke [_startIsolate] and not `main`. - readyPort = new RawReceivePort(); - var packageRootString = packageRoot?.toString(); - var packageConfigString = packageConfig?.toString(); - _spawnUri( readyPort.sendPort, spawnedUri.toString(), @@ -478,20 +455,17 @@ class Isolate { checked, null, /* environment */ - packageRootString, packageConfigString, debugName); return await _spawnCommon(readyPort); } catch (e) { - if (readyPort != null) { - readyPort.close(); - } + readyPort.close(); rethrow; } } static Future _spawnCommon(RawReceivePort readyPort) { - Completer completer = new Completer.sync(); + final completer = new Completer.sync(); readyPort.handler = (readyMessage) { readyPort.close(); if (readyMessage is List && readyMessage.length == 2) { @@ -536,7 +510,6 @@ class Isolate { bool errorsAreFatal, SendPort onExit, SendPort onError, - String packageRoot, String packageConfig, String debugName) native "Isolate_spawnFunction"; @@ -551,7 +524,6 @@ class Isolate { bool errorsAreFatal, bool checked, List environment, - String packageRoot, String packageConfig, String debugName) native "Isolate_spawnUri"; diff --git a/sdk_nnbd/lib/_internal/vm/bin/builtin.dart b/sdk_nnbd/lib/_internal/vm/bin/builtin.dart index 1befe5ac475..11d589c7dbe 100644 --- a/sdk_nnbd/lib/_internal/vm/bin/builtin.dart +++ b/sdk_nnbd/lib/_internal/vm/bin/builtin.dart @@ -9,6 +9,7 @@ import 'dart:async'; import 'dart:collection' hide LinkedList, LinkedListEntry; import 'dart:_internal' hide Symbol; import 'dart:io'; +import 'dart:convert'; import 'dart:isolate'; import 'dart:typed_data'; @@ -430,9 +431,67 @@ _parsePackagesFile(bool traceLoading, Uri packagesFile, List data) { return result; } +_loadPackageConfigFile(bool traceLoading, Uri packageConfig) { + try { + final Uint8List data = File.fromUri(packageConfig).readAsBytesSync(); + if (traceLoading) { + _log("Loaded package config file from $packageConfig."); + } + return _parsePackageConfig(traceLoading, packageConfig, data); + } catch (e, s) { + if (traceLoading) { + _log("Error loading packages: $e\n$s"); + } + return "Uncaught error ($e) loading packages file."; + } +} + +// The .dart_tool/package_config.json format is described in +// +// https://github.com/dart-lang/language/blob/master/accepted/future-releases/language-versioning/package-config-file-v2.md +// +// The returned list has the format: +// +// [0] Location of package_config.json file. +// [1] null +// [n*2] Name of n-th package +// [n*2 + 1] Location of n-th package's sources (as a String) +// +List _parsePackageConfig( + bool traceLoading, Uri packageConfig, Uint8List bytes) { + final Map packageJson = json.decode(utf8.decode(bytes)); + final version = packageJson['configVersion']; + if (version != 2) { + throw 'The package configuration file has an unsupported version.'; + } + // The first entry contains the location of the identified + // .dart_tool/package_config.json file instead of a mapping. + final result = [packageConfig.toString(), null]; + final List packages = packageJson['packages'] ?? []; + for (final Map package in packages) { + final String name = package['name']; + final String rootUri = package['rootUri']; + final String? packageUri = package['packageUri']; + final Uri resolvedRootUri = packageConfig.resolve(rootUri); + final Uri resolvedPackageUri = packageUri != null + ? resolvedRootUri.resolve(packageUri) + : resolvedRootUri; + if (packageUri != null && + !'$resolvedPackageUri'.contains('$resolvedRootUri')) { + throw 'The resolved "packageUri" is not a subdirectory of the "rootUri".'; + } + result.add(name); + result.add(resolvedPackageUri.toString()); + if (traceLoading) { + _log('Resolved package $name to be at $resolvedPackageUri'); + } + } + return result; +} + _loadPackagesFile(bool traceLoading, Uri packagesFile) { try { - var data = new File.fromUri(packagesFile).readAsBytesSync(); + final Uint8List data = File.fromUri(packagesFile).readAsBytesSync(); if (traceLoading) { _log("Loaded packages file from $packagesFile:\n" "${new String.fromCharCodes(data)}"); @@ -446,39 +505,49 @@ _loadPackagesFile(bool traceLoading, Uri packagesFile) { } } -_findPackagesFile(bool traceLoading, Uri base) { +_findPackagesConfiguration(bool traceLoading, Uri base) { try { - // Walk up the directory hierarchy to check for the existence of - // .packages files in parent directories and for the existence of a - // packages/ directory on the first iteration. - var dir = new File.fromUri(base).parent; - var prev = null; - // Keep searching until we reach the root. - while ((prev == null) || (prev.path != dir.path)) { - // Check for the existence of a .packages file and if it exists try to - // load and parse it. - var dirUri = dir.uri; - var packagesFile = dirUri.resolve(".packages"); + // Walk up the directory hierarchy to check for the existence of either one + // of + // - .dart_tool/package_config.json + // - .packages + var currentDir = new File.fromUri(base).parent; + while (true) { + final dirUri = currentDir.uri; + + // We prefer using `.dart_tool/package_config.json` over `.packages`. + final packageConfig = dirUri.resolve(".dart_tool/package_config.json"); + if (traceLoading) { + _log("Checking for $packageConfig file."); + } + bool exists = File.fromUri(packageConfig).existsSync(); + if (traceLoading) { + _log("$packageConfig exists: $exists"); + } + if (exists) { + return _loadPackageConfigFile(traceLoading, packageConfig); + } + + final packagesFile = dirUri.resolve(".packages"); if (traceLoading) { _log("Checking for $packagesFile file."); } - var exists = new File.fromUri(packagesFile).existsSync(); + exists = File.fromUri(packagesFile).existsSync(); if (traceLoading) { _log("$packagesFile exists: $exists"); } if (exists) { return _loadPackagesFile(traceLoading, packagesFile); } - // Move up one level. - prev = dir; - dir = dir.parent; + final parentDir = currentDir.parent; + if (currentDir == parentDir) break; + currentDir = parentDir; } - // No .packages file was found. if (traceLoading) { - _log("Could not resolve a package location from $base"); + _log("Could not resolve a package configuration from $base"); } - return "Could not resolve a package location for base at $base"; + return "Could not resolve a package configuration for base at $base"; } catch (e, s) { if (traceLoading) { _log("Error loading packages: $e\n$s"); @@ -509,7 +578,7 @@ _handlePackagesRequest(bool traceLoading, int tag, Uri resource) { try { if (tag == -1) { if (resource.scheme == '' || resource.scheme == 'file') { - return _findPackagesFile(traceLoading, resource); + return _findPackagesConfiguration(traceLoading, resource); } else { return "Unsupported scheme used to locate .packages file:'$resource'."; } @@ -580,6 +649,9 @@ void _setWorkingDirectory(String cwd) { } // Embedder Entrypoint: +// The embedder calls this method with the value of the --packages command line +// option. It can point to a ".packages" or a ".dart_tool/package_config.json" +// file. @pragma("vm:entry-point") String _setPackagesMap(String packagesParam) { if (!_setupCompleted) { diff --git a/sdk_nnbd/lib/_internal/vm/lib/internal_patch.dart b/sdk_nnbd/lib/_internal/vm/lib/internal_patch.dart index b484a2c1dfc..2faf79976cb 100644 --- a/sdk_nnbd/lib/_internal/vm/lib/internal_patch.dart +++ b/sdk_nnbd/lib/_internal/vm/lib/internal_patch.dart @@ -52,7 +52,6 @@ class VMLibraryHooks { // Implementation of package root/map provision. static var packageRootString; static var packageConfigString; - static var packageRootUriFuture; static var packageConfigUriFuture; static var resolvePackageUriFuture; diff --git a/sdk_nnbd/lib/_internal/vm/lib/isolate_patch.dart b/sdk_nnbd/lib/_internal/vm/lib/isolate_patch.dart index 77e38440737..7658010500f 100644 --- a/sdk_nnbd/lib/_internal/vm/lib/isolate_patch.dart +++ b/sdk_nnbd/lib/_internal/vm/lib/isolate_patch.dart @@ -272,7 +272,7 @@ void _startIsolate( // current isolate's control port and capabilities. // // TODO(floitsch): Send an error message if we can't find the entry point. - var readyMessage = new List.filled(2, null); + final readyMessage = List.filled(2, null); readyMessage[0] = controlPort.sendPort; readyMessage[1] = capabilities; @@ -342,7 +342,6 @@ class Isolate { } static bool _packageSupported() => - (VMLibraryHooks.packageRootUriFuture != null) && (VMLibraryHooks.packageConfigUriFuture != null) && (VMLibraryHooks.resolvePackageUriFuture != null); @@ -376,18 +375,8 @@ class Isolate { final RawReceivePort readyPort = new RawReceivePort(); try { - _spawnFunction( - readyPort.sendPort, - script.toString(), - entryPoint, - message, - paused, - errorsAreFatal, - onExit, - onError, - null, - packageConfig, - debugName); + _spawnFunction(readyPort.sendPort, script.toString(), entryPoint, message, + paused, errorsAreFatal, onExit, onError, packageConfig, debugName); return await _spawnCommon(readyPort); } catch (e, st) { readyPort.close(); @@ -430,24 +419,17 @@ class Isolate { } } // Resolve the uri against the current isolate's root Uri first. - var spawnedUri = _rootUri!.resolveUri(uri); + final Uri spawnedUri = _rootUri!.resolveUri(uri); // Inherit this isolate's package resolution setup if not overridden. - if (!automaticPackageResolution && - (packageRoot == null) && - (packageConfig == null)) { + if (!automaticPackageResolution && packageConfig == null) { if (Isolate._packageSupported()) { - packageRoot = await Isolate.packageRoot; packageConfig = await Isolate.packageConfig; } } // Ensure to resolve package: URIs being handed in as parameters. - if (packageRoot != null) { - // `packages/` directory is no longer supported. Force it null. - // TODO(mfairhurst) Should this throw an exception? - packageRoot = null; - } else if (packageConfig != null) { + if (packageConfig != null) { // Avoid calling resolvePackageUri if not strictly necessary in case // the API is not supported. if (packageConfig.isScheme("package")) { @@ -456,8 +438,7 @@ class Isolate { } // The VM will invoke [_startIsolate] and not `main`. - var packageRootString = packageRoot?.toString(); - var packageConfigString = packageConfig?.toString(); + final packageConfigString = packageConfig?.toString(); final RawReceivePort readyPort = new RawReceivePort(); try { @@ -473,7 +454,6 @@ class Isolate { checked, null, /* environment */ - packageRootString, packageConfigString, debugName); return await _spawnCommon(readyPort); @@ -529,7 +509,6 @@ class Isolate { bool errorsAreFatal, SendPort? onExit, SendPort? onError, - String? packageRoot, String? packageConfig, String? debugName) native "Isolate_spawnFunction"; @@ -544,7 +523,6 @@ class Isolate { bool errorsAreFatal, bool? checked, List? environment, - String? packageRoot, String? packageConfig, String? debugName) native "Isolate_spawnUri"; diff --git a/tests/lib/isolate/spawn_uri__package_uri__test.dart b/tests/lib/isolate/spawn_uri__package_uri__test.dart new file mode 100644 index 00000000000..313a55b95e0 --- /dev/null +++ b/tests/lib/isolate/spawn_uri__package_uri__test.dart @@ -0,0 +1,164 @@ +// 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. + +import 'dart:io'; + +import 'package:expect/expect.dart'; +import 'package:path/path.dart' as path; + +final executable = Platform.executable; + +main() async { + // Run the Dart VM with or without: + // --packages= + for (final runWithPackagesArg in const [true, false]) { + // Run the isolate with or without + // Isolate.spawnUri(..., packageConfig: ) + print('TEST runWithPackagesArg = $runWithPackagesArg '); + for (final spawnWithPackageConfig in const [true, false]) { + print('TEST spawnWithPackageConfig = $spawnWithPackageConfig '); + await runDotPackagesTest(runWithPackagesArg, spawnWithPackageConfig); + for (final optionalPackageUri in const [true, false]) { + print('TEST optionalPackageUri = $optionalPackageUri'); + await runPackageConfigTest( + runWithPackagesArg, spawnWithPackageConfig, optionalPackageUri); + } + } + } +} + +Future runPackageConfigTest( + bool withPackagesArg, bool spawnWithArg, bool optionalPackageUri) async { + await withApplicationDirAndDotDartToolPackageConfig( + (String tempDir, String packageJson, String mainFile) async { + final args = [if (withPackagesArg) '--packages=$packageJson', mainFile]; + await run(executable, args); + }, spawnWithArg, optionalPackageUri); +} + +Future runDotPackagesTest(bool withPackagesArg, bool spawnWithArg) async { + await withApplicationDirAndDotPackages( + (String tempDir, String dotPackagesFile, String mainFile) async { + final args = [ + if (withPackagesArg) '--packages=$dotPackagesFile', + mainFile, + ]; + await run(executable, args); + }, spawnWithArg); +} + +Future withApplicationDirAndDotPackages( + Future fn(String tempDir, String packagesDir, String mainFile), + bool spawnWithArg) async { + await withTempDir((String tempDir) async { + // Setup ".packages" + final dotPackagesFile = + path.join(tempDir, spawnWithArg ? 'baz.packages' : '.packages'); + await File(dotPackagesFile).writeAsString(buildDotPackages('foo')); + + final mainFile = path.join(tempDir, 'main.dart'); + final childIsolateFile = path.join(tempDir, 'child_isolate.dart'); + final importUri = 'package:foo/child_isolate.dart'; + await File(childIsolateFile).writeAsString(buildChildIsolate()); + await File(mainFile).writeAsString( + buildMainIsolate(importUri, spawnWithArg ? dotPackagesFile : null)); + + await fn(tempDir, dotPackagesFile, mainFile); + }); +} + +Future withApplicationDirAndDotDartToolPackageConfig( + Future fn(String tempDir, String packageJson, String mainFile), + bool spawnWithArg, + bool optionalPackageUri) async { + await withTempDir((String tempDir) async { + // Setup ".dart_tool/package_config.json" + final dotDartToolDir = path.join(tempDir, '.dart_tool'); + await Directory(dotDartToolDir).create(); + final packageConfigJsonFile = path.join( + dotDartToolDir, spawnWithArg ? 'baz.packages' : 'package_config.json'); + await File(packageConfigJsonFile) + .writeAsString(buildPackageConfig('foo', optionalPackageUri)); + + // Setup actual application + final mainFile = path.join(tempDir, 'main.dart'); + final childIsolateFile = path.join(tempDir, 'child_isolate.dart'); + final importUri = 'package:foo/child_isolate.dart'; + await File(childIsolateFile).writeAsString(buildChildIsolate()); + await File(mainFile).writeAsString(buildMainIsolate( + importUri, spawnWithArg ? packageConfigJsonFile : null)); + + await fn(tempDir, packageConfigJsonFile, mainFile); + }); +} + +Future withTempDir(Future fn(String dir)) async { + final dir = await Directory.systemTemp.createTemp('spawn_uri'); + try { + await fn(dir.absolute.path); + } finally { + await dir.delete(recursive: true); + } +} + +Future run(String executable, List args, + {String? cwd}) async { + print('Running $executable ${args.join(' ')}'); + final String workingDirectory = cwd ?? Directory.current.absolute.path; + final result = await Process.run(executable, ['--trace-loading', ...args], + workingDirectory: workingDirectory); + print('exitCode:\n${result.exitCode}'); + print('stdout:\n${result.stdout}'); + print('stdout:\n${result.stderr}'); + Expect.equals(0, result.exitCode); + return result; +} + +String buildDotPackages(String packageName) => '$packageName:.'; + +String buildPackageConfig(String packageName, bool optionalPackageUri) => ''' +{ + "configVersion": 2, + "packages": [ + { + "name": "$packageName", + "rootUri": "../" + ${optionalPackageUri ? ', "packageUri": "./"' : ''} + } + ] +} +'''; + +String buildChildIsolate() => ''' + import 'dart:isolate'; + + main(List args, SendPort message) { + message.send('child isolate is done'); + } +'''; + +String buildMainIsolate(String spawnUri, String? packageConfigUri) => ''' + import 'dart:isolate'; + import 'dart:io' as io; + + main(List args) async { + io.exitCode = 1; + + final rp = ReceivePort(); + final uri = Uri.parse('$spawnUri'); + final isolateArgs = ['a']; + await Isolate.spawnUri( + uri, + isolateArgs, + rp.sendPort, + packageConfig: ${packageConfigUri != null ? 'Uri.file(r"$packageConfigUri")' : 'null'}); + final childIsolateMessage = await rp.first; + if (childIsolateMessage != 'child isolate is done') { + throw 'Did not receive correct message from child isolate.'; + } + + // Test was successful. + io.exitCode = 0; + } +'''; diff --git a/tests/lib/lib.status b/tests/lib/lib.status index 432516aa1d9..855ab434c61 100644 --- a/tests/lib/lib.status +++ b/tests/lib/lib.status @@ -24,6 +24,9 @@ html/indexeddb_1_test/functional: Skip # Times out. Issue 21433 html/indexeddb_3_test: Skip # Times out 1 out of 10. html/worker_api_test: Skip # Issue 13221 +[ $runtime != vm ] +isolate/spawn_uri__package_uri__test: SkipByDesign # This test uses Isolate.spawnUri and only works in JIT mode. + [ $system == windows ] html/xhr_test/xhr: Skip # Times out. Issue 21527 diff --git a/tests/lib/lib_vm.status b/tests/lib/lib_vm.status index 92cc4270331..9465a916d6d 100644 --- a/tests/lib/lib_vm.status +++ b/tests/lib/lib_vm.status @@ -152,6 +152,7 @@ isolate/simple_message_test: Skip # https://dartbug.com/36097: Ongoing concurren isolate/spawn_function_custom_class_test: Skip # https://dartbug.com/36097: Ongoing concurrency work. isolate/spawn_function_test: Skip # https://dartbug.com/36097: Ongoing concurrency work. isolate/spawn_generic_test: Skip # https://dartbug.com/36097: Ongoing concurrency work. +isolate/spawn_uri__package_uri__test: Skip # https://dartbug.com/36097: Ongoing concurrency work. isolate/spawn_uri_exported_main_test: Skip # https://dartbug.com/36097: Ongoing concurrency work. isolate/spawn_uri_missing_from_isolate_test: Skip # https://dartbug.com/36097: Ongoing concurrency work. isolate/spawn_uri_missing_test: Skip # https://dartbug.com/36097: Ongoing concurrency work. diff --git a/tests/lib_2/isolate/spawn_uri__package_uri__test.dart b/tests/lib_2/isolate/spawn_uri__package_uri__test.dart new file mode 100644 index 00000000000..f34eb4646a8 --- /dev/null +++ b/tests/lib_2/isolate/spawn_uri__package_uri__test.dart @@ -0,0 +1,164 @@ +// 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. + +import 'dart:io'; + +import 'package:expect/expect.dart'; +import 'package:path/path.dart' as path; + +final executable = Platform.executable; + +main() async { + // Run the Dart VM with or without: + // --packages= + for (final runWithPackagesArg in const [true, false]) { + // Run the isolate with or without + // Isolate.spawnUri(..., packageConfig: ) + print('TEST runWithPackagesArg = $runWithPackagesArg '); + for (final spawnWithPackageConfig in const [true, false]) { + print('TEST spawnWithPackageConfig = $spawnWithPackageConfig '); + await runDotPackagesTest(runWithPackagesArg, spawnWithPackageConfig); + for (final optionalPackageUri in const [true, false]) { + print('TEST optionalPackageUri = $optionalPackageUri'); + await runPackageConfigTest( + runWithPackagesArg, spawnWithPackageConfig, optionalPackageUri); + } + } + } +} + +Future runPackageConfigTest( + bool withPackagesArg, bool spawnWithArg, bool optionalPackageUri) async { + await withApplicationDirAndDotDartToolPackageConfig( + (String tempDir, String packageJson, String mainFile) async { + final args = [if (withPackagesArg) '--packages=$packageJson', mainFile]; + await run(executable, args); + }, spawnWithArg, optionalPackageUri); +} + +Future runDotPackagesTest(bool withPackagesArg, bool spawnWithArg) async { + await withApplicationDirAndDotPackages( + (String tempDir, String dotPackagesFile, String mainFile) async { + final args = [ + if (withPackagesArg) '--packages=$dotPackagesFile', + mainFile, + ]; + await run(executable, args); + }, spawnWithArg); +} + +Future withApplicationDirAndDotPackages( + Future fn(String tempDir, String packagesDir, String mainFile), + bool spawnWithArg) async { + await withTempDir((String tempDir) async { + // Setup ".packages" + final dotPackagesFile = + path.join(tempDir, spawnWithArg ? 'baz.packages' : '.packages'); + await File(dotPackagesFile).writeAsString(buildDotPackages('foo')); + + final mainFile = path.join(tempDir, 'main.dart'); + final childIsolateFile = path.join(tempDir, 'child_isolate.dart'); + final importUri = 'package:foo/child_isolate.dart'; + await File(childIsolateFile).writeAsString(buildChildIsolate()); + await File(mainFile).writeAsString( + buildMainIsolate(importUri, spawnWithArg ? dotPackagesFile : null)); + + await fn(tempDir, dotPackagesFile, mainFile); + }); +} + +Future withApplicationDirAndDotDartToolPackageConfig( + Future fn(String tempDir, String packageJson, String mainFile), + bool spawnWithArg, + bool optionalPackageUri) async { + await withTempDir((String tempDir) async { + // Setup ".dart_tool/package_config.json" + final dotDartToolDir = path.join(tempDir, '.dart_tool'); + await Directory(dotDartToolDir).create(); + final packageConfigJsonFile = path.join( + dotDartToolDir, spawnWithArg ? 'baz.packages' : 'package_config.json'); + await File(packageConfigJsonFile) + .writeAsString(buildPackageConfig('foo', optionalPackageUri)); + + // Setup actual application + final mainFile = path.join(tempDir, 'main.dart'); + final childIsolateFile = path.join(tempDir, 'child_isolate.dart'); + final importUri = 'package:foo/child_isolate.dart'; + await File(childIsolateFile).writeAsString(buildChildIsolate()); + await File(mainFile).writeAsString(buildMainIsolate( + importUri, spawnWithArg ? packageConfigJsonFile : null)); + + await fn(tempDir, packageConfigJsonFile, mainFile); + }); +} + +Future withTempDir(Future fn(String dir)) async { + final dir = await Directory.systemTemp.createTemp('spawn_uri'); + try { + await fn(dir.absolute.path); + } finally { + await dir.delete(recursive: true); + } +} + +Future run(String executable, List args, + {String cwd}) async { + print('Running $executable ${args.join(' ')}'); + final String workingDirectory = cwd ?? Directory.current.absolute.path; + final result = await Process.run(executable, ['--trace-loading', ...args], + workingDirectory: workingDirectory); + print('exitCode:\n${result.exitCode}'); + print('stdout:\n${result.stdout}'); + print('stdout:\n${result.stderr}'); + Expect.equals(0, result.exitCode); + return result; +} + +String buildDotPackages(String packageName) => '$packageName:.'; + +String buildPackageConfig(String packageName, bool optionalPackageUri) => ''' +{ + "configVersion": 2, + "packages": [ + { + "name": "$packageName", + "rootUri": "../" + ${optionalPackageUri ? ', "packageUri": "./"' : ''} + } + ] +} +'''; + +String buildChildIsolate() => ''' + import 'dart:isolate'; + + main(List args, SendPort message) { + message.send('child isolate is done'); + } +'''; + +String buildMainIsolate(String spawnUri, String packageConfigUri) => ''' + import 'dart:isolate'; + import 'dart:io' as io; + + main(List args) async { + io.exitCode = 1; + + final rp = ReceivePort(); + final uri = Uri.parse('$spawnUri'); + final isolateArgs = ['a']; + await Isolate.spawnUri( + uri, + isolateArgs, + rp.sendPort, + packageConfig: ${packageConfigUri != null ? 'Uri.file(r"$packageConfigUri")' : 'null'}); + final childIsolateMessage = await rp.first; + if (childIsolateMessage != 'child isolate is done') { + throw 'Did not receive correct message from child isolate.'; + } + + // Test was successful. + io.exitCode = 0; + } +'''; diff --git a/tests/lib_2/lib_2.status b/tests/lib_2/lib_2.status index 432516aa1d9..855ab434c61 100644 --- a/tests/lib_2/lib_2.status +++ b/tests/lib_2/lib_2.status @@ -24,6 +24,9 @@ html/indexeddb_1_test/functional: Skip # Times out. Issue 21433 html/indexeddb_3_test: Skip # Times out 1 out of 10. html/worker_api_test: Skip # Issue 13221 +[ $runtime != vm ] +isolate/spawn_uri__package_uri__test: SkipByDesign # This test uses Isolate.spawnUri and only works in JIT mode. + [ $system == windows ] html/xhr_test/xhr: Skip # Times out. Issue 21527 diff --git a/tests/lib_2/lib_2_vm.status b/tests/lib_2/lib_2_vm.status index 78b99864325..77c441a046e 100644 --- a/tests/lib_2/lib_2_vm.status +++ b/tests/lib_2/lib_2_vm.status @@ -137,6 +137,7 @@ isolate/simple_message_test: Skip # https://dartbug.com/36097: Ongoing concurren isolate/spawn_function_custom_class_test: Skip # https://dartbug.com/36097: Ongoing concurrency work. isolate/spawn_function_test: Skip # https://dartbug.com/36097: Ongoing concurrency work. isolate/spawn_generic_test: Skip # https://dartbug.com/36097: Ongoing concurrency work. +isolate/spawn_uri__package_uri__test: Skip # https://dartbug.com/36097: Ongoing concurrency work. isolate/spawn_uri_exported_main_test: Skip # https://dartbug.com/36097: Ongoing concurrency work. isolate/spawn_uri_missing_from_isolate_test: Skip # https://dartbug.com/36097: Ongoing concurrency work. isolate/spawn_uri_missing_test: Skip # https://dartbug.com/36097: Ongoing concurrency work.