From a782dd82dbb268c45cead3baee25b43c079cb5dd Mon Sep 17 00:00:00 2001 From: asiva Date: Thu, 11 Jun 2020 01:22:35 +0000 Subject: [PATCH] [VM/runtime] - detect null safety before isolate initialization - Detect null safety when not specified before isolate initialization - for source files by having CFE parse the source file for @dart annotations - for kernel files by sniffing the kernel file for compilation mode - for appJIT files by sniffing the feature string - for AOT snapshots by sniffing the feature string - Remove workaround of returning null safety to false during bootstrapping - Add a new Dart C API call for detecting null safety Bug: 41766 Change-Id: Ia8cf264323a2d0d58c2855ce6491456aa6f1da07 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/150089 Commit-Queue: Siva Annamalai Reviewed-by: Alexander Markov --- pkg/vm/bin/kernel_service.dart | 168 ++++++++++++------ runtime/bin/dfe.cc | 79 ++++---- runtime/bin/dfe.h | 17 ++ runtime/bin/gen_snapshot.cc | 3 + runtime/bin/main.cc | 10 ++ runtime/include/dart_api.h | 46 ++++- runtime/vm/bootstrap.cc | 1 - runtime/vm/clustered_snapshot.cc | 51 ++++-- runtime/vm/clustered_snapshot.h | 1 + runtime/vm/dart.cc | 49 +++++ runtime/vm/dart.h | 9 + runtime/vm/dart_api_impl.cc | 21 ++- runtime/vm/isolate.cc | 17 +- runtime/vm/isolate.h | 5 - runtime/vm/kernel.h | 3 - runtime/vm/kernel_binary.cc | 14 -- runtime/vm/kernel_isolate.cc | 67 ++++++- runtime/vm/kernel_isolate.h | 5 + runtime/vm/tags.cc | 16 +- .../lib/isolate/detect_nullsafety_1_test.dart | 39 ++++ .../lib/isolate/detect_nullsafety_2_test.dart | 38 ++++ .../lib/isolate/detect_nullsafety_helper.dart | 56 ++++++ .../isolate/nnbd_spawn_autodetect_1_test.dart | 29 +++ .../isolate/nnbd_spawn_autodetect_2_test.dart | 29 +++ .../isolate/nnbd_spawn_autodetect_helper.dart | 74 ++++++++ .../isolate/nnbd_spawn_autodetect_test.dart | 69 ------- .../nnbd_spawnuri_autodetect_1_test.dart | 28 +++ .../nnbd_spawnuri_autodetect_2_test.dart | 28 +++ .../nnbd_spawnuri_autodetect_3_test.dart | 30 ++++ .../nnbd_spawnuri_autodetect_4_test.dart | 29 +++ .../nnbd_spawnuri_autodetect_helper.dart | 81 +++++++++ .../nnbd_spawnuri_autodetect_test.dart | 101 ----------- tests/lib/lib.status | 10 +- 33 files changed, 886 insertions(+), 337 deletions(-) create mode 100644 tests/lib/isolate/detect_nullsafety_1_test.dart create mode 100644 tests/lib/isolate/detect_nullsafety_2_test.dart create mode 100644 tests/lib/isolate/detect_nullsafety_helper.dart create mode 100644 tests/lib/isolate/nnbd_spawn_autodetect_1_test.dart create mode 100644 tests/lib/isolate/nnbd_spawn_autodetect_2_test.dart create mode 100644 tests/lib/isolate/nnbd_spawn_autodetect_helper.dart delete mode 100644 tests/lib/isolate/nnbd_spawn_autodetect_test.dart create mode 100644 tests/lib/isolate/nnbd_spawnuri_autodetect_1_test.dart create mode 100644 tests/lib/isolate/nnbd_spawnuri_autodetect_2_test.dart create mode 100644 tests/lib/isolate/nnbd_spawnuri_autodetect_3_test.dart create mode 100644 tests/lib/isolate/nnbd_spawnuri_autodetect_4_test.dart create mode 100644 tests/lib/isolate/nnbd_spawnuri_autodetect_helper.dart delete mode 100644 tests/lib/isolate/nnbd_spawnuri_autodetect_test.dart diff --git a/pkg/vm/bin/kernel_service.dart b/pkg/vm/bin/kernel_service.dart index 0216c2781fa..021c8583011 100644 --- a/pkg/vm/bin/kernel_service.dart +++ b/pkg/vm/bin/kernel_service.dart @@ -28,7 +28,6 @@ import 'dart:isolate'; import 'dart:typed_data' show Uint8List; import 'package:build_integration/file_system/multi_root.dart'; -import 'package:front_end/src/api_prototype/experimental_flags.dart'; import 'package:front_end/src/api_prototype/front_end.dart' as fe show CompilerResult; import 'package:front_end/src/api_prototype/memory_file_system.dart'; @@ -78,6 +77,7 @@ const int kTrainTag = 3; const int kCompileExpressionTag = 4; const int kListDependenciesTag = 5; const int kNotifyIsolateShutdownTag = 6; +const int kDetectNullabilityTag = 7; bool allowDartInternalImport = false; @@ -92,6 +92,62 @@ const int kNullSafetyOptionUnspecified = 0; const int kNullSafetyOptionWeak = 1; const int kNullSafetyOptionStrong = 2; +CompilerOptions setupCompilerOptions( + FileSystem fileSystem, + Uri platformKernelPath, + bool suppressWarnings, + bool enableAsserts, + int nullSafety, + List experimentalFlags, + bool bytecode, + Uri packagesUri, + List errors) { + final expFlags = []; + if (experimentalFlags != null) { + for (String flag in experimentalFlags) { + expFlags.addAll(flag.split(",")); + } + } + + return new CompilerOptions() + ..fileSystem = fileSystem + ..target = new VmTarget(new TargetFlags( + enableNullSafety: nullSafety == kNullSafetyOptionStrong)) + ..packagesFileUri = packagesUri + ..sdkSummary = platformKernelPath + ..verbose = verbose + ..omitPlatform = true + ..bytecode = bytecode + ..experimentalFlags = parseExperimentalFlags( + parseExperimentalArguments(expFlags), + onError: (msg) => errors.add(msg)) + ..environmentDefines = new EnvironmentMap() + ..nnbdMode = (nullSafety == kNullSafetyOptionStrong) + ? NnbdMode.Strong + : NnbdMode.Weak + ..onDiagnostic = (DiagnosticMessage message) { + bool printMessage; + switch (message.severity) { + case Severity.error: + case Severity.internalProblem: + // TODO(sigmund): support emitting code with errors as long as they + // are handled in the generated code. + printMessage = false; // errors are printed by VM + errors.addAll(message.plainTextFormatted); + break; + case Severity.warning: + printMessage = !suppressWarnings; + break; + case Severity.context: + case Severity.ignored: + throw "Unexpected severity: ${message.severity}"; + } + if (printMessage) { + printDiagnosticMessage(message, stderr.writeln); + } + }; +} + abstract class Compiler { final int isolateId; final FileSystem fileSystem; @@ -135,50 +191,16 @@ abstract class Compiler { print("DFE: platformKernelPath: ${platformKernelPath}"); } - var expFlags = List(); - if (experimentalFlags != null) { - for (String flag in experimentalFlags) { - expFlags.addAll(flag.split(",")); - } - } - - options = new CompilerOptions() - ..fileSystem = fileSystem - ..target = new VmTarget(new TargetFlags( - enableNullSafety: nullSafety == kNullSafetyOptionStrong)) - ..packagesFileUri = packagesUri - ..sdkSummary = platformKernelPath - ..verbose = verbose - ..omitPlatform = true - ..bytecode = bytecode - ..experimentalFlags = parseExperimentalFlags( - parseExperimentalArguments(expFlags), - onError: (msg) => errors.add(msg)) - ..environmentDefines = new EnvironmentMap() - ..nnbdMode = (nullSafety == kNullSafetyOptionStrong) - ? NnbdMode.Strong - : NnbdMode.Weak - ..onDiagnostic = (DiagnosticMessage message) { - bool printMessage; - switch (message.severity) { - case Severity.error: - case Severity.internalProblem: - // TODO(sigmund): support emitting code with errors as long as they - // are handled in the generated code. - printMessage = false; // errors are printed by VM - errors.addAll(message.plainTextFormatted); - break; - case Severity.warning: - printMessage = !suppressWarnings; - break; - case Severity.context: - case Severity.ignored: - throw "Unexpected severity: ${message.severity}"; - } - if (printMessage) { - printDiagnosticMessage(message, stderr.writeln); - } - }; + options = setupCompilerOptions( + fileSystem, + platformKernelPath, + suppressWarnings, + enableAsserts, + nullSafety, + experimentalFlags, + bytecode, + packagesUri, + errors); } Future compile(Uri script) { @@ -352,13 +374,6 @@ class IncrementalCompilerWrapper extends Compiler { @override Future compileInternal(Uri script) async { if (generator == null) { - if ((nullSafety == kNullSafetyOptionUnspecified) && - options.experimentalFlags[ExperimentalFlag.nonNullable]) { - await autoDetectNullSafetyMode(script, options); - // Reinitialize target to set correct null safety mode. - options.target = new VmTarget(new TargetFlags( - enableNullSafety: options.nnbdMode == NnbdMode.Strong)); - } generator = new IncrementalCompiler(options, script); } errors.clear(); @@ -420,13 +435,6 @@ class SingleShotCompilerWrapper extends Compiler { @override Future compileInternal(Uri script) async { - if ((nullSafety == kNullSafetyOptionUnspecified) && - options.experimentalFlags[ExperimentalFlag.nonNullable]) { - await autoDetectNullSafetyMode(script, options); - // Reinitialize target to set correct null safety mode. - options.target = new VmTarget(new TargetFlags( - enableNullSafety: options.nnbdMode == NnbdMode.Strong)); - } fe.CompilerResult compilerResult = requireMain ? await kernelForProgram(script, options) : await kernelForModule([script], options); @@ -768,6 +776,7 @@ Future _processLoadRequest(request) async { final String packageConfig = request[12]; final String multirootFilepaths = request[13]; final String multirootScheme = request[14]; + final String workingDirectory = request[15]; Uri platformKernelPath = null; List platformKernel = null; @@ -810,6 +819,30 @@ Future _processLoadRequest(request) async { } port.send(new CompilationResult.ok(null).toResponse()); return; + } else if (tag == kDetectNullabilityTag) { + FileSystem fileSystem = _buildFileSystem( + sourceFiles, platformKernel, multirootFilepaths, multirootScheme); + Uri packagesUri = null; + if (packageConfig != null) { + packagesUri = Uri.parse(packageConfig); + } else if (Platform.packageConfig != null) { + packagesUri = Uri.parse(Platform.packageConfig); + } + if (packagesUri != null && packagesUri.scheme == '') { + // Script does not have a scheme, assume that it is a path, + // resolve it against the working directory. + packagesUri = Uri.directory(workingDirectory).resolveUri(packagesUri); + } + final List errors = []; + var options = setupCompilerOptions(fileSystem, platformKernelPath, false, + false, nullSafety, experimentalFlags, false, packagesUri, errors); + + // script should only be null for kUpdateSourcesTag. + assert(script != null); + await autoDetectNullSafetyMode(script, options); + bool value = options.nnbdMode == NnbdMode.Strong; + port.send(new CompilationResult.nullSafety(value).toResponse()); + return; } // script should only be null for kUpdateSourcesTag. @@ -997,6 +1030,7 @@ Future trainInternal( null /* package_config */, null /* multirootFilepaths */, null /* multirootScheme */, + null /* original working directory */, ]; await _processLoadRequest(request); } @@ -1044,6 +1078,8 @@ abstract class CompilationResult { factory CompilationResult.ok(Uint8List bytes) = _CompilationOk; + factory CompilationResult.nullSafety(bool val) = _CompilationNullSafety; + factory CompilationResult.errors(List errors, Uint8List bytes) = _CompilationError; @@ -1075,6 +1111,20 @@ class _CompilationOk extends CompilationResult { String toString() => "_CompilationOk(${bytes.length} bytes)"; } +class _CompilationNullSafety extends CompilationResult { + final bool _null_safety; + + _CompilationNullSafety(this._null_safety) : super._() {} + + @override + Status get status => Status.ok; + + @override + get payload => _null_safety; + + String toString() => "_CompilationNullSafety($_null_safety)"; +} + abstract class _CompilationFail extends CompilationResult { _CompilationFail() : super._(); diff --git a/runtime/bin/dfe.cc b/runtime/bin/dfe.cc index 39dec0c9fe3..45b625d9ac6 100644 --- a/runtime/bin/dfe.cc +++ b/runtime/bin/dfe.cc @@ -4,8 +4,6 @@ #include "bin/dfe.h" -#include - #include "bin/abi_version.h" #include "bin/dartutils.h" #include "bin/directory.h" @@ -191,53 +189,54 @@ bool DFE::CanUseDartFrontend() const { (KernelServiceDillAvailable() || (frontend_filename() != nullptr)); } -class WindowsPathSanitizer { - public: - explicit WindowsPathSanitizer(const char* path) { - // For Windows we need to massage the paths a bit according to - // http://blogs.msdn.com/b/ie/archive/2006/12/06/file-uris-in-windows.aspx - // - // Convert - // C:\one\two\three - // to - // /C:/one/two/three - // - // (see builtin.dart#_sanitizeWindowsPath) - intptr_t len = strlen(path); - sanitized_uri_ = reinterpret_cast(malloc(len + 1 + 1)); - if (sanitized_uri_ == nullptr) { - OUT_OF_MEMORY(); - } - char* s = sanitized_uri_; - if (len > 2 && path[1] == ':') { - *s++ = '/'; - } - for (const char* p = path; *p != '\0'; ++p, ++s) { - *s = *p == '\\' ? '/' : *p; - } - *s = '\0'; +PathSanitizer::PathSanitizer(const char* path) { +#if defined(HOST_OS_WINDOWS) + // For Windows we need to massage the paths a bit according to + // http://blogs.msdn.com/b/ie/archive/2006/12/06/file-uris-in-windows.aspx + // + // Convert + // C:\one\two\three + // to + // /C:/one/two/three + // + // (see builtin.dart#_sanitizeWindowsPath) + if (path == nullptr) { + return; } - ~WindowsPathSanitizer() { free(sanitized_uri_); } + intptr_t len = strlen(path); + char* uri = reinterpret_cast(new char[len + 1 + 1]); + if (uri == nullptr) { + OUT_OF_MEMORY(); + } + char* s = uri; + if (len > 2 && path[1] == ':') { + *s++ = '/'; + } + for (const char* p = path; *p != '\0'; ++p, ++s) { + *s = *p == '\\' ? '/' : *p; + } + *s = '\0'; + sanitized_uri_ = std::unique_ptr(uri); +#else + sanitized_uri_ = path; +#endif // defined(HOST_OS_WINDOWS) +} - const char* sanitized_uri() { return sanitized_uri_; } - - private: - char* sanitized_uri_; - - DISALLOW_COPY_AND_ASSIGN(WindowsPathSanitizer); -}; +const char* PathSanitizer::sanitized_uri() const { +#if defined(HOST_OS_WINDOWS) + return sanitized_uri_.get(); +#else + return sanitized_uri_; +#endif // defined(HOST_OS_WINDOWS) +} Dart_KernelCompilationResult DFE::CompileScript(const char* script_uri, bool incremental, const char* package_config) { // TODO(aam): When Frontend is ready, VM should be passing vm_outline.dill // instead of vm_platform.dill to Frontend for compilation. -#if defined(HOST_OS_WINDOWS) - WindowsPathSanitizer path_sanitizer(script_uri); + PathSanitizer path_sanitizer(script_uri); const char* sanitized_uri = path_sanitizer.sanitized_uri(); -#else - const char* sanitized_uri = script_uri; -#endif return Dart_CompileToKernel( sanitized_uri, platform_strong_dill_for_compilation_, diff --git a/runtime/bin/dfe.h b/runtime/bin/dfe.h index fc33e649ce0..8bcaeafc5f7 100644 --- a/runtime/bin/dfe.h +++ b/runtime/bin/dfe.h @@ -5,6 +5,8 @@ #ifndef RUNTIME_BIN_DFE_H_ #define RUNTIME_BIN_DFE_H_ +#include + #include "include/dart_api.h" #include "include/dart_native_api.h" #include "platform/assert.h" @@ -121,6 +123,21 @@ class DFE { DISALLOW_COPY_AND_ASSIGN(DFE); }; +class PathSanitizer { + public: + explicit PathSanitizer(const char* path); + const char* sanitized_uri() const; + + private: +#if defined(HOST_OS_WINDOWS) + std::unique_ptr sanitized_uri_; +#else + const char* sanitized_uri_; +#endif // defined(HOST_OS_WINDOWS) + + DISALLOW_COPY_AND_ASSIGN(PathSanitizer); +}; + #if !defined(DART_PRECOMPILED_RUNTIME) extern DFE dfe; #endif diff --git a/runtime/bin/gen_snapshot.cc b/runtime/bin/gen_snapshot.cc index 3598a3c2112..afbc8966792 100644 --- a/runtime/bin/gen_snapshot.cc +++ b/runtime/bin/gen_snapshot.cc @@ -664,6 +664,9 @@ static int CreateIsolateAndSnapshot(const CommandLineOptions& inputs) { Dart_IsolateFlags isolate_flags; Dart_IsolateFlagsInitialize(&isolate_flags); + isolate_flags.null_safety = + Dart_DetectNullSafety(nullptr, nullptr, nullptr, nullptr, nullptr, + kernel_buffer, kernel_buffer_size); if (IsSnapshottingForPrecompilation()) { isolate_flags.obfuscate = obfuscate; isolate_flags.entry_points = no_entry_points; diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc index 3a49f5db837..e99d675fe98 100644 --- a/runtime/bin/main.cc +++ b/runtime/bin/main.cc @@ -599,6 +599,9 @@ static Dart_Isolate CreateIsolateGroupAndSetupHelper( const uint8_t* isolate_snapshot_data = app_isolate_snapshot_data; const uint8_t* isolate_snapshot_instructions = app_isolate_snapshot_instructions; + flags->null_safety = + Dart_DetectNullSafety(nullptr, nullptr, nullptr, isolate_snapshot_data, + isolate_snapshot_instructions, nullptr, -1); #else // JIT: Main isolate starts from the app snapshot, if any. Other isolates // use the core libraries snapshot. @@ -635,6 +638,13 @@ static Dart_Isolate CreateIsolateGroupAndSetupHelper( if (kernel_buffer == NULL && !isolate_run_app_snapshot) { dfe.ReadScript(script_uri, &kernel_buffer, &kernel_buffer_size); } + PathSanitizer script_uri_sanitizer(script_uri); + PathSanitizer packages_config_sanitizer(packages_config); + flags->null_safety = Dart_DetectNullSafety( + script_uri_sanitizer.sanitized_uri(), + packages_config_sanitizer.sanitized_uri(), + DartUtils::original_working_directory, isolate_snapshot_data, + isolate_snapshot_instructions, kernel_buffer, kernel_buffer_size); #endif // !defined(DART_PRECOMPILED_RUNTIME) auto isolate_group_data = new IsolateGroupData( diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h index 6a1a401e467..0e43ad99eb6 100644 --- a/runtime/include/dart_api.h +++ b/runtime/include/dart_api.h @@ -532,6 +532,7 @@ typedef struct { Dart_QualifiedFunctionName* entry_points; bool load_vmservice_library; bool copy_parent_code; + bool null_safety; } Dart_IsolateFlags; /** @@ -3313,8 +3314,8 @@ typedef enum { typedef struct { Dart_KernelCompilationStatus status; + bool null_safety; char* error; - uint8_t* kernel; intptr_t kernel_size; } Dart_KernelCompilationResult; @@ -3370,6 +3371,49 @@ DART_EXPORT void Dart_SetDartLibrarySourcesKernel( const uint8_t* platform_kernel, const intptr_t platform_kernel_size); +/** + * Detect the null safety opt-in status. + * + * When running from source, it is based on the opt-in status of `script_uri`. + * When running from a kernel buffer, it is based on the mode used when + * generating `kernel_buffer`. + * When running from an appJIT or AOT snapshot, it is based on the mode used + * when generating `snapshot_data`. + * + * \param script_uri Uri of the script that contains the source code + * + * \param package_config Uri of the package configuration file (either in format + * of .packages or .dart_tool/package_config.json) for the null safety + * detection to resolve package imports against. If this parameter is not + * passed the package resolution of the parent isolate should be used. + * + * \param original_working_directory current working directory when the VM + * process was launched, this is used to correctly resolve the path specified + * for package_config. + * + * \param snapshot_data + * + * \param snapshot_instructions Buffers containing a snapshot of the + * isolate or NULL if no snapshot is provided. If provided, the buffers must + * remain valid until the isolate shuts down. + * + * \param kernel_buffer + * + * \param kernel_buffer_size A buffer which contains a kernel/DIL program. Must + * remain valid until isolate shutdown. + * + * \return Returns true if the null safety is opted in by the input being + * run `script_uri`, `snapshot_data` or `kernel_buffer`. + * + */ +DART_EXPORT bool Dart_DetectNullSafety(const char* script_uri, + const char* package_config, + const char* original_working_directory, + const uint8_t* snapshot_data, + const uint8_t* snapshot_instructions, + const uint8_t* kernel_buffer, + intptr_t kernel_buffer_size); + #define DART_KERNEL_ISOLATE_NAME "kernel-service" /* diff --git a/runtime/vm/bootstrap.cc b/runtime/vm/bootstrap.cc index 62be369ebad..57445f7fdbd 100644 --- a/runtime/vm/bootstrap.cc +++ b/runtime/vm/bootstrap.cc @@ -109,7 +109,6 @@ static ErrorPtr BootstrapFromKernel(Thread* thread, LongJumpScope jump; if (setjmp(*jump.Set()) == 0) { - program->AutoDetectNullSafety(thread->isolate()); kernel::KernelLoader loader(program.get(), /*uri_to_source_table=*/nullptr); Isolate* isolate = thread->isolate(); diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc index 5e88feb1b2c..a999eb1f8db 100644 --- a/runtime/vm/clustered_snapshot.cc +++ b/runtime/vm/clustered_snapshot.cc @@ -6662,23 +6662,50 @@ char* SnapshotHeaderReader::InitializeGlobalVMFlagsFromSnapshot( #undef CHECK_FLAG #undef SET_FLAG - if (FLAG_null_safety == kNullSafetyOptionUnspecified) { - if (strncmp(cursor, "null-safety", end - cursor) == 0) { - FLAG_null_safety = kNullSafetyOptionStrong; - cursor = end; - continue; - } - if (strncmp(cursor, "no-null-safety", end - cursor) == 0) { - FLAG_null_safety = kNullSafetyOptionWeak; - cursor = end; - continue; - } + cursor = end; + } + + return nullptr; +} + +bool SnapshotHeaderReader::NullSafetyFromSnapshot(const Snapshot* snapshot) { + bool null_safety = false; + SnapshotHeaderReader header_reader(snapshot); + const char* features = nullptr; + intptr_t features_length = 0; + + char* error = header_reader.ReadFeatures(&features, &features_length); + if (error != nullptr) { + return false; + } + + ASSERT(features[features_length] == '\0'); + const char* cursor = features; + while (*cursor != '\0') { + while (*cursor == ' ') { + cursor++; + } + + const char* end = strstr(cursor, " "); + if (end == nullptr) { + end = features + features_length; + } + + if (strncmp(cursor, "null-safety", end - cursor) == 0) { + cursor = end; + null_safety = true; + continue; + } + if (strncmp(cursor, "no-null-safety", end - cursor) == 0) { + cursor = end; + null_safety = false; + continue; } cursor = end; } - return nullptr; + return null_safety; } ApiErrorPtr FullSnapshotReader::ReadVMSnapshot() { diff --git a/runtime/vm/clustered_snapshot.h b/runtime/vm/clustered_snapshot.h index bc86035ada2..1827670403d 100644 --- a/runtime/vm/clustered_snapshot.h +++ b/runtime/vm/clustered_snapshot.h @@ -514,6 +514,7 @@ struct SerializerWritingObjectScope { class SnapshotHeaderReader { public: static char* InitializeGlobalVMFlagsFromSnapshot(const Snapshot* snapshot); + static bool NullSafetyFromSnapshot(const Snapshot* snapshot); explicit SnapshotHeaderReader(const Snapshot* snapshot) : SnapshotHeaderReader(snapshot->kind(), diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc index 06afb21f830..3ec567d8947 100644 --- a/runtime/vm/dart.cc +++ b/runtime/vm/dart.cc @@ -746,6 +746,55 @@ ErrorPtr Dart::InitIsolateFromSnapshot(Thread* T, return Error::null(); } +bool Dart::DetectNullSafety(const char* script_uri, + const uint8_t* snapshot_data, + const uint8_t* snapshot_instructions, + const uint8_t* kernel_buffer, + intptr_t kernel_buffer_size, + const char* package_config, + const char* original_working_directory) { + // Before creating the isolate we first determine the null safety mode + // in which the isolate needs to run based on one of these factors : + // - if loading from source, based on opt-in status of the source + // - if loading from a kernel file, based on the mode used when + // generating the kernel file + // - if loading from an appJIT or AOT snapshot, based on the mode used + // when generating the snapshot. + ASSERT(FLAG_null_safety == kNullSafetyOptionUnspecified); + + // If snapshot is an appJIT/AOT snapshot we will figure out the mode by + // sniffing the feature string in the snapshot. + if (snapshot_data != nullptr) { + // Read the snapshot and check for null safety option. + const Snapshot* snapshot = Snapshot::SetupFromBuffer(snapshot_data); + if (Snapshot::IncludesCode(snapshot->kind())) { + return SnapshotHeaderReader::NullSafetyFromSnapshot(snapshot); + } + } + +#if !defined(DART_PRECOMPILED_RUNTIME) + // If kernel_buffer is specified, it could be a self contained + // kernel file or the kernel file of the application, + // figure out the null safety mode by sniffing the kernel file. + if (kernel_buffer != nullptr) { + const char* error = nullptr; + std::unique_ptr program = kernel::Program::ReadFromBuffer( + kernel_buffer, kernel_buffer_size, &error); + if (program != nullptr) { + return program->compilation_mode() == NNBDCompiledMode::kStrong; + } + return false; + } + + // If we are loading from source, figure out the mode from the source. + if (KernelIsolate::GetExperimentalFlag("non-nullable")) { + return KernelIsolate::DetectNullSafety(script_uri, package_config, + original_working_directory); + } +#endif // !defined(DART_PRECOMPILED_RUNTIME) + return false; +} + #if defined(DART_PRECOMPILED_RUNTIME) static void PrintLLVMConstantPool(Thread* T, Isolate* I) { StackZone printing_zone(T); diff --git a/runtime/vm/dart.h b/runtime/vm/dart.h index 6fa528d750d..78f754703d7 100644 --- a/runtime/vm/dart.h +++ b/runtime/vm/dart.h @@ -66,6 +66,15 @@ class Dart : public AllStatic { const uint8_t* snapshot_instructions, const uint8_t* kernel_buffer, intptr_t kernel_buffer_size); + + static bool DetectNullSafety(const char* script_uri, + const uint8_t* snapshot_data, + const uint8_t* snapshot_instructions, + const uint8_t* kernel_buffer, + intptr_t kernel_buffer_size, + const char* package_config, + const char* original_working_directory); + static void RunShutdownCallback(); static void ShutdownIsolate(Isolate* isolate); static void ShutdownIsolate(); diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc index ddb0cf7a8b8..3aff3a741c2 100644 --- a/runtime/vm/dart_api_impl.cc +++ b/runtime/vm/dart_api_impl.cc @@ -5449,7 +5449,6 @@ DART_EXPORT Dart_Handle Dart_LoadScriptFromKernel(const uint8_t* buffer, if (program == nullptr) { return Api::NewError("Can't load Kernel binary: %s.", error); } - program->AutoDetectNullSafety(I); const Object& tmp = kernel::KernelLoader::LoadEntireProgram(program.get()); program.reset(); @@ -5755,7 +5754,6 @@ DART_EXPORT Dart_Handle Dart_LoadLibraryFromKernel(const uint8_t* buffer, DARTSCOPE(Thread::Current()); API_TIMELINE_DURATION(T); StackZone zone(T); - Isolate* I = T->isolate(); CHECK_CALLBACK_STATE(T); @@ -5772,7 +5770,6 @@ DART_EXPORT Dart_Handle Dart_LoadLibraryFromKernel(const uint8_t* buffer, if (program == nullptr) { return Api::NewError("Can't load Kernel binary: %s.", error); } - program->AutoDetectNullSafety(I); const Object& result = kernel::KernelLoader::LoadEntireProgram(program.get(), false); program.reset(); @@ -6028,6 +6025,24 @@ DART_EXPORT void Dart_SetDartLibrarySourcesKernel( #endif } +DART_EXPORT bool Dart_DetectNullSafety(const char* script_uri, + const char* package_config, + const char* original_working_directory, + const uint8_t* snapshot_data, + const uint8_t* snapshot_instructions, + const uint8_t* kernel_buffer, + intptr_t kernel_buffer_size) { + bool null_safety; + if (FLAG_null_safety == kNullSafetyOptionUnspecified) { + null_safety = Dart::DetectNullSafety( + script_uri, snapshot_data, snapshot_instructions, kernel_buffer, + kernel_buffer_size, package_config, original_working_directory); + } else { + null_safety = (FLAG_null_safety == kNullSafetyOptionStrong); + } + return null_safety; +} + // --- Service support --- DART_EXPORT bool Dart_IsServiceIsolate(Dart_Isolate isolate) { diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc index dfb80a5661f..31cfccbfbc0 100644 --- a/runtime/vm/isolate.cc +++ b/runtime/vm/isolate.cc @@ -1455,6 +1455,7 @@ void Isolate::FlagsInitialize(Dart_IsolateFlags* api_flags) { api_flags->entry_points = NULL; api_flags->load_vmservice_library = false; api_flags->copy_parent_code = false; + api_flags->null_safety = false; } void Isolate::FlagsCopyTo(Dart_IsolateFlags* api_flags) const { @@ -1466,6 +1467,7 @@ void Isolate::FlagsCopyTo(Dart_IsolateFlags* api_flags) const { api_flags->entry_points = NULL; api_flags->load_vmservice_library = should_load_vmservice(); api_flags->copy_parent_code = false; + api_flags->null_safety = null_safety(); } void Isolate::FlagsCopyFrom(const Dart_IsolateFlags& api_flags) { @@ -1495,6 +1497,7 @@ void Isolate::FlagsCopyFrom(const Dart_IsolateFlags& api_flags) { #undef SET_FROM_FLAG set_should_load_vmservice(api_flags.load_vmservice_library); + set_null_safety(api_flags.null_safety); // Copy entry points list. ASSERT(embedder_entry_points_ == NULL); @@ -1757,26 +1760,12 @@ Isolate* Isolate::InitIsolate(const char* name_prefix, isolate_group->RegisterIsolate(result); if (ServiceIsolate::NameEquals(name_prefix)) { - // For now the service isolate always runs in weak mode. - result->set_null_safety(false); ASSERT(!ServiceIsolate::Exists()); ServiceIsolate::SetServiceIsolate(result); #if !defined(DART_PRECOMPILED_RUNTIME) } else if (KernelIsolate::NameEquals(name_prefix)) { - // For now the kernel isolate always runs in weak mode. - result->set_null_safety(false); ASSERT(!KernelIsolate::Exists()); KernelIsolate::SetKernelIsolate(result); -#endif // !defined(DART_PRECOMPILED_RUNTIME) - } else if (FLAG_null_safety != kNullSafetyOptionUnspecified) { - // If the null-safety option is specified on the command line then - // use the value specified on the command line, if the dill file being - // loaded is in a different mode than that specified on the command line - // we will get an error during kernel file loading. - result->set_null_safety(FLAG_null_safety == kNullSafetyOptionStrong); -#if !defined(DART_PRECOMPILED_RUNTIME) - } else if (!KernelIsolate::GetExperimentalFlag("non-nullable")) { - result->set_null_safety(false); #endif // !defined(DART_PRECOMPILED_RUNTIME) } diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h index dbf4a435872..7aa57a0a99a 100644 --- a/runtime/vm/isolate.h +++ b/runtime/vm/isolate.h @@ -1235,11 +1235,6 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry { } bool null_safety() const { - // TODO(asiva) : We return false when the null safety mode is not yet set - // instead of just asserting as some code runs during bootstrapping that - // requires the mode to be set. Once all of that is resolved this could - // turn into just an assert. - if (null_safety_not_set()) return false; ASSERT(!null_safety_not_set()); return NullSafetyBit::decode(isolate_flags_); } diff --git a/runtime/vm/kernel.h b/runtime/vm/kernel.h index f4df144bf30..f612540941c 100644 --- a/runtime/vm/kernel.h +++ b/runtime/vm/kernel.h @@ -95,9 +95,6 @@ class Program { intptr_t library_count() { return library_count_; } NNBDCompiledMode compilation_mode() const { return compilation_mode_; } - // Detect null-safety mode from this program if it was not set yet. - void AutoDetectNullSafety(Isolate* isolate); - private: Program() : typed_data_(NULL), kernel_data_(NULL), kernel_data_size_(-1) {} diff --git a/runtime/vm/kernel_binary.cc b/runtime/vm/kernel_binary.cc index 3a776457ebe..3720fe23ac7 100644 --- a/runtime/vm/kernel_binary.cc +++ b/runtime/vm/kernel_binary.cc @@ -226,20 +226,6 @@ std::unique_ptr Program::ReadFromTypedData( return kernel::Program::ReadFrom(&reader, error); } -void Program::AutoDetectNullSafety(Isolate* isolate) { - if (isolate->is_service_isolate() || isolate->is_kernel_isolate()) { - // For now the service isolate and kernel isolate will be running in - // weak mode and we assert for that here. - ASSERT(!isolate->null_safety()); - } else { - // If null safety is not specified on the command line we use the value - // from the dill file that the CFE has computed based on how it was invoked. - if (FLAG_null_safety == kNullSafetyOptionUnspecified) { - isolate->set_null_safety(compilation_mode() == NNBDCompiledMode::kStrong); - } - } -} - } // namespace kernel } // namespace dart #endif // !defined(DART_PRECOMPILED_RUNTIME) diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc index 3803fbf66dc..947d4688d0d 100644 --- a/runtime/vm/kernel_isolate.cc +++ b/runtime/vm/kernel_isolate.cc @@ -15,6 +15,7 @@ #include "vm/message_handler.h" #include "vm/native_arguments.h" #include "vm/native_entry.h" +#include "vm/native_message_handler.h" #include "vm/object.h" #include "vm/object_store.h" #include "vm/port.h" @@ -52,6 +53,10 @@ DEFINE_FLAG(charp, // 1 - Update in-memory file system with in-memory sources (used by tests). // 2 - Accept last compilation result. // 3 - APP JIT snapshot training run for kernel_service. +// 4 - Compile expressions in context (used by expression evaluation). +// 5 - Generate dependencies used to create a dependencies file. +// 6 - Triggers shutdown of the kernel isolate. +// 7 - Detects the nullability of a script based on it's opt-in status. const int KernelIsolate::kCompileTag = 0; const int KernelIsolate::kUpdateSourcesTag = 1; const int KernelIsolate::kAcceptTag = 2; @@ -59,6 +64,7 @@ const int KernelIsolate::kTrainTag = 3; const int KernelIsolate::kCompileExpressionTag = 4; const int KernelIsolate::kListDependenciesTag = 5; const int KernelIsolate::kNotifyIsolateShutdown = 6; +const int KernelIsolate::kDetectNullabilityTag = 7; const char* KernelIsolate::kName = DART_KERNEL_ISOLATE_NAME; Dart_IsolateGroupCreateCallback KernelIsolate::create_group_callback_ = NULL; @@ -695,7 +701,8 @@ class KernelCompilationRequest : public ValueObject { const char* package_config, const char* multiroot_filepaths, const char* multiroot_scheme, - const MallocGrowableArray* experimental_flags) { + const MallocGrowableArray* experimental_flags, + const char* original_working_directory) { // Build the message for the Kernel isolate. // tag is used to specify which operation the frontend should perform. Dart_CObject tag; @@ -767,7 +774,10 @@ class KernelCompilationRequest : public ValueObject { Dart_CObject null_safety; null_safety.type = Dart_CObject_kInt32; - null_safety.value.as_int32 = FLAG_null_safety; + null_safety.value.as_int32 = + (isolate != NULL) ? (isolate->null_safety() ? kNullSafetyOptionStrong + : kNullSafetyOptionWeak) + : FLAG_null_safety; intptr_t num_experimental_flags = experimental_flags->length(); Dart_CObject** experimental_flags_array = @@ -822,6 +832,17 @@ class KernelCompilationRequest : public ValueObject { } } + Dart_CObject original_working_directory_object; + { + if (original_working_directory != NULL) { + original_working_directory_object.type = Dart_CObject_kString; + original_working_directory_object.value.as_string = + const_cast(original_working_directory); + } else { + original_working_directory_object.type = Dart_CObject_kNull; + } + } + Dart_CObject* message_arr[] = {&tag, &send_port, &uri, @@ -836,7 +857,8 @@ class KernelCompilationRequest : public ValueObject { &bytecode, &package_config_uri, &multiroot_filepaths_object, - &multiroot_scheme_object}; + &multiroot_scheme_object, + &original_working_directory_object}; message.value.as_array.values = message_arr; message.value.as_array.length = ARRAY_SIZE(message_arr); // Send the message. @@ -862,11 +884,16 @@ class KernelCompilationRequest : public ValueObject { private: void LoadKernelFromResponse(Dart_CObject* response) { ASSERT((response->type == Dart_CObject_kTypedData) || + (response->type == Dart_CObject_kBool) || (response->type == Dart_CObject_kNull)); if (response->type == Dart_CObject_kNull) { return; } + if (response->type == Dart_CObject_kBool) { + result_.null_safety = response->value.as_bool; + return; + } ASSERT(response->value.as_typed_data.type == Dart_TypedData_kUint8); result_.kernel_size = response->value.as_typed_data.length; @@ -999,7 +1026,33 @@ Dart_KernelCompilationResult KernelIsolate::CompileToKernel( kCompileTag, kernel_port, script_uri, platform_kernel, platform_kernel_size, source_file_count, source_files, incremental_compile, package_config, multiroot_filepaths, - multiroot_scheme, experimental_flags_); + multiroot_scheme, experimental_flags_, NULL); +} + +bool KernelIsolate::DetectNullSafety(const char* script_uri, + const char* package_config, + const char* original_working_directory) { + // Start the kernel Isolate if it is not already running. + if (!Start()) { + Dart_KernelCompilationResult result = {}; + result.status = Dart_KernelCompilationStatus_Unknown; + result.error = strdup("Error while starting Kernel isolate task"); + return false; + } + // Wait for Kernel isolate to finish initialization. + Dart_Port kernel_port = WaitForKernelPort(); + if (kernel_port == ILLEGAL_PORT) { + Dart_KernelCompilationResult result = {}; + result.status = Dart_KernelCompilationStatus_Unknown; + result.error = strdup("Error while initializing Kernel isolate"); + return false; + } + KernelCompilationRequest request; + Dart_KernelCompilationResult result = request.SendAndWaitForResponse( + kDetectNullabilityTag, kernel_port, script_uri, nullptr, -1, 0, nullptr, + false, package_config, nullptr, nullptr, experimental_flags_, + original_working_directory); + return result.null_safety; } Dart_KernelCompilationResult KernelIsolate::ListDependencies() { @@ -1014,7 +1067,7 @@ Dart_KernelCompilationResult KernelIsolate::ListDependencies() { KernelCompilationRequest request; return request.SendAndWaitForResponse(kListDependenciesTag, kernel_port, NULL, NULL, 0, 0, NULL, false, NULL, NULL, - NULL, experimental_flags_); + NULL, experimental_flags_, NULL); } Dart_KernelCompilationResult KernelIsolate::AcceptCompilation() { @@ -1031,7 +1084,7 @@ Dart_KernelCompilationResult KernelIsolate::AcceptCompilation() { KernelCompilationRequest request; return request.SendAndWaitForResponse(kAcceptTag, kernel_port, NULL, NULL, 0, 0, NULL, true, NULL, NULL, NULL, - experimental_flags_); + experimental_flags_, NULL); } Dart_KernelCompilationResult KernelIsolate::CompileExpressionToKernel( @@ -1073,7 +1126,7 @@ Dart_KernelCompilationResult KernelIsolate::UpdateInMemorySources( KernelCompilationRequest request; return request.SendAndWaitForResponse( kUpdateSourcesTag, kernel_port, NULL, NULL, 0, source_files_count, - source_files, true, NULL, NULL, NULL, experimental_flags_); + source_files, true, NULL, NULL, NULL, experimental_flags_, NULL); } void KernelIsolate::NotifyAboutIsolateShutdown(const Isolate* isolate) { diff --git a/runtime/vm/kernel_isolate.h b/runtime/vm/kernel_isolate.h index fc9f3329aa2..fbf31e237da 100644 --- a/runtime/vm/kernel_isolate.h +++ b/runtime/vm/kernel_isolate.h @@ -30,6 +30,7 @@ class KernelIsolate : public AllStatic { static const int kCompileExpressionTag; static const int kListDependenciesTag; static const int kNotifyIsolateShutdown; + static const int kDetectNullabilityTag; static void InitializeState(); static bool Start(); @@ -53,6 +54,10 @@ class KernelIsolate : public AllStatic { const char* multiroot_filepaths = NULL, const char* multiroot_scheme = NULL); + static bool DetectNullSafety(const char* script_uri, + const char* package_config, + const char* original_working_directory); + static Dart_KernelCompilationResult AcceptCompilation(); static Dart_KernelCompilationResult UpdateInMemorySources( int source_files_count, diff --git a/runtime/vm/tags.cc b/runtime/vm/tags.cc index 8636df76d5a..ecc7d1879ff 100644 --- a/runtime/vm/tags.cc +++ b/runtime/vm/tags.cc @@ -78,16 +78,20 @@ VMTag::TagEntry VMTag::entries_[] = { VMTagScope::VMTagScope(Thread* thread, uword tag, bool conditional_set) : ThreadStackResource(thread) { - ASSERT(isolate_group() != NULL); - previous_tag_ = thread->vm_tag(); - if (conditional_set) { - thread->set_vm_tag(tag); + if (thread != NULL) { + ASSERT(isolate_group() != NULL); + previous_tag_ = thread->vm_tag(); + if (conditional_set) { + thread->set_vm_tag(tag); + } } } VMTagScope::~VMTagScope() { - ASSERT(isolate_group() != NULL); - thread()->set_vm_tag(previous_tag_); + if (thread() != NULL) { + ASSERT(isolate_group() != NULL); + thread()->set_vm_tag(previous_tag_); + } } VMTagCounters::VMTagCounters() { diff --git a/tests/lib/isolate/detect_nullsafety_1_test.dart b/tests/lib/isolate/detect_nullsafety_1_test.dart new file mode 100644 index 00000000000..1fecd54d3fe --- /dev/null +++ b/tests/lib/isolate/detect_nullsafety_1_test.dart @@ -0,0 +1,39 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "dart:io"; +import "detect_nullsafety_helper.dart"; + +void main() { + // Create temporary directory. + var tmpDir = Directory.systemTemp.createTempSync(); + var tmpDirPath = tmpDir.path; + String sourcePath = "$tmpDirPath/strong.dart"; + String dillPath = "$tmpDirPath/strong.dill"; + String jitPath = "$tmpDirPath/strong.appjit"; + + // Generate code for an isolate to run in strong mode. + generateIsolateSource(sourcePath, ""); + generateKernel(sourcePath, dillPath); + generateAppJIT(sourcePath, jitPath); + + try { + // Running from Source. + testNullSafetyMode(sourcePath, 'Strong Mode'); + // Without the enable experiment option it will be in weak mode. + testNullSafetyMode1(sourcePath, 'Weak Mode'); + + // Running from Kernel File. + testNullSafetyMode(dillPath, 'Strong Mode'); + // Without the enable experiment option it will be inferred to strong. + testNullSafetyMode1(dillPath, 'Strong Mode'); + + // Running from app JIT File. + testNullSafetyMode(jitPath, 'Strong Mode'); + // Without the enable experiment option it will be inferred to strong. + testNullSafetyMode1(jitPath, 'Strong Mode'); + } finally { + tmpDir.deleteSync(recursive: true); + } +} diff --git a/tests/lib/isolate/detect_nullsafety_2_test.dart b/tests/lib/isolate/detect_nullsafety_2_test.dart new file mode 100644 index 00000000000..722f74ee58a --- /dev/null +++ b/tests/lib/isolate/detect_nullsafety_2_test.dart @@ -0,0 +1,38 @@ +// 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 "detect_nullsafety_helper.dart"; + +void main() { + // Create temporary directory. + var tmpDir = Directory.systemTemp.createTempSync(); + var tmpDirPath = tmpDir.path; + String sourcePath = "$tmpDirPath/weak.dart"; + String dillPath = "$tmpDirPath/weak.dill"; + String jitPath = "$tmpDirPath/weak.appjit"; + + // Generate code for an isolate to run in weak mode. + generateIsolateSource(sourcePath, "2.6"); + generateKernel(sourcePath, dillPath); + generateAppJIT(sourcePath, jitPath); + + try { + testNullSafetyMode(sourcePath, 'Weak Mode'); + // Without the enable experiment option it will be in weak mode. + testNullSafetyMode1(sourcePath, 'Weak Mode'); + + // Running from Kernel File. + testNullSafetyMode(dillPath, 'Weak Mode'); + // Without the enable experiment option it will be inferred to weak. + testNullSafetyMode1(dillPath, 'Weak Mode'); + + // Running from app JIT File. + testNullSafetyMode(jitPath, 'Weak Mode'); + // Without the enable experiment option it will be inferred to weak. + testNullSafetyMode1(jitPath, 'Weak Mode'); + } finally { + tmpDir.deleteSync(recursive: true); + } +} diff --git a/tests/lib/isolate/detect_nullsafety_helper.dart b/tests/lib/isolate/detect_nullsafety_helper.dart new file mode 100644 index 00000000000..c8c263c6a48 --- /dev/null +++ b/tests/lib/isolate/detect_nullsafety_helper.dart @@ -0,0 +1,56 @@ +// 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:async_helper/async_minitest.dart"; + +void generateIsolateSource(String filePath, String version) { + File mainIsolate = new File(filePath); + mainIsolate.writeAsStringSync(''' + // @dart=$version + void main() { + try { + int x = null as int; + print("Weak Mode"); + } catch (ex) { + print("Strong Mode"); + } + } + '''); +} + +void generateOutput(String sourcePath, String outPath, String type) { + var exec = Platform.resolvedExecutable; + var args = []; + args.add("--snapshot-kind=$type"); + args.add("--snapshot=$outPath"); + args.add("--enable-experiment=non-nullable"); + args.add(sourcePath); + var result = Process.runSync(exec, args); +} + +void generateKernel(String sourcePath, String outPath) { + generateOutput(sourcePath, outPath, "kernel"); +} + +void generateAppJIT(String sourcePath, String outPath) { + generateOutput(sourcePath, outPath, "app-jit"); +} + +void testNullSafetyMode(String filePath, String expected) { + var exec = Platform.resolvedExecutable; + var args = []; + args.add("--enable-experiment=non-nullable"); + args.add(filePath); + var result = Process.runSync(exec, args); + expect(result.stdout.contains('$expected'), true); +} + +void testNullSafetyMode1(String filePath, String expected) { + var exec = Platform.resolvedExecutable; + var args = []; + args.add(filePath); + var result = Process.runSync(exec, args); + expect(result.stdout.contains('$expected'), true); +} diff --git a/tests/lib/isolate/nnbd_spawn_autodetect_1_test.dart b/tests/lib/isolate/nnbd_spawn_autodetect_1_test.dart new file mode 100644 index 00000000000..6b18ca10590 --- /dev/null +++ b/tests/lib/isolate/nnbd_spawn_autodetect_1_test.dart @@ -0,0 +1,29 @@ +// 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 "nnbd_spawn_autodetect_helper.dart"; + +void main() { + // Create temporary directory. + var tmpDir = Directory.systemTemp.createTempSync(); + var tmpDirPath = tmpDir.path; + String sourcePath = "$tmpDirPath/strong.dart"; + String dillPath = "$tmpDirPath/strong.dill"; + String jitPath = "$tmpDirPath/strong.appjit"; + + // Generate code for an isolate to run in strong mode. + generateIsolateSource(sourcePath, ""); + generateKernel(sourcePath, dillPath); + generateAppJIT(sourcePath, jitPath); + + try { + // Strong Isolate Spawning another Strong Isolate using spawn. + testNullSafetyMode(sourcePath, 're: strong'); + testNullSafetyMode(dillPath, 're: strong'); + testNullSafetyMode(jitPath, 're: strong'); + } finally { + tmpDir.deleteSync(recursive: true); + } +} diff --git a/tests/lib/isolate/nnbd_spawn_autodetect_2_test.dart b/tests/lib/isolate/nnbd_spawn_autodetect_2_test.dart new file mode 100644 index 00000000000..1d19f15d6ba --- /dev/null +++ b/tests/lib/isolate/nnbd_spawn_autodetect_2_test.dart @@ -0,0 +1,29 @@ +// 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 "nnbd_spawn_autodetect_helper.dart"; + +void main() { + // Create temporary directory. + var tmpDir = Directory.systemTemp.createTempSync(); + var tmpDirPath = tmpDir.path; + String sourcePath = "$tmpDirPath/strong.dart"; + String dillPath = "$tmpDirPath/strong.dill"; + String jitPath = "$tmpDirPath/strong.appjit"; + + // Generate code for an isolate to run in strong mode. + generateIsolateSource(sourcePath, "2.6"); + generateKernel(sourcePath, dillPath); + generateAppJIT(sourcePath, jitPath); + + try { + // Strong Isolate Spawning another Strong Isolate using spawn. + testNullSafetyMode(sourcePath, 're: weak'); + testNullSafetyMode(dillPath, 're: weak'); + testNullSafetyMode(jitPath, 're: weak'); + } finally { + tmpDir.deleteSync(recursive: true); + } +} diff --git a/tests/lib/isolate/nnbd_spawn_autodetect_helper.dart b/tests/lib/isolate/nnbd_spawn_autodetect_helper.dart new file mode 100644 index 00000000000..e6896365845 --- /dev/null +++ b/tests/lib/isolate/nnbd_spawn_autodetect_helper.dart @@ -0,0 +1,74 @@ +// 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:async_helper/async_minitest.dart"; + +void generateIsolateSource(String filePath, String version) { + File isolateSource = new File(filePath); + isolateSource.writeAsStringSync(''' + // @dart=$version + import \'dart:isolate\'; + + spawnFunc(List args) { + var data = args[0]; + var replyTo = args[1]; + try { + int x = null as int; + replyTo.send(\'re: weak\'); + } catch (ex) { + replyTo.send(\'re: strong\'); + } + } + + void main() async { + const String debugName = \'spawnedIsolate\'; + final exitPort = ReceivePort(); + final port = new ReceivePort(); + port.listen((msg) { + print(msg); + port.close(); + }); + + final isolate = await Isolate.spawn( + spawnFunc, + [\'re: hi\', port.sendPort], + paused: false, + debugName: debugName, + onExit: exitPort.sendPort); + + // Explicitly await spawned isolate exit to enforce main isolate not + // completing (and the stand-alone runtime exiting) before the spawned + // isolate is done. + await exitPort.first; + } + '''); +} + +void generateOutput(String sourcePath, String outPath, String type) { + var exec = Platform.resolvedExecutable; + var args = []; + args.add("--snapshot-kind=$type"); + args.add("--snapshot=$outPath"); + args.add("--enable-experiment=non-nullable"); + args.add(sourcePath); + var result = Process.runSync(exec, args); +} + +void generateKernel(String sourcePath, String outPath) { + generateOutput(sourcePath, outPath, "kernel"); +} + +void generateAppJIT(String sourcePath, String outPath) { + generateOutput(sourcePath, outPath, "app-jit"); +} + +void testNullSafetyMode(String filePath, String expected) { + var exec = Platform.resolvedExecutable; + var args = []; + args.add("--enable-experiment=non-nullable"); + args.add(filePath); + var result = Process.runSync(exec, args); + expect(result.stdout.contains('$expected'), true); +} diff --git a/tests/lib/isolate/nnbd_spawn_autodetect_test.dart b/tests/lib/isolate/nnbd_spawn_autodetect_test.dart deleted file mode 100644 index 3e9c617a98d..00000000000 --- a/tests/lib/isolate/nnbd_spawn_autodetect_test.dart +++ /dev/null @@ -1,69 +0,0 @@ -// 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:async_helper/async_minitest.dart"; - -void testNullSafetyMode(String filePath, String version, String expected) { - File mainIsolate = new File(filePath); - mainIsolate.writeAsStringSync(''' - // $version - import \'dart:isolate\'; - - spawnFunc(List args) { - var data = args[0]; - var replyTo = args[1]; - try { - int x = null as int; - replyTo.send(\'re: weak\'); - } catch (ex) { - replyTo.send(\'re: strong\'); - } - } - - void main() async { - const String debugName = \'spawnedIsolate\'; - final exitPort = ReceivePort(); - final port = new ReceivePort(); - port.listen((msg) { - print(msg); - port.close(); - }); - - final isolate = await Isolate.spawn( - spawnFunc, - [\'re: hi\', port.sendPort], - paused: false, - debugName: debugName, - onExit: exitPort.sendPort); - - // Explicitly await spawned isolate exit to enforce main isolate not - // completing (and the stand-alone runtime exiting) before the spawned - // isolate is done. - await exitPort.first; - } - '''); - var exec = Platform.resolvedExecutable; - var args = []; - args.add("--enable-experiment=non-nullable"); - args.add(mainIsolate.path); - var result = Process.runSync(exec, args); - expect(result.stdout.contains('$expected'), true); -} - -void main() { - // Create temporary directory. - var tmpDir = Directory.systemTemp.createTempSync(); - var tmpDirPath = tmpDir.path; - - try { - // Strong Isolate Spawning another Strong Isolate using spawn. - testNullSafetyMode("$tmpDirPath/strong.dart", '', 're: strong'); - - // Weak Isolate Spawning a Weak Isolate using spawn. - testNullSafetyMode("$tmpDirPath/weak.dart", '@dart=2.6', 're: weak'); - } finally { - tmpDir.deleteSync(recursive: true); - } -} diff --git a/tests/lib/isolate/nnbd_spawnuri_autodetect_1_test.dart b/tests/lib/isolate/nnbd_spawnuri_autodetect_1_test.dart new file mode 100644 index 00000000000..013d55887da --- /dev/null +++ b/tests/lib/isolate/nnbd_spawnuri_autodetect_1_test.dart @@ -0,0 +1,28 @@ +// 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 "nnbd_spawnuri_autodetect_helper.dart"; + +void main() { + // Create temporary directory. + var tmpDir = Directory.systemTemp.createTempSync(); + var tmpDirPath = tmpDir.path; + String sourcePath = "$tmpDirPath/strong_isolate.dart"; + + // Generate code for an isolate to run in strong mode. + generateIsolateSource(sourcePath, ""); + + try { + // Strong Isolate Spawning another Strong Isolate using spawnUri. + testNullSafetyMode( + "$tmpDirPath/strong_strong.dart", "", sourcePath, 're: strong'); + + // Weak Isolate Spawning a Strong Isolate using spawnUri. + testNullSafetyMode( + "$tmpDirPath/weak_strong.dart", "2.6", sourcePath, 're: strong'); + } finally { + tmpDir.deleteSync(recursive: true); + } +} diff --git a/tests/lib/isolate/nnbd_spawnuri_autodetect_2_test.dart b/tests/lib/isolate/nnbd_spawnuri_autodetect_2_test.dart new file mode 100644 index 00000000000..e2f9d1c44af --- /dev/null +++ b/tests/lib/isolate/nnbd_spawnuri_autodetect_2_test.dart @@ -0,0 +1,28 @@ +// 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 "nnbd_spawnuri_autodetect_helper.dart"; + +void main() { + // Create temporary directory. + var tmpDir = Directory.systemTemp.createTempSync(); + var tmpDirPath = tmpDir.path; + String sourcePath = "$tmpDirPath/weak_isolate.dart"; + + // Generate code for an isolate to run in strong mode. + generateIsolateSource(sourcePath, "2.6"); + + try { + // Strong Isolate Spawning another weak Isolate using spawnUri. + testNullSafetyMode( + "$tmpDirPath/strong_weak.dart", "", sourcePath, 're: weak'); + + // Weak Isolate Spawning another Weak Isolate using spawnUri. + testNullSafetyMode( + "$tmpDirPath/weak_weak.dart", "2.6", sourcePath, 're: weak'); + } finally { + tmpDir.deleteSync(recursive: true); + } +} diff --git a/tests/lib/isolate/nnbd_spawnuri_autodetect_3_test.dart b/tests/lib/isolate/nnbd_spawnuri_autodetect_3_test.dart new file mode 100644 index 00000000000..b7650db1540 --- /dev/null +++ b/tests/lib/isolate/nnbd_spawnuri_autodetect_3_test.dart @@ -0,0 +1,30 @@ +// 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 "nnbd_spawnuri_autodetect_helper.dart"; + +void main() { + // Create temporary directory. + var tmpDir = Directory.systemTemp.createTempSync(); + var tmpDirPath = tmpDir.path; + String sourcePath = "$tmpDirPath/strong_isolate.dart"; + String outPath = "$tmpDirPath/strong_isolate.dill"; + + // Generate code for an isolate to run in strong mode. + generateIsolateSource(sourcePath, ""); + generateKernel(sourcePath, outPath); + + try { + // Strong Isolate Spawning another Strong Isolate using spawnUri. + testNullSafetyMode( + "$tmpDirPath/strong_strong.dart", "", outPath, 're: strong'); + + // Weak Isolate Spawning a Strong Isolate using spawnUri. + testNullSafetyMode( + "$tmpDirPath/weak_strong.dart", "2.6", outPath, 're: strong'); + } finally { + tmpDir.deleteSync(recursive: true); + } +} diff --git a/tests/lib/isolate/nnbd_spawnuri_autodetect_4_test.dart b/tests/lib/isolate/nnbd_spawnuri_autodetect_4_test.dart new file mode 100644 index 00000000000..875d4d057e1 --- /dev/null +++ b/tests/lib/isolate/nnbd_spawnuri_autodetect_4_test.dart @@ -0,0 +1,29 @@ +// 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 "nnbd_spawnuri_autodetect_helper.dart"; + +void main() { + // Create temporary directory. + var tmpDir = Directory.systemTemp.createTempSync(); + var tmpDirPath = tmpDir.path; + String sourcePath = "$tmpDirPath/weak_isolate.dart"; + String outPath = "$tmpDirPath/weak_isolate.dill"; + + // Generate code for an isolate to run in strong mode. + generateIsolateSource(sourcePath, "2.6"); + generateKernel(sourcePath, outPath); + + try { + // Strong Isolate Spawning another weak Isolate using spawnUri. + testNullSafetyMode("$tmpDirPath/strong_weak.dart", "", outPath, 're: weak'); + + // Weak Isolate Spawning another Weak Isolate using spawnUri. + testNullSafetyMode( + "$tmpDirPath/weak_weak.dart", "2.6", outPath, 're: weak'); + } finally { + tmpDir.deleteSync(recursive: true); + } +} diff --git a/tests/lib/isolate/nnbd_spawnuri_autodetect_helper.dart b/tests/lib/isolate/nnbd_spawnuri_autodetect_helper.dart new file mode 100644 index 00000000000..4cca7e9e61d --- /dev/null +++ b/tests/lib/isolate/nnbd_spawnuri_autodetect_helper.dart @@ -0,0 +1,81 @@ +// 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:async_helper/async_minitest.dart"; + +void generateIsolateSource(String filePath, String version) { + File isolateSource = new File("$filePath"); + isolateSource.writeAsStringSync(''' + // @dart=$version + library SpawnUriIsolate; + main(List args, replyTo) { + var data = args[0]; + try { + int x = null as int; + replyTo.send(\'re: weak\'); + } catch (ex) { + replyTo.send(\'re: strong\'); + } + } + '''); +} + +void generateOutput(String sourcePath, String outPath, String type) { + var exec = Platform.resolvedExecutable; + var args = []; + args.add("--snapshot-kind=$type"); + args.add("--snapshot=$outPath"); + args.add("--enable-experiment=non-nullable"); + args.add(sourcePath); + var result = Process.runSync(exec, args); +} + +void generateKernel(String sourcePath, String outPath) { + generateOutput(sourcePath, outPath, "kernel"); +} + +void generateAppJIT(String sourcePath, String outPath) { + generateOutput(sourcePath, outPath, "app-jit"); +} + +void testNullSafetyMode( + String filePath, String version, String uri, String expected) { + File mainIsolate = new File(filePath); + mainIsolate.writeAsStringSync(''' + // @dart=$version + library spawn_tests; + + import \'dart:isolate\'; + + void main() async { + const String debugName = \'spawnedIsolate\'; + final exitPort = ReceivePort(); + final port = new ReceivePort(); + port.listen((msg) { + print(msg); + port.close(); + }); + + final isolate = await Isolate.spawnUri( + Uri.parse(\'$uri\'), + [\'re: hi\'], + port.sendPort, + paused: false, + debugName: debugName, + onExit: exitPort.sendPort); + + // Explicitly await spawned isolate exit to enforce main isolate not + // completing (and the stand-alone runtime exiting) before the spawned + // isolate is done. + await exitPort.first; + } + '''); + var exec = Platform.resolvedExecutable; + var args = []; + args.add("--enable-experiment=non-nullable"); + args.add(mainIsolate.path); + var result = Process.runSync(exec, args); + expect(result.stdout.contains('$expected'), true); +} diff --git a/tests/lib/isolate/nnbd_spawnuri_autodetect_test.dart b/tests/lib/isolate/nnbd_spawnuri_autodetect_test.dart deleted file mode 100644 index 49afc5711e6..00000000000 --- a/tests/lib/isolate/nnbd_spawnuri_autodetect_test.dart +++ /dev/null @@ -1,101 +0,0 @@ -// 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:async_helper/async_minitest.dart"; - -void testNullSafetyMode(String filePath, String uri, String expected) { - File mainIsolate = new File(filePath); - mainIsolate.writeAsStringSync(''' - library spawn_tests; - - import \'dart:isolate\'; - - void main() async { - const String debugName = \'spawnedIsolate\'; - final exitPort = ReceivePort(); - final port = new ReceivePort(); - port.listen((msg) { - print(msg); - port.close(); - }); - - final isolate = await Isolate.spawnUri( - Uri.parse(\'$uri\'), - [\'re: hi\'], - port.sendPort, - paused: false, - debugName: debugName, - onExit: exitPort.sendPort); - - // Explicitly await spawned isolate exit to enforce main isolate not - // completing (and the stand-alone runtime exiting) before the spawned - // isolate is done. - await exitPort.first; - } - '''); - var exec = Platform.resolvedExecutable; - var args = []; - args.add("--enable-experiment=non-nullable"); - args.add(mainIsolate.path); - var result = Process.runSync(exec, args); - expect(result.stdout.contains('$expected'), true); -} - -void main() { - // Create temporary directory. - var tmpDir = Directory.systemTemp.createTempSync(); - var tmpDirPath = tmpDir.path; - - // Generate code for an isolate to run in strong mode. - File strongIsolate = new File("$tmpDirPath/strong_isolate.dart"); - strongIsolate.writeAsStringSync(''' - library SpawnUriStrongIsolate; - main(List args, replyTo) { - var data = args[0]; - try { - int x = null as int; - replyTo.send(\'re: weak\'); - } catch (ex) { - replyTo.send(\'re: strong\'); - } - } - '''); - - // Generate code for an isolate to run in weak mode. - File weakIsolate = new File("$tmpDirPath/weak_isolate.dart"); - weakIsolate.writeAsStringSync(''' - // @dart=2.7 - library SpawnUriStrongIsolate; - main(List args, replyTo) { - var data = args[0]; - try { - int x = null as int; - replyTo.send(\'re: weak\'); - } catch (ex) { - replyTo.send(\'re: strong\'); - } - } - '''); - - try { - // Strong Isolate Spawning another Strong Isolate using spawnUri. - testNullSafetyMode( - "$tmpDirPath/strong_strong.dart", strongIsolate.path, 're: strong'); - - // Strong Isolate Spawning a Weak Isolate using spawnUri. - testNullSafetyMode( - "$tmpDirPath/strong_weak.dart", weakIsolate.path, 're: weak'); - - // Weak Isolate Spawning a Strong Isolate using spawnUri. - testNullSafetyMode( - "$tmpDirPath/weak_strong.dart", strongIsolate.path, 're: strong'); - - // Weak Isolate Spawning a Weak Isolate using spawnUri. - testNullSafetyMode( - "$tmpDirPath/weak_weak.dart", weakIsolate.path, 're: weak'); - } finally { - tmpDir.deleteSync(recursive: true); - } -} diff --git a/tests/lib/lib.status b/tests/lib/lib.status index 6e52e7754f3..9b87a8d8047 100644 --- a/tests/lib/lib.status +++ b/tests/lib/lib.status @@ -79,6 +79,8 @@ isolate/count_test: Skip # Isolate.spawnUri isolate/cross_isolate_message_test: Skip # Isolate.spawnUri isolate/deferred_in_isolate2_test: Skip # Isolate.spawnUri isolate/deferred_in_isolate_test: Skip # Isolate.spawnUri +isolate/detect_nullsafety_1_test: Skip # Tests Source, Kernel, appJIT modes +isolate/detect_nullsafety_2_test: Skip # Tests Source, Kernel, appJIT modes isolate/error_at_spawnuri_test: Skip # Isolate.spawnUri isolate/error_exit_at_spawnuri_test: Skip # Isolate.spawnUri isolate/exit_at_spawnuri_test: Skip # Isolate.spawnUri @@ -95,8 +97,12 @@ isolate/message_test: Skip # Isolate.spawnUri isolate/mint_maker_test: Skip # Isolate.spawnUri isolate/nested_spawn2_test: Skip # Isolate.spawnUri isolate/nested_spawn_test: Skip # Isolate.spawnUri -isolate/nnbd_spawn_autodetect_test: Skip # Auto detect not for precompiled. -isolate/nnbd_spawnuri_autodetect_test: Skip # Auto detect not for precompiled. +isolate/nnbd_spawn_autodetect_1_test: Skip # Tests Source, Kernel, appJIT modes +isolate/nnbd_spawn_autodetect_2_test: Skip # Tests Source, Kernel, appJIT modes +isolate/nnbd_spawnuri_autodetect_1_test: Skip # Uses Isolate.spawnUri +isolate/nnbd_spawnuri_autodetect_2_test: Skip # Uses Isolate.spawnUri +isolate/nnbd_spawnuri_autodetect_3_test: Skip # Uses Isolate.spawnUri +isolate/nnbd_spawnuri_autodetect_4_test: Skip # Uses Isolate.spawnUri isolate/no_package_test: Skip # Isolate.spawnUri isolate/package_config_test: Skip # Isolate.spawnUri isolate/raw_port_test: Skip # Isolate.spawnUri