Ensure Platform.executable is not resolved. TEST=new test case added Bug: 61206 Change-Id: I0522869f57d519168542b453dc2e827d9d5e6486 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/442942 Commit-Queue: Siva Annamalai <asiva@google.com> Reviewed-by: Nicholas Shahan <nshahan@google.com>
This commit is contained in:
+23
-7
@@ -502,7 +502,7 @@ class DartDev {
|
||||
int idx = 0;
|
||||
char err_msg[256];
|
||||
err_msg[0] = '\0';
|
||||
intptr_t num_args = argc + 2;
|
||||
intptr_t num_args = argc + 3;
|
||||
char** exec_argv = new char*[num_args];
|
||||
#if defined(DART_HOST_OS_WINDOWS)
|
||||
char* exec_name = StringUtilsWin::ArgumentEscape(dartvm_path.get());
|
||||
@@ -515,11 +515,20 @@ class DartDev {
|
||||
Platform::ResolveExecutablePathInto(dart_path, kPathBufSize);
|
||||
idx += 1;
|
||||
#if defined(DART_HOST_OS_WINDOWS)
|
||||
char* dart_name = Utils::SCreate("--executable_name=%s", dart_path);
|
||||
char* dart_name =
|
||||
Utils::SCreate("--resolved_executable_name=%s", dart_path);
|
||||
exec_argv[idx] = StringUtilsWin::ArgumentEscape(dart_name);
|
||||
free(dart_name);
|
||||
idx += 1;
|
||||
dart_name =
|
||||
Utils::SCreate("--executable_name=%s", Platform::GetExecutableName());
|
||||
exec_argv[idx] = StringUtilsWin::ArgumentEscape(dart_name);
|
||||
free(dart_name);
|
||||
#else
|
||||
exec_argv[idx] = Utils::SCreate("--executable_name=%s", dart_path);
|
||||
exec_argv[idx] = Utils::SCreate("--resolved_executable_name=%s", dart_path);
|
||||
idx += 1;
|
||||
exec_argv[idx] =
|
||||
Utils::SCreate("--executable_name=%s", Platform::GetExecutableName());
|
||||
#endif
|
||||
for (intptr_t i = 1; i < argc; ++i) {
|
||||
#if defined(DART_HOST_OS_WINDOWS)
|
||||
@@ -629,9 +638,9 @@ class DartDev {
|
||||
};
|
||||
// Total count of arguments to be passed to the script being execed.
|
||||
if (mark_main_isolate_as_system_isolate) {
|
||||
argc_ = argc + num_vm_options + 4;
|
||||
argc_ = argc + num_vm_options + 5;
|
||||
} else {
|
||||
argc_ = argc + num_vm_options + 3;
|
||||
argc_ = argc + num_vm_options + 4;
|
||||
}
|
||||
|
||||
// Array of arguments to be passed to the script being execed.
|
||||
@@ -657,11 +666,18 @@ class DartDev {
|
||||
char dart_path[kPathBufSize];
|
||||
Platform::ResolveExecutablePathInto(dart_path, kPathBufSize);
|
||||
#if defined(DART_HOST_OS_WINDOWS)
|
||||
char* dart_name = Utils::SCreate("--executable_name=%s", dart_path);
|
||||
char* dart_name =
|
||||
Utils::SCreate("--resolved_executable_name=%s", dart_path);
|
||||
argv_[idx++] = StringUtilsWin::ArgumentEscape(dart_name);
|
||||
free(dart_name);
|
||||
dart_name =
|
||||
Utils::SCreate("--executable_name=%s", Platform::GetExecutableName());
|
||||
argv_[idx++] = StringUtilsWin::ArgumentEscape(dart_name);
|
||||
free(dart_name);
|
||||
#else
|
||||
argv_[idx++] = Utils::SCreate("--executable_name=%s", dart_path);
|
||||
argv_[idx++] = Utils::SCreate("--resolved_executable_name=%s", dart_path);
|
||||
argv_[idx++] =
|
||||
Utils::SCreate("--executable_name=%s", Platform::GetExecutableName());
|
||||
#endif
|
||||
}
|
||||
if (mark_main_isolate_as_system_isolate) {
|
||||
|
||||
@@ -148,6 +148,9 @@ bool Options::ParseDartDevArguments(int argc,
|
||||
CommandLineOptions* dart_vm_options,
|
||||
CommandLineOptions* dart_options,
|
||||
bool* skip_dartdev) {
|
||||
// Store the executable name.
|
||||
Platform::SetExecutableName(argv[0]);
|
||||
|
||||
// First figure out if a dartdev command has been explicitly specified.
|
||||
*skip_dartdev = false;
|
||||
int tmp_i = 1;
|
||||
@@ -317,9 +320,6 @@ bool Options::ParseDartDevArguments(int argc,
|
||||
i++;
|
||||
}
|
||||
|
||||
// Store the executable name.
|
||||
Platform::SetExecutableName(argv[0]);
|
||||
|
||||
// Verify consistency of arguments.
|
||||
if ((packages_file_ != nullptr) && (strlen(packages_file_) == 0)) {
|
||||
Syslog::PrintErr("Empty package file name specified.\n");
|
||||
|
||||
@@ -226,10 +226,12 @@ bool Options::ParseArguments(int argc,
|
||||
// Store the executable name.
|
||||
if (Options::executable_name() != nullptr) {
|
||||
Platform::SetExecutableName(Options::executable_name());
|
||||
Platform::SetResolvedExecutableName(Options::executable_name());
|
||||
} else {
|
||||
Platform::SetExecutableName(argv[0]);
|
||||
}
|
||||
if (Options::resolved_executable_name() != nullptr) {
|
||||
Platform::SetResolvedExecutableName(Options::resolved_executable_name());
|
||||
}
|
||||
}
|
||||
|
||||
// Verify consistency of arguments.
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace bin {
|
||||
V(namespace, namespc) \
|
||||
V(write_service_info, vm_write_service_info_filename) \
|
||||
V(executable_name, executable_name) \
|
||||
V(resolved_executable_name, resolved_executable_name) \
|
||||
/* The purpose of these flags is documented in */ \
|
||||
/* pkg/dartdev/lib/src/commands/compilation_server.dart. */ \
|
||||
V(resident_server_info_file, resident_server_info_file_path) \
|
||||
|
||||
@@ -255,18 +255,36 @@ const char* Platform::ResolveExecutablePath() {
|
||||
}
|
||||
|
||||
intptr_t Platform::ResolveExecutablePathInto(char* result, size_t result_size) {
|
||||
// Get the required length of the buffer.
|
||||
uint32_t path_size = 0;
|
||||
if (_NSGetExecutablePath(nullptr, &path_size) == 0) {
|
||||
// A temporary buffer to hold the initial executable path.
|
||||
char exe_path[PATH_MAX + 1];
|
||||
uint32_t exe_path_size = sizeof(exe_path);
|
||||
|
||||
// Get the path of the executable.
|
||||
if (_NSGetExecutablePath(exe_path, &exe_path_size) != 0) {
|
||||
// The buffer was too small. Get the required size and handle the error.
|
||||
// For this specific use case, we assume PATH_MAX is sufficient.
|
||||
return -1;
|
||||
}
|
||||
if (path_size > result_size) {
|
||||
|
||||
// Now, canonicalize the path to resolve symlinks.
|
||||
// realpath is the standard POSIX function for this purpose.
|
||||
char resolved_exe_path[PATH_MAX + 1];
|
||||
if (realpath(exe_path, resolved_exe_path) == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
if (_NSGetExecutablePath(result, &path_size) != 0) {
|
||||
|
||||
// Check if the resolved path fits into the destination buffer.
|
||||
size_t resolved_size = strlen(resolved_exe_path);
|
||||
if (resolved_size >= result_size) {
|
||||
// The buffer is too small.
|
||||
return -1;
|
||||
}
|
||||
return path_size;
|
||||
|
||||
// Safely copy the resolved path.
|
||||
strncpy(result, resolved_exe_path, resolved_size);
|
||||
result[resolved_size] = '\0';
|
||||
|
||||
return resolved_size;
|
||||
}
|
||||
|
||||
void Platform::SetProcessName(const char* name) {
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2025, 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.
|
||||
|
||||
// This test was generated by Gemini.
|
||||
|
||||
import 'dart:io';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
// The simple script to be run in a child process.
|
||||
// It prints both executable paths to standard output.
|
||||
const String _testScriptContent = '''
|
||||
import 'dart:io';
|
||||
|
||||
void main() {
|
||||
print(Platform.executable);
|
||||
print(Platform.resolvedExecutable);
|
||||
}
|
||||
''';
|
||||
|
||||
void main() {
|
||||
group('Platform executable properties', () {
|
||||
test(
|
||||
'Platform.executable and Platform.resolvedExecutable should differ '
|
||||
'when executed via a symbolic link',
|
||||
() {
|
||||
// 1. Get the absolute path to the current Dart executable.
|
||||
final dartExecutablePath = Platform.resolvedExecutable;
|
||||
|
||||
// 2. Create a temporary directory and the necessary files.
|
||||
final tempDir = Directory.systemTemp.createTempSync(
|
||||
'dart_symlink_test_',
|
||||
);
|
||||
final symlinkPath =
|
||||
'${tempDir.path}${Platform.pathSeparator}dart_symlink';
|
||||
final childScriptPath =
|
||||
'${tempDir.path}${Platform.pathSeparator}child_script.dart';
|
||||
|
||||
try {
|
||||
// Create the symbolic link.
|
||||
Link(symlinkPath).createSync(dartExecutablePath, recursive: true);
|
||||
|
||||
// Write the child script to a file.
|
||||
File(childScriptPath).writeAsStringSync(_testScriptContent);
|
||||
|
||||
// 3. Execute the child script using the symlink.
|
||||
final result = Process.runSync(
|
||||
symlinkPath, // This is the key: use the symlink path
|
||||
[childScriptPath],
|
||||
runInShell: true,
|
||||
);
|
||||
|
||||
// Verify the child process exited successfully.
|
||||
expect(
|
||||
result.exitCode,
|
||||
0,
|
||||
reason: 'Child process failed: ${result.stderr}',
|
||||
);
|
||||
|
||||
// 4. Parse the output.
|
||||
final output = result.stdout.toString().trim().split('\n');
|
||||
final executablePath = output[0];
|
||||
final resolvedExecutablePath = output[1];
|
||||
|
||||
// 5. Assert the paths are different.
|
||||
expect(executablePath, isNot(equals(resolvedExecutablePath)));
|
||||
print('Executable: $executablePath');
|
||||
print('Resolved Executable: $resolvedExecutablePath');
|
||||
} finally {
|
||||
// Clean up the temporary directory.
|
||||
if (tempDir.existsSync()) {
|
||||
tempDir.deleteSync(recursive: true);
|
||||
}
|
||||
}
|
||||
},
|
||||
// This is the important part: use the 'skip' parameter.
|
||||
// The test will be skipped unless the platform is Linux or MacOS.
|
||||
skip: (!Platform.isLinux && !Platform.isMacOS) ? true : false,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -49,6 +49,7 @@ io/http_response_deadline_test: Skip # Flaky.
|
||||
io/http_reuse_server_port_test: Skip # Flaky.
|
||||
io/http_server_close_response_after_error_test: Skip # Flaky.
|
||||
io/http_shutdown_test: Skip # Flaky.
|
||||
io/platform_executable_test: Skip # The test assumes a JIT invocation.
|
||||
io/process_child_test: Skip # The test does not exercise the exec path on AOT
|
||||
io/raw_datagram_socket_test: Skip # Flaky.
|
||||
io/raw_secure_server_closing_test: Skip # Flaky
|
||||
|
||||
Reference in New Issue
Block a user