Version 3.7.0-263.0.dev Merge 57c4da6911dcbf22224e0fce055f2c72248fdb11 into dev
diff --git a/pkg/analysis_server/lib/src/server/driver.dart b/pkg/analysis_server/lib/src/server/driver.dart index a07ee20..75f1684 100644 --- a/pkg/analysis_server/lib/src/server/driver.dart +++ b/pkg/analysis_server/lib/src/server/driver.dart
@@ -141,9 +141,7 @@ DetachableFileSystemManager? detachableFileSystemManager; /// The instrumentation service that is to be used by the analysis server. - InstrumentationService? instrumentationService; - - HttpAnalysisServer? httpServer; + late final InstrumentationService _instrumentationService; /// Use the given command-line [arguments] to start this server. /// @@ -272,10 +270,7 @@ var logFilePath = results.option(PROTOCOL_TRAFFIC_LOG) ?? results.option(PROTOCOL_TRAFFIC_LOG_ALIAS); - var allInstrumentationServices = - this.instrumentationService == null - ? <InstrumentationService>[] - : [this.instrumentationService!]; + var allInstrumentationServices = <InstrumentationService>[]; if (logFilePath != null) { _rollLogFiles(logFilePath, 5); allInstrumentationServices.add( @@ -291,21 +286,20 @@ allInstrumentationServices.add( CrashReportingInstrumentation(crashReportSender), ); - var instrumentationService = MulticastInstrumentationService( + _instrumentationService = MulticastInstrumentationService( allInstrumentationServices, ); - this.instrumentationService = instrumentationService; - instrumentationService.logVersion( + _instrumentationService.logVersion( results.option(TRAIN_USING) != null ? 'training-0' - : _readUuid(instrumentationService), + : _readUuid(_instrumentationService), analysisServerOptions.clientId ?? '', analysisServerOptions.clientVersion ?? '', PROTOCOL_VERSION, defaultSdk.languageVersion.toString(), ); - AnalysisEngine.instance.instrumentationService = instrumentationService; + AnalysisEngine.instance.instrumentationService = _instrumentationService; int? diagnosticServerPort; var portValue = @@ -337,7 +331,7 @@ analysisServerOptions, dartSdkManager, analyticsManager, - instrumentationService, + _instrumentationService, diagnosticServerPort, errorNotifier, ); @@ -349,7 +343,7 @@ dartSdkManager, analyticsManager, crashReportingAttachmentsBuilder, - instrumentationService, + _instrumentationService, RequestStatisticsHelper(), diagnosticServerPort, errorNotifier, @@ -406,9 +400,8 @@ analyticsManager, detachableFileSystemManager, ); - httpServer = HttpAnalysisServer(socketServer); - diagnosticServer.httpServer = httpServer!; + diagnosticServer.httpServer = HttpAnalysisServer(socketServer); if (diagnosticServerPort != null) { diagnosticServer.startOnPort(diagnosticServerPort); } @@ -440,18 +433,14 @@ exitCode = await devServer.processDirectories([trainDirectory]); if (exitCode != 0) exit(exitCode); - var httpServer = this.httpServer; - if (httpServer != null) { - httpServer.close(); - } + diagnosticServer.httpServer.close(); await instrumentationService.shutdown(); - unawaited(socketServer.analysisServer!.shutdown()); try { tempDriverDir.deleteSync(recursive: true); } catch (_) { - // ignore any exception + // Ignore any exception. } exit(exitCode); @@ -473,10 +462,7 @@ errorNotifier.sendSilentExceptionsToClient = true; } serveResult.then((_) async { - var httpServer = this.httpServer; - if (httpServer != null) { - httpServer.close(); - } + diagnosticServer.httpServer.close(); await instrumentationService.shutdown(); unawaited(socketServer.analysisServer!.shutdown()); if (sendPort == null) exit(0); @@ -485,7 +471,7 @@ print: results.flag(INTERNAL_PRINT_TO_CONSOLE) ? null - : httpServer!.recordPrint, + : diagnosticServer.httpServer.recordPrint, ); } } @@ -518,8 +504,7 @@ detachableFileSystemManager, ); errorNotifier.server = socketServer.analysisServer; - - diagnosticServer.httpServer = httpServer = HttpAnalysisServer(socketServer); + diagnosticServer.httpServer = HttpAnalysisServer(socketServer); if (diagnosticServerPort != null) { diagnosticServer.startOnPort(diagnosticServerPort); @@ -948,8 +933,6 @@ class _DiagnosticServerImpl extends DiagnosticServer { late HttpAnalysisServer httpServer; - _DiagnosticServerImpl(); - @override Future<int> getServerPort() async => (await httpServer.serveHttp())!;
diff --git a/pkg/analysis_server/lib/starter.dart b/pkg/analysis_server/lib/starter.dart index 8414612..826d468 100644 --- a/pkg/analysis_server/lib/starter.dart +++ b/pkg/analysis_server/lib/starter.dart
@@ -7,7 +7,6 @@ import 'package:analysis_server/src/server/crash_reporting_attachments.dart'; import 'package:analysis_server/src/server/detachable_filesystem_manager.dart'; import 'package:analysis_server/src/server/driver.dart'; -import 'package:analyzer/instrumentation/instrumentation.dart'; /// An object that can be used to start an analysis server. This class exists so /// that clients can configure an analysis server before starting it. @@ -27,10 +26,6 @@ /// available. set detachableFileSystemManager(DetachableFileSystemManager manager); - /// Set the instrumentation [service] that is to be used by the analysis - /// server. - set instrumentationService(InstrumentationService service); - /// Use the given command-line [arguments] to start this server. void start( List<String> arguments, {
diff --git a/pkg/compiler/lib/src/universe/selector.dart b/pkg/compiler/lib/src/universe/selector.dart index 1727928..62b208a 100644 --- a/pkg/compiler/lib/src/universe/selector.dart +++ b/pkg/compiler/lib/src/universe/selector.dart
@@ -43,8 +43,7 @@ final int hashCode; @override - bool operator ==(other) => - identical(this, other) || other is Selector && hashCode == other.hashCode; + bool operator ==(other) => identical(this, other); int get argumentCount => callStructure.argumentCount; int get namedArgumentCount => callStructure.namedArgumentCount;
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart index b15b5f0..bdea89f 100644 --- a/pkg/dart2wasm/lib/code_generator.dart +++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -2001,9 +2001,10 @@ w.ValueType visitStaticSet(StaticSet node, w.ValueType expectedType) { bool preserved = expectedType != voidMarker; Member target = node.target; - w.ValueType paramType = target is Field - ? translator.globals.getGlobalForStaticField(target).type.type - : translator.signatureForDirectCall(target.reference).inputs.single; + final reference = + target is Field ? target.setterReference! : target.reference; + w.ValueType paramType = + translator.signatureForDirectCall(reference).inputs.single; translateExpression(node.value, paramType); if (!preserved) { call(node.targetReference); @@ -2012,7 +2013,7 @@ w.Local temp = addLocal(paramType); b.local_tee(temp); - call(node.targetReference); + call(reference); b.local_get(temp); return temp.type; }
diff --git a/pkg/dart2wasm/lib/functions.dart b/pkg/dart2wasm/lib/functions.dart index 4b93afc..bcc96f5 100644 --- a/pkg/dart2wasm/lib/functions.dart +++ b/pkg/dart2wasm/lib/functions.dart
@@ -478,12 +478,11 @@ final isGetter = target.isImplicitGetter; final isSetter = target.isImplicitSetter; if (isGetter || isSetter) { - final global = translator.globals.getGlobalForStaticField(member); - final globalType = global.type.type; + final fieldType = translator.translateTypeOfField(member); if (isGetter) { - return translator.typesBuilder.defineFunction(const [], [globalType]); + return translator.typesBuilder.defineFunction(const [], [fieldType]); } - return translator.typesBuilder.defineFunction([globalType], const []); + return translator.typesBuilder.defineFunction([fieldType], const []); } }
diff --git a/pkg/dartdev/lib/src/commands/compile.dart b/pkg/dartdev/lib/src/commands/compile.dart index 54ad454..75f113c 100644 --- a/pkg/dartdev/lib/src/commands/compile.dart +++ b/pkg/dartdev/lib/src/commands/compile.dart
@@ -17,6 +17,7 @@ import '../native_assets.dart'; import '../sdk.dart'; import '../utils.dart'; +import '../vm_interop_handler.dart'; const int genericErrorExitCode = 255; const int compileErrorExitCode = 254; @@ -93,17 +94,18 @@ final args = argResults!; var snapshot = sdk.dart2jsAotSnapshot; var runtime = sdk.dartAotRuntime; + var isAOT = true; if (!Sdk.checkArtifactExists(snapshot, logError: false)) { // AOT snapshots cannot be generated on IA32, so we need this fallback // branch until support for IA32 is dropped (https://dartbug.com/49969). snapshot = sdk.dart2jsSnapshot; - runtime = sdk.dart; if (!Sdk.checkArtifactExists(snapshot)) { return genericErrorExitCode; } + runtime = sdk.dart; + isAOT = false; } final dart2jsCommand = [ - runtime, snapshot, '--libraries-spec=${sdk.librariesJson}', '--cfe-invocation-modes=compile', @@ -112,8 +114,13 @@ if (args.rest.isNotEmpty) ...args.rest.sublist(0), ]; try { - final exitCode = await runProcessInheritStdio(dart2jsCommand); - return exitCode; + VmInteropHandler.run( + runtime, + dart2jsCommand, + packageConfigOverride: null, + isAOT : isAOT, + ); + return 0; } catch (e, st) { log.stderr('Error: JS compilation failed'); log.stderr(e.toString());
diff --git a/pkg/dartdev/lib/src/vm_interop_handler.dart b/pkg/dartdev/lib/src/vm_interop_handler.dart index 37eed1d..43bd647 100644 --- a/pkg/dartdev/lib/src/vm_interop_handler.dart +++ b/pkg/dartdev/lib/src/vm_interop_handler.dart
@@ -18,6 +18,9 @@ /// /// If [markMainIsolateAsSystemIsolate] is given and set to true, the spawned /// isolate will run with `--mark-main-isolate-as-system-isolate` enabled. + /// + /// If [isAOT] is given and set to true, the script is executed by + /// execing the dartaotruntime. static void run( String script, List<String> args, { @@ -27,11 +30,12 @@ // // See https://github.com/dart-lang/sdk/issues/53576 bool markMainIsolateAsSystemIsolate = false, + bool isAOT = false, }) { final port = _port; if (port == null) return; final message = <dynamic>[ - _kResultRun, + isAOT ? _kResultRunAOT : _kResultRunJIT, script, packageConfigOverride, markMainIsolateAsSystemIsolate, @@ -51,8 +55,9 @@ } // Note: keep in sync with runtime/bin/dartdev_isolate.h - static const int _kResultRun = 1; - static const int _kResultExit = 2; + static const int _kResultRunJIT = 1; + static const int _kResultRunAOT = 2; + static const int _kResultExit = 3; static SendPort? _port; }
diff --git a/runtime/bin/dartdev_isolate.cc b/runtime/bin/dartdev_isolate.cc index c7aab45..95c9fd5 100644 --- a/runtime/bin/dartdev_isolate.cc +++ b/runtime/bin/dartdev_isolate.cc
@@ -38,6 +38,7 @@ DartDevIsolate::DartDevRunner(); bool DartDevIsolate::should_run_dart_dev_ = false; bool DartDevIsolate::print_usage_error_ = false; +CommandLineOptions* DartDevIsolate::vm_options_ = nullptr; Monitor* DartDevIsolate::DartDevRunner::monitor_ = new Monitor(); DartDevIsolate::DartDev_Result DartDevIsolate::DartDevRunner::result_ = DartDevIsolate::DartDev_Result_Unknown; @@ -138,7 +139,7 @@ Thread::Start("DartDev Runner", RunCallback, reinterpret_cast<uword>(this)); monitor_->WaitMicros(Monitor::kNoTimeout); - if (result_ == DartDevIsolate::DartDev_Result_Run) { + if (result_ == DartDevIsolate::DartDev_Result_RunJIT) { // Clear the DartDev dart_options and replace them with the processed // options provided by DartDev. dart_options_->Reset(); @@ -157,8 +158,8 @@ ASSERT(message->type == Dart_CObject_kArray); int32_t type = GetArrayItem(message, 0)->value.as_int32; switch (type) { - case DartDevIsolate::DartDev_Result_Run: { - result_ = DartDevIsolate::DartDev_Result_Run; + case DartDevIsolate::DartDev_Result_RunJIT: { + result_ = DartDevIsolate::DartDev_Result_RunJIT; ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kString); auto item2 = GetArrayItem(message, 2); @@ -204,6 +205,86 @@ } break; } + case DartDevIsolate::DartDev_Result_RunAOT: { + result_ = DartDevIsolate::DartDev_Result_RunAOT; + ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kString); + auto item2 = GetArrayItem(message, 2); + + ASSERT(item2->type == Dart_CObject_kString || + item2->type == Dart_CObject_kNull); + + auto item3 = GetArrayItem(message, 3); + + ASSERT(item3->type == Dart_CObject_kBool); + const bool mark_main_isolate_as_system_isolate = item3->value.as_bool; + if (mark_main_isolate_as_system_isolate) { + Options::set_mark_main_isolate_as_system_isolate(true); + } + + if (*script_ != nullptr) { + free(*script_); + } + if (*package_config_override_ != nullptr) { + free(*package_config_override_); + *package_config_override_ = nullptr; + } + *script_ = Utils::StrDup(GetArrayItem(message, 1)->value.as_string); + + if (item2->type == Dart_CObject_kString) { + *package_config_override_ = Utils::StrDup(item2->value.as_string); + } + + intptr_t num_vm_options = 0; + const char** vm_options = nullptr; + ASSERT(GetArrayItem(message, 4)->type == Dart_CObject_kArray); + Dart_CObject* args = GetArrayItem(message, 4); + intptr_t argc = args->value.as_array.length; + Dart_CObject** dart_args = args->value.as_array.values; + + if (vm_options_ != nullptr) { + num_vm_options = vm_options_->count(); + vm_options = vm_options_->arguments(); + } + auto deleter = [](char** args) { + for (intptr_t i = 0; i < argc_; ++i) { + free(args[i]); + } + delete[] args; + }; + // Total count of arguments to be passed to the script being execed. + argc_ = argc + num_vm_options + 1; + + // Array of arguments to be passed to the script being execed. + argv_ = std::unique_ptr<char*[], void (*)(char**)>(new char*[argc_ + 1], + deleter); + + intptr_t idx = 0; + // Copy in name of the script to run (dartaotruntime). + argv_[0] = Utils::StrDup(GetArrayItem(message, 1)->value.as_string); + idx += 1; + // Copy in any vm options that need to be passed to the execed process. + for (intptr_t i = 0; i < num_vm_options; ++i) { + argv_[i + idx] = Utils::StrDup(vm_options[i]); + } + idx += num_vm_options; + // Copy in the dart options that need to be passed to the command. + for (intptr_t i = 0; i < argc; ++i) { + argv_[i + idx] = Utils::StrDup(dart_args[i]->value.as_string); + } + // Null terminate the argv array. + argv_[argc + idx] = nullptr; + + // Exec the script to be run and pass the arguments. + char err_msg[256]; + err_msg[0] = '\0'; + int ret = Process::Exec(nullptr, *script_, + const_cast<const char**>(argv_.get()), argc_, + nullptr, err_msg, sizeof(err_msg)); + if (ret != 0) { + ProcessError(err_msg, kErrorExitCode); + } + break; + } case DartDevIsolate::DartDev_Result_Exit: { ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kInt32); int32_t dartdev_exit_code = GetArrayItem(message, 1)->value.as_int32; @@ -314,7 +395,9 @@ Dart_IsolateGroupCreateCallback create_isolate, char** packages_file, char** script, + CommandLineOptions* vm_options, CommandLineOptions* dart_options) { + vm_options_ = vm_options; runner_.Run(create_isolate, packages_file, script, dart_options); return runner_.result(); }
diff --git a/runtime/bin/dartdev_isolate.h b/runtime/bin/dartdev_isolate.h index 79566c6..ed997cc 100644 --- a/runtime/bin/dartdev_isolate.h +++ b/runtime/bin/dartdev_isolate.h
@@ -27,8 +27,9 @@ // Note: keep in sync with pkg/dartdev/lib/vm_interop_handler.dart typedef enum { DartDev_Result_Unknown = -1, - DartDev_Result_Run = 1, - DartDev_Result_Exit = 2, + DartDev_Result_RunJIT = 1, + DartDev_Result_RunAOT = 2, + DartDev_Result_Exit = 3, } DartDev_Result; // Returns true if there does not exist a file at |script_uri| or the URI is @@ -58,6 +59,7 @@ Dart_IsolateGroupCreateCallback create_isolate, char** packages_file, char** script, + CommandLineOptions* vm_options, CommandLineOptions* dart_options); protected: @@ -83,11 +85,11 @@ static char** package_config_override_; static std::unique_ptr<char*[], void (*)(char**)> argv_; static intptr_t argc_; + static Monitor* monitor_; Dart_IsolateGroupCreateCallback create_isolate_; CommandLineOptions* dart_options_; const char* packages_file_; - static Monitor* monitor_; DISALLOW_ALLOCATION(); }; @@ -98,6 +100,7 @@ static DartDevRunner runner_; static bool should_run_dart_dev_; static bool print_usage_error_; + static CommandLineOptions* vm_options_; DISALLOW_ALLOCATION(); DISALLOW_IMPLICIT_CONSTRUCTORS(DartDevIsolate);
diff --git a/runtime/bin/main_impl.cc b/runtime/bin/main_impl.cc index 9e3aa8c..49522df 100644 --- a/runtime/bin/main_impl.cc +++ b/runtime/bin/main_impl.cc
@@ -1417,11 +1417,11 @@ Options::gen_snapshot_kind() == SnapshotKind::kNone) { DartDevIsolate::DartDev_Result dartdev_result = DartDevIsolate::RunDartDev( CreateIsolateGroupAndSetup, &package_config_override, &script_name, - &dart_options); + &vm_options, &dart_options); ASSERT(dartdev_result != DartDevIsolate::DartDev_Result_Unknown); ran_dart_dev = true; should_run_user_program = - (dartdev_result == DartDevIsolate::DartDev_Result_Run); + (dartdev_result == DartDevIsolate::DartDev_Result_RunJIT); if (should_run_user_program) { try_load_snapshots_lambda(); }
diff --git a/runtime/bin/process.cc b/runtime/bin/process.cc index f990e80..dfb0b77 100644 --- a/runtime/bin/process.cc +++ b/runtime/bin/process.cc
@@ -114,9 +114,9 @@ const char* path = DartUtils::GetStringValue(path_handle); Dart_Handle arguments = Dart_GetNativeArgument(args, 3); intptr_t args_length = 0; - char** string_args = + const char** string_args = const_cast<const char**>( ExtractCStringList(arguments, status_handle, - "Arguments must be builtin strings", &args_length); + "Arguments must be builtin strings", &args_length)); if (string_args == nullptr) { Dart_SetBooleanReturnValue(args, false); return;
diff --git a/runtime/bin/process.h b/runtime/bin/process.h index 9bd547c..2aa40cd 100644 --- a/runtime/bin/process.h +++ b/runtime/bin/process.h
@@ -94,7 +94,7 @@ // process exit streams. static int Start(Namespace* namespc, const char* path, - char* arguments[], + const char* arguments[], intptr_t arguments_length, const char* working_directory, char* environment[], @@ -114,6 +114,23 @@ intptr_t exit_handler, ProcessResult* result); + // Exec process. + // On systems that support 'exec' it will use it to replace + // the current process image with the image corresponding to 'path' + // On systems that do not support it (Windows) it will start in a + // child process in the same group as the parent so that when the parent + // is killed the child also dies. + // Returns 0 if the process could be execed successfully + // Returns -1 if the exec could not be done successfully and 'errmsg' + // points to the error message + static int Exec(Namespace* namespc, + const char* path, + const char* arguments[], + intptr_t arguments_length, + const char* working_directory, + char* errmsg, + intptr_t errmsg_len); + // Kill a process with a given pid. static bool Kill(intptr_t id, int signal);
diff --git a/runtime/bin/process_fuchsia.cc b/runtime/bin/process_fuchsia.cc index eed2af0..4c6a168 100644 --- a/runtime/bin/process_fuchsia.cc +++ b/runtime/bin/process_fuchsia.cc
@@ -511,7 +511,7 @@ public: ProcessStarter(Namespace* namespc, const char* path, - char* arguments[], + const char* arguments[], intptr_t arguments_length, const char* working_directory, char* environment[], @@ -540,7 +540,7 @@ read_err_ = -1; write_out_ = -1; - program_arguments_ = reinterpret_cast<char**>(Dart_ScopeAllocate( + program_arguments_ = reinterpret_cast<const char**>(Dart_ScopeAllocate( (arguments_length + 2) * sizeof(*program_arguments_))); program_arguments_[0] = const_cast<char*>(path_); for (int i = 0; i < arguments_length; i++) { @@ -795,7 +795,7 @@ int read_err_; // Pipe for stderr to child process. int write_out_; // Pipe for stdin to child process. - char** program_arguments_; + const char** program_arguments_; char** program_environment_; Namespace* namespc_; @@ -815,7 +815,7 @@ int Process::Start(Namespace* namespc, const char* path, - char* arguments[], + const char* arguments[], intptr_t arguments_length, const char* working_directory, char* environment[], @@ -838,6 +838,20 @@ return starter.Start(); } +// The command line dart utility does not run on Fuchsia, this functionality +// is not supported on that platform. +int Process::Exec(Namespace* namespc, + const char* path, + const char** arguments, + intptr_t arguments_length, + const char* working_directory, + char* errmsg, + intptr_t errmsg_len) { + snprintf(errmsg, errmsg_len, + "Process::Exec is not supported on this platform"); + return -1; +} + intptr_t Process::SetSignalHandler(intptr_t signal) { errno = ENOSYS; return -1;
diff --git a/runtime/bin/process_linux.cc b/runtime/bin/process_linux.cc index 64e74fc..da95e5a 100644 --- a/runtime/bin/process_linux.cc +++ b/runtime/bin/process_linux.cc
@@ -246,11 +246,47 @@ bool ExitCodeHandler::terminate_done_ = false; Monitor* ExitCodeHandler::monitor_ = nullptr; +// Tries to find path relative to the current namespace unless it should be +// searched in the PATH environment variable. +// The path that should be passed to exec is returned in realpath. +// Returns true on success, and false if there was an error that should +// be reported to the parent. +static bool PathInNamespace(char* realpath, + intptr_t realpath_size, + Namespace* namespc, + const char* path) { + // Perform a PATH search if there's no slash in the path. + if (Namespace::IsDefault(namespc) || strchr(path, '/') == nullptr) { + // TODO(zra): If there is a non-default namespace, the entries in PATH + // should be treated as relative to the namespace. + strncpy(realpath, path, realpath_size); + realpath[realpath_size - 1] = '\0'; + return true; + } + NamespaceScope ns(namespc, path); + const int fd = + TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_RDONLY | O_CLOEXEC)); + if (fd == -1) { + return false; + } + char procpath[PATH_MAX]; + snprintf(procpath, PATH_MAX, "/proc/self/fd/%d", fd); + const intptr_t length = + TEMP_FAILURE_RETRY(readlink(procpath, realpath, realpath_size)); + if (length < 0) { + FDUtils::SaveErrorAndClose(fd); + return false; + } + realpath[length] = '\0'; + FDUtils::SaveErrorAndClose(fd); + return true; +} + class ProcessStarter { public: ProcessStarter(Namespace* namespc, const char* path, - char* arguments[], + const char* arguments[], intptr_t arguments_length, const char* working_directory, char* environment[], @@ -281,7 +317,7 @@ exec_control_[0] = -1; exec_control_[1] = -1; - program_arguments_ = reinterpret_cast<char**>(Dart_ScopeAllocate( + program_arguments_ = reinterpret_cast<const char**>(Dart_ScopeAllocate( (arguments_length + 2) * sizeof(*program_arguments_))); program_arguments_[0] = const_cast<char*>(path_); for (int i = 0; i < arguments_length; i++) { @@ -447,31 +483,7 @@ // Returns true on success, and false if there was an error that should // be reported to the parent. bool FindPathInNamespace(char* realpath, intptr_t realpath_size) { - // Perform a PATH search if there's no slash in the path. - if (Namespace::IsDefault(namespc_) || strchr(path_, '/') == nullptr) { - // TODO(zra): If there is a non-default namespace, the entries in PATH - // should be treated as relative to the namespace. - strncpy(realpath, path_, realpath_size); - realpath[realpath_size - 1] = '\0'; - return true; - } - NamespaceScope ns(namespc_, path_); - const int fd = - TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), O_RDONLY | O_CLOEXEC)); - if (fd == -1) { - return false; - } - char procpath[PATH_MAX]; - snprintf(procpath, PATH_MAX, "/proc/self/fd/%d", fd); - const intptr_t length = - TEMP_FAILURE_RETRY(readlink(procpath, realpath, realpath_size)); - if (length < 0) { - FDUtils::SaveErrorAndClose(fd); - return false; - } - realpath[length] = '\0'; - FDUtils::SaveErrorAndClose(fd); - return true; + return PathInNamespace(realpath, realpath_size, namespc_, path_); } void ExecProcess() { @@ -772,7 +784,7 @@ int write_out_[2]; // Pipe for stdin to child process. int exec_control_[2]; // Pipe to get the result from exec. - char** program_arguments_; + const char** program_arguments_; char** program_environment_; Namespace* namespc_; @@ -792,7 +804,7 @@ int Process::Start(Namespace* namespc, const char* path, - char* arguments[], + const char* arguments[], intptr_t arguments_length, const char* working_directory, char* environment[], @@ -914,6 +926,31 @@ return true; } +int Process::Exec(Namespace* namespc, + const char* path, + const char* arguments[], + intptr_t arguments_length, + const char* working_directory, + char* errmsg, + intptr_t errmsg_len) { + if (working_directory != nullptr && + !Directory::SetCurrent(namespc, working_directory)) { + Utils::StrError(errno, errmsg, errmsg_len); + return -1; + } + + char realpath[PATH_MAX]; + if (!PathInNamespace(realpath, PATH_MAX, namespc, path)) { + Utils::StrError(errno, errmsg, errmsg_len); + return -1; + } + // TODO(dart:io) Test for the existence of execveat, and use it instead. + execvp(const_cast<const char*>(realpath), + const_cast<char* const*>(arguments)); + Utils::StrError(errno, errmsg, errmsg_len); + return -1; +} + bool Process::Kill(intptr_t id, int signal) { return (TEMP_FAILURE_RETRY(kill(id, signal)) != -1); }
diff --git a/runtime/bin/process_macos.cc b/runtime/bin/process_macos.cc index d7eb45e..94568e3 100644 --- a/runtime/bin/process_macos.cc +++ b/runtime/bin/process_macos.cc
@@ -249,7 +249,7 @@ class ProcessStarter { public: ProcessStarter(const char* path, - char* arguments[], + const char* arguments[], intptr_t arguments_length, const char* working_directory, char* environment[], @@ -279,7 +279,7 @@ exec_control_[0] = -1; exec_control_[1] = -1; - program_arguments_ = reinterpret_cast<char**>(Dart_ScopeAllocate( + program_arguments_ = reinterpret_cast<const char**>(Dart_ScopeAllocate( (arguments_length + 2) * sizeof(*program_arguments_))); program_arguments_[0] = const_cast<char*>(path_); for (int i = 0; i < arguments_length; i++) { @@ -740,7 +740,7 @@ int write_out_[2]; // Pipe for stdin to child process. int exec_control_[2]; // Pipe to get the result from exec. - char** program_arguments_; + const char** program_arguments_; char** program_environment_; const char* path_; @@ -760,7 +760,7 @@ int Process::Start(Namespace* namespc, const char* path, - char* arguments[], + const char* arguments[], intptr_t arguments_length, const char* working_directory, char* environment[], @@ -898,6 +898,24 @@ #endif // defined(DART_HOST_OS_IOS) } +int Process::Exec(Namespace* namespc, + const char* path, + const char* arguments[], + intptr_t arguments_length, + const char* working_directory, + char* errmsg, + intptr_t errmsg_len) { + if (working_directory != nullptr && + TEMP_FAILURE_RETRY(chdir(working_directory)) == -1) { + Utils::StrError(errno, errmsg, errmsg_len); + return -1; + } + + execvp(const_cast<const char*>(path), const_cast<char* const*>(arguments)); + Utils::StrError(errno, errmsg, errmsg_len); + return -1; +} + static int SignalMap(intptr_t id) { switch (static_cast<ProcessSignals>(id)) { case kSighup:
diff --git a/runtime/bin/process_win.cc b/runtime/bin/process_win.cc index e9a353b..b9949c9 100644 --- a/runtime/bin/process_win.cc +++ b/runtime/bin/process_win.cc
@@ -20,6 +20,7 @@ #include "bin/utils.h" #include "bin/utils_win.h" #include "platform/syslog.h" +#include "platform/text_buffer.h" namespace dart { namespace bin { @@ -344,7 +345,7 @@ class ProcessStarter { public: ProcessStarter(const char* path, - char* arguments[], + const char* arguments[], intptr_t arguments_length, const char* working_directory, char* environment[], @@ -373,11 +374,12 @@ stderr_handles_[kWriteHandle] = INVALID_HANDLE_VALUE; exit_handles_[kReadHandle] = INVALID_HANDLE_VALUE; exit_handles_[kWriteHandle] = INVALID_HANDLE_VALUE; + child_process_handle_ = INVALID_HANDLE_VALUE; // Transform input strings to system format. const wchar_t* system_path = StringUtilsWin::Utf8ToWide(path_); - wchar_t** system_arguments; - system_arguments = reinterpret_cast<wchar_t**>( + const wchar_t** system_arguments; + system_arguments = reinterpret_cast<const wchar_t**>( Dart_ScopeAllocate(arguments_length * sizeof(*system_arguments))); for (int i = 0; i < arguments_length; i++) { system_arguments[i] = StringUtilsWin::Utf8ToWide(arguments[i]); @@ -562,7 +564,42 @@ *exit_handler_ = reinterpret_cast<intptr_t>(exit_handle); } } + child_process_handle_ = process_info.hProcess; + CloseHandle(process_info.hThread); + // Return process id. + *id_ = process_info.dwProcessId; + return 0; + } + + int StartForExec() { + // Setup info + STARTUPINFOEXW startup_info; + ZeroMemory(&startup_info, sizeof(startup_info)); + startup_info.StartupInfo.cb = sizeof(startup_info); + ASSERT(mode_ == kInheritStdio); + ASSERT(Process::ModeIsAttached(mode_)); + ASSERT(!Process::ModeHasStdio(mode_)); + + PROCESS_INFORMATION process_info; + ZeroMemory(&process_info, sizeof(process_info)); + + // Create process. + DWORD creation_flags = + EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT; + BOOL result = CreateProcessW( + nullptr, // ApplicationName + command_line_, + nullptr, // ProcessAttributes + nullptr, // ThreadAttributes + TRUE, // InheritHandles + creation_flags, environment_block_, system_working_directory_, + reinterpret_cast<STARTUPINFOW*>(&startup_info), &process_info); + + if (result == 0) { + return SetOsErrorMessage(os_error_message_); + } + child_process_handle_ = process_info.hProcess; CloseHandle(process_info.hThread); // Return process id. @@ -627,6 +664,7 @@ HANDLE stdout_handles_[2]; HANDLE stderr_handles_[2]; HANDLE exit_handles_[2]; + HANDLE child_process_handle_; const wchar_t* system_working_directory_; wchar_t* command_line_; @@ -651,7 +689,7 @@ int Process::Start(Namespace* namespc, const char* path, - char* arguments[], + const char* arguments[], intptr_t arguments_length, const char* working_directory, char* environment[], @@ -880,6 +918,92 @@ return true; } +int Process::Exec(Namespace* namespc, + const char* path, + const char* arguments[], + intptr_t arguments_length, + const char* working_directory, + char* errmsg, + intptr_t errmsg_len) { + // Create a Job object with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE + HANDLE hjob = CreateJobObject(nullptr, nullptr); + if (hjob == nullptr) { + BufferFormatter f(errmsg, errmsg_len); + f.Printf("Process::Exec - CreateJobObject failed %d\n", GetLastError()); + return -1; + } + JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; + DWORD qresult; + if (!QueryInformationJobObject(hjob, JobObjectExtendedLimitInformation, &info, + sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION), + &qresult)) { + BufferFormatter f(errmsg, errmsg_len); + f.Printf("Process::Exec - QueryInformationJobObject failed %d\n", + GetLastError()); + return -1; + } + info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + if (!SetInformationJobObject(hjob, JobObjectExtendedLimitInformation, &info, + sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION))) { + BufferFormatter f(errmsg, errmsg_len); + f.Printf("Process::Exec - SetInformationJobObject failed %d\n", + GetLastError()); + return -1; + } + + // Put the current process into the job object (there is a race here + // as the process can crash before it is in the Job object, but since + // we haven't spawned any children yet this race is harmless) + if (!AssignProcessToJobObject(hjob, GetCurrentProcess())) { + BufferFormatter f(errmsg, errmsg_len); + f.Printf("Process::Exec - AssignProcessToJobObject failed %d\n", + GetLastError()); + return -1; + } + + // Spawn the new child process (this child will automatically get + // added to the Job object). + // If the parent process is killed or it crashes the Job object + // will get destroyed and all the child processes will also get killed. + // arguments includes the name of the executable to run which is the same + // as the value passed in 'path', we strip that off when starting the + // process. + intptr_t pid = -1; + char* os_error_message = nullptr; // Scope allocated by Process::Start. + ProcessStarter starter(path, &(arguments[1]), (arguments_length - 1), + working_directory, nullptr, 0, kInheritStdio, nullptr, + nullptr, nullptr, &pid, nullptr, &os_error_message); + int result = starter.StartForExec(); + if (result != 0) { + BufferFormatter f(errmsg, errmsg_len); + f.Printf("Process::Exec - %s\n", os_error_message); + return -1; + } + + // Now wait for this child process to terminate (normal exit or crash). + HANDLE child_process = starter.child_process_handle_; + ASSERT(child_process != INVALID_HANDLE_VALUE); + DWORD wait_result = WaitForSingleObject(child_process, INFINITE); + if (wait_result != WAIT_OBJECT_0) { + BufferFormatter f(errmsg, errmsg_len); + f.Printf("Process::Exec - WaitForSingleObject failed %d\n", GetLastError()); + CloseHandle(child_process); + return -1; + } + int retval; + if (!GetExitCodeProcess(child_process, reinterpret_cast<DWORD*>(&retval))) { + BufferFormatter f(errmsg, errmsg_len); + f.Printf("Process::Exec - GetExitCodeProcess failed %d\n", GetLastError()); + CloseHandle(child_process); + return -1; + } + CloseHandle(child_process); + // We exit the process here to simulate the same behaviour as exec on systems + // that support it. + ExitProcess(retval); + return 0; +} + bool Process::Kill(intptr_t id, int signal) { USE(signal); // signal is not used on Windows. HANDLE process_handle;
diff --git a/runtime/observatory/tests/service/uri_mappings_lookup_test.dart b/runtime/observatory/tests/service/uri_mappings_lookup_test.dart index 43a5fed..e29cf98 100644 --- a/runtime/observatory/tests/service/uri_mappings_lookup_test.dart +++ b/runtime/observatory/tests/service/uri_mappings_lookup_test.dart
@@ -30,7 +30,7 @@ expect(uris[0], isNull); expect(uris[1], 'org-dartlang-sdk:///sdk/lib/io/io.dart'); expect(uris[2], startsWith('file:///')); - expect(uris[2], endsWith('third_party/pkg/pool/lib/pool.dart')); + expect(uris[2], endsWith('pool/lib/pool.dart')); expect(uris[3], scriptUri); if (Platform.isWindows || Platform.isMacOS) { expect(uris[4], scriptUri);
diff --git a/tools/VERSION b/tools/VERSION index 404e011..2435522 100644 --- a/tools/VERSION +++ b/tools/VERSION
@@ -27,5 +27,5 @@ MAJOR 3 MINOR 7 PATCH 0 -PRERELEASE 262 +PRERELEASE 263 PRERELEASE_PATCH 0