Version 2.18.0-41.0.dev Merge commit '6cea65bab7bba76b492b81a4372a5b6a4b07ea99' into 'dev'
diff --git a/DEPS b/DEPS index f68d086..db75ce7 100644 --- a/DEPS +++ b/DEPS
@@ -54,7 +54,7 @@ # Checkout Android dependencies only on Mac and Linux. "download_android_deps": - "(host_os == mac or host_os == linux) and host_cpu == x64", + "host_os == mac or (host_os == linux and host_cpu == x64)", # Checkout extra javascript engines for testing or benchmarking. # d8, the V8 shell, is always checked out. @@ -144,7 +144,7 @@ "root_certificates_rev": "692f6d6488af68e0121317a9c2c9eb393eb0ee50", "rust_revision": "b7856f695d65a8ebc846754f97d15814bcb1c244", "shelf_packages_handler_rev": "78302e67c035047e6348e692b0c1182131f0fe35", - "shelf_proxy_rev": "ccd311f64d8689e7a145d703ba267975d6df9e28", + "shelf_proxy_rev": "124615d0614b38814970aa9638725d9d6b435268", "shelf_rev": "78ac724a7944700340a3cef28c84bccbe62e9367", "shelf_static_rev": "202ec1a53c9a830c17cf3b718d089cf7eba568ad", "shelf_web_socket_rev": "24fb8a04befa75a94ac63a27047b231d1a22aab4", @@ -531,7 +531,7 @@ Var("dart_root") + "/third_party/android_tools/ndk": { "packages": [ { - "package": "flutter/android/ndk/${{platform}}", + "package": "flutter/android/ndk/${{os}}-amd64", "version": "version:r21.0.6113669" } ], @@ -542,7 +542,7 @@ Var("dart_root") + "/third_party/android_tools/sdk/build-tools": { "packages": [ { - "package": "flutter/android/sdk/build-tools/${{platform}}", + "package": "flutter/android/sdk/build-tools/${{os}}-amd64", "version": "version:30.0.1" } ], @@ -553,7 +553,7 @@ Var("dart_root") + "/third_party/android_tools/sdk/platform-tools": { "packages": [ { - "package": "flutter/android/sdk/platform-tools/${{platform}}", + "package": "flutter/android/sdk/platform-tools/${{os}}-amd64", "version": "version:29.0.2" } ], @@ -575,7 +575,7 @@ Var("dart_root") + "/third_party/android_tools/sdk/tools": { "packages": [ { - "package": "flutter/android/sdk/tools/${{platform}}", + "package": "flutter/android/sdk/tools/${{os}}-amd64", "version": "version:26.1.1" } ],
diff --git a/benchmarks/Startup/dart/Startup.dart b/benchmarks/Startup/dart/Startup.dart new file mode 100644 index 0000000..49889fb --- /dev/null +++ b/benchmarks/Startup/dart/Startup.dart
@@ -0,0 +1,85 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:convert'; +import 'dart:io'; + +import 'package:compiler/src/dart2js.dart' as dart2js; + +Future<void> main(List<String> args) async { + if (args.contains('--child')) { + return; + } + + // Include dart2js and prevent tree-shaking to make this program have a + // non-trival snapshot size. + if (args.contains('--train')) { + args.remove('--train'); + return dart2js.main(args); + } + + var tempDir; + var events; + try { + tempDir = await Directory.systemTemp.createTemp(); + final timelinePath = + tempDir.uri.resolve('Startup-timeline.json').toFilePath(); + final p = await Process.run(Platform.executable, [ + ...Platform.executableArguments, + '--timeline_recorder=file:$timelinePath', + '--timeline_streams=VM,Isolate,Embedder', + Platform.script.toFilePath(), + '--child' + ]); + if (p.exitCode != 0) { + print(p.stdout); + print(p.stderr); + throw 'Child process failed: ${p.exitCode}'; + } + + events = jsonDecode(await File(timelinePath).readAsString()); + } finally { + await tempDir.delete(recursive: true); + } + + var mainIsolateId; + for (final event in events) { + if (event['name'] == 'InitializeIsolate' && + event['args']['isolateName'] == 'main') { + mainIsolateId = event['args']['isolateId']; + } + } + if (mainIsolateId == null) { + throw 'Could not determine main isolate'; + } + + void report(String name, String isolateId) { + var filtered = events.where((event) => event['name'] == name); + if (isolateId != null) { + filtered = + filtered.where((event) => event['args']['isolateId'] == isolateId); + } + var micros; + final durations = filtered.where((event) => event['ph'] == 'X'); + final begins = filtered.where((event) => event['ph'] == 'B'); + final ends = filtered.where((event) => event['ph'] == 'E'); + if (durations.length == 1 && begins.length == 0 && ends.length == 0) { + micros = durations.single['dur']; + } else if (durations.length == 0 && + begins.length == 1 && + ends.length == 1) { + micros = ends.single['ts'] - begins.single['ts']; + } else { + print(durations.toList()); + print(begins.toList()); + print(ends.toList()); + throw '$name is missing or ambiguous'; + } + print('Startup.$name(RunTime): $micros us.'); + } + + report('CreateIsolateGroupAndSetupHelper', null); + report('InitializeIsolate', mainIsolateId); + report('ReadProgramSnapshot', mainIsolateId); +}
diff --git a/benchmarks/Startup/dart2/Startup.dart b/benchmarks/Startup/dart2/Startup.dart new file mode 100644 index 0000000..26fb07b --- /dev/null +++ b/benchmarks/Startup/dart2/Startup.dart
@@ -0,0 +1,87 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// @dart=2.9 + +import 'dart:convert'; +import 'dart:io'; + +import 'package:compiler/src/dart2js.dart' as dart2js; + +Future<void> main(List<String> args) async { + if (args.contains('--child')) { + return; + } + + // Include dart2js and prevent tree-shaking to make this program have a + // non-trival snapshot size. + if (args.contains('--train')) { + args.remove('--train'); + return dart2js.main(args); + } + + var tempDir; + var events; + try { + tempDir = await Directory.systemTemp.createTemp(); + final timelinePath = + tempDir.uri.resolve('Startup-timeline.json').toFilePath(); + final p = await Process.run(Platform.executable, [ + ...Platform.executableArguments, + '--timeline_recorder=file:$timelinePath', + '--timeline_streams=VM,Isolate,Embedder', + Platform.script.toFilePath(), + '--child' + ]); + if (p.exitCode != 0) { + print(p.stdout); + print(p.stderr); + throw 'Child process failed: ${p.exitCode}'; + } + + events = jsonDecode(await File(timelinePath).readAsString()); + } finally { + await tempDir.delete(recursive: true); + } + + var mainIsolateId; + for (final event in events) { + if (event['name'] == 'InitializeIsolate' && + event['args']['isolateName'] == 'main') { + mainIsolateId = event['args']['isolateId']; + } + } + if (mainIsolateId == null) { + throw 'Could not determine main isolate'; + } + + void report(String name, String isolateId) { + var filtered = events.where((event) => event['name'] == name); + if (isolateId != null) { + filtered = + filtered.where((event) => event['args']['isolateId'] == isolateId); + } + var micros; + final durations = filtered.where((event) => event['ph'] == 'X'); + final begins = filtered.where((event) => event['ph'] == 'B'); + final ends = filtered.where((event) => event['ph'] == 'E'); + if (durations.length == 1 && begins.length == 0 && ends.length == 0) { + micros = durations.single['dur']; + } else if (durations.length == 0 && + begins.length == 1 && + ends.length == 1) { + micros = ends.single['ts'] - begins.single['ts']; + } else { + print(durations.toList()); + print(begins.toList()); + print(ends.toList()); + throw '$name is missing or ambiguous'; + } + print('Startup.$name(RunTime): $micros us.'); + } + + report('CreateIsolateGroupAndSetupHelper', null); + report('InitializeIsolate', mainIsolateId); + report('ReadProgramSnapshot', mainIsolateId); +}
diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn index 3889bb4..afb3a74 100644 --- a/build/config/BUILDCONFIG.gn +++ b/build/config/BUILDCONFIG.gn
@@ -369,13 +369,13 @@ # default toolchain. if (is_win) { - # On windows we use the same toolchain for host and target by default. if (is_clang) { - host_toolchain = "//build/toolchain/win:clang_$current_cpu" + host_toolchain = "//build/toolchain/win:clang_$host_cpu" + set_default_toolchain("//build/toolchain/win:clang_$current_cpu") } else { - host_toolchain = "//build/toolchain/win:$current_cpu" + host_toolchain = "//build/toolchain/win:$host_cpu" + set_default_toolchain("//build/toolchain/win:$current_cpu") } - set_default_toolchain("$host_toolchain") } else if (is_android) { if (host_os == "linux") { if (host_cpu == "x86" || host_cpu == "x64") {
diff --git a/build/config/android/config.gni b/build/config/android/config.gni index c61d41a..f5def03 100644 --- a/build/config/android/config.gni +++ b/build/config/android/config.gni
@@ -26,6 +26,9 @@ # architecture, which is different than the names GN uses. if (host_cpu == "x64" || host_cpu == "x86") { android_host_arch = "x86_64" + } else if (host_cpu == "arm64") { + # Run existing Android toolchain via Rosetta. + android_host_arch = "x86_64" } else { assert(false, "Need Android toolchain support for your build CPU arch.") }
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index 6cfcf26..f2984fb 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn
@@ -67,6 +67,18 @@ "/GS", # Enable buffer security checking. "/FS", # Preserve previous PDB behavior. ] + + if (is_clang) { + if (current_cpu == "x86") { + cflags += [ "-m32" ] + } else if (current_cpu == "x64") { + cflags += [ "-m64" ] + } else if (current_cpu == "arm64") { + cflags += [ "--target=arm64-windows" ] + } else { + assert(false, "Unknown current_cpu: $current_cpu") + } + } } else { # Common GCC compiler flags setup. # --------------------------------
diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn index daf26ed..98c2e238 100644 --- a/build/config/win/BUILD.gn +++ b/build/config/win/BUILD.gn
@@ -34,14 +34,7 @@ # Linker flags for Windows SDK setup, this is applied only to EXEs and DLLs. config("sdk_link") { - if (current_cpu == "x64") { - ldflags = [ "/MACHINE:X64" ] - lib_dirs = [ - "$windows_sdk_path\Lib\winv6.3\um\x64", - "$visual_studio_path\VC\lib\amd64", - "$visual_studio_path\VC\atlmfc\lib\amd64", - ] - } else { + if (current_cpu == "x86") { ldflags = [ "/MACHINE:X86", "/SAFESEH", # Not compatible with x64 so use only for x86. @@ -54,6 +47,29 @@ if (!is_asan) { ldflags += [ "/largeaddressaware" ] } + } else if (current_cpu == "x64") { + ldflags = [ "/MACHINE:X64" ] + lib_dirs = [ + "$windows_sdk_path\Lib\winv6.3\um\x64", + "$visual_studio_path\VC\lib\amd64", + "$visual_studio_path\VC\atlmfc\lib\amd64", + ] + } else if (current_cpu == "arm") { + ldflags = [ "/MACHINE:ARM" ] + lib_dirs = [ + "$windows_sdk_path\Lib\winv6.3\um\arm", + "$visual_studio_path\VC\lib\arm", + "$visual_studio_path\VC\atlmfc\lib\arm", + ] + } else if (current_cpu == "arm64") { + ldflags = [ "/MACHINE:ARM64" ] + lib_dirs = [ + "$windows_sdk_path\Lib\winv6.3\um\arm64", + "$visual_studio_path\VC\lib\arm64", + "$visual_studio_path\VC\atlmfc\lib\arm64", + ] + } else { + assert(false, "Unknown current_cpu: $current_cpu") } } @@ -67,6 +83,7 @@ "/ignore:4221", "/ignore:4197", # Disable multiple Dart_True export warning. "/NXCOMPAT", + "/DYNAMICBASE", # Suggested by Microsoft Devrel to avoid # LINK : fatal error LNK1248: image size (80000000) @@ -74,30 +91,25 @@ # which started happening more regularly after VS2013 Update 4. "/maxilksize:2147483647", ] - - # ASLR makes debugging with windbg difficult because Chrome.exe and - # Chrome.dll share the same base name. As result, windbg will name the - # Chrome.dll module like chrome_<base address>, where <base address> - # typically changes with each launch. This in turn means that breakpoints in - # Chrome.dll don't stick from one launch to the next. For this reason, we - # turn ASLR off in debug builds. - if (is_debug) { - ldflags += [ "/DYNAMICBASE:NO" ] - } else { - ldflags += [ "/DYNAMICBASE" ] - } } # Subsystem ------------------------------------------------------------------- # This is appended to the subsystem to specify a minimum version. -if (current_cpu == "x64") { +if (current_cpu == "x86") { + # 5.01 = Windows XP. + subsystem_version_suffix = ",5.01" +} else if (current_cpu == "x64") { # The number after the comma is the minimum required OS version. # 5.02 = Windows Server 2003. subsystem_version_suffix = ",5.02" +} else if (current_cpu == "arm") { + subsystem_version_suffix = ",6.02" +} else if (current_cpu == "arm64") { + # Windows ARM64 requires Windows 10. + subsystem_version_suffix = ",10.0" } else { - # 5.01 = Windows XP. - subsystem_version_suffix = ",5.01" + assert(false, "Unknown current_cpu: $current_cpu") } config("console") {
diff --git a/build/toolchain/win/BUILD.gn b/build/toolchain/win/BUILD.gn index f7b2587..d70d49e 100644 --- a/build/toolchain/win/BUILD.gn +++ b/build/toolchain/win/BUILD.gn
@@ -3,12 +3,8 @@ # found in the LICENSE file. declare_args() { - # Path to the directory containing the VC binaries for the right - # combination of host and target architectures. Currently only the - # 64-bit host toolchain is supported, with either 32-bit or 64-bit targets. - # If vc_bin_dir is not specified on the command line (and it normally - # isn't), we will dynamically determine the right value to use at runtime. - vc_bin_dir = "" + # Path to the clang toolchain. + clang_base_path = "//buildtools/win-x64/clang" } import("//build/config/win/visual_studio_version.gni") @@ -17,53 +13,55 @@ # Should only be running on Windows. assert(is_win) +# Setup the Visual Studio state. +# +# Its arguments are the VS path and the compiler wrapper tool. It will write +# "environment.x86" and "environment.x64" to the build directory and return a +# list to us. + # This tool will is used as a wrapper for various commands below. tool_wrapper_path = rebase_path("tool_wrapper.py", root_build_dir) -# Setup the Visual Studio state. -toolchain_data = exec_script("setup_toolchain.py", - [ - visual_studio_path, - windows_sdk_path, - visual_studio_runtime_dirs, - "win", - current_cpu, - "environment." + current_cpu, - ], - "scope") - -if (vc_bin_dir == "") { - vc_bin_dir = toolchain_data.vc_bin_dir -} - if (use_goma) { goma_prefix = "$goma_dir/gomacc.exe " } else { goma_prefix = "" } -# Parameters: -# current_cpu: current_cpu to pass as a build arg -# environment: File name of environment file. -template("msvc_toolchain") { - env = invoker.environment - +if (current_toolchain == default_toolchain) { if (is_debug) { configuration = "Debug" } else { configuration = "Release" } - exec_script("../../vs_toolchain.py", + exec_script("//build/vs_toolchain.py", [ "copy_dlls", rebase_path(root_build_dir), configuration, - invoker.current_cpu, + target_cpu, ]) +} - cl = invoker.cl - +# Parameters: +# toolchain_args: Settings for the toolchain, including at least: +# current_cpu: current_cpu to pass as a build arg +# environment: File name of environment file. +template("msvc_toolchain") { toolchain(target_name) { + # When invoking this toolchain not as the default one, these args will be + # passed to the build. They are ignored when this is the default toolchain. + assert(defined(invoker.toolchain_args)) + toolchain_args = { + if (defined(invoker.toolchain_args)) { + forward_variables_from(invoker.toolchain_args, "*") + } + } + + env = invoker.environment + + cl = invoker.cl + # Make these apply to all tools below. lib_switch = "" lib_dir_switch = "/LIBPATH:" @@ -74,11 +72,7 @@ # TODO(brettw) enable this when GN support in the binary has been rolled. #precompiled_header_type = "msvc" pdbname = "{{target_out_dir}}/{{target_output_name}}_c.pdb" - flags = "" - if (invoker.current_cpu == "x86") { - flags = "-m32" - } - command = "ninja -t msvc -e $env -- $cl $flags /nologo /showIncludes /FC @$rspfile /c {{source}} /Fo{{output}} /Fd$pdbname" + command = "ninja -t msvc -e $env -- $cl /nologo /showIncludes /FC @$rspfile /c {{source}} /Fo{{output}} /Fd$pdbname" depsformat = "msvc" description = "CC {{output}}" outputs = [ @@ -96,7 +90,7 @@ # The PDB name needs to be different between C and C++ compiled files. pdbname = "{{target_out_dir}}/{{target_output_name}}_cc.pdb" flags = "" - if (invoker.current_cpu == "x86") { + if (is_clang && invoker.current_cpu == "x86") { flags = "-m32" } command = "ninja -t msvc -e $env -- $cl $flags /nologo /showIncludes /FC @$rspfile /c {{source}} /Fo{{output}} /Fd$pdbname" @@ -109,7 +103,8 @@ } tool("rc") { - command = "$python_path $tool_wrapper_path rc-wrapper $env rc.exe {{defines}} {{include_dirs}} /fo{{output}} {{source}}" + command = "$python_path $tool_wrapper_path rc-wrapper $env rc.exe /nologo {{defines}} {{include_dirs}} /fo{{output}} {{source}}" + depsformat = "msvc" outputs = [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.res", ] @@ -117,14 +112,17 @@ } tool("asm") { - if (invoker.current_cpu == "x64") { - ml = "ml64.exe" - x64 = "-D_ML64_X64" + if (toolchain_args.current_cpu == "x86") { + command = "$python_path $tool_wrapper_path asm-wrapper $env ml.exe {{defines}} {{include_dirs}} {{asmflags}} /c /Fo {{output}} {{source}}" + } else if (toolchain_args.current_cpu == "x64") { + command = "$python_path $tool_wrapper_path asm-wrapper $env ml64.exe -D_ML64_X64 {{defines}} {{include_dirs}} {{asmflags}} /c /Fo {{output}} {{source}}" + } else if (toolchain_args.current_cpu == "arm") { + command = "$python_path $tool_wrapper_path asm-wrapper $env armasm.exe {{include_dirs}} {{asmflags}} -o {{output}} {{source}}" + } else if (toolchain_args.current_cpu == "arm64") { + command = "$python_path $tool_wrapper_path asm-wrapper $env armasm64.exe {{include_dirs}} {{asmflags}} -o {{output}} {{source}}" } else { - ml = "ml.exe" - x64 = "" + assert(false, "Unknown current_cpu: ${toolchain_args.current_cpu}") } - command = "$python_path $tool_wrapper_path asm-wrapper $env $ml $x64 {{defines}} {{include_dirs}} {{asmflags}} /c /Fo {{output}} {{source}}" description = "ASM {{output}}" outputs = [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.obj", @@ -148,22 +146,17 @@ } tool("solink") { - dllname = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" # e.g. - # foo.dll - libname = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.lib" # e.g. - # foo.dll.lib + dllname = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" # e.g. foo.dll + libname = + "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.lib" # e.g. foo.dll.lib rspfile = "${dllname}.rsp" link_command = "$python_path $tool_wrapper_path link-wrapper $env False link.exe /nologo /IMPLIB:$libname /DLL /OUT:$dllname /PDB:${dllname}.pdb @$rspfile" # TODO(brettw) support manifests - #manifest_command = "$python_path gyp-win-tool manifest-wrapper $env mt.exe -nologo -manifest $manifests -out:${dllname}.manifest" + #manifest_command = "$python_path $tool_wrapper_path manifest-wrapper $env mt.exe -nologo -manifest $manifests -out:${dllname}.manifest" #command = "cmd /c $link_command && $manifest_command" - # Force rebuild of the .lib-file(if it is being produced) because msvc - # linker seems to keep it untouched sometimes even when .obj-files are - # being updated. - cleanup_lib_command = "$python_path $tool_wrapper_path delete-file $libname" - command = "cmd /c $cleanup_lib_command && cmd /c $link_command" + command = link_command default_output_extension = ".dll" description = "LINK(DLL) {{output}}" @@ -177,12 +170,12 @@ # The use of inputs_newline is to work around a fixed per-line buffer # size in the linker. rspfile_content = "{{libs}} {{solibs}} {{inputs_newline}} {{ldflags}}" + + restat = true } tool("solink_module") { - dllname = - "{{output_dir}}/{{target_output_name}}{{output_extension}}" # e.g. - # foo.dll + dllname = "{{output_dir}}/{{target_output_name}}{{output_extension}}" # e.g. foo.dll pdbname = "${dllname}.pdb" rspfile = "${dllname}.rsp" @@ -207,11 +200,12 @@ binary_output = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" rspfile = "$binary_output.rsp" + pdbfile = "$binary_output.pdb" - link_command = "$python_path $tool_wrapper_path link-wrapper $env False link.exe /nologo /OUT:$binary_output /PDB:$binary_output.pdb @$rspfile" + link_command = "$python_path $tool_wrapper_path link-wrapper $env False link.exe /nologo /OUT:$binary_output /PDB:$pdbfile @$rspfile" # TODO(brettw) support manifests - #manifest_command = "$python_path gyp-win-tool manifest-wrapper $env mt.exe -nologo -manifest $manifests -out:{{output}}.manifest" + #manifest_command = "$python_path $tool_wrapper_path manifest-wrapper $env mt.exe -nologo -manifest $manifests -out:{{output}}.manifest" #command = "cmd /c $link_command && $manifest_command" command = link_command @@ -220,6 +214,7 @@ outputs = [ binary_output, "{{root_out_dir}}/{{target_output_name}}.lib", + pdbfile, ] # The use of inputs_newline is to work around a fixed per-line buffer @@ -230,58 +225,61 @@ } tool("stamp") { - command = "$python_path $tool_wrapper_path stamp {{output}}" + command = "cmd /c type nul > \"{{output}}\"" description = "STAMP {{output}}" } tool("copy") { - command = "$python_path $tool_wrapper_path recursive-mirror {{source}} {{output}}" + command = + "$python_path $tool_wrapper_path recursive-mirror {{source}} {{output}}" description = "COPY {{source}} {{output}}" } + } +} - # When invoking this toolchain not as the default one, these args will be - # passed to the build. They are ignored when this is the default toolchain. +template("win_toolchains") { + assert(defined(invoker.toolchain_arch)) + toolchain_arch = invoker.toolchain_arch + + win_toolchain_data = exec_script("setup_toolchain.py", + [ + visual_studio_path, + windows_sdk_path, + visual_studio_runtime_dirs, + toolchain_arch, + ], + "scope") + + msvc_toolchain(target_name) { + environment = "environment." + toolchain_arch + cl = "${goma_prefix}\"${win_toolchain_data.vc_bin_dir}/cl.exe\"" toolchain_args = { - current_cpu = invoker.current_cpu - if (defined(invoker.is_clang)) { - is_clang = invoker.is_clang + if (defined(invoker.toolchain_args)) { + forward_variables_from(invoker.toolchain_args, "*") } + current_cpu = toolchain_arch + is_clang = false + } + } + msvc_toolchain("clang_" + target_name) { + environment = "environment." + toolchain_arch + prefix = rebase_path("$clang_base_path/bin", root_build_dir) + cl = "${goma_prefix}$prefix/clang-cl.exe" + toolchain_args = { + if (defined(invoker.toolchain_args)) { + forward_variables_from(invoker.toolchain_args, "*") + } + current_cpu = toolchain_arch + is_clang = true } } } -# TODO(dpranke): Declare both toolchains all of the time when we -# get it sorted out how we want to support them both in a single build. -# Right now only one of these can be enabled at a time because the -# runtime libraries get copied to root_build_dir and would collide. -if (current_cpu == "x86") { - msvc_toolchain("x86") { - environment = "environment.x86" - current_cpu = "x86" - cl = "${goma_prefix}\"${vc_bin_dir}/cl.exe\"" - is_clang = false - } - msvc_toolchain("clang_x86") { - environment = "environment.x86" - current_cpu = "x86" - prefix = rebase_path("//buildtools/win-x64/clang/bin", root_build_dir) - cl = "${goma_prefix}$prefix/clang-cl.exe" - is_clang = true - } +win_toolchains(target_cpu) { + toolchain_arch = target_cpu } - -if (current_cpu == "x64") { - msvc_toolchain("x64") { - environment = "environment.x64" - current_cpu = "x64" - cl = "${goma_prefix}\"${vc_bin_dir}/cl.exe\"" - is_clang = false - } - msvc_toolchain("clang_x64") { - environment = "environment.x64" - current_cpu = "x64" - prefix = rebase_path("//buildtools/win-x64/clang/bin", root_build_dir) - cl = "${goma_prefix}$prefix/clang-cl.exe" - is_clang = true +if (host_cpu != target_cpu) { + win_toolchains(host_cpu) { + toolchain_arch = host_cpu } }
diff --git a/build/toolchain/win/setup_toolchain.py b/build/toolchain/win/setup_toolchain.py index ee3d8ee..d1d4fb5 100644 --- a/build/toolchain/win/setup_toolchain.py +++ b/build/toolchain/win/setup_toolchain.py
@@ -10,319 +10,141 @@ # win tool. The script assumes that the root build directory is the current dir # and the files will be written to the current directory. -from __future__ import print_function - -import json +import errno import os import re import subprocess import sys -sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)) -import gn_helpers - -SCRIPT_DIR = os.path.dirname(__file__) def _ExtractImportantEnvironment(output_of_set): - """Extracts environment variables required for the toolchain to run from + """Extracts environment variables required for the toolchain to run from a textual dump output by the cmd.exe 'set' command.""" - envvars_to_save = ( - 'cipd_cache_dir', # needed by vpython - 'homedrive', # needed by vpython - 'homepath', # needed by vpython - 'goma_.*', # TODO(scottmg): This is ugly, but needed for goma. - 'include', - 'lib', - 'libpath', - 'luci_context', # needed by vpython - 'path', - 'pathext', - 'systemroot', - 'temp', - 'tmp', - 'userprofile', # needed by vpython - 'vpython_virtualenv_root' # needed by vpython - ) - env = {} - # This occasionally happens and leads to misleading SYSTEMROOT error messages - # if not caught here. - if output_of_set.count('=') == 0: - raise Exception('Invalid output_of_set. Value is:\n%s' % output_of_set) - for line in output_of_set.splitlines(): - for envvar in envvars_to_save: - if re.match(envvar + '=', line.lower()): - var, setting = line.split('=', 1) - if envvar == 'path': - # Our own rules and actions in Chromium rely on python being in the - # path. Add the path to this python here so that if it's not in the - # path when ninja is run later, python will still be found. - setting = os.path.dirname( - sys.executable) + os.pathsep + setting - env[var.upper()] = setting - break - if sys.platform in ('win32', 'cygwin'): - for required in ('SYSTEMROOT', 'TEMP', 'TMP'): - if required not in env: - raise Exception('Environment variable "%s" ' - 'required to be set to valid path' % required) - return env + envvars_to_save = ( + 'goma_.*', # TODO(scottmg): This is ugly, but needed for goma. + 'include', + 'lib', + 'libpath', + 'path', + 'pathext', + 'systemroot', + 'temp', + 'tmp', + ) + env = {} + for line in output_of_set.splitlines(): + for envvar in envvars_to_save: + if re.match(envvar + '=', line.decode().lower()): + var, setting = line.decode().split('=', 1) + if envvar == 'path': + # Our own rules (for running gyp-win-tool) and other actions in + # Chromium rely on python being in the path. Add the path to this + # python here so that if it's not in the path when ninja is run + # later, python will still be found. + setting = os.path.dirname(sys.executable) + os.pathsep + setting + env[var.upper()] = setting + break + for required in ('SYSTEMROOT', 'TEMP', 'TMP'): + if required not in env: + raise Exception('Environment variable "%s" ' + 'required to be set to valid path' % required) + return env -def _DetectVisualStudioPath(): - """Return path to the installed Visual Studio. - """ - - # Use the code in build/vs_toolchain.py to avoid duplicating code. - chromium_dir = os.path.abspath(os.path.join(SCRIPT_DIR, '..', '..', '..')) - sys.path.append(os.path.join(chromium_dir, 'build')) - import vs_toolchain - return vs_toolchain.DetectVisualStudioPath() - - -def _LoadEnvFromBat(args): - """Given a bat command, runs it and returns env vars set by it.""" - args = args[:] - args.extend(('&&', 'set')) - popen = subprocess.Popen(args, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - variables, _ = popen.communicate() - if popen.returncode != 0: - raise Exception('"%s" failed with error %d' % (args, popen.returncode)) - return variables.decode(errors='ignore') - - -def _LoadToolchainEnv(cpu, toolchain_root, sdk_dir, target_store): - """Returns a dictionary with environment variables that must be set while - running binaries from the toolchain (e.g. INCLUDE and PATH for cl.exe).""" - # Check if we are running in the SDK command line environment and use - # the setup script from the SDK if so. |cpu| should be either - # 'x86' or 'x64' or 'arm' or 'arm64'. - assert cpu in ('x86', 'x64', 'arm', 'arm64') - if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', 1))) and sdk_dir: - # Load environment from json file. - env = os.path.normpath(os.path.join(sdk_dir, - 'bin/SetEnv.%s.json' % cpu)) - env = json.load(open(env))['env'] - if env['VSINSTALLDIR'] == [["..", "..\\"]]: - # Old-style paths were relative to the win_sdk\bin directory. - json_relative_dir = os.path.join(sdk_dir, 'bin') - else: - # New-style paths are relative to the toolchain directory. - json_relative_dir = toolchain_root - for k in env: - entries = [os.path.join(*([json_relative_dir] + e)) for e in env[k]] - # clang-cl wants INCLUDE to be ;-separated even on non-Windows, - # lld-link wants LIB to be ;-separated even on non-Windows. Path gets :. - # The separator for INCLUDE here must match the one used in main() below. - sep = os.pathsep if k == 'PATH' else ';' - env[k] = sep.join(entries) - # PATH is a bit of a special case, it's in addition to the current PATH. - env['PATH'] = env['PATH'] + os.pathsep + os.environ['PATH'] - # Augment with the current env to pick up TEMP and friends. - for k in os.environ: - if k not in env: - env[k] = os.environ[k] - - varlines = [] - for k in sorted(env.keys()): - varlines.append('%s=%s' % (str(k), str(env[k]))) - variables = '\n'.join(varlines) - - # Check that the json file contained the same environment as the .cmd file. - if sys.platform in ('win32', 'cygwin'): - script = os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.cmd')) - arg = '/' + cpu - json_env = _ExtractImportantEnvironment(variables) - cmd_env = _ExtractImportantEnvironment( - _LoadEnvFromBat([script, arg])) - assert _LowercaseDict(json_env) == _LowercaseDict(cmd_env) - else: - if 'GYP_MSVS_OVERRIDE_PATH' not in os.environ: - os.environ['GYP_MSVS_OVERRIDE_PATH'] = _DetectVisualStudioPath() - # We only support x64-hosted tools. - script_path = os.path.normpath( - os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'], - 'VC/vcvarsall.bat')) - if not os.path.exists(script_path): - # vcvarsall.bat for VS 2017 fails if run after running vcvarsall.bat from - # VS 2013 or VS 2015. Fix this by clearing the vsinstalldir environment - # variable. Since vcvarsall.bat appends to the INCLUDE, LIB, and LIBPATH - # environment variables we need to clear those to avoid getting double - # entries when vcvarsall.bat has been run before gn gen. vcvarsall.bat - # also adds to PATH, but there is no clean way of clearing that and it - # doesn't seem to cause problems. - if 'VSINSTALLDIR' in os.environ: - del os.environ['VSINSTALLDIR'] - del os.environ['INCLUDE'] - del os.environ['LIB'] - del os.environ['LIBPATH'] - other_path = os.path.normpath( - os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'], - 'VC/Auxiliary/Build/vcvarsall.bat')) - if not os.path.exists(other_path): - raise Exception( - '%s is missing - make sure VC++ tools are installed.' % - script_path) - script_path = other_path - cpu_arg = "amd64" - if (cpu != 'x64'): - # x64 is default target CPU thus any other CPU requires a target set - cpu_arg += '_' + cpu - args = [ - script_path, - cpu_arg, - ] - # Store target must come before any SDK version declaration - if (target_store): - args.append('store') - # Explicitly specifying the SDK version to build with to avoid accidentally - # building with a new and untested SDK. This should stay in sync with the - # packaged toolchain in build/vs_toolchain.py. - args.append('10.0.19041.0') - variables = _LoadEnvFromBat(args) - return _ExtractImportantEnvironment(variables) +def _SetupScript(target_cpu, sdk_dir): + """Returns a command (with arguments) to be used to set up the + environment.""" + # Check if we are running in the SDK command line environment and use + # the setup script from the SDK if so. + assert target_cpu in ('x86', 'x64', 'arm64') + if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', 1))) and sdk_dir: + return [os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.Cmd')), + '/' + target_cpu] + else: + # We only support x64-hosted tools. + # TODO(scottmg|dpranke): Non-depot_tools toolchain: need to get Visual + # Studio install location from registry. + return [os.path.normpath(os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'], + 'VC/Auxiliary/Build/vcvarsall.bat')), + 'amd64_x86' if target_cpu == 'x86' else 'amd64'] def _FormatAsEnvironmentBlock(envvar_dict): - """Format as an 'environment block' directly suitable for CreateProcess. + """Format as an 'environment block' directly suitable for CreateProcess. Briefly this is a list of key=value\0, terminated by an additional \0. See CreateProcess documentation for more details.""" - block = '' - nul = '\0' - for key, value in envvar_dict.items(): - block += key + '=' + value + nul - block += nul - return block + block = '' + nul = '\0' + for key, value in envvar_dict.items(): + block += key + '=' + value + nul + block += nul + return block -def _LowercaseDict(d): - """Returns a copy of `d` with both key and values lowercased. +def _CopyTool(source_path): + """Copies the given tool to the current directory, including a warning not + to edit it.""" + with open(source_path) as source_file: + tool_source = source_file.readlines() - Args: - d: dict to lowercase (e.g. {'A': 'BcD'}). - - Returns: - A dict with both keys and values lowercased (e.g.: {'a': 'bcd'}). - """ - return {k.lower(): d[k].lower() for k in d} - - -def FindFileInEnvList(env, env_name, separator, file_name, optional=False): - parts = env[env_name].split(separator) - for path in parts: - if os.path.exists(os.path.join(path, file_name)): - return os.path.realpath(path) - assert optional, "%s is not found in %s:\n%s\nCheck if it is installed." % ( - file_name, env_name, '\n'.join(parts)) - return '' + # Add header and write it out to the current directory (which should be the + # root build dir). + with open("gyp-win-tool", 'w') as tool_file: + tool_file.write(''.join([tool_source[0], + '# Generated by setup_toolchain.py do not edit.\n'] + + tool_source[1:])) def main(): - if len(sys.argv) != 7: - print('Usage setup_toolchain.py ' - '<visual studio path> <win sdk path> ' - '<runtime dirs> <target_os> <target_cpu> ' - '<environment block name|none>') - sys.exit(2) - # toolchain_root and win_sdk_path are only read if the hermetic Windows - # toolchain is set, that is if DEPOT_TOOLS_WIN_TOOLCHAIN is not set to 0. - # With the hermetic Windows toolchain, the visual studio path in argv[1] - # is the root of the Windows toolchain directory. - toolchain_root = sys.argv[1] - win_sdk_path = sys.argv[2] + if len(sys.argv) != 5: + print('Usage setup_toolchain.py ' + '<visual studio path> <win sdk path> ' + '<runtime dirs> <target_cpu>') + sys.exit(2) + win_sdk_path = sys.argv[2] + runtime_dirs = sys.argv[3] + target_cpu = sys.argv[4] - runtime_dirs = sys.argv[3] - target_os = sys.argv[4] - target_cpu = sys.argv[5] - environment_block_name = sys.argv[6] - if (environment_block_name == 'none'): - environment_block_name = '' + cpus = ('x86', 'x64', 'arm64') + assert target_cpu in cpus + vc_bin_dir = '' - if (target_os == 'winuwp'): - target_store = True - else: - target_store = False + # TODO(scottmg|goma): Do we need an equivalent of + # ninja_use_custom_environment_files? - cpus = ('x86', 'x64', 'arm', 'arm64') - assert target_cpu in cpus - vc_bin_dir = '' - vc_lib_path = '' - vc_lib_atlmfc_path = '' - vc_lib_um_path = '' - include = '' - lib = '' + for cpu in cpus: + # Extract environment variables for subprocesses. + args = _SetupScript(cpu, win_sdk_path) + args.extend(('&&', 'set')) + popen = subprocess.Popen( + args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + variables, _ = popen.communicate() + env = _ExtractImportantEnvironment(variables) + env['PATH'] = runtime_dirs + ';' + env['PATH'] - # TODO(scottmg|goma): Do we need an equivalent of - # ninja_use_custom_environment_files? + if cpu == target_cpu: + for path in env['PATH'].split(os.pathsep): + if os.path.exists(os.path.join(path, 'cl.exe')): + vc_bin_dir = os.path.realpath(path) + break - def relflag( - s): # Make s relative to builddir when cwd and sdk on same drive. - try: - return os.path.relpath(s) - except ValueError: - return s + # The Windows SDK include directories must be first. They both have a sal.h, + # and the SDK one is newer and the SDK uses some newer features from it not + # present in the Visual Studio one. - def q(s): # Quote s if it contains spaces or other weird characters. - return s if re.match(r'^[a-zA-Z0-9._/\\:-]*$', s) else '"' + s + '"' + if win_sdk_path: + additional_includes = ('{sdk_dir}\\Include\\shared;' + + '{sdk_dir}\\Include\\um;' + + '{sdk_dir}\\Include\\winrt;').format( + sdk_dir=win_sdk_path) + env['INCLUDE'] = additional_includes + env['INCLUDE'] + env_block = _FormatAsEnvironmentBlock(env) + with open('environment.' + cpu, 'wb') as f: + f.write(env_block.encode()) - for cpu in cpus: - if cpu == target_cpu: - # Extract environment variables for subprocesses. - env = _LoadToolchainEnv(cpu, toolchain_root, win_sdk_path, - target_store) - env['PATH'] = runtime_dirs + os.pathsep + env['PATH'] - - vc_bin_dir = FindFileInEnvList(env, 'PATH', os.pathsep, 'cl.exe') - vc_lib_path = FindFileInEnvList(env, 'LIB', ';', 'msvcrt.lib') - vc_lib_atlmfc_path = FindFileInEnvList(env, - 'LIB', - ';', - 'atls.lib', - optional=True) - vc_lib_um_path = FindFileInEnvList(env, 'LIB', ';', 'user32.lib') - - # The separator for INCLUDE here must match the one used in - # _LoadToolchainEnv() above. - include = [ - p.replace('"', r'\"') for p in env['INCLUDE'].split(';') if p - ] - include = list(map(relflag, include)) - - lib = [p.replace('"', r'\"') for p in env['LIB'].split(';') if p] - lib = list(map(relflag, lib)) - - include_I = ' '.join([q('/I' + i) for i in include]) - include_imsvc = ' '.join([q('-imsvc' + i) for i in include]) - libpath_flags = ' '.join([q('-libpath:' + i) for i in lib]) - - if (environment_block_name != ''): - env_block = _FormatAsEnvironmentBlock(env) - with open(environment_block_name, 'w') as f: - f.write(env_block) - - print('vc_bin_dir = ' + gn_helpers.ToGNString(vc_bin_dir)) - assert include_I - print('include_flags_I = ' + gn_helpers.ToGNString(include_I)) - assert include_imsvc - if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', - 1))) and win_sdk_path: - print('include_flags_imsvc = ' + - gn_helpers.ToGNString(q('/winsysroot' + relflag(toolchain_root)))) - else: - print('include_flags_imsvc = ' + gn_helpers.ToGNString(include_imsvc)) - print('vc_lib_path = ' + gn_helpers.ToGNString(vc_lib_path)) - # Possible atlmfc library path gets introduced in the future for store thus - # output result if a result exists. - if (vc_lib_atlmfc_path != ''): - print('vc_lib_atlmfc_path = ' + - gn_helpers.ToGNString(vc_lib_atlmfc_path)) - print('vc_lib_um_path = ' + gn_helpers.ToGNString(vc_lib_um_path)) - print('paths = ' + gn_helpers.ToGNString(env['PATH'])) - assert libpath_flags - print('libpath_flags = ' + gn_helpers.ToGNString(libpath_flags)) + assert vc_bin_dir + print('vc_bin_dir = "%s"' % vc_bin_dir) if __name__ == '__main__': - main() + main()
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py index 5ac59ba..6314e5e 100644 --- a/build/vs_toolchain.py +++ b/build/vs_toolchain.py
@@ -1,17 +1,18 @@ #!/usr/bin/env python3 +# # Copyright 2014 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# For Dart developers: +# For Dart/Flutter developers: # This file keeps the MSVC toolchain up-to-date for Google developers. # It is copied from Chromium: # https://cs.chromium.org/chromium/src/build/vs_toolchain.py -# with modifications that update paths and remove dependencies on gyp. +# with modifications that update paths, and remove dependencies on gyp. # To update to a new MSVC toolchain, copy the updated script from the Chromium -# tree, edit to make it work in the Dart tree by updating paths in the original script. +# tree, and edit to make it work in the Dart tree by updating paths in the original script. -from __future__ import print_function + import collections import glob @@ -25,49 +26,24 @@ import subprocess import sys + from gn_helpers import ToGNString -# VS 2019 16.61 with 10.0.19041 SDK, and 10.0.17134 version of -# d3dcompiler_47.dll, with ARM64 libraries and UWP support. -# See go/chromium-msvc-toolchain for instructions about how to update the -# toolchain. -# -# When updating the toolchain, consider the following areas impacted by the -# toolchain version: -# -# * //base/win/windows_version.cc NTDDI preprocessor check -# Triggers a compiler error if the available SDK is older than the minimum. -# * //build/config/win/BUILD.gn NTDDI_VERSION value -# Affects the availability of APIs in the toolchain headers. -# * //docs/windows_build_instructions.md mentions of VS or Windows SDK. -# Keeps the document consistent with the toolchain version. -TOOLCHAIN_HASH = '20d5f2553f' - script_dir = os.path.dirname(os.path.realpath(__file__)) -dart_src = os.path.abspath(os.path.join(script_dir, os.pardir)) -sys.path.insert(0, os.path.join(dart_src, 'tools')) json_data_file = os.path.join(script_dir, 'win_toolchain.json') +sys.path.insert(0, os.path.join(script_dir, '..', 'tools')) + # VS versions are listed in descending order of priority (highest first). MSVS_VERSIONS = collections.OrderedDict([ - ('2019', '16.0'), - ('2017', '15.0'), + ('2017', '15.0'), + ('2019', '16.0'), + ('2022', '17.0'), ]) -# List of preferred VC toolset version based on MSVS -MSVC_TOOLSET_VERSION = { - '2019': 'VC142', - '2017': 'VC141', -} - - -def _HostIsWindows(): - """Returns True if running on a Windows host (including under cygwin).""" - return sys.platform in ('win32', 'cygwin') - def SetEnvironmentAndGetRuntimeDllDirs(): - """Sets up os.environ to use the depot_tools VS toolchain with gyp, and + """Sets up os.environ to use the depot_tools VS toolchain with gyp, and returns the location of the VC runtime DLLs so they can be copied into the output directory after gyp generation. @@ -75,70 +51,73 @@ generated separately because there are multiple folders for the arm64 VC runtime. """ - vs_runtime_dll_dirs = None - depot_tools_win_toolchain = \ - bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))) - # When running on a non-Windows host, only do this if the SDK has explicitly - # been downloaded before (in which case json_data_file will exist). - if ((_HostIsWindows() or os.path.exists(json_data_file)) and - depot_tools_win_toolchain): - if ShouldUpdateToolchain(): - if len(sys.argv) > 1 and sys.argv[1] == 'update': - update_result = Update() - else: - update_result = Update(no_download=True) - if update_result != 0: - raise Exception('Failed to update, error code %d.' % - update_result) - with open(json_data_file, 'r') as tempf: - toolchain_data = json.load(tempf) + vs_runtime_dll_dirs = None + depot_tools_win_toolchain = \ + bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))) + # When running on a non-Windows host, only do this if the SDK has explicitly + # been downloaded before (in which case json_data_file will exist). + if ((sys.platform in ('win32', 'cygwin') or os.path.exists(json_data_file)) + and depot_tools_win_toolchain): + if ShouldUpdateToolchain(): + if len(sys.argv) > 1 and sys.argv[1] == 'update': + update_result = Update() + else: + update_result = Update(no_download=True) + if update_result != 0: + raise Exception('Failed to update, error code %d.' % update_result) + with open(json_data_file, 'r') as tempf: + toolchain_data = json.load(tempf) - toolchain = toolchain_data['path'] - version = toolchain_data['version'] - win_sdk = toolchain_data.get('win_sdk') - wdk = toolchain_data['wdk'] - # TODO(scottmg): The order unfortunately matters in these. They should be - # split into separate keys for x64/x86/arm64. (See CopyDlls call below). - # http://crbug.com/345992 - vs_runtime_dll_dirs = toolchain_data['runtime_dirs'] - # The number of runtime_dirs in the toolchain_data was two (x64/x86) but - # changed to three (x64/x86/arm64) and this code needs to handle both - # possibilities, which can change independently from this code. - if len(vs_runtime_dll_dirs) == 2: - vs_runtime_dll_dirs.append('Arm64Unused') + toolchain = toolchain_data['path'] + version = toolchain_data['version'] + win_sdk = toolchain_data.get('win_sdk') + if not win_sdk: + win_sdk = toolchain_data['win8sdk'] + wdk = toolchain_data['wdk'] + # TODO(scottmg): The order unfortunately matters in these. They should be + # split into separate keys for x64/x86/arm64. (See CopyDlls call below). + # http://crbug.com/345992 + vs_runtime_dll_dirs = toolchain_data['runtime_dirs'] + # The number of runtime_dirs in the toolchain_data was two (x64/x86) but + # changed to three (x64/x86/arm64) and this code needs to handle both + # possibilities, which can change independently from this code. + if len(vs_runtime_dll_dirs) == 2: + vs_runtime_dll_dirs.append('Arm64Unused') - os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain + os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain + os.environ['GYP_MSVS_VERSION'] = version - os.environ['WINDOWSSDKDIR'] = win_sdk - os.environ['WDK_DIR'] = wdk - # Include the VS runtime in the PATH in case it's not machine-installed. - runtime_path = os.path.pathsep.join(vs_runtime_dll_dirs) - os.environ['PATH'] = runtime_path + os.path.pathsep + os.environ['PATH'] - elif sys.platform == 'win32' and not depot_tools_win_toolchain: - if not 'GYP_MSVS_OVERRIDE_PATH' in os.environ: - os.environ['GYP_MSVS_OVERRIDE_PATH'] = DetectVisualStudioPath() + os.environ['WINDOWSSDKDIR'] = win_sdk + os.environ['WDK_DIR'] = wdk + # Include the VS runtime in the PATH in case it's not machine-installed. + runtime_path = os.path.pathsep.join(vs_runtime_dll_dirs) + os.environ['PATH'] = runtime_path + os.path.pathsep + os.environ['PATH'] + elif sys.platform == 'win32' and not depot_tools_win_toolchain: + if not 'GYP_MSVS_OVERRIDE_PATH' in os.environ: + os.environ['GYP_MSVS_OVERRIDE_PATH'] = DetectVisualStudioPath() + if not 'GYP_MSVS_VERSION' in os.environ: + os.environ['GYP_MSVS_VERSION'] = GetVisualStudioVersion() - # When using an installed toolchain these files aren't needed in the output - # directory in order to run binaries locally, but they are needed in order - # to create isolates or the mini_installer. Copying them to the output - # directory ensures that they are available when needed. - bitness = platform.architecture()[0] - # When running 64-bit python the x64 DLLs will be in System32 - # ARM64 binaries will not be available in the system directories because we - # don't build on ARM64 machines. - x64_path = 'System32' if bitness == '64bit' else 'Sysnative' - x64_path = os.path.join(os.path.expandvars('%windir%'), x64_path) - vs_runtime_dll_dirs = [ - x64_path, - os.path.join(os.path.expandvars('%windir%'), 'SysWOW64'), - 'Arm64Unused' - ] + # When using an installed toolchain these files aren't needed in the output + # directory in order to run binaries locally, but they are needed in order + # to create isolates or the mini_installer. Copying them to the output + # directory ensures that they are available when needed. + bitness = platform.architecture()[0] + # When running 64-bit python the x64 DLLs will be in System32 + # ARM64 binaries will not be available in the system directories because we + # don't build on ARM64 machines. + x64_path = 'System32' if bitness == '64bit' else 'Sysnative' + x64_path = os.path.join(os.path.expandvars('%windir%'), x64_path) + vs_runtime_dll_dirs = [x64_path, + os.path.join(os.path.expandvars('%windir%'), + 'SysWOW64'), + 'Arm64Unused'] - return vs_runtime_dll_dirs + return vs_runtime_dll_dirs def _RegistryGetValueUsingWinReg(key, value): - """Use the _winreg module to obtain the value of a registry key. + """Use the _winreg module to obtain the value of a registry key. Args: key: The registry key. @@ -147,249 +126,228 @@ contents of the registry key's value, or None on failure. Throws ImportError if _winreg is unavailable. """ - import _winreg - try: - root, subkey = key.split('\\', 1) - assert root == 'HKLM' # Only need HKLM for now. - with _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, subkey) as hkey: - return _winreg.QueryValueEx(hkey, value)[0] - except WindowsError: - return None + import winreg + try: + root, subkey = key.split('\\', 1) + assert root == 'HKLM' # Only need HKLM for now. + with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, subkey) as hkey: + return winreg.QueryValueEx(hkey, value)[0] + except WindowsError: + return None def _RegistryGetValue(key, value): - try: - return _RegistryGetValueUsingWinReg(key, value) - except ImportError: - raise Exception('The python library _winreg not found.') + try: + return _RegistryGetValueUsingWinReg(key, value) + except ImportError: + raise Exception('The python library _winreg not found.') def GetVisualStudioVersion(): - """Return best available version of Visual Studio. + """Return best available version of Visual Studio. """ - supported_versions = list(MSVS_VERSIONS.keys()) - # VS installed in depot_tools for Googlers - if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))): - return supported_versions[0] + env_version = os.environ.get('GYP_MSVS_VERSION') + if env_version: + return env_version - # VS installed in system for external developers - supported_versions_str = ', '.join( - '{} ({})'.format(v, k) for k, v in MSVS_VERSIONS.items()) - available_versions = [] - for version in supported_versions: - # Checking vs%s_install environment variables. - # For example, vs2019_install could have the value - # "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community". - # Only vs2017_install and vs2019_install are supported. - path = os.environ.get('vs%s_install' % version) - if path and os.path.exists(path): - available_versions.append(version) - break - # Detecting VS under possible paths. - path = os.path.expandvars('%ProgramFiles(x86)%' + - '/Microsoft Visual Studio/%s' % version) - if path and any( - os.path.exists(os.path.join(path, edition)) - for edition in ('Enterprise', 'Professional', 'Community', - 'Preview', 'BuildTools')): - available_versions.append(version) - break + supported_versions = list(MSVS_VERSIONS.keys()) - if not available_versions: - raise Exception('No supported Visual Studio can be found.' - ' Supported versions are: %s.' % supported_versions_str) - return available_versions[0] + # VS installed in depot_tools for Googlers + if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))): + return list(supported_versions)[0] + + # VS installed in system for external developers + supported_versions_str = ', '.join('{} ({})'.format(v,k) + for k,v in list(MSVS_VERSIONS.items())) + available_versions = [] + for version in supported_versions: + for path in ( + os.environ.get('vs%s_install' % version), + os.path.expandvars('%ProgramFiles(x86)%' + + '/Microsoft Visual Studio/%s' % version), + os.path.expandvars('%ProgramFiles%' + + '/Microsoft Visual Studio/%s' % version)): + if path and os.path.exists(path): + available_versions.append(version) + break + + if not available_versions: + raise Exception('No supported Visual Studio can be found.' + ' Supported versions are: %s.' % supported_versions_str) + return available_versions[0] def DetectVisualStudioPath(): - """Return path to the installed Visual Studio. + """Return path to the GYP_MSVS_VERSION of Visual Studio. """ - # Note that this code is used from - # build/toolchain/win/setup_toolchain.py as well. - version_as_year = GetVisualStudioVersion() + # Note that this code is used from + # build/toolchain/win/setup_toolchain.py as well. + version_as_year = GetVisualStudioVersion() - # The VC++ >=2017 install location needs to be located using COM instead of - # the registry. For details see: - # https://blogs.msdn.microsoft.com/heaths/2016/09/15/changes-to-visual-studio-15-setup/ - # For now we use a hardcoded default with an environment variable override. - for path in (os.environ.get('vs%s_install' % version_as_year), - os.path.expandvars('%ProgramFiles(x86)%' + - '/Microsoft Visual Studio/%s/Enterprise' % - version_as_year), - os.path.expandvars('%ProgramFiles(x86)%' + - '/Microsoft Visual Studio/%s/Professional' % - version_as_year), - os.path.expandvars('%ProgramFiles(x86)%' + - '/Microsoft Visual Studio/%s/Community' % - version_as_year), - os.path.expandvars('%ProgramFiles(x86)%' + - '/Microsoft Visual Studio/%s/Preview' % - version_as_year), - os.path.expandvars('%ProgramFiles(x86)%' + - '/Microsoft Visual Studio/%s/BuildTools' % - version_as_year)): - if path and os.path.exists(path): - return path + # The VC++ >=2017 install location needs to be located using COM instead of + # the registry. For details see: + # https://blogs.msdn.microsoft.com/heaths/2016/09/15/changes-to-visual-studio-15-setup/ + # For now we use a hardcoded default with an environment variable override. + possible_install_paths = ( + os.path.expandvars('%s/Microsoft Visual Studio/%s/%s' % + (program_path_var, version_as_year, product)) + for program_path_var in ('%ProgramFiles%', '%ProgramFiles(x86)%') + for product in ('Enterprise', 'Professional', 'Community', 'Preview')) + for path in ( + os.environ.get('vs%s_install' % version_as_year), *possible_install_paths): + if path and os.path.exists(path): + return path - raise Exception('Visual Studio Version %s not found.' % version_as_year) + raise Exception('Visual Studio Version %s (from GYP_MSVS_VERSION)' + ' not found.' % version_as_year) def _CopyRuntimeImpl(target, source, verbose=True): - """Copy |source| to |target| if it doesn't already exist or if it needs to be + """Copy |source| to |target| if it doesn't already exist or if it needs to be updated (comparing last modified time as an approximate float match as for some reason the values tend to differ by ~1e-07 despite being copies of the same file... https://crbug.com/603603). """ - if (os.path.isdir(os.path.dirname(target)) and - (not os.path.isfile(target) or - abs(os.stat(target).st_mtime - os.stat(source).st_mtime) >= 0.01)): - if verbose: - print('Copying %s to %s...' % (source, target)) - if os.path.exists(target): - # Make the file writable so that we can delete it now, and keep it - # readable. - os.chmod(target, stat.S_IWRITE | stat.S_IREAD) - os.unlink(target) - shutil.copy2(source, target) - # Make the file writable so that we can overwrite or delete it later, - # keep it readable. - os.chmod(target, stat.S_IWRITE | stat.S_IREAD) - + if (os.path.isdir(os.path.dirname(target)) and + (not os.path.isfile(target) or + abs(os.stat(target).st_mtime - os.stat(source).st_mtime) >= 0.01)): + if verbose: + print('Copying %s to %s...' % (source, target)) + if os.path.exists(target): + # Make the file writable so that we can delete it now, and keep it + # readable. + os.chmod(target, stat.S_IWRITE | stat.S_IREAD) + os.unlink(target) + shutil.copy2(source, target) + # Make the file writable so that we can overwrite or delete it later, + # keep it readable. + os.chmod(target, stat.S_IWRITE | stat.S_IREAD) def _SortByHighestVersionNumberFirst(list_of_str_versions): - """This sorts |list_of_str_versions| according to version number rules + """This sorts |list_of_str_versions| according to version number rules so that version "1.12" is higher than version "1.9". Does not work with non-numeric versions like 1.4.a8 which will be higher than 1.4.a12. It does handle the versions being embedded in file paths. """ + def to_int_if_int(x): + try: + return int(x) + except ValueError: + return x - def to_int_if_int(x): - try: - return int(x) - except ValueError: - return x + def to_number_sequence(x): + part_sequence = re.split(r'[\\/\.]', x) + return [to_int_if_int(x) for x in part_sequence] - def to_number_sequence(x): - part_sequence = re.split(r'[\\/\.]', x) - return [to_int_if_int(x) for x in part_sequence] + list_of_str_versions.sort(key=to_number_sequence, reverse=True) - list_of_str_versions.sort(key=to_number_sequence, reverse=True) - - -def _CopyUCRTRuntime(target_dir, source_dir, target_cpu, suffix): - """Copy both the msvcp and vccorlib runtime DLLs, only if the target doesn't +def _CopyUCRTRuntime(target_dir, source_dir, target_cpu, dll_pattern, suffix): + """Copy both the msvcp and vccorlib runtime DLLs, only if the target doesn't exist, but the target directory does exist.""" - if target_cpu == 'arm64': - # Windows ARM64 VCRuntime is located at {toolchain_root}/VC/Redist/MSVC/ - # {x.y.z}/[debug_nonredist/]arm64/Microsoft.VC14x.CRT/. - # Select VC toolset directory based on Visual Studio version - vc_redist_root = FindVCRedistRoot() - if suffix.startswith('.'): - vc_toolset_dir = 'Microsoft.{}.CRT' \ - .format(MSVC_TOOLSET_VERSION[GetVisualStudioVersion()]) - source_dir = os.path.join(vc_redist_root, 'arm64', vc_toolset_dir) - else: - vc_toolset_dir = 'Microsoft.{}.DebugCRT' \ - .format(MSVC_TOOLSET_VERSION[GetVisualStudioVersion()]) - source_dir = os.path.join(vc_redist_root, 'debug_nonredist', - 'arm64', vc_toolset_dir) - file_parts = ('msvcp140', 'vccorlib140', 'vcruntime140') - if target_cpu == 'x64' and GetVisualStudioVersion() != '2017': - file_parts = file_parts + ('vcruntime140_1',) - for file_part in file_parts: - dll = file_part + suffix - target = os.path.join(target_dir, dll) - source = os.path.join(source_dir, dll) - _CopyRuntimeImpl(target, source) - # Copy the UCRT files from the Windows SDK. This location includes the - # api-ms-win-crt-*.dll files that are not found in the Windows directory. - # These files are needed for component builds. If WINDOWSSDKDIR is not set - # use the default SDK path. This will be the case when - # DEPOT_TOOLS_WIN_TOOLCHAIN=0 and vcvarsall.bat has not been run. - win_sdk_dir = os.path.normpath( - os.environ.get( - 'WINDOWSSDKDIR', - os.path.expandvars('%ProgramFiles(x86)%' - '\\Windows Kits\\10'))) - # ARM64 doesn't have a redist for the ucrt DLLs because they are always - # present in the OS. - if target_cpu != 'arm64': - # Starting with the 10.0.17763 SDK the ucrt files are in a version-named - # directory - this handles both cases. - redist_dir = os.path.join(win_sdk_dir, 'Redist') - version_dirs = glob.glob(os.path.join(redist_dir, '10.*')) - if len(version_dirs) > 0: - _SortByHighestVersionNumberFirst(version_dirs) - redist_dir = version_dirs[0] - ucrt_dll_dirs = os.path.join(redist_dir, 'ucrt', 'DLLs', target_cpu) - ucrt_files = glob.glob(os.path.join(ucrt_dll_dirs, 'api-ms-win-*.dll')) - assert len(ucrt_files) > 0 - for ucrt_src_file in ucrt_files: - file_part = os.path.basename(ucrt_src_file) - ucrt_dst_file = os.path.join(target_dir, file_part) - _CopyRuntimeImpl(ucrt_dst_file, ucrt_src_file, False) - # We must copy ucrtbase.dll for x64/x86, and ucrtbased.dll for all CPU types. - if target_cpu != 'arm64' or not suffix.startswith('.'): - if not suffix.startswith('.'): - # ucrtbased.dll is located at {win_sdk_dir}/bin/{a.b.c.d}/{target_cpu}/ - # ucrt/. - sdk_bin_root = os.path.join(win_sdk_dir, 'bin') - sdk_bin_sub_dirs = glob.glob(os.path.join(sdk_bin_root, '10.*')) - # Select the most recent SDK if there are multiple versions installed. - _SortByHighestVersionNumberFirst(sdk_bin_sub_dirs) - for directory in sdk_bin_sub_dirs: - sdk_redist_root_version = os.path.join(sdk_bin_root, directory) - if not os.path.isdir(sdk_redist_root_version): - continue - source_dir = os.path.join(sdk_redist_root_version, target_cpu, - 'ucrt') - break - _CopyRuntimeImpl(os.path.join(target_dir, 'ucrtbase' + suffix), - os.path.join(source_dir, 'ucrtbase' + suffix)) + if target_cpu == 'arm64': + # Windows ARM64 VCRuntime is located at {toolchain_root}/VC/Redist/MSVC/ + # {x.y.z}/[debug_nonredist/]arm64/Microsoft.VC141.CRT/. + vc_redist_root = FindVCRedistRoot() + if suffix.startswith('.'): + source_dir = os.path.join(vc_redist_root, + 'arm64', 'Microsoft.VC141.CRT') + else: + source_dir = os.path.join(vc_redist_root, 'debug_nonredist', + 'arm64', 'Microsoft.VC141.DebugCRT') + for file_part in ('msvcp', 'vccorlib', 'vcruntime'): + dll = dll_pattern % file_part + target = os.path.join(target_dir, dll) + source = os.path.join(source_dir, dll) + _CopyRuntimeImpl(target, source) + # Copy the UCRT files from the Windows SDK. This location includes the + # api-ms-win-crt-*.dll files that are not found in the Windows directory. + # These files are needed for component builds. If WINDOWSSDKDIR is not set + # use the default SDK path. This will be the case when + # DEPOT_TOOLS_WIN_TOOLCHAIN=0 and vcvarsall.bat has not been run. + win_sdk_dir = os.path.normpath( + os.environ.get('WINDOWSSDKDIR', + os.path.expandvars('%ProgramFiles(x86)%' + '\\Windows Kits\\10'))) + # ARM64 doesn't have a redist for the ucrt DLLs because they are always + # present in the OS. + if target_cpu != 'arm64': + # Starting with the 10.0.17763 SDK the ucrt files are in a version-named + # directory - this handles both cases. + redist_dir = os.path.join(win_sdk_dir, 'Redist') + version_dirs = glob.glob(os.path.join(redist_dir, '10.*')) + if len(version_dirs) > 0: + _SortByHighestVersionNumberFirst(version_dirs) + redist_dir = version_dirs[0] + ucrt_dll_dirs = os.path.join(redist_dir, 'ucrt', 'DLLs', target_cpu) + ucrt_files = glob.glob(os.path.join(ucrt_dll_dirs, 'api-ms-win-*.dll')) + assert len(ucrt_files) > 0 + for ucrt_src_file in ucrt_files: + file_part = os.path.basename(ucrt_src_file) + ucrt_dst_file = os.path.join(target_dir, file_part) + _CopyRuntimeImpl(ucrt_dst_file, ucrt_src_file, False) + # We must copy ucrtbase.dll for x64/x86, and ucrtbased.dll for all CPU types. + if target_cpu != 'arm64' or not suffix.startswith('.'): + if not suffix.startswith('.'): + # ucrtbased.dll is located at {win_sdk_dir}/bin/{a.b.c.d}/{target_cpu}/ + # ucrt/. + sdk_bin_root = os.path.join(win_sdk_dir, 'bin') + sdk_bin_sub_dirs = glob.glob(os.path.join(sdk_bin_root, '10.*')) + # Select the most recent SDK if there are multiple versions installed. + _SortByHighestVersionNumberFirst(sdk_bin_sub_dirs) + for directory in sdk_bin_sub_dirs: + sdk_redist_root_version = os.path.join(sdk_bin_root, directory) + if not os.path.isdir(sdk_redist_root_version): + continue + source_dir = os.path.join(sdk_redist_root_version, target_cpu, 'ucrt') + break + _CopyRuntimeImpl(os.path.join(target_dir, 'ucrtbase' + suffix), + os.path.join(source_dir, 'ucrtbase' + suffix)) def FindVCComponentRoot(component): - """Find the most recent Tools or Redist or other directory in an MSVC install. + """Find the most recent Tools or Redist or other directory in an MSVC install. Typical results are {toolchain_root}/VC/{component}/MSVC/{x.y.z}. The {x.y.z} version number part changes frequently so the highest version number found is used. """ - SetEnvironmentAndGetRuntimeDllDirs() - assert ('GYP_MSVS_OVERRIDE_PATH' in os.environ) - vc_component_msvc_root = os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'], - 'VC', component, 'MSVC') - vc_component_msvc_contents = glob.glob( - os.path.join(vc_component_msvc_root, '14.*')) - # Select the most recent toolchain if there are several. - _SortByHighestVersionNumberFirst(vc_component_msvc_contents) - for directory in vc_component_msvc_contents: - if os.path.isdir(directory): - return directory - raise Exception('Unable to find the VC %s directory.' % component) + SetEnvironmentAndGetRuntimeDllDirs() + assert ('GYP_MSVS_OVERRIDE_PATH' in os.environ) + vc_component_msvc_root = os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'], + 'VC', component, 'MSVC') + vc_component_msvc_contents = os.listdir(vc_component_msvc_root) + # Select the most recent toolchain if there are several. + _SortByHighestVersionNumberFirst(vc_component_msvc_contents) + for directory in vc_component_msvc_contents: + if not os.path.isdir(os.path.join(vc_component_msvc_root, directory)): + continue + if re.match(r'14\.\d+\.\d+', directory): + return os.path.join(vc_component_msvc_root, directory) + raise Exception('Unable to find the VC %s directory.' % component) def FindVCRedistRoot(): - """In >=VS2017, Redist binaries are located in + """In >=VS2017, Redist binaries are located in {toolchain_root}/VC/Redist/MSVC/{x.y.z}/{target_cpu}/. This returns the '{toolchain_root}/VC/Redist/MSVC/{x.y.z}/' path. """ - return FindVCComponentRoot('Redist') + return FindVCComponentRoot('Redist') def _CopyRuntime(target_dir, source_dir, target_cpu, debug): - """Copy the VS runtime DLLs, only if the target doesn't exist, but the target + """Copy the VS runtime DLLs, only if the target doesn't exist, but the target directory does exist. Handles VS 2015, 2017 and 2019.""" - suffix = 'd.dll' if debug else '.dll' - # VS 2015, 2017 and 2019 use the same CRT DLLs. - _CopyUCRTRuntime(target_dir, source_dir, target_cpu, suffix) + suffix = 'd.dll' if debug else '.dll' + # VS 2015, 2017 and 2019 use the same CRT DLLs. + _CopyUCRTRuntime(target_dir, source_dir, target_cpu, '%s140' + suffix, + suffix) def CopyDlls(target_dir, configuration, target_cpu): - """Copy the VS runtime DLLs into the requested directory as needed. + """Copy the VS runtime DLLs into the requested directory as needed. configuration is one of 'Debug' or 'Release'. target_cpu is one of 'x86', 'x64' or 'arm64'. @@ -397,27 +355,27 @@ The debug configuration gets both the debug and release DLLs; the release config only the latter. """ - vs_runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs() - if not vs_runtime_dll_dirs: - return + vs_runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs() + if not vs_runtime_dll_dirs: + return - x64_runtime, x86_runtime, arm64_runtime = vs_runtime_dll_dirs - if target_cpu == 'x64': - runtime_dir = x64_runtime - elif target_cpu == 'x86': - runtime_dir = x86_runtime - elif target_cpu == 'arm64': - runtime_dir = arm64_runtime - else: - raise Exception('Unknown target_cpu: ' + target_cpu) - _CopyRuntime(target_dir, runtime_dir, target_cpu, debug=False) - if configuration == 'Debug': - _CopyRuntime(target_dir, runtime_dir, target_cpu, debug=True) - _CopyDebugger(target_dir, target_cpu) + x64_runtime, x86_runtime, arm64_runtime = vs_runtime_dll_dirs + if target_cpu == 'x64': + runtime_dir = x64_runtime + elif target_cpu == 'x86': + runtime_dir = x86_runtime + elif target_cpu == 'arm64': + runtime_dir = arm64_runtime + else: + raise Exception('Unknown target_cpu: ' + target_cpu) + _CopyRuntime(target_dir, runtime_dir, target_cpu, debug=False) + if configuration == 'Debug': + _CopyRuntime(target_dir, runtime_dir, target_cpu, debug=True) + _CopyDebugger(target_dir, target_cpu) def _CopyDebugger(target_dir, target_cpu): - """Copy dbghelp.dll and dbgcore.dll into the requested directory as needed. + """Copy dbghelp.dll and dbgcore.dll into the requested directory as needed. target_cpu is one of 'x86', 'x64' or 'arm64'. @@ -429,150 +387,148 @@ dbgcore.dll is needed when using some functions from dbghelp.dll (like MinidumpWriteDump). """ - win_sdk_dir = SetEnvironmentAndGetSDKDir() - if not win_sdk_dir: - return + win_sdk_dir = SetEnvironmentAndGetSDKDir() + if not win_sdk_dir: + return - # List of debug files that should be copied, the first element of the tuple is - # the name of the file and the second indicates if it's optional. - debug_files = [('dbghelp.dll', False), ('dbgcore.dll', True)] - # The UCRT is not a redistributable component on arm64. - if target_cpu != 'arm64': - debug_files.extend([('api-ms-win-downlevel-kernel32-l2-1-0.dll', False), - ('api-ms-win-eventing-provider-l1-1-0.dll', False)]) - for debug_file, is_optional in debug_files: - full_path = os.path.join(win_sdk_dir, 'Debuggers', target_cpu, - debug_file) - if not os.path.exists(full_path): - if is_optional: - continue - else: - raise Exception( - '%s not found in "%s"\r\nYou must install' - 'Windows 10 SDK version 10.0.19041.0 including the ' - '"Debugging Tools for Windows" feature.' % - (debug_file, full_path)) - target_path = os.path.join(target_dir, debug_file) - _CopyRuntimeImpl(target_path, full_path) + # List of debug files that should be copied, the first element of the tuple is + # the name of the file and the second indicates if it's optional. + debug_files = [('dbghelp.dll', False), ('dbgcore.dll', True)] + for debug_file, is_optional in debug_files: + full_path = os.path.join(win_sdk_dir, 'Debuggers', target_cpu, debug_file) + if not os.path.exists(full_path): + if is_optional: + continue + else: + # TODO(crbug.com/773476): remove version requirement. + raise Exception('%s not found in "%s"\r\nYou must install the ' + '"Debugging Tools for Windows" feature from the Windows' + ' 10 SDK.' + % (debug_file, full_path)) + target_path = os.path.join(target_dir, debug_file) + _CopyRuntimeImpl(target_path, full_path) def _GetDesiredVsToolchainHashes(): - """Load a list of SHA1s corresponding to the toolchains that we want installed + """Load a list of SHA1s corresponding to the toolchains that we want installed to build with.""" + env_version = GetVisualStudioVersion() + if env_version == '2017': + # VS 2017 Update 9 (15.9.12) with 10.0.18362 SDK, 10.0.17763 version of + # Debuggers, and 10.0.17134 version of d3dcompiler_47.dll, with ARM64 + # libraries. + toolchain_hash = '418b3076791776573a815eb298c8aa590307af63' # Third parties that do not have access to the canonical toolchain can map # canonical toolchain version to their own toolchain versions. - toolchain_hash_mapping_key = 'GYP_MSVS_HASH_%s' % TOOLCHAIN_HASH - return [os.environ.get(toolchain_hash_mapping_key, TOOLCHAIN_HASH)] + toolchain_hash_mapping_key = 'GYP_MSVS_HASH_%s' % toolchain_hash + return [os.environ.get(toolchain_hash_mapping_key, toolchain_hash)] + raise Exception('Unsupported VS version %s' % env_version) def ShouldUpdateToolchain(): - """Check if the toolchain should be upgraded.""" - if not os.path.exists(json_data_file): - return True - with open(json_data_file, 'r') as tempf: - toolchain_data = json.load(tempf) - version = toolchain_data['version'] - env_version = GetVisualStudioVersion() - # If there's a mismatch between the version set in the environment and the one - # in the json file then the toolchain should be updated. - return version != env_version + """Check if the toolchain should be upgraded.""" + if not os.path.exists(json_data_file): + return True + with open(json_data_file, 'r') as tempf: + toolchain_data = json.load(tempf) + version = toolchain_data['version'] + env_version = GetVisualStudioVersion() + # If there's a mismatch between the version set in the environment and the one + # in the json file then the toolchain should be updated. + return version != env_version def Update(force=False, no_download=False): - """Requests an update of the toolchain to the specific hashes we have at + """Requests an update of the toolchain to the specific hashes we have at this revision. The update outputs a .json of the various configuration information required to pass to gyp which we use in |GetToolchainDir()|. If no_download is true then the toolchain will be configured if present but will not be downloaded. """ - if force != False and force != '--force': - print('Unknown parameter "%s"' % force, file=sys.stderr) - return 1 - if force == '--force' or os.path.exists(json_data_file): - force = True + if force != False and force != '--force': + print('Unknown parameter "%s"' % force, file=sys.stderr) + return 1 + if force == '--force' or os.path.exists(json_data_file): + force = True - depot_tools_win_toolchain = \ - bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))) - if (_HostIsWindows() or force) and depot_tools_win_toolchain: - import find_depot_tools - depot_tools_path = find_depot_tools.add_depot_tools_to_path() + depot_tools_win_toolchain = \ + bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))) + if ((sys.platform in ('win32', 'cygwin') or force) and + depot_tools_win_toolchain): + import find_depot_tools + depot_tools_path = find_depot_tools.add_depot_tools_to_path() - # On Linux, the file system is usually case-sensitive while the Windows - # SDK only works on case-insensitive file systems. If it doesn't already - # exist, set up a ciopfs fuse mount to put the SDK in a case-insensitive - # part of the file system. - toolchain_dir = os.path.join(depot_tools_path, 'win_toolchain', - 'vs_files') - # For testing this block, unmount existing mounts with - # fusermount -u third_party/depot_tools/win_toolchain/vs_files - if sys.platform.startswith( - 'linux') and not os.path.ismount(toolchain_dir): - import distutils.spawn - ciopfs = distutils.spawn.find_executable('ciopfs') - if not ciopfs: - # ciopfs not found in PATH; try the one downloaded from the DEPS hook. - ciopfs = os.path.join(script_dir, 'ciopfs') - if not os.path.isdir(toolchain_dir): - os.mkdir(toolchain_dir) - if not os.path.isdir(toolchain_dir + '.ciopfs'): - os.mkdir(toolchain_dir + '.ciopfs') - # Without use_ino, clang's #pragma once and Wnonportable-include-path - # both don't work right, see https://llvm.org/PR34931 - # use_ino doesn't slow down builds, so it seems there's no drawback to - # just using it always. - subprocess.check_call([ - ciopfs, '-o', 'use_ino', toolchain_dir + '.ciopfs', - toolchain_dir - ]) + # On Linux, the file system is usually case-sensitive while the Windows + # SDK only works on case-insensitive file systems. If it doesn't already + # exist, set up a ciopfs fuse mount to put the SDK in a case-insensitive + # part of the file system. + toolchain_dir = os.path.join(depot_tools_path, 'win_toolchain', 'vs_files') + # For testing this block, unmount existing mounts with + # fusermount -u third_party/depot_tools/win_toolchain/vs_files + if sys.platform.startswith('linux') and not os.path.ismount(toolchain_dir): + import distutils.spawn + ciopfs = distutils.spawn.find_executable('ciopfs') + if not ciopfs: + # ciopfs not found in PATH; try the one downloaded from the DEPS hook. + ciopfs = os.path.join(script_dir, 'ciopfs') + if not os.path.isdir(toolchain_dir): + os.mkdir(toolchain_dir) + if not os.path.isdir(toolchain_dir + '.ciopfs'): + os.mkdir(toolchain_dir + '.ciopfs') + # Without use_ino, clang's #pragma once and Wnonportable-include-path + # both don't work right, see https://llvm.org/PR34931 + # use_ino doesn't slow down builds, so it seems there's no drawback to + # just using it always. + subprocess.check_call([ + ciopfs, '-o', 'use_ino', toolchain_dir + '.ciopfs', toolchain_dir]) - get_toolchain_args = [ - # TODO(athom): use sys.executable (python3). - # Note: depot_tools contains python.bat not python.exe - # so for python to land on the first python in the PATH - # irrespective of its extension we pass shell=True below. - 'python', - os.path.join(depot_tools_path, 'win_toolchain', - 'get_toolchain_if_necessary.py'), - '--output-json', - json_data_file, - ] + _GetDesiredVsToolchainHashes() - if force: - get_toolchain_args.append('--force') - if no_download: - get_toolchain_args.append('--no-download') - subprocess.check_call(get_toolchain_args, shell=True) + # Necessary so that get_toolchain_if_necessary.py will put the VS toolkit + # in the correct directory. + os.environ['GYP_MSVS_VERSION'] = GetVisualStudioVersion() + get_toolchain_args = [ + sys.executable, + os.path.join(depot_tools_path, + 'win_toolchain', + 'get_toolchain_if_necessary.py'), + '--output-json', json_data_file, + ] + _GetDesiredVsToolchainHashes() + if force: + get_toolchain_args.append('--force') + if no_download: + get_toolchain_args.append('--no-download') + subprocess.check_call(get_toolchain_args) - return 0 + return 0 def NormalizePath(path): - while path.endswith('\\'): - path = path[:-1] - return path + while path.endswith('\\'): + path = path[:-1] + return path def SetEnvironmentAndGetSDKDir(): - """Gets location information about the current sdk (must have been + """Gets location information about the current sdk (must have been previously updated by 'update'). This is used for the GN build.""" - SetEnvironmentAndGetRuntimeDllDirs() + SetEnvironmentAndGetRuntimeDllDirs() - # If WINDOWSSDKDIR is not set, search the default SDK path and set it. - if not 'WINDOWSSDKDIR' in os.environ: - default_sdk_path = os.path.expandvars('%ProgramFiles(x86)%' - '\\Windows Kits\\10') - if os.path.isdir(default_sdk_path): - os.environ['WINDOWSSDKDIR'] = default_sdk_path + # If WINDOWSSDKDIR is not set, search the default SDK path and set it. + if not 'WINDOWSSDKDIR' in os.environ: + default_sdk_path = os.path.expandvars('%ProgramFiles(x86)%' + '\\Windows Kits\\10') + if os.path.isdir(default_sdk_path): + os.environ['WINDOWSSDKDIR'] = default_sdk_path - return NormalizePath(os.environ['WINDOWSSDKDIR']) + return NormalizePath(os.environ['WINDOWSSDKDIR']) def GetToolchainDir(): - """Gets location information about the current toolchain (must have been + """Gets location information about the current toolchain (must have been previously updated by 'update'). This is used for the GN build.""" - runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs() - win_sdk_dir = SetEnvironmentAndGetSDKDir() + runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs() + win_sdk_dir = SetEnvironmentAndGetSDKDir() - print('''vs_path = %s + print('''vs_path = %s sdk_path = %s vs_version = %s wdk_dir = %s @@ -584,16 +540,16 @@ def main(): - commands = { - 'update': Update, - 'get_toolchain_dir': GetToolchainDir, - 'copy_dlls': CopyDlls, - } - if len(sys.argv) < 2 or sys.argv[1] not in commands: - print('Expected one of: %s' % ', '.join(commands), file=sys.stderr) - return 1 - return commands[sys.argv[1]](*sys.argv[2:]) + commands = { + 'update': Update, + 'get_toolchain_dir': GetToolchainDir, + 'copy_dlls': CopyDlls, + } + if len(sys.argv) < 2 or sys.argv[1] not in commands: + print('Expected one of: %s' % ', '.join(commands), file=sys.stderr) + return 1 + return commands[sys.argv[1]](*sys.argv[2:]) if __name__ == '__main__': - sys.exit(main()) + sys.exit(main())
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart b/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart index e0a09f2..de81796 100644 --- a/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart +++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
@@ -251,7 +251,7 @@ this[i], separator, ], - last, + if (isNotEmpty) last, ]; }
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart index 59b7a26..18ceb87 100644 --- a/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart +++ b/pkg/_fe_analyzer_shared/lib/src/macros/bootstrap.dart
@@ -34,7 +34,7 @@ } constructorEntries.writeln('},'); }); - constructorEntries.writeln("}"); + constructorEntries.writeln('},'); }); return template .replaceFirst(_importMarker, imports.toString())
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart index 830127a..36c0cb7 100644 --- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart +++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -7024,6 +7024,67 @@ // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. const Template<Message Function(String name)> + templateJsInteropStaticInteropTrustTypesUsageNotAllowed = + const Template<Message Function(String name)>( + problemMessageTemplate: + r"""JS interop class '#name' has an `@trustTypes` annotation, but `@trustTypes` is only supported within the sdk.""", + correctionMessageTemplate: + r"""Try removing the `@trustTypes` annotation.""", + withArguments: + _withArgumentsJsInteropStaticInteropTrustTypesUsageNotAllowed); + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Code<Message Function(String name)> + codeJsInteropStaticInteropTrustTypesUsageNotAllowed = + const Code<Message Function(String name)>( + "JsInteropStaticInteropTrustTypesUsageNotAllowed", +); + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +Message _withArgumentsJsInteropStaticInteropTrustTypesUsageNotAllowed( + String name) { + if (name.isEmpty) throw 'No name provided'; + name = demangleMixinApplicationName(name); + return new Message(codeJsInteropStaticInteropTrustTypesUsageNotAllowed, + problemMessage: + """JS interop class '${name}' has an `@trustTypes` annotation, but `@trustTypes` is only supported within the sdk.""", + correctionMessage: """Try removing the `@trustTypes` annotation.""", + arguments: {'name': name}); +} + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Template<Message Function(String name)> + templateJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop = + const Template<Message Function(String name)>( + problemMessageTemplate: + r"""JS interop class '#name' has an `@trustTypes` annotation, but no `@staticInterop` annotation.""", + correctionMessageTemplate: + r"""Try marking the class using `@staticInterop`.""", + withArguments: + _withArgumentsJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop); + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Code<Message Function(String name)> + codeJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop = + const Code<Message Function(String name)>( + "JsInteropStaticInteropTrustTypesUsedWithoutStaticInterop", +); + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +Message _withArgumentsJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop( + String name) { + if (name.isEmpty) throw 'No name provided'; + name = demangleMixinApplicationName(name); + return new Message( + codeJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop, + problemMessage: + """JS interop class '${name}' has an `@trustTypes` annotation, but no `@staticInterop` annotation.""", + correctionMessage: """Try marking the class using `@staticInterop`.""", + arguments: {'name': name}); +} + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Template<Message Function(String name)> templateJsInteropStaticInteropWithInstanceMembers = const Template<Message Function(String name)>( problemMessageTemplate:
diff --git a/pkg/_js_interop_checks/lib/js_interop_checks.dart b/pkg/_js_interop_checks/lib/js_interop_checks.dart index 13917c7..351c410 100644 --- a/pkg/_js_interop_checks/lib/js_interop_checks.dart +++ b/pkg/_js_interop_checks/lib/js_interop_checks.dart
@@ -22,7 +22,9 @@ templateJsInteropStaticInteropWithInstanceMembers, templateJsInteropStaticInteropWithNonStaticSupertype, templateJsInteropJSClassExtendsDartClass, - templateJsInteropNativeClassInAnnotation; + templateJsInteropNativeClassInAnnotation, + templateJsInteropStaticInteropTrustTypesUsageNotAllowed, + templateJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop; import 'src/js_interop.dart'; @@ -39,7 +41,6 @@ /// Libraries that use `external` to exclude from checks on external. static final Iterable<String> _pathsWithAllowedDartExternalUsage = <String>[ '_foreign_helper', // for foreign helpers - '_late_helper', // for dart2js late variable utilities '_interceptors', // for ddc JS string '_native_typed_data', '_runtime', // for ddc types at runtime @@ -67,6 +68,11 @@ 'generated_tests/web_2/native/native_test', ]; + List<Pattern> _allowedTrustTypesTestPatterns = [ + RegExp(r'(?<!generated_)tests/lib/js'), + RegExp(r'(?<!generated_)tests/lib_2/js'), + ]; + bool _libraryIsGlobalNamespace = false; JsInteropChecks( @@ -104,6 +110,25 @@ _classHasJSAnnotation = hasJSInteropAnnotation(cls); _classHasAnonymousAnnotation = hasAnonymousAnnotation(cls); _classHasStaticInteropAnnotation = hasStaticInteropAnnotation(cls); + bool classHasTrustTypesAnnotation = hasTrustTypesAnnotation(cls); + if (classHasTrustTypesAnnotation) { + if (!_isAllowedTrustTypesUsage(cls)) { + _diagnosticsReporter.report( + templateJsInteropStaticInteropTrustTypesUsageNotAllowed + .withArguments(cls.name), + cls.fileOffset, + cls.name.length, + cls.fileUri); + } + if (!_classHasStaticInteropAnnotation) { + _diagnosticsReporter.report( + templateJsInteropStaticInteropTrustTypesUsedWithoutStaticInterop + .withArguments(cls.name), + cls.fileOffset, + cls.name.length, + cls.fileUri); + } + } var superclass = cls.superclass; if (superclass != null && superclass != _coreTypes.objectClass) { var superHasJSAnnotation = hasJSInteropAnnotation(superclass); @@ -357,6 +382,14 @@ } } + /// Verifies that use of `@trustTypes` is allowed. + bool _isAllowedTrustTypesUsage(Class cls) { + Uri uri = cls.enclosingLibrary.importUri; + return uri.isScheme('dart') && uri.path == 'ui' || + _allowedTrustTypesTestPatterns + .any((pattern) => uri.path.contains(pattern)); + } + /// Verifies given member is one of the allowed usages of external: /// a dart low level library, a foreign helper, a native test, /// or a from environment constructor.
diff --git a/pkg/_js_interop_checks/lib/src/js_interop.dart b/pkg/_js_interop_checks/lib/src/js_interop.dart index 9874a1c..3f3c517 100644 --- a/pkg/_js_interop_checks/lib/src/js_interop.dart +++ b/pkg/_js_interop_checks/lib/src/js_interop.dart
@@ -20,6 +20,11 @@ bool hasStaticInteropAnnotation(Annotatable a) => a.annotations.any(_isStaticInteropAnnotation); +/// Returns true iff the node has an `@trustTypes` annotation from +/// `package:js` or from the internal `dart:_js_annotations`. +bool hasTrustTypesAnnotation(Annotatable a) => + a.annotations.any(_isTrustTypesAnnotation); + /// If [a] has a `@JS('...')` annotation, returns the value inside the /// parentheses. /// @@ -80,6 +85,9 @@ bool _isStaticInteropAnnotation(Expression value) => _isInteropAnnotation(value, '_StaticInterop'); +bool _isTrustTypesAnnotation(Expression value) => + _isInteropAnnotation(value, '_TrustTypes'); + /// Returns true if [value] is the `Native` annotation from `dart:_js_helper`. bool _isNativeAnnotation(Expression value) { var c = _annotationClass(value);
diff --git a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart index 57b3a31..5205099 100644 --- a/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart +++ b/pkg/_js_interop_checks/lib/src/transformations/js_util_optimizer.dart
@@ -7,16 +7,19 @@ import 'package:kernel/core_types.dart'; import 'package:kernel/type_environment.dart'; -import '../js_interop.dart' show getJSName; +import '../js_interop.dart' show getJSName, hasTrustTypesAnnotation; /// Replaces js_util methods with inline calls to foreign_helper JS which /// emits the code as a JavaScript code fragment. class JsUtilOptimizer extends Transformer { final Procedure _callMethodTarget; + final Procedure _callMethodTrustTypeTarget; final List<Procedure> _callMethodUncheckedTargets; + final List<Procedure> _callMethodUncheckedTrustTypeTargets; final Procedure _callConstructorTarget; final List<Procedure> _callConstructorUncheckedTargets; final Procedure _getPropertyTarget; + final Procedure _getPropertyTrustTypeTarget; final Procedure _setPropertyTarget; final Procedure _setPropertyUncheckedTarget; @@ -37,14 +40,21 @@ final CoreTypes _coreTypes; final StatefulStaticTypeContext _staticTypeContext; Map<Reference, ExtensionMemberDescriptor>? _extensionMemberIndex; + late Set<Reference> _shouldTrustType; JsUtilOptimizer(this._coreTypes, ClassHierarchy hierarchy) : _callMethodTarget = _coreTypes.index.getTopLevelProcedure('dart:js_util', 'callMethod'), + _callMethodTrustTypeTarget = _coreTypes.index + .getTopLevelProcedure('dart:js_util', '_callMethodTrustType'), _callMethodUncheckedTargets = List<Procedure>.generate( 5, (i) => _coreTypes.index.getTopLevelProcedure( 'dart:js_util', '_callMethodUnchecked$i')), + _callMethodUncheckedTrustTypeTargets = List<Procedure>.generate( + 5, + (i) => _coreTypes.index.getTopLevelProcedure( + 'dart:js_util', '_callMethodUncheckedTrustType$i')), _callConstructorTarget = _coreTypes.index .getTopLevelProcedure('dart:js_util', 'callConstructor'), _callConstructorUncheckedTargets = List<Procedure>.generate( @@ -53,6 +63,8 @@ 'dart:js_util', '_callConstructorUnchecked$i')), _getPropertyTarget = _coreTypes.index .getTopLevelProcedure('dart:js_util', 'getProperty'), + _getPropertyTrustTypeTarget = _coreTypes.index + .getTopLevelProcedure('dart:js_util', '_getPropertyTrustType'), _setPropertyTarget = _coreTypes.index .getTopLevelProcedure('dart:js_util', 'setProperty'), _setPropertyUncheckedTarget = _coreTypes.index @@ -93,14 +105,16 @@ if (node.isExternal && node.isExtensionMember) { var index = _extensionMemberIndex ??= _createExtensionMembersIndex(node.enclosingLibrary); - var nodeDescriptor = index[node.reference]!; + Reference reference = node.reference; + var nodeDescriptor = index[reference]!; + bool shouldTrustType = _shouldTrustType.contains(reference); if (!nodeDescriptor.isStatic) { if (nodeDescriptor.kind == ExtensionMemberKind.Getter) { - transformedBody = _getExternalGetterBody(node); + transformedBody = _getExternalGetterBody(node, shouldTrustType); } else if (nodeDescriptor.kind == ExtensionMemberKind.Setter) { transformedBody = _getExternalSetterBody(node); } else if (nodeDescriptor.kind == ExtensionMemberKind.Method) { - transformedBody = _getExternalMethodBody(node); + transformedBody = _getExternalMethodBody(node, shouldTrustType); } } } @@ -120,9 +134,17 @@ Map<Reference, ExtensionMemberDescriptor> _createExtensionMembersIndex( Library library) { _extensionMemberIndex = {}; - library.extensions.forEach((extension) => extension.members.forEach( - (descriptor) => - _extensionMemberIndex![descriptor.member] = descriptor)); + _shouldTrustType = {}; + library.extensions + .forEach((extension) => extension.members.forEach((descriptor) { + Reference reference = descriptor.member; + _extensionMemberIndex![reference] = descriptor; + DartType onType = extension.onType; + if (onType is InterfaceType && + hasTrustTypesAnnotation(onType.className.asClass)) { + _shouldTrustType.add(reference); + } + })); return _extensionMemberIndex!; } @@ -130,11 +152,13 @@ /// /// The new function body will call the optimized version of /// `js_util.getProperty` for the given external getter. - ReturnStatement _getExternalGetterBody(Procedure node) { + ReturnStatement _getExternalGetterBody(Procedure node, bool shouldTrustType) { var function = node.function; assert(function.positionalParameters.length == 1); + Procedure target = + shouldTrustType ? _getPropertyTrustTypeTarget : _getPropertyTarget; var getPropertyInvocation = StaticInvocation( - _getPropertyTarget, + target, Arguments([ VariableGet(function.positionalParameters.first), StringLiteral(_getExtensionMemberName(node)) @@ -170,10 +194,12 @@ /// /// The new function body will call the optimized version of /// `js_util.callMethod` for the given external method. - ReturnStatement _getExternalMethodBody(Procedure node) { + ReturnStatement _getExternalMethodBody(Procedure node, bool shouldTrustType) { var function = node.function; + Procedure target = + shouldTrustType ? _callMethodTrustTypeTarget : _callMethodTarget; var callMethodInvocation = StaticInvocation( - _callMethodTarget, + target, Arguments([ VariableGet(function.positionalParameters.first), StringLiteral(_getExtensionMemberName(node)), @@ -185,7 +211,8 @@ function.returnType ])) ..fileOffset = node.fileOffset; - return ReturnStatement(_lowerCallMethod(callMethodInvocation)); + return ReturnStatement(_lowerCallMethod(callMethodInvocation, + shouldTrustType: shouldTrustType)); } /// Returns the extension member name. @@ -214,7 +241,8 @@ if (node.target == _setPropertyTarget) { node = _lowerSetProperty(node); } else if (node.target == _callMethodTarget) { - node = _lowerCallMethod(node); + // Never trust types on explicit `js_util` calls. + node = _lowerCallMethod(node, shouldTrustType: false); } else if (node.target == _callConstructorTarget) { node = _lowerCallConstructor(node); } @@ -245,13 +273,17 @@ /// Calls will be lowered when using a List literal or constant list with 0-4 /// elements for the `callMethod` arguments, or the `List.empty()` factory. /// Removing the checks allows further inlining by the compilers. - StaticInvocation _lowerCallMethod(StaticInvocation node) { + StaticInvocation _lowerCallMethod(StaticInvocation node, + {required bool shouldTrustType}) { Arguments arguments = node.arguments; assert(arguments.positional.length == 3); assert(arguments.named.isEmpty); + List<Procedure> targets = shouldTrustType + ? _callMethodUncheckedTrustTypeTargets + : _callMethodUncheckedTargets; return _lowerToCallUnchecked( - node, _callMethodUncheckedTargets, arguments.positional.sublist(0, 2)); + node, targets, arguments.positional.sublist(0, 2)); } /// Lowers the given js_util `callConstructor` call to `_callConstructorUncheckedN`
diff --git a/pkg/analysis_server/test/integration/server/bazel_changes_test.dart b/pkg/analysis_server/test/integration/server/bazel_changes_test.dart index 3a64173..5dd8d39 100644 --- a/pkg/analysis_server/test/integration/server/bazel_changes_test.dart +++ b/pkg/analysis_server/test/integration/server/bazel_changes_test.dart
@@ -127,7 +127,9 @@ await processedNotification.future; expect(errors, isNotEmpty); - expect(errors[0].message, contains('generated.dart')); + var error = errors.singleWhere((e) => e.code == 'uri_does_not_exist', + orElse: () => throw "'uri_does_not_exist' error was not found"); + expect(error.message, contains('generated.dart')); // This seems to be necessary (at least when running the test from source), // because it takes a while for the watcher isolate to start.
diff --git a/pkg/analysis_server/test/lsp/diagnostic_test.dart b/pkg/analysis_server/test/lsp/diagnostic_test.dart index c5024cb..0c6572e 100644 --- a/pkg/analysis_server/test/lsp/diagnostic_test.dart +++ b/pkg/analysis_server/test/lsp/diagnostic_test.dart
@@ -5,6 +5,7 @@ import 'package:analysis_server/lsp_protocol/protocol_generated.dart'; import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin; import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin; +import 'package:linter/src/rules.dart'; import 'package:test/test.dart'; import 'package:test_reflective_loader/test_reflective_loader.dart'; @@ -378,6 +379,71 @@ } } + /// Tests that diagnostic ordering is stable when minor changes are made to + /// the file that does not alter the diagnostics besides extending their + /// range and adding to their messages. + /// + /// https://github.com/Dart-Code/Dart-Code/issues/3934 + Future<void> test_stableOrder() async { + /// Helper to pad out the content in a way that has previously triggered + /// this issue. + String wrappedContent(String content) => ''' +// +// +// +// + +void f() { + $content +} +'''; + + registerLintRules(); + newFile(analysisOptionsPath, ''' +linter: + rules: + - prefer_typing_uninitialized_variables + +analyzer: + language: + strict-inference: true + '''); + + newFile(mainFilePath, ''); + await initialize(); + await openFile(mainFileUri, ''); + + // Collect the initial set of diagnostic to compare against. + var docVersion = 1; + final originalDiagnosticsUpdate = waitForDiagnostics(mainFileUri); + await replaceFile(docVersion++, mainFileUri, wrappedContent('final bar;')); + final originalDiagnostics = await originalDiagnosticsUpdate; + + // Helper to update the content and verify the same diagnostics are returned + // in the same order, despite the changes to offset/message altering + // hashcodes. + Future<void> verifyDiagnostics(String content) async { + final diagnosticsUpdate = waitForDiagnostics(mainFileUri); + await replaceFile(docVersion++, mainFileUri, wrappedContent(content)); + final diagnostics = await diagnosticsUpdate; + expect( + diagnostics!.map((d) => d.code), + originalDiagnostics!.map((d) => d.code), + ); + } + + // These changes do not affect the errors being produced (besides offset/ + // message text) but will cause hashcode changes that previously altered the + // returned order. + await verifyDiagnostics('final dbar;'); + await verifyDiagnostics('final dybar;'); + await verifyDiagnostics('final dynbar;'); + await verifyDiagnostics('final dynabar;'); + await verifyDiagnostics('final dynambar;'); + await verifyDiagnostics('final dynamibar;'); + await verifyDiagnostics('final dynamicbar;'); + } + Future<void> test_todos_boolean() async { // TODOs only show up if there's also some code in the file. const contents = '''
diff --git a/pkg/analyzer/lib/error/listener.dart b/pkg/analyzer/lib/error/listener.dart index 88555b2..c21fe48 100644 --- a/pkg/analyzer/lib/error/listener.dart +++ b/pkg/analyzer/lib/error/listener.dart
@@ -2,8 +2,6 @@ // 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:collection'; - import 'package:analyzer/dart/ast/ast.dart' show AstNode, ConstructorDeclaration; import 'package:analyzer/dart/ast/token.dart'; @@ -257,7 +255,7 @@ @override void onError(AnalysisError error) { - (_errors ??= HashSet<AnalysisError>()).add(error); + (_errors ??= {}).add(error); } }
diff --git a/pkg/analyzer/lib/src/summary2/macro_application.dart b/pkg/analyzer/lib/src/summary2/macro_application.dart index 9f618ae..1931e3b 100644 --- a/pkg/analyzer/lib/src/summary2/macro_application.dart +++ b/pkg/analyzer/lib/src/summary2/macro_application.dart
@@ -172,6 +172,30 @@ ); } + static macro.ParameterDeclarationImpl _buildFormalParameter( + FormalParameter node, + ) { + if (node is DefaultFormalParameter) { + node = node.parameter; + } + + final macro.TypeAnnotationImpl typeAnnotation; + if (node is SimpleFormalParameter) { + typeAnnotation = _buildTypeAnnotation(node.type); + } else { + throw UnimplementedError('(${node.runtimeType}) $node'); + } + + return macro.ParameterDeclarationImpl( + id: macro.RemoteInstance.uniqueId, + identifier: + _buildIdentifier(node.identifier!), // TODO(scheglov) might be null + isNamed: node.isNamed, + isRequired: node.isRequired, + type: typeAnnotation, + ); + } + static macro.IdentifierImpl _buildIdentifier(Identifier node) { final String name; if (node is SimpleIdentifier) { @@ -185,8 +209,27 @@ ); } - static macro.TypeAnnotationImpl _buildTypeAnnotation(TypeAnnotation node) { - if (node is NamedType) { + static macro.TypeAnnotationImpl _buildTypeAnnotation(TypeAnnotation? node) { + if (node == null) { + return macro.OmittedTypeAnnotationImpl( + id: macro.RemoteInstance.uniqueId, + ); + } else if (node is GenericFunctionType) { + return macro.FunctionTypeAnnotationImpl( + id: macro.RemoteInstance.uniqueId, + isNullable: node.question != null, + namedParameters: node.parameters.parameters + .where((e) => e.isNamed) + .map(_buildFormalParameter) + .toList(), + positionalParameters: node.parameters.parameters + .where((e) => e.isPositional) + .map(_buildFormalParameter) + .toList(), + returnType: _buildTypeAnnotation(node.returnType), + typeParameters: _buildTypeParameters(node.typeParameters), + ); + } else if (node is NamedType) { return macro.NamedTypeAnnotationImpl( id: macro.RemoteInstance.uniqueId, identifier: _buildIdentifier(node.name), @@ -262,22 +305,38 @@ return -operandValue; } } else if (node is SetOrMapLiteral) { - final result = <Object?, Object?>{}; - for (final element in node.elements) { - if (element is! MapLiteralEntry) { - _throwError(element, 'MapLiteralEntry expected'); - } - final key = evaluate(element.key); - final value = evaluate(element.value); - result[key] = value; - } - return result; + return _setOrMapLiteral(node); } else if (node is SimpleStringLiteral) { return node.value; } _throwError(node, 'Not supported: ${node.runtimeType}'); } + Object _setOrMapLiteral(SetOrMapLiteral node) { + if (node.elements.every((e) => e is Expression)) { + final result = <Object?>{}; + for (final element in node.elements) { + if (element is! Expression) { + _throwError(element, 'Expression expected'); + } + final value = evaluate(element); + result.add(value); + } + return result; + } + + final result = <Object?, Object?>{}; + for (final element in node.elements) { + if (element is! MapLiteralEntry) { + _throwError(element, 'MapLiteralEntry expected'); + } + final key = evaluate(element.key); + final value = evaluate(element.value); + result[key] = value; + } + return result; + } + Never _throwError(AstNode node, String message) { throw ArgumentMacroApplicationError( annotationIndex: annotationIndex,
diff --git a/pkg/analyzer/lib/src/summary2/macro_application_error.dart b/pkg/analyzer/lib/src/summary2/macro_application_error.dart index a7b9018..de56919 100644 --- a/pkg/analyzer/lib/src/summary2/macro_application_error.dart +++ b/pkg/analyzer/lib/src/summary2/macro_application_error.dart
@@ -33,7 +33,8 @@ @override String toStringForTest() { - return 'Argument(annotation: $annotationIndex, argument: $argumentIndex)'; + return 'Argument(annotation: $annotationIndex, ' + 'argument: $argumentIndex, message: $message)'; } @override @@ -118,7 +119,7 @@ @override String toStringForTest() { - return 'Unknown(annotation: $annotationIndex)'; + return 'Unknown(annotation: $annotationIndex, message: $message)'; } @override
diff --git a/pkg/analyzer/test/error/error_listener_test.dart b/pkg/analyzer/test/error/error_listener_test.dart new file mode 100644 index 0000000..25a0b3b --- /dev/null +++ b/pkg/analyzer/test/error/error_listener_test.dart
@@ -0,0 +1,46 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:analyzer/error/error.dart'; +import 'package:analyzer/error/listener.dart'; +import 'package:test/test.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +main() { + defineReflectiveSuite(() { + defineReflectiveTests(RecordingErrorListenerTest); + }); +} + +@reflectiveTest +class RecordingErrorListenerTest { + test_orderedAsReported() { + final listener = RecordingErrorListener(); + listener.onError(_MockAnalysisError(expectedIndex: 0, hashCode: 1)); + listener.onError(_MockAnalysisError(expectedIndex: 1, hashCode: 10)); + listener.onError(_MockAnalysisError(expectedIndex: 2, hashCode: -50)); + listener.onError(_MockAnalysisError(expectedIndex: 3, hashCode: 20)); + listener.onError(_MockAnalysisError(expectedIndex: 4, hashCode: 1)); + + // Expect the errors are returned in the order they are reported, and not + // affected by their hashcodes. + expect( + listener.errors.cast<_MockAnalysisError>().map((e) => e.expectedIndex), + [0, 1, 2, 3, 4], + ); + } +} + +/// An [AnalysisError] that allows setting an explicit hash code. +class _MockAnalysisError implements AnalysisError { + @override + int hashCode; + + int expectedIndex; + + _MockAnalysisError({required this.expectedIndex, required this.hashCode}); + + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +}
diff --git a/pkg/analyzer/test/error/test_all.dart b/pkg/analyzer/test/error/test_all.dart index 19eca56..684e462 100644 --- a/pkg/analyzer/test/error/test_all.dart +++ b/pkg/analyzer/test/error/test_all.dart
@@ -4,11 +4,13 @@ import 'package:test_reflective_loader/test_reflective_loader.dart'; +import 'error_listener_test.dart' as error_listener; import 'error_reporter_test.dart' as error_reporter; import 'error_test.dart' as error_test; main() { defineReflectiveSuite(() { + error_listener.main(); error_reporter.main(); error_test.main(); }, name: 'error');
diff --git a/pkg/analyzer/test/src/summary/macro/declaration_text.dart b/pkg/analyzer/test/src/summary/macro/declaration_text.dart index 066c9b6..85ec1ed 100644 --- a/pkg/analyzer/test/src/summary/macro/declaration_text.dart +++ b/pkg/analyzer/test/src/summary/macro/declaration_text.dart
@@ -131,15 +131,12 @@ _TypeStringBuilder(this._sink); void write(TypeAnnotation type) { - if (type is NamedTypeAnnotation) { - _sink.write(type.identifier.name); - _sink.writeList( - elements: type.typeArguments, - write: write, - separator: ', ', - open: '<', - close: '>', - ); + if (type is FunctionTypeAnnotation) { + _writeFunctionTypeAnnotation(type); + } else if (type is NamedTypeAnnotation) { + _writeNamedTypeAnnotation(type); + } else if (type is OmittedTypeAnnotation) { + _sink.write('OmittedType'); } else { throw UnimplementedError('(${type.runtimeType}) $type'); } @@ -147,6 +144,80 @@ _sink.write('?'); } } + + void _writeFormalParameter(ParameterDeclaration node) { + final String closeSeparator; + if (node.isNamed) { + _sink.write('{'); + closeSeparator = '}'; + if (node.isRequired) { + _sink.write('required '); + } + } else if (!node.isRequired) { + _sink.write('['); + closeSeparator = ']'; + } else { + closeSeparator = ''; + } + + write(node.type); + _sink.write(' '); + _sink.write(node.identifier.name); + + _sink.write(closeSeparator); + } + + void _writeFunctionTypeAnnotation(FunctionTypeAnnotation type) { + write(type.returnType); + _sink.write(' Function'); + + _sink.writeList( + elements: type.typeParameters, + write: _writeTypeParameter, + separator: ', ', + open: '<', + close: '>', + ); + + _sink.write('('); + var hasFormalParameter = false; + for (final formalParameter in type.positionalParameters) { + if (hasFormalParameter) { + _sink.write(', '); + } + _writeFormalParameter(formalParameter); + hasFormalParameter = true; + } + for (final formalParameter in type.namedParameters) { + if (hasFormalParameter) { + _sink.write(', '); + } + _writeFormalParameter(formalParameter); + hasFormalParameter = true; + } + _sink.write(')'); + } + + void _writeNamedTypeAnnotation(NamedTypeAnnotation type) { + _sink.write(type.identifier.name); + _sink.writeList( + elements: type.typeArguments, + write: write, + separator: ', ', + open: '<', + close: '>', + ); + } + + void _writeTypeParameter(TypeParameterDeclaration node) { + _sink.write(node.identifier.name); + + final bound = node.bound; + if (bound != null) { + _sink.write(' extends '); + write(bound); + } + } } extension on StringSink { @@ -158,22 +229,24 @@ String? close, }) { elements = elements.toList(); - if (elements.isNotEmpty) { - if (open != null) { - this.write(open); + if (elements.isEmpty) { + return; + } + + if (open != null) { + this.write(open); + } + var isFirst = true; + for (var element in elements) { + if (isFirst) { + isFirst = false; + } else { + this.write(separator); } - var isFirst = true; - for (var element in elements) { - if (isFirst) { - isFirst = false; - } else { - this.write(separator); - } - write(element); - } - if (close != null) { - this.write(close); - } + write(element); + } + if (close != null) { + this.write(close); } } }
diff --git a/pkg/analyzer/test/src/summary/macro_test.dart b/pkg/analyzer/test/src/summary/macro_test.dart index 0c3f764..f21ac8a 100644 --- a/pkg/analyzer/test/src/summary/macro_test.dart +++ b/pkg/analyzer/test/src/summary/macro_test.dart
@@ -5,6 +5,7 @@ import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart' as macro; import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/src/dart/element/element.dart'; import 'package:analyzer/src/summary2/kernel_compilation_service.dart'; import 'package:analyzer/src/summary2/macro.dart'; @@ -83,7 +84,8 @@ }, constructorParametersCode: '(this.foo, this.bar)', argumentsCode: '(0, const Object())', - expectedErrors: 'Argument(annotation: 0, argument: 1)', + expectedErrors: 'Argument(annotation: 0, argument: 1, ' + 'message: Not supported: InstanceCreationExpressionImpl)', ); } @@ -235,6 +237,19 @@ ); } + test_arguments_typesPhase_type_set() async { + await _assertTypesPhaseArgumentsText( + fields: { + 'foo': 'Set<Object?>', + }, + constructorParametersCode: '(this.foo)', + argumentsCode: '({1, 2, 3})', + expected: r''' +foo: {1, 2, 3} +''', + ); + } + test_arguments_typesPhase_type_string() async { await _assertTypesPhaseArgumentsText( fields: {'foo': 'String'}, @@ -385,6 +400,88 @@ '''); } + test_introspect_types_functionTypeAnnotation_formalParameters_namedOptional_simpleFormalParameter() async { + await _assertTypesPhaseIntrospectionText(r''' +class A extends B<void Function(int a, {int? b, int? c})> {} +''', r''' +class A + superclass: B<void Function(int a, {int? b}, {int? c})> +'''); + } + + test_introspect_types_functionTypeAnnotation_formalParameters_namedRequired_simpleFormalParameter() async { + await _assertTypesPhaseIntrospectionText(r''' +class A extends B<void Function(int a, {required int b, required int c})> {} +''', r''' +class A + superclass: B<void Function(int a, {required int b}, {required int c})> +'''); + } + + test_introspect_types_functionTypeAnnotation_formalParameters_positionalOptional_simpleFormalParameter() async { + await _assertTypesPhaseIntrospectionText(r''' +class A extends B<void Function(int a, [int b, int c])> {} +''', r''' +class A + superclass: B<void Function(int a, [int b], [int c])> +'''); + } + + /// TODO(scheglov) Tests for unnamed positional formal parameters. + test_introspect_types_functionTypeAnnotation_formalParameters_positionalRequired_simpleFormalParameter() async { + await _assertTypesPhaseIntrospectionText(r''' +class A extends B<void Function(int a, double b)> {} +''', r''' +class A + superclass: B<void Function(int a, double b)> +'''); + } + + test_introspect_types_functionTypeAnnotation_nullable() async { + await _assertTypesPhaseIntrospectionText(r''' +class A extends B<void Function()?> {} +''', r''' +class A + superclass: B<void Function()?> +'''); + } + + test_introspect_types_functionTypeAnnotation_returnType() async { + await _assertTypesPhaseIntrospectionText(r''' +class A extends B<void Function()> {} +''', r''' +class A + superclass: B<void Function()> +'''); + } + + test_introspect_types_functionTypeAnnotation_returnType_omitted() async { + await _assertTypesPhaseIntrospectionText(r''' +class A extends B<Function()> {} +''', r''' +class A + superclass: B<OmittedType Function()> +'''); + } + + test_introspect_types_functionTypeAnnotation_typeParameters() async { + await _assertTypesPhaseIntrospectionText(r''' +class A extends B<void Function<T, U extends num>()> {} +''', r''' +class A + superclass: B<void Function<T, U extends num>()> +'''); + } + + test_introspect_types_namedTypeAnnotation_prefixed() async { + await _assertTypesPhaseIntrospectionText(r''' +class A extends prefix.B {} +''', r''' +class A + superclass: B +'''); + } + test_macroFlag_class() async { var library = await buildLibrary(r''' macro class A {} @@ -504,6 +601,14 @@ {'package:test/arguments_text.dart'} ]); + final A = library.definingCompilationUnit.getType('A'); + if (expectedErrors != null) { + expect(_errorsStrForClassElement(A), expectedErrors); + return; + } else { + _assertNoErrorsForClassElement(A); + } + if (expected != null) { final x = library.parts.single.topLevelVariables.single; expect(x.name, 'x'); @@ -514,13 +619,6 @@ print(actual); } expect(actual, expected); - } else if (expectedErrors != null) { - var A = library.definingCompilationUnit.getType('A'); - A as ClassElementImpl; - expect( - A.macroApplicationErrors.map((e) => e.toStringForTest()).join('\n'), - expectedErrors, - ); } else { fail("Either 'expected' or 'expectedErrors' must be provided."); } @@ -561,10 +659,26 @@ {'package:test/declaration_text.dart'} ]); + _assertNoErrorsForClassElement( + library.definingCompilationUnit.getType('A'), + ); + var x = library.parts.single.topLevelVariables.single; expect(x.name, 'x'); x as ConstTopLevelVariableElementImpl; var x_literal = x.constantInitializer as SimpleStringLiteral; return x_literal.value; } + + static void _assertNoErrorsForClassElement(ClassElement? element) { + var actual = _errorsStrForClassElement(element); + expect(actual, isEmpty); + } + + static String _errorsStrForClassElement(ClassElement? element) { + element as ClassElementImpl; + return element.macroApplicationErrors.map((e) { + return e.toStringForTest(); + }).join('\n'); + } }
diff --git a/pkg/compiler/lib/compiler.dart b/pkg/compiler/lib/compiler.dart index 9b2f5c1..5fd5ff1 100644 --- a/pkg/compiler/lib/compiler.dart +++ b/pkg/compiler/lib/compiler.dart
@@ -10,7 +10,7 @@ import 'package:front_end/src/api_unstable/dart2js.dart' as fe; -import 'src/compiler.dart'; +import 'src/compiler.dart' show Compiler; import 'src/options.dart' show CompilerOptions; // Unless explicitly allowed, passing [:null:] for any argument to the
diff --git a/pkg/compiler/lib/src/common/elements.dart b/pkg/compiler/lib/src/common/elements.dart index 6aedcca..2919892 100644 --- a/pkg/compiler/lib/src/common/elements.dart +++ b/pkg/compiler/lib/src/common/elements.dart
@@ -247,7 +247,7 @@ LibraryEntity get foreignLibrary => _foreignLibrary ??= _env.lookupLibrary(Uris.dart__foreign_helper); - /// The dart:_rti library. + /// The dart:_internal library. LibraryEntity get rtiLibrary => _rtiLibrary ??= _env.lookupLibrary(Uris.dart__rti, required: true); @@ -926,24 +926,9 @@ FunctionEntity get defineProperty => _findHelperFunction('defineProperty'); - FunctionEntity get throwLateFieldNI => - _findLateHelperFunction('throwLateFieldNI'); - - FunctionEntity get throwLateFieldAI => - _findLateHelperFunction('throwLateFieldAI'); - FunctionEntity get throwLateFieldADI => _findLateHelperFunction('throwLateFieldADI'); - FunctionEntity get throwUnnamedLateFieldNI => - _findLateHelperFunction('throwUnnamedLateFieldNI'); - - FunctionEntity get throwUnnamedLateFieldAI => - _findLateHelperFunction('throwUnnamedLateFieldAI'); - - FunctionEntity get throwUnnamedLateFieldADI => - _findLateHelperFunction('throwUnnamedLateFieldADI'); - bool isExtractTypeArguments(FunctionEntity member) { return member.name == 'extractTypeArguments' && member.library == internalLibrary; @@ -1135,9 +1120,6 @@ /// Most foreign helpers are located in the `dart:_foreign_helper` library. bool isForeignHelper(MemberEntity member) { return member.library == foreignLibrary || - isLateReadCheck(member) || - isLateWriteOnceCheck(member) || - isLateInitializeOnceCheck(member) || isCreateInvocationMirrorHelper(member); } @@ -1157,23 +1139,11 @@ _isTopLevelFunctionNamed('isJsSentinel', member); /// Returns `true` if [member] is the `_lateReadCheck` function defined in - /// dart:_late_helper. + /// dart:_internal. bool isLateReadCheck(MemberEntity member) => member.library == lateHelperLibrary && _isTopLevelFunctionNamed('_lateReadCheck', member); - /// Returns `true` if [member] is the `_lateWriteOnceCheck` function defined - /// in dart:_late_helper. - bool isLateWriteOnceCheck(MemberEntity member) => - member.library == lateHelperLibrary && - _isTopLevelFunctionNamed('_lateWriteOnceCheck', member); - - /// Returns `true` if [member] is the `_lateInitializeOnceCheck` function - /// defined in dart:_late_helper. - bool isLateInitializeOnceCheck(MemberEntity member) => - member.library == lateHelperLibrary && - _isTopLevelFunctionNamed('_lateInitializeOnceCheck', member); - /// Returns `true` if [member] is the `createSentinel` function defined in /// dart:_internal. bool isCreateSentinel(MemberEntity member) =>
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart index fc65993..93ca227 100644 --- a/pkg/compiler/lib/src/compiler.dart +++ b/pkg/compiler/lib/src/compiler.dart
@@ -246,9 +246,8 @@ /// Dumps a list of unused [ir.Library]'s in the [KernelResult]. This *must* /// be called before [setMainAndTrimComponent], because that method will /// discard the unused [ir.Library]s. - void dumpUnusedLibraries(ir.Component component, List<Uri> libraries) { - var usedUris = libraries.toSet(); - bool isUnused(ir.Library l) => !usedUris.contains(l.importUri); + void dumpUnusedLibraries(ir.Component component, Set<Uri> libraries) { + bool isUnused(ir.Library l) => !libraries.contains(l.importUri); String libraryString(ir.Library library) { return '${library.importUri}(${library.fileUri})'; } @@ -270,7 +269,7 @@ /// Trims a component down to only the provided library uris. ir.Component trimComponent( - ir.Component component, List<Uri> librariesToInclude) { + ir.Component component, Set<Uri> librariesToInclude) { var irLibraryMap = <Uri, ir.Library>{}; var irLibraries = <ir.Library>[]; for (var library in component.libraries) { @@ -325,11 +324,8 @@ } } - JClosedWorld computeClosedWorld( - ir.Component component, - List<ModuleData> moduleData, - Uri rootLibraryUri, - Iterable<Uri> libraries) { + JClosedWorld computeClosedWorld(ir.Component component, ModuleData moduleData, + Uri rootLibraryUri, Iterable<Uri> libraries) { frontendStrategy.registerLoadedLibraries(component, libraries); frontendStrategy.registerModuleData(moduleData); ResolutionEnqueuer resolutionEnqueuer = frontendStrategy @@ -402,16 +398,31 @@ untrimmedComponentForDumpInfo = component; } if (options.cfeOnly) { + // [ModuleData] must be deserialized with the full component, i.e. + // before trimming. + ModuleData moduleData; + if (options.modularAnalysisInputs != null) { + moduleData = await serializationTask.deserializeModuleData(component); + } + + Set<Uri> includedLibraries = output.libraries.toSet(); if (options.fromDill) { - List<Uri> libraries = output.libraries; if (options.dumpUnusedLibraries) { - dumpUnusedLibraries(component, libraries); + dumpUnusedLibraries(component, includedLibraries); } if (options.entryUri != null) { - component = trimComponent(component, libraries); + component = trimComponent(component, includedLibraries); } } - await serializationTask.serializeComponent(component); + if (moduleData == null) { + await serializationTask.serializeComponent(component); + } else { + // Trim [moduleData] down to only the included libraries. + moduleData.impactData + .removeWhere((uri, _) => !includedLibraries.contains(uri)); + await serializationTask.serializeModuleData( + moduleData, component, includedLibraries); + } } return output.withNewComponent(component); } else { @@ -434,7 +445,7 @@ 'runModularAnalysis', () async => modular_analysis.run(input)); } - Future<List<ModuleData>> produceModuleData(load_kernel.Output output) async { + Future<ModuleData> produceModuleData(load_kernel.Output output) async { ir.Component component = output.component; if (options.modularMode) { Set<Uri> moduleLibraries = output.moduleLibraries.toSet(); @@ -444,7 +455,7 @@ serializationTask.serializeModuleData( moduleData, component, moduleLibraries); } - return [moduleData]; + return moduleData; } else { return await serializationTask.deserializeModuleData(component); } @@ -530,7 +541,7 @@ } Future<ClosedWorldAndIndices> produceClosedWorld( - load_kernel.Output output, List<ModuleData> moduleData) async { + load_kernel.Output output, ModuleData moduleData) async { ir.Component component = output.component; ClosedWorldAndIndices closedWorldAndIndices; if (options.readClosedWorldUri == null) { @@ -626,7 +637,7 @@ // Run modular analysis. This may be null if modular analysis was not // requested for this pipeline. - List<ModuleData> moduleData; + ModuleData moduleData; if (options.modularMode || options.hasModularAnalysisInputs) { moduleData = await produceModuleData(output); }
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart index 6c6fd69..099cf5d 100644 --- a/pkg/compiler/lib/src/dart2js.dart +++ b/pkg/compiler/lib/src/dart2js.dart
@@ -318,15 +318,13 @@ fail("Cannot use ${Flags.writeModularAnalysis} " "and write serialized codegen simultaneously."); } - if (writeStrategy == WriteStrategy.toKernel) { - fail("Cannot use ${Flags.writeModularAnalysis} " - "and run the CFE simultaneously."); - } if (argument != Flags.writeModularAnalysis) { writeModularAnalysisUri = fe.nativeToUri(extractPath(argument, isDirectory: false)); } - writeStrategy = WriteStrategy.toModularAnalysis; + writeStrategy = writeStrategy == WriteStrategy.toKernel + ? WriteStrategy.toKernelWithModularAnalysis + : WriteStrategy.toModularAnalysis; } void setReadData(String argument) { @@ -371,10 +369,6 @@ } void setCfeOnly(String argument) { - if (writeStrategy == WriteStrategy.toModularAnalysis) { - fail("Cannot use ${Flags.cfeOnly} " - "and write serialized modular analysis simultaneously."); - } if (writeStrategy == WriteStrategy.toClosedWorld) { fail("Cannot use ${Flags.cfeOnly} " "and write serialized closed world simultaneously."); @@ -387,7 +381,9 @@ fail("Cannot use ${Flags.cfeOnly} " "and write serialized codegen simultaneously."); } - writeStrategy = WriteStrategy.toKernel; + writeStrategy = writeStrategy == WriteStrategy.toModularAnalysis + ? WriteStrategy.toKernelWithModularAnalysis + : WriteStrategy.toKernel; } void setReadCodegen(String argument) { @@ -825,6 +821,12 @@ "and read serialized codegen simultaneously."); } break; + case WriteStrategy.toKernelWithModularAnalysis: + out ??= Uri.base.resolve('out.dill'); + options.add(Flags.cfeOnly); + writeModularAnalysisUri ??= Uri.base.resolve('$out.mdata'); + options.add('${Flags.writeModularAnalysis}=${writeModularAnalysisUri}'); + break; case WriteStrategy.toModularAnalysis: writeModularAnalysisUri ??= Uri.base.resolve('$out.mdata'); options.add('${Flags.writeModularAnalysis}=${writeModularAnalysisUri}'); @@ -1007,6 +1009,15 @@ String output = fe.relativizeUri(Uri.base, out, Platform.isWindows); summary += 'compiled to dill: ${output}.'; break; + case WriteStrategy.toKernelWithModularAnalysis: + processName = 'Compiled'; + outputName = 'kernel and bytes data'; + outputSize = outputProvider.totalDataWritten; + String output = fe.relativizeUri(Uri.base, out, Platform.isWindows); + String dataOutput = fe.relativizeUri( + Uri.base, writeModularAnalysisUri, Platform.isWindows); + summary += 'compiled to dill and data: ${output} and ${dataOutput}.'; + break; case WriteStrategy.toModularAnalysis: processName = 'Serialized'; outputName = 'bytes data'; @@ -1547,6 +1558,7 @@ enum WriteStrategy { toKernel, + toKernelWithModularAnalysis, toModularAnalysis, toClosedWorld, toData,
diff --git a/pkg/compiler/lib/src/io/code_output.dart b/pkg/compiler/lib/src/io/code_output.dart index 19d4300..b603c00 100644 --- a/pkg/compiler/lib/src/io/code_output.dart +++ b/pkg/compiler/lib/src/io/code_output.dart
@@ -6,7 +6,7 @@ library dart2js.code_output; -import '../../compiler.dart'; +import '../../compiler.dart' as api show OutputSink; import 'code_output_listener.dart'; export 'code_output_listener.dart'; import 'source_information.dart'; @@ -223,7 +223,7 @@ class StreamCodeOutput extends AbstractCodeOutput { @override int length = 0; - final OutputSink output; + final api.OutputSink output; StreamCodeOutput(this.output, [List<CodeOutputListener> listeners]) : super(listeners);
diff --git a/pkg/compiler/lib/src/io/source_file.dart b/pkg/compiler/lib/src/io/source_file.dart index be4708b..961127c 100644 --- a/pkg/compiler/lib/src/io/source_file.dart +++ b/pkg/compiler/lib/src/io/source_file.dart
@@ -13,7 +13,7 @@ import 'package:kernel/ast.dart' as kernel show Location, Source; import 'location_provider.dart' show LocationProvider; -import '../../compiler.dart'; +import '../../compiler.dart' show Input, InputKind; /// Represents a file of source code. The content can be either a [String] or /// a UTF-8 encoded [List<int>] of bytes.
diff --git a/pkg/compiler/lib/src/ir/modular.dart b/pkg/compiler/lib/src/ir/modular.dart index a5da488..5487173 100644 --- a/pkg/compiler/lib/src/ir/modular.dart +++ b/pkg/compiler/lib/src/ir/modular.dart
@@ -46,27 +46,45 @@ ir.Member node, EnumSet<PragmaAnnotation> pragmaAnnotations); } -/// Data computed for an entire compilation module. +/// [ModuleData] is the data computed modularly, i.e. modularly computed impact +/// data. Currently, we aggregate this data when computing the closed world, so it +/// reflects all of the modularly computed data across the entire program. class ModuleData { static const String tag = 'ModuleData'; // TODO(joshualitt) Support serializing ModularMemberData; - final Map<ir.Member, ImpactBuilderData> impactData; + final Map<Uri, Map<ir.Member, ImpactBuilderData>> impactData; - ModuleData(this.impactData); + ModuleData([Map<Uri, Map<ir.Member, ImpactBuilderData>> impactData]) + : this.impactData = impactData ?? {}; - factory ModuleData.fromDataSource(DataSourceReader source) { + factory ModuleData.fromImpactData( + Map<Uri, Map<ir.Member, ImpactBuilderData>> impactData) => + ModuleData(impactData); + + ModuleData readMoreFromDataSource(DataSourceReader source) { source.begin(tag); - var impactData = source - .readMemberNodeMap(() => ImpactBuilderData.fromDataSource(source)); + int uriCount = source.readInt(); + for (int i = 0; i < uriCount; i++) { + Uri uri = source.readUri(); + impactData[uri] = source + .readMemberNodeMap(() => ImpactBuilderData.fromDataSource(source)); + } source.end(tag); - return ModuleData(impactData); + return this; } + factory ModuleData.fromDataSource(DataSourceReader source) => + ModuleData().readMoreFromDataSource(source); + void toDataSink(DataSinkWriter sink) { sink.begin(tag); - sink.writeMemberNodeMap<ImpactBuilderData>( - impactData, (e) => e.toDataSink(sink)); + sink.writeInt(impactData.keys.length); + impactData.forEach((uri, data) { + sink.writeUri(uri); + sink.writeMemberNodeMap<ImpactBuilderData>( + data, (e) => e.toDataSink(sink)); + }); sink.end(tag); } }
diff --git a/pkg/compiler/lib/src/js_backend/backend_impact.dart b/pkg/compiler/lib/src/js_backend/backend_impact.dart index f6dfa17..37855c0 100644 --- a/pkg/compiler/lib/src/js_backend/backend_impact.dart +++ b/pkg/compiler/lib/src/js_backend/backend_impact.dart
@@ -779,28 +779,4 @@ ], otherImpacts: [ _needsString('Needed to encode the new RTI ruleset.') ]); - - BackendImpact _lateFieldReadCheck; - - BackendImpact get lateFieldReadCheck => - _lateFieldReadCheck ??= BackendImpact(globalUses: [ - _commonElements.throwUnnamedLateFieldNI, - _commonElements.throwLateFieldNI, - ]); - - BackendImpact _lateFieldWriteOnceCheck; - - BackendImpact get lateFieldWriteOnceCheck => - _lateFieldWriteOnceCheck ??= BackendImpact(globalUses: [ - _commonElements.throwUnnamedLateFieldAI, - _commonElements.throwLateFieldAI, - ]); - - BackendImpact _lateFieldInitializeOnceCheck; - - BackendImpact get lateFieldInitializeOnceCheck => - _lateFieldInitializeOnceCheck ??= BackendImpact(globalUses: [ - _commonElements.throwUnnamedLateFieldADI, - _commonElements.throwLateFieldADI, - ]); }
diff --git a/pkg/compiler/lib/src/js_backend/frequency_assignment.dart b/pkg/compiler/lib/src/js_backend/frequency_assignment.dart index a145c9b..d01096c 100644 --- a/pkg/compiler/lib/src/js_backend/frequency_assignment.dart +++ b/pkg/compiler/lib/src/js_backend/frequency_assignment.dart
@@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -// @dart = 2.10 - import 'dart:collection' show SplayTreeMap; /// Assigns names from [nameSequence] to items using a naive algorithm. @@ -63,7 +61,7 @@ List<_Pool> pools = _Pool.makePools(names); // First cohort with unassigned items. - _Cohort firstCohort = _Cohort.makeCohorts(items, countOf); + _Cohort? firstCohort = _Cohort.makeCohorts(items, countOf); for (var pool in pools) { // Completely allocate smaller pools before allocating larger @@ -125,7 +123,7 @@ /// A [_Pool] is a set of identifiers of the same length from which names are /// allocated. class _Pool { - final List<String /*?*/ > _names = []; + final List<String?> _names = []; // Keep the unused (available) slots in an ordered set for efficiently finding // the next available slot (i.e. linear rehash). We are concerned about @@ -164,8 +162,7 @@ (throw StateError('No entries left in pool')); String allocate(int slot) { - String name = _names[slot]; - assert(name != null); + String name = _names[slot]!; _names[slot] = null; _availableSlots.remove(slot); return name; @@ -178,23 +175,23 @@ /// A [_Cohort] is a set of entities which occur with the same frequency. The /// entities are identified by integers. class _Cohort { - _Cohort next; // Next cohort in decreasing frequency. + _Cohort? next; // Next cohort in decreasing frequency. final int count; // This is the cohort of items occuring [count] times. Set<int> unassigned = Set(); _Cohort(this.count); - _Cohort skipEmpty() { - _Cohort cohort = this; + _Cohort? skipEmpty() { + _Cohort? cohort = this; while (cohort != null && cohort.remaining == 0) cohort = cohort.next; return cohort; } int get remaining => unassigned.length; - static _Cohort makeCohorts(int items, int Function(int) countOf) { + static _Cohort? makeCohorts(int items, int Function(int) countOf) { // Build _Cohorts. - _Cohort first, current; + _Cohort? first, current; int lastCount = -1; for (int item = 0; item < items; item++) { int count = countOf(item); @@ -208,7 +205,7 @@ } current = next; } - current.unassigned.add(item); + current!.unassigned.add(item); } return first; }
diff --git a/pkg/compiler/lib/src/js_backend/name_sequence.dart b/pkg/compiler/lib/src/js_backend/name_sequence.dart index 5f97bc8..1f7bdf1 100644 --- a/pkg/compiler/lib/src/js_backend/name_sequence.dart +++ b/pkg/compiler/lib/src/js_backend/name_sequence.dart
@@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -// @dart = 2.10 - import 'package:front_end/src/api_unstable/dart2js.dart' show $0, $9, $A, $Z, $_, $a, $z;
diff --git a/pkg/compiler/lib/src/js_backend/resolution_listener.dart b/pkg/compiler/lib/src/js_backend/resolution_listener.dart index c6b8b41..88e96d3 100644 --- a/pkg/compiler/lib/src/js_backend/resolution_listener.dart +++ b/pkg/compiler/lib/src/js_backend/resolution_listener.dart
@@ -292,19 +292,6 @@ _registerBackendImpact(worldImpact, _impacts.noSuchMethodSupport); } - if (_commonElements.isLateReadCheck(member)) { - _registerBackendImpact(worldImpact, _impacts.lateFieldReadCheck); - } - - if (_commonElements.isLateWriteOnceCheck(member)) { - _registerBackendImpact(worldImpact, _impacts.lateFieldWriteOnceCheck); - } - - if (_commonElements.isLateInitializeOnceCheck(member)) { - _registerBackendImpact( - worldImpact, _impacts.lateFieldInitializeOnceCheck); - } - if (member.isGetter && member.name == Identifiers.runtimeType_) { // Enable runtime type support if we discover a getter called // runtimeType. We have to enable runtime type before hitting the
diff --git a/pkg/compiler/lib/src/js_backend/string_abbreviation.dart b/pkg/compiler/lib/src/js_backend/string_abbreviation.dart index b41e069..391bf6a 100644 --- a/pkg/compiler/lib/src/js_backend/string_abbreviation.dart +++ b/pkg/compiler/lib/src/js_backend/string_abbreviation.dart
@@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -// @dart = 2.10 - import 'package:front_end/src/api_unstable/dart2js.dart' show $0, $9, $A, $Z, $a, $z; @@ -21,7 +19,7 @@ class _Node { final String string; - String assignment; + late String assignment; _Node(this.string); } @@ -47,7 +45,7 @@ // Partition on the code unit at position [index], setting [terminating] if // some string ends at this length; Map<int, List<_Node>> partition = {}; - _Node terminating; + _Node? terminating; for (final node in nodes) { String string = node.string; @@ -71,8 +69,8 @@ var keys = partition.keys.toList(); var keyEncodings = _discriminators(keys, path.isEmpty); for (int key in keys) { - var children = partition[key]; - var discriminator = keyEncodings[key]; + var children = partition[key]!; + var discriminator = keyEncodings[key]!; _partition(children, minLength, [...path, discriminator], index + 1); } return;
diff --git a/pkg/compiler/lib/src/js_emitter/headers.dart b/pkg/compiler/lib/src/js_emitter/headers.dart index 66df30c..90e4098 100644 --- a/pkg/compiler/lib/src/js_emitter/headers.dart +++ b/pkg/compiler/lib/src/js_emitter/headers.dart
@@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -// @dart = 2.10 - library dart2js.js_emitter.headers; import '../options.dart';
diff --git a/pkg/compiler/lib/src/js_emitter/sorter.dart b/pkg/compiler/lib/src/js_emitter/sorter.dart index fa27579..0de3e33 100644 --- a/pkg/compiler/lib/src/js_emitter/sorter.dart +++ b/pkg/compiler/lib/src/js_emitter/sorter.dart
@@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -// @dart = 2.10 - library dart2js.js_emitter.sorter; import '../elements/entities.dart';
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart index 9d05fc1..9430808 100644 --- a/pkg/compiler/lib/src/kernel/dart2js_target.dart +++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -235,6 +235,14 @@ const Dart2jsDartLibrarySupport(); } +const implicitlyUsedLibraries = <String>[ + 'dart:_foreign_helper', + 'dart:_interceptors', + 'dart:_js_helper', + 'dart:_late_helper', + 'dart:js_util' +]; + // TODO(sigmund): this "extraRequiredLibraries" needs to be removed... // compile-platform should just specify which libraries to compile instead. const requiredLibraries = <String, List<String>>{
diff --git a/pkg/compiler/lib/src/kernel/kernel_strategy.dart b/pkg/compiler/lib/src/kernel/kernel_strategy.dart index 94f87ab..cd1467f 100644 --- a/pkg/compiler/lib/src/kernel/kernel_strategy.dart +++ b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
@@ -229,7 +229,7 @@ } } - void registerModuleData(List<ModuleData> data) { + void registerModuleData(ModuleData data) { if (data == null) { _modularStrategy = KernelModularStrategy(_compilerTask, _elementMap); } else { @@ -452,9 +452,10 @@ final Map<ir.Member, ImpactBuilderData> _cache = {}; DeserializedModularStrategy( - this._compilerTask, this._elementMap, List<ModuleData> data) { - for (var module in data) { - _cache.addAll(module.impactData); + this._compilerTask, this._elementMap, ModuleData data) { + for (Map<ir.Member, ImpactBuilderData> moduleData + in data.impactData.values) { + _cache.addAll(moduleData); } }
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart index e899bd1..ab584e3 100644 --- a/pkg/compiler/lib/src/options.dart +++ b/pkg/compiler/lib/src/options.dart
@@ -208,7 +208,7 @@ Uri? writeModularAnalysisUri; /// Helper to determine if compiler is being run just for modular analysis. - bool get modularMode => writeModularAnalysisUri != null; + bool get modularMode => writeModularAnalysisUri != null && !cfeOnly; List<Uri>? modularAnalysisInputs;
diff --git a/pkg/compiler/lib/src/phase/load_kernel.dart b/pkg/compiler/lib/src/phase/load_kernel.dart index 57d15e7..f654265 100644 --- a/pkg/compiler/lib/src/phase/load_kernel.dart +++ b/pkg/compiler/lib/src/phase/load_kernel.dart
@@ -18,7 +18,8 @@ import '../commandline_options.dart'; import '../common.dart'; import '../kernel/front_end_adapter.dart'; -import '../kernel/dart2js_target.dart' show Dart2jsTarget; +import '../kernel/dart2js_target.dart' + show Dart2jsTarget, implicitlyUsedLibraries; import '../kernel/transformations/clone_mixin_methods_with_super.dart' as transformMixins show transformLibraries; import '../options.dart'; @@ -164,9 +165,11 @@ _inferNullSafetyMode(options, isStrongDill); _validateNullSafetyMode(options); - // Modular compiles do not include the platform on the input dill - // either. - if (options.platformBinaries != null) { + // When compiling modularly, a dill for the SDK will be provided. In those + // cases we ignore the implicit platform binary. + bool platformBinariesIncluded = + options.modularMode || options.hasModularAnalysisInputs; + if (options.platformBinaries != null && !platformBinariesIncluded) { var platformUri = options.platformBinaries .resolve(_getPlatformFilename(options, targetName)); // Modular analysis can be run on the sdk by providing directly the @@ -331,11 +334,20 @@ search(root); - // Libraries dependencies do not show implicit imports to `dart:core`. - var dartCore = component.libraries.firstWhere((lib) { - return lib.importUri.isScheme('dart') && lib.importUri.path == 'core'; - }); - search(dartCore); + // Libraries dependencies do not show implicit imports to certain internal + // libraries. + const Set<String> alwaysInclude = { + 'dart:_internal', + 'dart:core', + 'dart:async', + ...implicitlyUsedLibraries, + }; + for (String uri in alwaysInclude) { + Library library = component.libraries.firstWhere((lib) { + return '${lib.importUri}' == uri; + }); + search(library); + } libraries = libraries.where(seen.contains); }
diff --git a/pkg/compiler/lib/src/phase/modular_analysis.dart b/pkg/compiler/lib/src/phase/modular_analysis.dart index 748e97c..a1cf19a 100644 --- a/pkg/compiler/lib/src/phase/modular_analysis.dart +++ b/pkg/compiler/lib/src/phase/modular_analysis.dart
@@ -53,12 +53,12 @@ return elementMap; } -ModuleData run(Input input) { - final options = input.options; - final reporter = input.reporter; - final elementMap = _createElementMap( - options, reporter, input.environment, input.component, input.libraries); - final result = <ir.Member, ImpactBuilderData>{}; +Map<ir.Member, ImpactBuilderData> _computeForLibrary( + CompilerOptions options, + DiagnosticReporter reporter, + KernelToElementMap elementMap, + ir.Library library) { + Map<ir.Member, ImpactBuilderData> result = {}; void computeForMember(ir.Member member) { final scopeModel = ScopeModel.from(member, elementMap.constantEvaluator); final annotations = processMemberAnnotations( @@ -68,12 +68,23 @@ .impactBuilderData; } + library.members.forEach(computeForMember); + for (final cls in library.classes) { + cls.members.forEach(computeForMember); + } + return result; +} + +ModuleData run(Input input) { + final options = input.options; + final reporter = input.reporter; + final elementMap = _createElementMap( + options, reporter, input.environment, input.component, input.libraries); + Map<Uri, Map<ir.Member, ImpactBuilderData>> result = {}; for (final library in input.component.libraries) { if (!input.moduleLibraries.contains(library.importUri)) continue; - library.members.forEach(computeForMember); - for (final cls in library.classes) { - cls.members.forEach(computeForMember); - } + result[library.importUri] = + _computeForLibrary(options, reporter, elementMap, library); } - return ModuleData(result); + return ModuleData.fromImpactData(result); }
diff --git a/pkg/compiler/lib/src/serialization/task.dart b/pkg/compiler/lib/src/serialization/task.dart index 9c39ef3..6e5f7df 100644 --- a/pkg/compiler/lib/src/serialization/task.dart +++ b/pkg/compiler/lib/src/serialization/task.dart
@@ -198,7 +198,6 @@ // DataSource source = new ObjectSource(encoding, useDataKinds: true); // source.registerComponentLookup(new ComponentLookup(component)); // ModuleData.fromDataSource(source); - BytesSink bytes = BytesSink(); DataSinkWriter binarySink = DataSinkWriter(BinaryDataSink(bytes), useDataKinds: true); @@ -211,17 +210,17 @@ } } - Future<List<ModuleData>> deserializeModuleData(ir.Component component) async { + Future<ModuleData> deserializeModuleData(ir.Component component) async { return await measureIoSubtask('deserialize module data', () async { _reporter.log('Reading data from ${_options.modularAnalysisInputs}'); - List<ModuleData> results = []; + final results = ModuleData(); for (Uri uri in _options.modularAnalysisInputs) { api.Input<List<int>> dataInput = await _provider.readFromUri(uri, inputKind: api.InputKind.binary); DataSourceReader source = DataSourceReader(BinaryDataSource(dataInput.data)); source.registerComponentLookup(ComponentLookup(component)); - results.add(ModuleData.fromDataSource(source)); + results.readMoreFromDataSource(source); } return results; });
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart index 02d707c..c34f02f 100644 --- a/pkg/compiler/lib/src/ssa/builder_kernel.dart +++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -4212,12 +4212,6 @@ _handleForeignCreateJsSentinel(invocation); } else if (name == 'isJsSentinel') { _handleForeignIsJsSentinel(invocation); - } else if (name == '_lateReadCheck') { - _handleLateReadCheck(invocation); - } else if (name == '_lateWriteOnceCheck') { - _handleLateWriteOnceCheck(invocation); - } else if (name == '_lateInitializeOnceCheck') { - _handleLateInitializeOnceCheck(invocation); } else { reporter.internalError( _elementMap.getSpannable(targetElement, invocation), @@ -4907,14 +4901,6 @@ } void _handleForeignCreateJsSentinel(ir.StaticInvocation invocation) { - if (_unexpectedForeignArguments(invocation, - minPositional: 0, maxPositional: 0, typeArgumentCount: 1)) { - stack.add( - // Result expected on stack. - graph.addConstantNull(closedWorld)); - return; - } - SourceInformation sourceInformation = _sourceInformationBuilder.buildCall(invocation, invocation); stack.add(graph.addConstantLateSentinel(closedWorld, @@ -4922,14 +4908,6 @@ } void _handleForeignIsJsSentinel(ir.StaticInvocation invocation) { - if (_unexpectedForeignArguments(invocation, - minPositional: 1, maxPositional: 1)) { - stack.add( - // Result expected on stack. - graph.addConstantNull(closedWorld)); - return; - } - SourceInformation sourceInformation = _sourceInformationBuilder.buildCall(invocation, invocation); HInstruction checkedExpression = @@ -4938,72 +4916,6 @@ ..sourceInformation = sourceInformation); } - // TODO(fishythefish): Support specialization of late sentinels based on type. - - void _handleLateReadCheck(ir.StaticInvocation invocation) { - if (_unexpectedForeignArguments(invocation, - minPositional: 2, maxPositional: 2, typeArgumentCount: 1)) { - stack.add( - // Result expected on stack. - graph.addConstantNull(closedWorld)); - return; - } - - SourceInformation sourceInformation = - _sourceInformationBuilder.buildCall(invocation, invocation); - - List<HInstruction> arguments = - _visitPositionalArguments(invocation.arguments); - HInstruction value = arguments[0]; - HInstruction name = options.omitLateNames ? null : arguments[1]; - - push(HLateReadCheck(value, name, - _abstractValueDomain.excludeLateSentinel(value.instructionType)) - ..sourceInformation = sourceInformation); - } - - void _handleLateWriteOnceCheck(ir.StaticInvocation invocation) { - if (_unexpectedForeignArguments(invocation, - minPositional: 2, maxPositional: 2)) { - stack.add( - // Result expected on stack. - graph.addConstantNull(closedWorld)); - return; - } - - SourceInformation sourceInformation = - _sourceInformationBuilder.buildCall(invocation, invocation); - - List<HInstruction> arguments = - _visitPositionalArguments(invocation.arguments); - HInstruction value = arguments[0]; - HInstruction name = options.omitLateNames ? null : arguments[1]; - - push(HLateWriteOnceCheck(value, name, _abstractValueDomain.dynamicType) - ..sourceInformation = sourceInformation); - } - - void _handleLateInitializeOnceCheck(ir.StaticInvocation invocation) { - if (_unexpectedForeignArguments(invocation, - minPositional: 2, maxPositional: 2)) { - stack.add( - // Result expected on stack. - graph.addConstantNull(closedWorld)); - return; - } - - SourceInformation sourceInformation = - _sourceInformationBuilder.buildCall(invocation, invocation); - - List<HInstruction> arguments = - _visitPositionalArguments(invocation.arguments); - HInstruction value = arguments[0]; - HInstruction name = options.omitLateNames ? null : arguments[1]; - - push(HLateInitializeOnceCheck(value, name, _abstractValueDomain.dynamicType) - ..sourceInformation = sourceInformation); - } - void _pushStaticInvocation(MemberEntity target, List<HInstruction> arguments, AbstractValue typeMask, List<DartType> typeArguments, {SourceInformation sourceInformation, InterfaceType instanceType}) {
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart index c394f07..dbdf302 100644 --- a/pkg/compiler/lib/src/ssa/codegen.dart +++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -729,8 +729,7 @@ instruction is HAsCheck || instruction is HAsCheckSimple || instruction is HBoolConversion || - instruction is HNullCheck || - instruction is HLateReadCheck) { + instruction is HNullCheck) { String inputName = variableNames.getName(instruction.checkedInput); if (variableNames.getName(instruction) == inputName) { needsAssignment = false; @@ -3018,94 +3017,6 @@ } @override - void visitLateReadCheck(HLateReadCheck node) { - // We generate code roughly equivalent to invoking: - // - // T _lateReadCheck<T>(T value, String name) { - // if (isSentinel(value)) throw LateError.fieldNI(name); - // return value; - // } - - assert(!node.isRedundant(_closedWorld)); - - final sourceInformation = node.sourceInformation; - - _emitIsLateSentinel(node.checkedInput, sourceInformation); - final condition = pop(); - - if (node.hasName) { - use(node.name); - _pushCallStatic( - _commonElements.throwLateFieldNI, [pop()], sourceInformation); - } else { - _pushCallStatic( - _commonElements.throwUnnamedLateFieldNI, const [], sourceInformation); - } - - final lateError = - pop().toStatement().withSourceInformation(sourceInformation); - pushStatement(js.If.noElse(condition, lateError) - .withSourceInformation(sourceInformation)); - } - - @override - void visitLateWriteOnceCheck(HLateWriteOnceCheck node) { - // We generate code roughly equivalent to invoking: - // - // void _lateWriteOnceCheck(Object? value, String name) { - // if (!isSentinel(value)) throw LateError.fieldAI(name); - // } - - assert(!node.isRedundant(_closedWorld)); - - final sourceInformation = node.sourceInformation; - _emitIsLateSentinel(node.checkedInput, sourceInformation, inverse: true); - final condition = pop(); - - if (node.hasName) { - use(node.name); - _pushCallStatic( - _commonElements.throwLateFieldAI, [pop()], sourceInformation); - } else { - _pushCallStatic( - _commonElements.throwUnnamedLateFieldAI, [], sourceInformation); - } - final lateError = - pop().toStatement().withSourceInformation(sourceInformation); - pushStatement(js.If.noElse(condition, lateError) - .withSourceInformation(sourceInformation)); - } - - @override - void visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) { - // We generate code roughly equivalent to invoking: - // - // void _lateInitializeOnceCheck(Object? value, String name) { - // if (!isSentinel(value)) throw LateError.fieldADI(name); - // } - - assert(!node.isRedundant(_closedWorld)); - - final sourceInformation = node.sourceInformation; - _emitIsLateSentinel(node.checkedInput, sourceInformation, inverse: true); - final condition = pop(); - - if (node.hasName) { - use(node.name); - _pushCallStatic( - _commonElements.throwLateFieldADI, [pop()], sourceInformation); - } else { - _pushCallStatic( - _commonElements.throwUnnamedLateFieldADI, [], sourceInformation); - } - - final lateError = - pop().toStatement().withSourceInformation(sourceInformation); - pushStatement(js.If.noElse(condition, lateError) - .withSourceInformation(sourceInformation)); - } - - @override void visitTypeKnown(HTypeKnown node) { // [HTypeKnown] instructions are removed before generating code. assert(false); @@ -3388,9 +3299,9 @@ StaticUse.directInvoke(method, selector.callStructure, null)); } - _emitIsLateSentinel(HInstruction input, SourceInformation sourceInformation, + _emitIsLateSentinel(HIsLateSentinel node, SourceInformation sourceInformation, {inverse = false}) { - use(input); + use(node.inputs[0]); js.Expression value = pop(); js.Expression sentinel = _emitter.constantReference(LateSentinelConstantValue()); @@ -3400,5 +3311,5 @@ @override visitIsLateSentinel(HIsLateSentinel node) => - _emitIsLateSentinel(node.inputs.single, node.sourceInformation); + _emitIsLateSentinel(node, node.sourceInformation); }
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart index ca162ab..600cbac 100644 --- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart +++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -778,19 +778,6 @@ } @override - void visitLateReadCheck(HLateReadCheck instruction) { - // If the checked value is used, the input might still have one use - // (i.e. this HLateReadCheck), but it cannot be generated at use, since we - // will rely on non-generate-at-use to assign the value to a variable. - // - // However, if the checked value is unused then the input may be generated - // at use in the check. - if (instruction.usedBy.isEmpty) { - visitInstruction(instruction); - } - } - - @override void visitTypeKnown(HTypeKnown instruction) { // [HTypeKnown] instructions are removed before code generation. assert(false);
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart index 6e8e464..16ac75c 100644 --- a/pkg/compiler/lib/src/ssa/nodes.dart +++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -105,9 +105,6 @@ R visitPrimitiveCheck(HPrimitiveCheck node); R visitBoolConversion(HBoolConversion node); R visitNullCheck(HNullCheck node); - R visitLateReadCheck(HLateReadCheck node); - R visitLateWriteOnceCheck(HLateWriteOnceCheck node); - R visitLateInitializeOnceCheck(HLateInitializeOnceCheck node); R visitTypeKnown(HTypeKnown node); R visitYield(HYield node); @@ -594,13 +591,6 @@ @override visitNullCheck(HNullCheck node) => visitCheck(node); @override - visitLateReadCheck(HLateReadCheck node) => visitCheck(node); - @override - visitLateWriteOnceCheck(HLateWriteOnceCheck node) => visitCheck(node); - @override - visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) => - visitCheck(node); - @override visitPrimitiveCheck(HPrimitiveCheck node) => visitCheck(node); @override visitTypeKnown(HTypeKnown node) => visitCheck(node); @@ -1111,10 +1101,6 @@ static const int STRING_CONCAT_TYPECODE = 59; static const int STRINGIFY_TYPECODE = 60; - static const int LATE_READ_CHECK_TYPECODE = 61; - static const int LATE_WRITE_ONCE_CHECK_TYPECODE = 62; - static const int LATE_INITIALIZE_ONCE_CHECK_TYPECODE = 63; - HInstruction(this.inputs, this.instructionType) { assert(inputs.every((e) => e != null), "inputs: $inputs"); } @@ -3691,121 +3677,6 @@ } } -/// A check for a late sentinel to determine if a late field may be read from or -/// written to. -abstract class HLateCheck extends HCheck { - final HInstruction name; - - HLateCheck(HInstruction input, this.name, AbstractValue type) - : super([input, if (name != null) name], type); - - bool get hasName => name != null; - - @override - bool isControlFlow() => true; - - @override - bool isCodeMotionInvariant() => false; -} - -/// A check that a late field has been initialized and can therefore be read. -class HLateReadCheck extends HLateCheck { - HLateReadCheck(HInstruction input, HInstruction name, AbstractValue type) - : super(input, name, type); - - @override - accept(HVisitor visitor) => visitor.visitLateReadCheck(this); - - @override - int typeCode() => HInstruction.LATE_READ_CHECK_TYPECODE; - - @override - bool typeEquals(HInstruction other) => other is HLateReadCheck; - - @override - bool dataEquals(HLateReadCheck other) => true; - - bool isRedundant(JClosedWorld closedWorld) { - AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain; - AbstractValue inputType = checkedInput.instructionType; - return abstractValueDomain.isLateSentinel(inputType).isDefinitelyFalse; - } - - @override - String toString() { - return 'HLateReadCheck($checkedInput)'; - } -} - -/// A check that a late final field has not been initialized yet and can -/// therefore be written to. -/// -/// The difference between [HLateWriteOnceCheck] and [HLateInitializeOnceCheck] -/// is that the latter occurs on writes performed as part of the initializer -/// expression. -class HLateWriteOnceCheck extends HLateCheck { - HLateWriteOnceCheck(HInstruction input, HInstruction name, AbstractValue type) - : super(input, name, type); - - @override - accept(HVisitor visitor) => visitor.visitLateWriteOnceCheck(this); - - @override - int typeCode() => HInstruction.LATE_WRITE_ONCE_CHECK_TYPECODE; - - @override - bool typeEquals(HInstruction other) => other is HLateWriteOnceCheck; - - @override - bool dataEquals(HLateWriteOnceCheck other) => true; - - bool isRedundant(JClosedWorld closedWorld) { - AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain; - AbstractValue inputType = checkedInput.instructionType; - return abstractValueDomain.isLateSentinel(inputType).isDefinitelyTrue; - } - - @override - String toString() { - return 'HLateWriteOnceCheck($checkedInput)'; - } -} - -/// A check that a late final field has not been initialized yet and can -/// therefore be initialized. -/// -/// The difference between [HLateWriteOnceCheck] and [HLateInitializeOnceCheck] -/// is that the latter occurs on writes performed as part of the initializer -/// expression. -class HLateInitializeOnceCheck extends HLateCheck { - HLateInitializeOnceCheck( - HInstruction input, HInstruction name, AbstractValue type) - : super(input, name, type); - - @override - accept(HVisitor visitor) => visitor.visitLateInitializeOnceCheck(this); - - @override - int typeCode() => HInstruction.LATE_INITIALIZE_ONCE_CHECK_TYPECODE; - - @override - bool typeEquals(HInstruction other) => other is HLateInitializeOnceCheck; - - @override - bool dataEquals(HLateInitializeOnceCheck other) => true; - - bool isRedundant(JClosedWorld closedWorld) { - AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain; - AbstractValue inputType = checkedInput.instructionType; - return abstractValueDomain.isLateSentinel(inputType).isDefinitelyTrue; - } - - @override - String toString() { - return 'HLateInitializeOnceCheck($checkedInput)'; - } -} - /// The [HTypeKnown] instruction marks a value with a refined type. class HTypeKnown extends HCheck { AbstractValue knownType;
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart index 75f9512..81af4d3 100644 --- a/pkg/compiler/lib/src/ssa/optimize.dart +++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -1399,24 +1399,6 @@ } @override - HInstruction visitLateReadCheck(HLateReadCheck node) { - if (node.isRedundant(_closedWorld)) return node.checkedInput; - return node; - } - - @override - HInstruction visitLateWriteOnceCheck(HLateWriteOnceCheck node) { - if (node.isRedundant(_closedWorld)) return node.checkedInput; - return node; - } - - @override - HInstruction visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) { - if (node.isRedundant(_closedWorld)) return node.checkedInput; - return node; - } - - @override HInstruction visitTypeKnown(HTypeKnown node) { return node.isRedundant(_closedWorld) ? node.checkedInput : node; } @@ -3789,8 +3771,6 @@ @override void visitNullCheck(HNullCheck instruction) {} @override - void visitLateReadCheck(HLateReadCheck instruction) {} - @override void visitParameterValue(HParameterValue instruction) {} @override void visitRelational(HRelational instruction) {}
diff --git a/pkg/compiler/lib/src/ssa/ssa_tracer.dart b/pkg/compiler/lib/src/ssa/ssa_tracer.dart index 98f96f1..a47bd34 100644 --- a/pkg/compiler/lib/src/ssa/ssa_tracer.dart +++ b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
@@ -668,27 +668,6 @@ } @override - String visitLateReadCheck(HLateReadCheck node) { - String checkedInput = temporaryId(node.checkedInput); - String comment = node.hasName ? "(${temporaryId(node.name)})" : ""; - return "LateReadCheck$comment: $checkedInput"; - } - - @override - String visitLateWriteOnceCheck(HLateWriteOnceCheck node) { - String checkedInput = temporaryId(node.checkedInput); - String comment = node.hasName ? "(${temporaryId(node.name)})" : ""; - return "LateWriteOnceCheck$comment: $checkedInput"; - } - - @override - String visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) { - String checkedInput = temporaryId(node.checkedInput); - String comment = node.hasName ? "(${temporaryId(node.name)})" : ""; - return "LateInitializeOnceCheck$comment: $checkedInput"; - } - - @override String visitTypeKnown(HTypeKnown node) { assert(node.inputs.length <= 2); String result =
diff --git a/pkg/compiler/lib/src/ssa/types_propagation.dart b/pkg/compiler/lib/src/ssa/types_propagation.dart index 1ddc6f2..082183b 100644 --- a/pkg/compiler/lib/src/ssa/types_propagation.dart +++ b/pkg/compiler/lib/src/ssa/types_propagation.dart
@@ -449,20 +449,6 @@ } @override - AbstractValue visitLateReadCheck(HLateReadCheck instruction) { - HInstruction input = instruction.checkedInput; - AbstractValue inputType = input.instructionType; - AbstractValue outputType = - abstractValueDomain.excludeLateSentinel(inputType); - if (inputType != outputType) { - // Replace dominated uses of input with uses of this check so the uses - // benefit from the stronger type. - input.replaceAllUsersDominatedBy(instruction.next, instruction); - } - return outputType; - } - - @override AbstractValue visitAsCheck(HAsCheck instruction) { return _narrowAsCheck(instruction, instruction.checkedInput, instruction.checkedType.abstractValue);
diff --git a/pkg/compiler/test/codegen/late_field_redundancy_test.dart b/pkg/compiler/test/codegen/late_field_redundancy_test.dart deleted file mode 100644 index a0ebf05..0000000 --- a/pkg/compiler/test/codegen/late_field_redundancy_test.dart +++ /dev/null
@@ -1,42 +0,0 @@ -// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -// @dart = 2.10 - -import 'package:async_helper/async_helper.dart'; -import 'package:expect/expect.dart'; -import '../helpers/compiler_helper.dart'; - -const String TEST = r""" -class Foo { - late int x; -} - -void entry() { - final foo = Foo(); - foo.x = 42; - test(foo); -} - -@pragma('dart2js:noInline') -void test(Foo foo) { - final a = foo.x; - final b = foo.x; - print([a, b]); -} -"""; - -void main() { - asyncTest(() async { - await compile(TEST, - entry: 'entry', - methodName: 'test', - disableTypeInference: false, - disableInlining: false, - soundNullSafety: true, check: (String generated) { - RegExp regexp = new RegExp(r'=== \$'); - Expect.equals(1, regexp.allMatches(generated).length); - }); - }); -}
diff --git a/pkg/compiler/test/codegen/late_field_test.dart b/pkg/compiler/test/codegen/late_field_test.dart deleted file mode 100644 index fa34ffe..0000000 --- a/pkg/compiler/test/codegen/late_field_test.dart +++ /dev/null
@@ -1,60 +0,0 @@ -// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -// @dart = 2.10 - -import 'dart:async'; -import 'package:async_helper/async_helper.dart'; -import '../helpers/compiler_helper.dart'; - -const String TEST_DIRECT = r""" -class Foo { - late int x; -} - -int test() { - final foo = Foo(); - foo.x = 40; - return foo.x + 2; - // present: '42' - // absent: '+ 2' - // absent: 'add' -} -"""; - -const String TEST_INDIRECT = r""" -class Foo { - late int x; -} - -int entry() { - final foo = Foo(); - foo.x = 40; - return test(foo); -} - -@pragma('dart2js:noInline') -int test(Foo foo) { - return foo.x + 2; - // present: '+ 2' - // absent: 'add' -} -"""; - -Future check(String test, {String entry: 'test'}) { - return compile(test, - entry: entry, - methodName: 'test', - check: checkerForAbsentPresent(test), - disableTypeInference: false, - disableInlining: false, - soundNullSafety: true); -} - -void main() { - asyncTest(() async { - await check(TEST_DIRECT); - await check(TEST_INDIRECT, entry: 'entry'); - }); -}
diff --git a/pkg/compiler/test/codegen/late_test.dart b/pkg/compiler/test/codegen/late_test.dart new file mode 100644 index 0000000..af820fb --- /dev/null +++ b/pkg/compiler/test/codegen/late_test.dart
@@ -0,0 +1,38 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// @dart = 2.10 + +import 'dart:async'; +import 'package:async_helper/async_helper.dart'; +import '../helpers/compiler_helper.dart'; + +const String TEST = r""" +class Foo { + late int x; +} + +int foo() { + final foo = Foo(); + foo.x = 40; + return foo.x + 2; + // present: '+ 2' + // absent: 'add' +} +"""; + +Future check(String test) { + return compile(test, + entry: 'foo', + check: checkerForAbsentPresent(test), + disableTypeInference: false, + disableInlining: false, + soundNullSafety: true); +} + +void main() { + asyncTest(() async { + await check(TEST); + }); +}
diff --git a/pkg/compiler/test/dump_info/data/marker.options b/pkg/compiler/test/dump_info/data/marker.options new file mode 100644 index 0000000..620f7d3 --- /dev/null +++ b/pkg/compiler/test/dump_info/data/marker.options
@@ -0,0 +1,2 @@ +spec=pkg/compiler/test/dump_info/dump_info_test.dart +
diff --git a/pkg/compiler/test/dump_info/data/members.dart b/pkg/compiler/test/dump_info/data/members.dart new file mode 100644 index 0000000..11d6982 --- /dev/null +++ b/pkg/compiler/test/dump_info/data/members.dart
@@ -0,0 +1,245 @@ +class C { + /*member: C.value:function=[{ + "id": "field/memory:sdk/tests/web/native/main.dart::C.value", + "kind": "field", + "name": "value", + "size": 0, + "outputUnit": "outputUnit/main", + "parent": "class/memory:sdk/tests/web/native/main.dart::C", + "children": [], + "inferredType": "[exact=Error]", + "code": "", + "type": "dynamic" +}]*/ + final value; + /*member: C.counter:function=[{ + "id": "field/memory:sdk/tests/web/native/main.dart::C.counter", + "kind": "field", + "name": "counter", + "size": 18, + "outputUnit": "outputUnit/main", + "parent": "class/memory:sdk/tests/web/native/main.dart::C", + "children": [], + "inferredType": "[subclass=JSPositiveInt]", + "code": "$.C_counter = 0;\n", + "type": "int" +}]*/ + static int counter = 0; + /*member: C.y:function=[{ + "id": "field/memory:sdk/tests/web/native/main.dart::C.y", + "kind": "field", + "name": "y", + "size": 124, + "outputUnit": "outputUnit/main", + "parent": "class/memory:sdk/tests/web/native/main.dart::C", + "children": [], + "inferredType": "[null|exact=JSBool]", + "code": "_lazy($, \"C_y\", \"$get$C_y\", () => {\n var t1 = $.C_counter + 1;\n $.C_counter = t1;\n return t1 === 4;\n });\n", + "type": "bool" +}]*/ + static bool y = C.compute(); + /*member: C.compute:function=[{ + "id": "function/memory:sdk/tests/web/native/main.dart::C.compute", + "kind": "function", + "name": "compute", + "size": 0, + "outputUnit": "outputUnit/main", + "parent": "class/memory:sdk/tests/web/native/main.dart::C", + "children": [], + "modifiers": { + "static": true, + "const": false, + "factory": false, + "external": false + }, + "returnType": "bool", + "inferredReturnType": "[exact=JSBool]", + "parameters": [], + "sideEffects": "SideEffects(reads static; writes static)", + "inlinedCount": 1, + "code": "", + "type": "bool Function()" +}]*/ + static bool compute() { + C.counter += 1; + return counter == 4; + } + + /*member: C._default:function=[{ + "id": "function/memory:sdk/tests/web/native/main.dart::C.C._default", + "kind": "function", + "name": "C._default", + "size": 0, + "outputUnit": "outputUnit/main", + "parent": "class/memory:sdk/tests/web/native/main.dart::C", + "children": [], + "modifiers": { + "static": false, + "const": false, + "factory": false, + "external": false + }, + "returnType": "dynamic", + "inferredReturnType": "[exact=C]", + "parameters": [ + { + "name": "message", + "type": "[exact=Error]", + "declaredType": "Object" + } + ], + "sideEffects": "SideEffects(reads nothing; writes nothing)", + "inlinedCount": 1, + "code": "", + "type": "dynamic Function(Object)" +}]*/ + C._default(Object message) : value = message; + + /*member: C.create:function=[{ + "id": "function/memory:sdk/tests/web/native/main.dart::C.C.create", + "kind": "function", + "name": "C.create", + "size": 0, + "outputUnit": "outputUnit/main", + "parent": "class/memory:sdk/tests/web/native/main.dart::C", + "children": [], + "modifiers": { + "static": false, + "const": false, + "factory": true, + "external": false + }, + "returnType": "C", + "inferredReturnType": "[exact=C]", + "parameters": [ + { + "name": "object", + "type": "[exact=JSUInt31]", + "declaredType": "dynamic" + } + ], + "sideEffects": "SideEffects(reads nothing; writes nothing)", + "inlinedCount": 1, + "code": "", + "type": "C Function(dynamic)" +}]*/ + factory C.create(object) { + return C._default(Error()); + } +} + +/*member: F:function=[{ + "id": "function/memory:sdk/tests/web/native/main.dart::F", + "kind": "function", + "name": "F", + "size": 52, + "outputUnit": "outputUnit/main", + "parent": "library/memory:sdk/tests/web/native/main.dart::", + "children": [], + "modifiers": { + "static": false, + "const": false, + "factory": false, + "external": false + }, + "returnType": "void", + "inferredReturnType": "[null]", + "parameters": [], + "sideEffects": "SideEffects(reads nothing; writes nothing)", + "inlinedCount": 0, + "code": "F() {\n }\n_static_0(A, \"main__F$closure\", \"F\", 0);\n", + "type": "void Function()" +}]*/ +void F() {} + +class B { + static void M() {} + static const int a = 2123; +} + +class A { + /*member: A.a:function=[{ + "id": "field/memory:sdk/tests/web/native/main.dart::A.a", + "kind": "field", + "name": "a", + "size": 0, + "outputUnit": "outputUnit/main", + "parent": "class/memory:sdk/tests/web/native/main.dart::A", + "children": [], + "inferredType": "Value([exact=JSString], value: \"hello\")", + "code": "", + "type": "dynamic" +}]*/ + final a; + +/*member: A.:function=[{ + "id": "function/memory:sdk/tests/web/native/main.dart::A.A", + "kind": "function", + "name": "A", + "size": 0, + "outputUnit": "outputUnit/main", + "parent": "class/memory:sdk/tests/web/native/main.dart::A", + "children": [], + "modifiers": { + "static": false, + "const": true, + "factory": false, + "external": false + }, + "returnType": "dynamic", + "inferredReturnType": "[exact=A]", + "parameters": [], + "sideEffects": "SideEffects(reads nothing; writes nothing)", + "inlinedCount": 1, + "code": "", + "type": "dynamic Function()" +}]*/ + const A() : a = "hello"; +} + +/*member: constList:function=[{ + "id": "field/memory:sdk/tests/web/native/main.dart::constList", + "kind": "field", + "name": "constList", + "size": 0, + "outputUnit": "outputUnit/main", + "parent": "library/memory:sdk/tests/web/native/main.dart::", + "children": [], + "inferredType": "Container([exact=JSUnmodifiableArray], element: [exact=A], length: 1)", + "code": "", + "type": "List<A>" +}]*/ +final constList = const [ + const A(), +]; + +/*member: main:function=[{ + "id": "function/memory:sdk/tests/web/native/main.dart::main", + "kind": "function", + "name": "main", + "size": 199, + "outputUnit": "outputUnit/main", + "parent": "library/memory:sdk/tests/web/native/main.dart::", + "children": [], + "modifiers": { + "static": false, + "const": false, + "factory": false, + "external": false + }, + "returnType": "dynamic", + "inferredReturnType": "[null]", + "parameters": [], + "sideEffects": "SideEffects(reads anything; writes anything)", + "inlinedCount": 0, + "code": "main() {\n null.add$1(0, [B.List_A, B.C_A, 2123, 2133, A.main__F$closure(), $.$get$C_y(), \"hello\"]);\n null.add$1(0, B.C_A);\n null.add$1(0, new A.C());\n A.printString(\"null\");\n }", + "type": "dynamic Function()" +}]*/ +main() { + dynamic l = [constList, const A(), B.a, B.a + 10, F, C.y, A().a]; + dynamic r; + r.add(l); + r.add(const A()); + r.add(C.create(10)); + print(r); +}
diff --git a/pkg/compiler/test/dump_info/dump_info_test.dart b/pkg/compiler/test/dump_info/dump_info_test.dart new file mode 100644 index 0000000..8410f9b --- /dev/null +++ b/pkg/compiler/test/dump_info/dump_info_test.dart
@@ -0,0 +1,189 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// @dart = 2.7 + +import 'dart:convert'; +import 'dart:io'; +import 'package:_fe_analyzer_shared/src/testing/features.dart'; +import 'package:async_helper/async_helper.dart'; +import 'package:compiler/src/compiler.dart'; +import 'package:compiler/src/dump_info.dart'; +import 'package:compiler/src/elements/entities.dart'; +import 'package:compiler/src/js_model/element_map.dart'; +import 'package:compiler/src/js_model/js_world.dart'; +import 'package:dart2js_info/info.dart' as info; +import 'package:dart2js_info/json_info_codec.dart' as info; +import 'package:kernel/ast.dart' as ir; +import '../equivalence/id_equivalence.dart'; +import '../equivalence/id_equivalence_helper.dart'; + +main(List<String> args) { + asyncTest(() async { + Directory dataDir = new Directory.fromUri(Platform.script.resolve('data')); + print('Testing output of dump-info'); + print('=================================================================='); + await checkTests(dataDir, const DumpInfoDataComputer(), + args: args, testedConfigs: allSpecConfigs, options: ['--dump-info']); + }); +} + +class Tags { + static const String library = 'library'; + static const String clazz = 'class'; + static const String classType = 'classType'; + static const String function = 'function'; + static const String typeDef = 'typedef'; + static const String field = 'field'; + static const String constant = 'constant'; + static const String holding = 'holding'; + static const String dependencies = 'dependencies'; + static const String outputUnits = 'outputUnits'; + static const String deferredFiles = 'deferredFiles'; +} + +class DumpInfoDataComputer extends DataComputer<Features> { + const DumpInfoDataComputer(); + + static const String wildcard = '%'; + + @override + void computeMemberData(Compiler compiler, MemberEntity member, + Map<Id, ActualData<Features>> actualMap, + {bool verbose: false}) { + JsonEncoder encoder = const JsonEncoder.withIndent(' '); + var converter = info.AllInfoToJsonConverter( + isBackwardCompatible: true, filterTreeshaken: false); + DumpInfoStateData dumpInfoState = compiler.dumpInfoStateForTesting; + + Features features = new Features(); + var functionInfo = dumpInfoState.entityToInfo[member]; + if (functionInfo == null) return; + + if (functionInfo is info.FunctionInfo) { + features.addElement( + Tags.function, encoder.convert(functionInfo.accept(converter))); + } + + if (functionInfo is info.FieldInfo) { + features.addElement( + Tags.function, encoder.convert(functionInfo.accept(converter))); + } + + JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting; + JsToElementMap elementMap = closedWorld.elementMap; + ir.Member node = elementMap.getMemberDefinition(member).node; + Id id = computeMemberId(node); + ir.TreeNode nodeWithOffset = computeTreeNodeWithOffset(node); + actualMap[id] = new ActualData<Features>(id, features, + nodeWithOffset?.location?.file, nodeWithOffset?.fileOffset, member); + } + + @override + DataInterpreter<Features> get dataValidator => + const JsonFeaturesDataInterpreter(wildcard: wildcard); +} + +/// Feature interpreter for Features with Json values. +/// +/// The data annotation reader removes whitespace, but this fork adds them +/// back for readability. +class JsonFeaturesDataInterpreter implements DataInterpreter<Features> { + final String wildcard; + + const JsonFeaturesDataInterpreter({this.wildcard}); + + @override + String isAsExpected(Features actualFeatures, String expectedData) { + JsonEncoder encoder = const JsonEncoder.withIndent(' '); + + if (wildcard != null && expectedData == wildcard) { + return null; + } else if (expectedData == '') { + return actualFeatures.isNotEmpty ? "Expected empty data." : null; + } else { + List<String> errorsFound = []; + Features expectedFeatures = Features.fromText(expectedData); + Set<String> validatedFeatures = new Set<String>(); + expectedFeatures.forEach((String key, Object expectedValue) { + validatedFeatures.add(key); + Object actualValue = actualFeatures[key]; + if (!actualFeatures.containsKey(key)) { + errorsFound.add('No data found for $key'); + } else if (expectedValue == '') { + if (actualValue != '') { + errorsFound.add('Non-empty data found for $key'); + } + } else if (wildcard != null && expectedValue == wildcard) { + return; + } else if (expectedValue is List) { + if (actualValue is List) { + List actualList = actualValue.toList(); + for (Object expectedObject in expectedValue) { + String expectedText = + encoder.convert(jsonDecode('$expectedObject')); + bool matchFound = false; + if (wildcard != null && expectedText.endsWith(wildcard)) { + // Wildcard matcher. + String prefix = + expectedText.substring(0, expectedText.indexOf(wildcard)); + List matches = []; + for (Object actualObject in actualList) { + if ('$actualObject'.startsWith(prefix)) { + matches.add(actualObject); + matchFound = true; + } + } + for (Object match in matches) { + actualList.remove(match); + } + } else { + for (Object actualObject in actualList) { + if (expectedText == '$actualObject') { + actualList.remove(actualObject); + matchFound = true; + break; + } + } + } + if (!matchFound) { + errorsFound.add("No match found for $key=[$expectedText]"); + } + } + if (actualList.isNotEmpty) { + errorsFound + .add("Extra data found $key=[${actualList.join(',')}]"); + } + } else { + errorsFound.add("List data expected for $key: " + "expected '$expectedValue', found '${actualValue}'"); + } + } else if (expectedValue != actualValue) { + errorsFound.add("Mismatch for $key: expected '$expectedValue', " + "found '${actualValue}'"); + } + }); + actualFeatures.forEach((String key, Object value) { + if (!validatedFeatures.contains(key)) { + if (value == '') { + errorsFound.add("Extra data found '$key'"); + } else { + errorsFound.add("Extra data found $key=$value"); + } + } + }); + return errorsFound.isNotEmpty ? errorsFound.join('\n ') : null; + } + } + + @override + String getText(Features actualData, [String indentation]) { + return actualData.getText(indentation); + } + + @override + bool isEmpty(Features actualData) { + return actualData == null || actualData.isEmpty; + } +}
diff --git a/pkg/compiler/tool/modular_test_suite.dart b/pkg/compiler/tool/modular_test_suite.dart index 430c81a..491d6ad 100644 --- a/pkg/compiler/tool/modular_test_suite.dart +++ b/pkg/compiler/tool/modular_test_suite.dart
@@ -26,9 +26,7 @@ FullDillCompilationStep(onlyOnSdk: true), ModularAnalysisStep(onlyOnSdk: true), ModularAnalysisStep(), - // TODO(joshualitt): Re-enable ConcatenateDillStep after it works - // correctly alongside modular analysis. - // ConcatenateDillsStep(useModularAnalysis: true), + ConcatenateDillsStep(useModularAnalysis: true), ComputeClosedWorldStep(useModularAnalysis: true), GlobalAnalysisStep(), Dart2jsCodegenStep(codeId0),
diff --git a/pkg/compiler/tool/modular_test_suite_helper.dart b/pkg/compiler/tool/modular_test_suite_helper.dart index 91525b6..ac95cce 100644 --- a/pkg/compiler/tool/modular_test_suite_helper.dart +++ b/pkg/compiler/tool/modular_test_suite_helper.dart
@@ -32,6 +32,7 @@ const fullDillId = DataId("concatenate.dill"); const modularUpdatedDillId = DataId("modular.dill"); const modularDataId = DataId("modular.data"); +const modularFullDataId = DataId("concatenate.modular.data"); const closedWorldId = DataId("world"); const globalUpdatedDillId = DataId("global.dill"); const globalDataId = DataId("global.data"); @@ -304,10 +305,16 @@ DataId get idForDill => useModularAnalysis ? modularUpdatedDillId : dillId; - List<DataId> get dependencies => [idForDill]; + List<DataId> get dependencies => [ + idForDill, + if (useModularAnalysis) modularDataId, + ]; @override - List<DataId> get resultData => const [fullDillId]; + List<DataId> get resultData => [ + fullDillId, + if (useModularAnalysis) modularFullDataId, + ]; @override bool get needsSources => false; @@ -344,6 +351,10 @@ '${Flags.inputDill}=${toUri(module, dillId)}', for (String flag in flags) '--enable-experiment=$flag', '${Flags.dillDependencies}=${dillDependencies.join(',')}', + if (useModularAnalysis) ...[ + '${Flags.readModularAnalysis}=${dataDependencies.join(',')}', + '${Flags.writeModularAnalysis}=${toUri(module, modularFullDataId)}', + ], '${Flags.cfeOnly}', '--out=${toUri(module, fullDillId)}', ]; @@ -364,14 +375,11 @@ class ComputeClosedWorldStep extends IOModularStep { final bool useModularAnalysis; - DataId get idForDill => - useModularAnalysis ? modularUpdatedDillId : fullDillId; - ComputeClosedWorldStep({this.useModularAnalysis}); List<DataId> get dependencies => [ - idForDill, - if (useModularAnalysis) modularDataId, + fullDillId, + if (useModularAnalysis) modularFullDataId, ]; @override @@ -394,25 +402,16 @@ List<String> flags) async { if (_options.verbose) print("\nstep: dart2js compute closed world on $module"); - Set<Module> transitiveDependencies = computeTransitiveDependencies(module); - Iterable<String> dillDependencies = - transitiveDependencies.map((m) => '${toUri(m, idForDill)}'); - List<String> dataDependencies = transitiveDependencies - .map((m) => '${toUri(m, modularDataId)}') - .toList(); - dataDependencies.add('${toUri(module, modularDataId)}'); List<String> args = [ '--packages=${sdkRoot.toFilePath()}/$packageConfigJsonPath', _dart2jsScript, // TODO(sigmund): remove this dependency on libraries.json if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot', '${Flags.entryUri}=$fakeRoot${module.mainSource}', - '${Flags.inputDill}=${toUri(module, idForDill)}', + '${Flags.inputDill}=${toUri(module, fullDillId)}', for (String flag in flags) '--enable-experiment=$flag', - if (useModularAnalysis) ...[ - '${Flags.dillDependencies}=${dillDependencies.join(',')}', - '${Flags.readModularAnalysis}=${dataDependencies.join(',')}', - ], + if (useModularAnalysis) + '${Flags.readModularAnalysis}=${toUri(module, modularFullDataId)}', '${Flags.writeClosedWorld}=${toUri(module, closedWorldId)}', Flags.noClosedWorldInData, '--out=${toUri(module, globalUpdatedDillId)}',
diff --git a/pkg/front_end/lib/src/fasta/kernel/macro/identifiers.dart b/pkg/front_end/lib/src/fasta/kernel/macro/identifiers.dart index 8ca4ce2..1ecd8aa 100644 --- a/pkg/front_end/lib/src/fasta/kernel/macro/identifiers.dart +++ b/pkg/front_end/lib/src/fasta/kernel/macro/identifiers.dart
@@ -141,14 +141,14 @@ Uri? uri; String? staticScope; macro.IdentifierKind kind; - if (memberBuilder.isStatic || memberBuilder.isConstructor) { + if (memberBuilder.isTopLevel) { + uri = memberBuilder.libraryBuilder.importUri; + kind = macro.IdentifierKind.topLevelMember; + } else if (memberBuilder.isStatic || memberBuilder.isConstructor) { ClassBuilder classBuilder = memberBuilder.classBuilder!; staticScope = classBuilder.name; uri = classBuilder.libraryBuilder.importUri; kind = macro.IdentifierKind.staticInstanceMember; - } else if (memberBuilder.isTopLevel) { - uri = memberBuilder.libraryBuilder.importUri; - kind = macro.IdentifierKind.topLevelMember; } else { kind = macro.IdentifierKind.instanceMember; }
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart index 72cd04e..3ec34d7 100644 --- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart +++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -3595,7 +3595,9 @@ Expression _hoist(Expression expression, DartType type, List<VariableDeclaration>? hoistedExpressions) { - if (hoistedExpressions != null && expression is! ThisExpression) { + if (hoistedExpressions != null && + expression is! ThisExpression && + expression is! FunctionExpression) { VariableDeclaration variable = createVariable(expression, type); hoistedExpressions.add(variable); return createVariableGet(variable);
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status index 3d82ad2..dae8e71 100644 --- a/pkg/front_end/messages.status +++ b/pkg/front_end/messages.status
@@ -577,6 +577,10 @@ JsInteropStaticInteropWithInstanceMembers/example: Fail # Web compiler specific JsInteropStaticInteropWithNonStaticSupertype/analyzerCode: Fail # Web compiler specific JsInteropStaticInteropWithNonStaticSupertype/example: Fail # Web compiler specific +JsInteropStaticInteropTrustTypesUsageNotAllowed/analyzerCode: Fail # Web compiler specific +JsInteropStaticInteropTrustTypesUsageNotAllowed/example: Fail # Web compiler specific +JsInteropStaticInteropTrustTypesUsedWithoutStaticInterop/analyzerCode: Fail # Web compiler specific +JsInteropStaticInteropTrustTypesUsedWithoutStaticInterop/example: Fail # Web compiler specific LanguageVersionInvalidInDotPackages/analyzerCode: Fail LanguageVersionMismatchInPart/analyzerCode: Fail LanguageVersionMismatchInPart/part_wrapped_script: Fail # Part in (now) part.
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml index 2e1aa57..887e231 100644 --- a/pkg/front_end/messages.yaml +++ b/pkg/front_end/messages.yaml
@@ -5197,6 +5197,14 @@ problemMessage: "JS interop class '#name' has an `@staticInterop` annotation, but has supertype '#name2', which is non-static." correctionMessage: "Try marking the supertype as a static interop class using `@staticInterop`." +JsInteropStaticInteropTrustTypesUsedWithoutStaticInterop: + problemMessage: "JS interop class '#name' has an `@trustTypes` annotation, but no `@staticInterop` annotation." + correctionMessage: "Try marking the class using `@staticInterop`." + +JsInteropStaticInteropTrustTypesUsageNotAllowed: + problemMessage: "JS interop class '#name' has an `@trustTypes` annotation, but `@trustTypes` is only supported within the sdk." + correctionMessage: "Try removing the `@trustTypes` annotation." + DefaultListConstructorError: problemMessage: "Can't use the default List constructor." correctionMessage: "Try using List.filled instead."
diff --git a/pkg/front_end/test/spell_checking_list_messages.txt b/pkg/front_end/test/spell_checking_list_messages.txt index 329dc17..00b69d3 100644 --- a/pkg/front_end/test/spell_checking_list_messages.txt +++ b/pkg/front_end/test/spell_checking_list_messages.txt
@@ -87,6 +87,7 @@ team this.namedconstructor this.x +trusttypes type3.#name u unsound
diff --git a/pkg/front_end/testcases/extensions/issue40596.dart.weak.expect b/pkg/front_end/testcases/extensions/issue40596.dart.weak.expect index ca760ff..23c03fe 100644 --- a/pkg/front_end/testcases/extensions/issue40596.dart.weak.expect +++ b/pkg/front_end/testcases/extensions/issue40596.dart.weak.expect
@@ -11,9 +11,9 @@ } static method main() → void { asy::StreamController<core::String> controller = asy::StreamController::•<core::String>(); - let final asy::StreamController<core::String> #t1 = controller in let final (dynamic) → Null #t2 = (dynamic s) → Null { + let final asy::StreamController<core::String> #t1 = controller in self::Extension|call<core::String>(#t1.{asy::StreamController::stream}{asy::Stream<core::String>}, (dynamic s) → Null { core::print(s); - } in self::Extension|call<core::String>(#t1.{asy::StreamController::stream}{asy::Stream<core::String>}, #t2); + }); } static method Extension|call<T extends core::Object? = dynamic>(lowered final asy::Stream<self::Extension|call::T%> #this, core::Function onData) → asy::StreamSubscription<self::Extension|call::T%> { return #this.{asy::Stream::listen}((self::Extension|call::T% d) → void {
diff --git a/pkg/front_end/testcases/extensions/issue40596.dart.weak.modular.expect b/pkg/front_end/testcases/extensions/issue40596.dart.weak.modular.expect index ca760ff..23c03fe 100644 --- a/pkg/front_end/testcases/extensions/issue40596.dart.weak.modular.expect +++ b/pkg/front_end/testcases/extensions/issue40596.dart.weak.modular.expect
@@ -11,9 +11,9 @@ } static method main() → void { asy::StreamController<core::String> controller = asy::StreamController::•<core::String>(); - let final asy::StreamController<core::String> #t1 = controller in let final (dynamic) → Null #t2 = (dynamic s) → Null { + let final asy::StreamController<core::String> #t1 = controller in self::Extension|call<core::String>(#t1.{asy::StreamController::stream}{asy::Stream<core::String>}, (dynamic s) → Null { core::print(s); - } in self::Extension|call<core::String>(#t1.{asy::StreamController::stream}{asy::Stream<core::String>}, #t2); + }); } static method Extension|call<T extends core::Object? = dynamic>(lowered final asy::Stream<self::Extension|call::T%> #this, core::Function onData) → asy::StreamSubscription<self::Extension|call::T%> { return #this.{asy::Stream::listen}((self::Extension|call::T% d) → void {
diff --git a/pkg/front_end/testcases/extensions/issue40596.dart.weak.transformed.expect b/pkg/front_end/testcases/extensions/issue40596.dart.weak.transformed.expect index ca760ff..23c03fe 100644 --- a/pkg/front_end/testcases/extensions/issue40596.dart.weak.transformed.expect +++ b/pkg/front_end/testcases/extensions/issue40596.dart.weak.transformed.expect
@@ -11,9 +11,9 @@ } static method main() → void { asy::StreamController<core::String> controller = asy::StreamController::•<core::String>(); - let final asy::StreamController<core::String> #t1 = controller in let final (dynamic) → Null #t2 = (dynamic s) → Null { + let final asy::StreamController<core::String> #t1 = controller in self::Extension|call<core::String>(#t1.{asy::StreamController::stream}{asy::Stream<core::String>}, (dynamic s) → Null { core::print(s); - } in self::Extension|call<core::String>(#t1.{asy::StreamController::stream}{asy::Stream<core::String>}, #t2); + }); } static method Extension|call<T extends core::Object? = dynamic>(lowered final asy::Stream<self::Extension|call::T%> #this, core::Function onData) → asy::StreamSubscription<self::Extension|call::T%> { return #this.{asy::Stream::listen}((self::Extension|call::T% d) → void {
diff --git a/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.expect b/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.expect index c62f518..d5843e3 100644 --- a/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.expect +++ b/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.expect
@@ -35,5 +35,5 @@ Constructor coverage from constants: org-dartlang-testcase:///issue46123.dart: -- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9) +- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9) - Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.modular.expect b/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.modular.expect index c62f518..d5843e3 100644 --- a/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.modular.expect +++ b/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.modular.expect
@@ -35,5 +35,5 @@ Constructor coverage from constants: org-dartlang-testcase:///issue46123.dart: -- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9) +- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9) - Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.transformed.expect b/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.transformed.expect index c62f518..d5843e3 100644 --- a/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.transformed.expect +++ b/pkg/front_end/testcases/general/constants/js_semantics/issue46123.dart.weak.transformed.expect
@@ -35,5 +35,5 @@ Constructor coverage from constants: org-dartlang-testcase:///issue46123.dart: -- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9) +- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9) - Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.expect b/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.expect index 215e011..5ef5a68 100644 --- a/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.expect +++ b/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.expect
@@ -37,5 +37,5 @@ Constructor coverage from constants: org-dartlang-testcase:///issue46123b.dart: -- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9) +- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9) - Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.modular.expect b/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.modular.expect index 215e011..5ef5a68 100644 --- a/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.modular.expect +++ b/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.modular.expect
@@ -37,5 +37,5 @@ Constructor coverage from constants: org-dartlang-testcase:///issue46123b.dart: -- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9) +- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9) - Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)
diff --git a/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.transformed.expect b/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.transformed.expect index 215e011..5ef5a68 100644 --- a/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.transformed.expect +++ b/pkg/front_end/testcases/general/constants/js_semantics/issue46123b.dart.weak.transformed.expect
@@ -37,5 +37,5 @@ Constructor coverage from constants: org-dartlang-testcase:///issue46123b.dart: -- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:21:9) +- JS. (from org-dartlang-testcase-sdk:///pkg/js/lib/js.dart:23:9) - Object. (from org-dartlang-sdk:///lib/core/object.dart:25:9)
diff --git a/pkg/frontend_server/lib/compute_kernel.dart b/pkg/frontend_server/lib/compute_kernel.dart index 6e89be6..2c70e9d 100644 --- a/pkg/frontend_server/lib/compute_kernel.dart +++ b/pkg/frontend_server/lib/compute_kernel.dart
@@ -317,7 +317,7 @@ // TODO: Handle invalidation of precompiled macros. // TODO: Handle multiple macro libraries compiled to a single precompiled // kernel file. - var macroExecutor = state.options.macroExecutor; + var macroExecutor = state.processedOpts.macroExecutor; var format = parsedArgs['precompiled-macro-format']; for (var parts in (parsedArgs['precompiled-macro'] as List<String>) .map((arg) => arg.split(';'))) {
diff --git a/pkg/js/lib/js.dart b/pkg/js/lib/js.dart index 5f41532..8c867ae 100644 --- a/pkg/js/lib/js.dart +++ b/pkg/js/lib/js.dart
@@ -5,6 +5,8 @@ /// Annotations to mark interfaces to JavaScript. library js; +import 'package:meta/meta.dart'; + export 'dart:js' show allowInterop, allowInteropCaptureThis; /// An annotation that indicates a library, class, or member is implemented @@ -45,3 +47,16 @@ /// These classes should not contain any instance members, inherited or /// otherwise, and should instead use static extension members. const _StaticInterop staticInterop = _StaticInterop(); + +/// NOTE: [trustTypes] is an experimental annotation that may disappear at any +/// point in time. It exists solely to help users who wish to migrate classes +/// from the older style of JS interop to the new static interop model but wish +/// to preserve the older semantics for type checks. This annotation must be +/// used alongside [staticInterop] and it affects any external methods in any +/// extension to the static interop class. +@experimental +class _TrustTypes { + const _TrustTypes(); +} + +const _TrustTypes trustTypes = _TrustTypes();
diff --git a/pkg/js/pubspec.yaml b/pkg/js/pubspec.yaml index 5f5000f..c8d12d3 100644 --- a/pkg/js/pubspec.yaml +++ b/pkg/js/pubspec.yaml
@@ -6,5 +6,8 @@ environment: sdk: ">=2.16.0-100.0.dev <3.0.0" +dependencies: + meta: ^1.7.0 + dev_dependencies: lints: any
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart index 6ef2967..f3f7728 100644 --- a/pkg/kernel/lib/ast.dart +++ b/pkg/kernel/lib/ast.dart
@@ -3855,6 +3855,7 @@ visitList(positionalParameters, v); visitList(namedParameters, v); returnType.accept(v); + futureValueType?.accept(v); body?.accept(v); } @@ -3864,6 +3865,9 @@ v.transformList(positionalParameters, this); v.transformList(namedParameters, this); returnType = v.visitDartType(returnType); + if (futureValueType != null) { + futureValueType = v.visitDartType(futureValueType!); + } if (body != null) { body = v.transform(body!); body?.parent = this; @@ -3876,6 +3880,9 @@ v.transformVariableDeclarationList(positionalParameters, this); v.transformVariableDeclarationList(namedParameters, this); returnType = v.visitDartType(returnType, cannotRemoveSentinel); + if (futureValueType != null) { + futureValueType = v.visitDartType(futureValueType!, cannotRemoveSentinel); + } if (body != null) { body = v.transformOrRemoveStatement(body!); body?.parent = this;
diff --git a/pkg/nnbd_migration/pubspec.yaml b/pkg/nnbd_migration/pubspec.yaml index 19a70c2..4ed12f1 100644 --- a/pkg/nnbd_migration/pubspec.yaml +++ b/pkg/nnbd_migration/pubspec.yaml
@@ -25,7 +25,7 @@ analyzer_utilities: path: ../analyzer_utilities http: ^0.13.4 - pedantic: ^1.9.0 + lints: any test: ^1.6.4 test_reflective_loader: ^0.2.0
diff --git a/pkg/test_runner/lib/src/output_log.dart b/pkg/test_runner/lib/src/output_log.dart index fa8caae..7f04b1d 100644 --- a/pkg/test_runner/lib/src/output_log.dart +++ b/pkg/test_runner/lib/src/output_log.dart
@@ -6,9 +6,9 @@ import 'dart:convert'; import 'dart:io'; -const _nonUtf8Error = '[test.dart: This test output contains non-UTF8 data]'; +const _nonUtf8Error = '[test.dart: This test output contains non-UTF8 data.]'; const _truncatedError = - '[test.dart: This test output was too long and was truncated here.'; + '[test.dart: This test output was too long and was truncated here.]'; /// Records the output from a test. class OutputLog implements StreamConsumer<List<int>> { @@ -37,6 +37,10 @@ if (_data.length + data.length > _maxLength) { _data.addAll(data.take(_maxLength - _data.length)); + final newline = utf8.encode("\n"); + if (_data.last != newline.last) { + _data.addAll(newline); + } _data.addAll(utf8.encode(_truncatedError)); _wasTruncated = true; } else { @@ -56,6 +60,10 @@ var malformed = utf8.decode(_data, allowMalformed: true); _data.clear(); _data.addAll(utf8.encode(malformed)); + final newline = utf8.encode("\n"); + if (_data.last != newline.last) { + _data.addAll(newline); + } _data.addAll(utf8.encode(_nonUtf8Error)); return true; }
diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h index 0f8cabf..d71457b 100644 --- a/runtime/platform/globals.h +++ b/runtime/platform/globals.h
@@ -339,10 +339,10 @@ #endif // !defined(ARCH_IS_64_BIT) && !defined(FFI_UNIT_TESTS) #elif defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM) || \ defined(TARGET_ARCH_RISCV32) -#if defined(HOST_ARCH_X64) && defined(TARGET_ARCH_ARM) -// This is simarm_x64, which is the only case where host/target architecture -// mismatch is allowed. Unless, we're running FFI unit tests. -#define IS_SIMARM_X64 1 +#if defined(ARCH_IS_64_BIT) && defined(TARGET_ARCH_ARM) +// This is simarm_x64 or simarm_arm64, which is the only case where host/target +// architecture mismatch is allowed. Unless, we're running FFI unit tests. +#define IS_SIMARM_HOST64 1 #elif !defined(ARCH_IS_32_BIT) && !defined(FFI_UNIT_TESTS) #error Mismatched Host/Target architectures. #endif // !defined(ARCH_IS_32_BIT) && !defined(FFI_UNIT_TESTS) @@ -360,7 +360,7 @@ #elif defined(TARGET_ARCH_ARM) #if !defined(HOST_ARCH_ARM) #define TARGET_HOST_MISMATCH 1 -#if !defined(IS_SIMARM_X64) +#if !defined(IS_SIMARM_HOST64) #define USING_SIMULATOR 1 #endif #endif
diff --git a/runtime/vm/app_snapshot.cc b/runtime/vm/app_snapshot.cc index e2b8a40..ccdda66 100644 --- a/runtime/vm/app_snapshot.cc +++ b/runtime/vm/app_snapshot.cc
@@ -8199,7 +8199,6 @@ TIMELINE_DURATION(thread(), Isolate, "ReadAlloc"); for (intptr_t i = 0; i < num_clusters_; i++) { clusters_[i] = ReadCluster(); - TIMELINE_DURATION(thread(), Isolate, clusters_[i]->name()); clusters_[i]->ReadAlloc(this); #if defined(DEBUG) intptr_t serializers_next_ref_index_ = Read<int32_t>(); @@ -8215,7 +8214,6 @@ TIMELINE_DURATION(thread(), Isolate, "ReadFill"); SafepointWriteRwLocker ml(thread(), isolate_group()->program_lock()); for (intptr_t i = 0; i < num_clusters_; i++) { - TIMELINE_DURATION(thread(), Isolate, clusters_[i]->name()); clusters_[i]->ReadFill(this, primary); #if defined(DEBUG) int32_t section_marker = Read<int32_t>(); @@ -8247,7 +8245,6 @@ { TIMELINE_DURATION(thread(), Isolate, "PostLoad"); for (intptr_t i = 0; i < num_clusters_; i++) { - TIMELINE_DURATION(thread(), Isolate, clusters_[i]->name()); clusters_[i]->PostLoad(this, refs, primary); } }
diff --git a/runtime/vm/compiler/assembler/disassembler_arm.cc b/runtime/vm/compiler/assembler/disassembler_arm.cc index 51bb568..4d860a5 100644 --- a/runtime/vm/compiler/assembler/disassembler_arm.cc +++ b/runtime/vm/compiler/assembler/disassembler_arm.cc
@@ -1512,14 +1512,14 @@ *object = NULL; // TODO(36839): Make DecodeLoadObjectFromPoolOrThread work on simarm_x64. -#if !defined(IS_SIMARM_X64) +#if !defined(IS_SIMARM_HOST64) if (!code.IsNull()) { *object = &Object::Handle(); if (!DecodeLoadObjectFromPoolOrThread(pc, code, *object)) { *object = NULL; } } -#endif // !defined(IS_SIMARM_X64) +#endif // !defined(IS_SIMARM_HOST64) } #endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc index 367faf6..930ba5d 100644 --- a/runtime/vm/compiler/backend/il.cc +++ b/runtime/vm/compiler/backend/il.cc
@@ -6534,14 +6534,13 @@ const Register saved_fp, const Register temp0, const Register temp1) { - if (compiler::Assembler::EmittingComments()) { - __ Comment("EmitParamMoves"); - } + __ Comment("EmitParamMoves"); // Moves for return pointer. const auto& return_location = marshaller_.Location(compiler::ffi::kResultIndex); if (return_location.IsPointerToMemory()) { + __ Comment("return_location.IsPointerToMemory"); const auto& pointer_location = return_location.AsPointerToMemory().pointer_location(); const auto& pointer_register = @@ -6568,10 +6567,13 @@ arg_index++) { const intptr_t num_defs = marshaller_.NumDefinitions(arg_index); const auto& arg_target = marshaller_.Location(arg_index); + __ Comment("arg_index %" Pd " arg_target %s", arg_index, + arg_target.ToCString()); // First deal with moving all individual definitions passed in to the // FfiCall to the right native location based on calling convention. for (intptr_t i = 0; i < num_defs; i++) { + __ Comment(" def_index %" Pd, def_index); const Location origin = rebase.Rebase(locs()->in(def_index)); const Representation origin_rep = RequiredInputRepresentation(def_index) == kTagged @@ -6594,6 +6596,23 @@ // originate from parameters and thus are non-constant. UNREACHABLE(); } else { +#if defined(INCLUDE_IL_PRINTER) + __ Comment("def_target %s <- origin %s %s", def_target.ToCString(), + origin.ToCString(), RepresentationToCString(origin_rep)); +#endif // defined(INCLUDE_IL_PRINTER) +#ifdef DEBUG + // Stack arguments split are in word-size chunks. These chunks can copy + // too much. However, that doesn't matter in practise because we process + // the stack in order. + // It only matters for the last chunk, it should not overwrite what was + // already on the stack. + if (def_target.IsStack()) { + const auto& def_target_stack = def_target.AsStack(); + ASSERT(def_target_stack.offset_in_bytes() + + def_target.payload_type().SizeInBytes() <= + marshaller_.RequiredStackSpaceInBytes()); + } +#endif compiler->EmitMoveToNative(def_target, origin, origin_rep, &temp_alloc); } def_index++; @@ -6604,6 +6623,7 @@ // Note that the step above has already moved the pointer into the expected // native location. if (arg_target.IsPointerToMemory()) { + __ Comment("arg_target.IsPointerToMemory"); NoTemporaryAllocator temp_alloc; const auto& pointer_loc = arg_target.AsPointerToMemory().pointer_location(); @@ -6635,13 +6655,13 @@ const auto& src = compiler::ffi::NativeRegistersLocation( zone_, pointer_loc.payload_type(), pointer_loc.container_type(), temp0); + __ Comment("pointer_loc %s <- src %s", pointer_loc.ToCString(), + src.ToCString()); compiler->EmitNativeMove(pointer_loc, src, &temp_alloc); } } - if (compiler::Assembler::EmittingComments()) { - __ Comment("EmitParamMovesEnd"); - } + __ Comment("EmitParamMovesEnd"); } void FfiCallInstr::EmitReturnMoves(FlowGraphCompiler* compiler,
diff --git a/runtime/vm/compiler/ffi/marshaller.cc b/runtime/vm/compiler/ffi/marshaller.cc index ca4f6d6..91497a7 100644 --- a/runtime/vm/compiler/ffi/marshaller.cc +++ b/runtime/vm/compiler/ffi/marshaller.cc
@@ -453,6 +453,13 @@ // First the native arguments are on the stack. // This is governed by the native ABI, the rest we can chose freely. stack_offset += native_calling_convention_.StackTopInBytes(); +#if (defined(DART_TARGET_OS_MACOS_IOS) || defined(DART_TARGET_OS_MACOS)) && \ + defined(TARGET_ARCH_ARM64) + // Add extra padding for possibly non stack-aligned word-size writes. + // TODO(https://dartbug.com/48806): Re-engineer the moves to not over- + // approximate struct sizes on stack. + stack_offset += 4; +#endif stack_offset = Utils::RoundUp(stack_offset, compiler::target::kWordSize); if (arg_index == kResultIndex) { return stack_offset;
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc index bf4c995..2ccf215 100644 --- a/runtime/vm/dart.cc +++ b/runtime/vm/dart.cc
@@ -155,7 +155,7 @@ static DartInitializationState init_state_; static void CheckOffsets() { -#if !defined(IS_SIMARM_X64) +#if !defined(IS_SIMARM_HOST64) // These offsets are embedded in precompiled instructions. We need the // compiler and the runtime to agree. bool ok = true; @@ -241,7 +241,7 @@ #undef CHECK_CONSTANT #undef CHECK_OFFSET #undef CHECK_PAYLOAD_SIZEOF -#endif // !defined(IS_SIMARM_X64) +#endif // !defined(IS_SIMARM_HOST64) } char* Dart::DartInit(const Dart_InitializeParams* params) {
diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc index 2081ad0..0b79720 100644 --- a/runtime/vm/dart_entry.cc +++ b/runtime/vm/dart_entry.cc
@@ -169,7 +169,7 @@ Thread* thread) { ASSERT(!code.IsNull()); ASSERT(thread->no_callback_scope_depth() == 0); - ASSERT(!IsolateGroup::Current()->null_safety_not_set()); + ASSERT(!thread->isolate_group()->null_safety_not_set()); const uword stub = StubCode::InvokeDartCode().EntryPoint(); SuspendLongJumpScope suspend_long_jump_scope(thread);
diff --git a/runtime/vm/globals.h b/runtime/vm/globals.h index c564a48..559dc44 100644 --- a/runtime/vm/globals.h +++ b/runtime/vm/globals.h
@@ -112,7 +112,7 @@ #define SUPPORT_TIMELINE 1 #endif -#if defined(ARCH_IS_64_BIT) && !defined(IS_SIMARM_X64) +#if defined(ARCH_IS_64_BIT) && !defined(IS_SIMARM_HOST64) #define HASH_IN_OBJECT_HEADER 1 #endif
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc index 713bf97..68ba680 100644 --- a/runtime/vm/heap/heap.cc +++ b/runtime/vm/heap/heap.cc
@@ -67,10 +67,9 @@ } } -uword Heap::AllocateNew(intptr_t size) { - ASSERT(Thread::Current()->no_safepoint_scope_depth() == 0); - CollectForDebugging(); - Thread* thread = Thread::Current(); +uword Heap::AllocateNew(Thread* thread, intptr_t size) { + ASSERT(thread->no_safepoint_scope_depth() == 0); + CollectForDebugging(thread); uword addr = new_space_.TryAllocate(thread, size); if (LIKELY(addr != 0)) { return addr; @@ -89,18 +88,17 @@ // It is possible a GC doesn't clear enough space. // In that case, we must fall through and allocate into old space. - return AllocateOld(size, OldPage::kData); + return AllocateOld(thread, size, OldPage::kData); } -uword Heap::AllocateOld(intptr_t size, OldPage::PageType type) { - ASSERT(Thread::Current()->no_safepoint_scope_depth() == 0); +uword Heap::AllocateOld(Thread* thread, intptr_t size, OldPage::PageType type) { + ASSERT(thread->no_safepoint_scope_depth() == 0); if (old_space_.GrowthControlState()) { - CollectForDebugging(); + CollectForDebugging(thread); uword addr = old_space_.TryAllocate(size, type); if (addr != 0) { return addr; } - Thread* thread = Thread::Current(); // Wait for any GC tasks that are in progress. WaitForSweeperTasks(thread); addr = old_space_.TryAllocate(size, type); @@ -135,7 +133,7 @@ } if (old_space_.GrowthControlState()) { - WaitForSweeperTasks(Thread::Current()); + WaitForSweeperTasks(thread); old_space_.TryReleaseReservation(); } else { // We may or may not be a safepoint, so we don't know how to wait for the @@ -428,7 +426,7 @@ void Heap::CollectNewSpaceGarbage(Thread* thread, GCType type, GCReason reason) { - NoActiveIsolateScope no_active_isolate_scope; + NoActiveIsolateScope no_active_isolate_scope(thread); ASSERT(reason != GCReason::kPromotion); ASSERT(reason != GCReason::kFinalize); if (thread->isolate_group() == Dart::vm_isolate_group()) { @@ -468,7 +466,7 @@ void Heap::CollectOldSpaceGarbage(Thread* thread, GCType type, GCReason reason) { - NoActiveIsolateScope no_active_isolate_scope; + NoActiveIsolateScope no_active_isolate_scope(thread); ASSERT(type != GCType::kScavenge); ASSERT(reason != GCReason::kNewSpace); @@ -704,9 +702,9 @@ gc_on_nth_allocation_ = num_allocations; } -void Heap::CollectForDebugging() { +void Heap::CollectForDebugging(Thread* thread) { if (gc_on_nth_allocation_ == kNoForcedGarbageCollection) return; - if (Thread::Current()->IsAtSafepoint()) { + if (thread->IsAtSafepoint()) { // CollectAllGarbage is not supported when we are at a safepoint. // Allocating when at a safepoint is not a common case. return; @@ -717,7 +715,7 @@ gc_on_nth_allocation_ = kNoForcedGarbageCollection; } else { // Prevent generated code from using the TLAB fast path on next allocation. - new_space_.AbandonRemainingTLABForDebugging(Thread::Current()); + new_space_.AbandonRemainingTLABForDebugging(thread); } }
diff --git a/runtime/vm/heap/heap.h b/runtime/vm/heap/heap.h index 705e5a9..8248ee1 100644 --- a/runtime/vm/heap/heap.h +++ b/runtime/vm/heap/heap.h
@@ -59,19 +59,19 @@ Scavenger* new_space() { return &new_space_; } PageSpace* old_space() { return &old_space_; } - uword Allocate(intptr_t size, Space space) { + uword Allocate(Thread* thread, intptr_t size, Space space) { ASSERT(!read_only_); switch (space) { case kNew: // Do not attempt to allocate very large objects in new space. if (!IsAllocatableInNewSpace(size)) { - return AllocateOld(size, OldPage::kData); + return AllocateOld(thread, size, OldPage::kData); } - return AllocateNew(size); + return AllocateNew(thread, size); case kOld: - return AllocateOld(size, OldPage::kData); + return AllocateOld(thread, size, OldPage::kData); case kCode: - return AllocateOld(size, OldPage::kExecutable); + return AllocateOld(thread, size, OldPage::kExecutable); default: UNREACHABLE(); } @@ -325,8 +325,8 @@ intptr_t max_new_gen_semi_words, // Max capacity of new semi-space. intptr_t max_old_gen_words); - uword AllocateNew(intptr_t size); - uword AllocateOld(intptr_t size, OldPage::PageType type); + uword AllocateNew(Thread* thread, intptr_t size); + uword AllocateOld(Thread* thread, intptr_t size, OldPage::PageType type); // Visit all pointers. Caller must ensure concurrent sweeper is not running, // and the visitor must not allocate. @@ -355,7 +355,7 @@ void AddRegionsToObjectSet(ObjectSet* set) const; // Trigger major GC if 'gc_on_nth_allocation_' is set. - void CollectForDebugging(); + void CollectForDebugging(Thread* thread); IsolateGroup* isolate_group_; bool is_vm_isolate_;
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index a8eb8b1..fc87889 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc
@@ -538,6 +538,7 @@ void Object::InitNullAndBool(IsolateGroup* isolate_group) { // Should only be run by the vm isolate. ASSERT(isolate_group == Dart::vm_isolate_group()); + Thread* thread = Thread::Current(); auto heap = isolate_group->heap(); // TODO(iposva): NoSafepointScope needs to be added here. @@ -547,7 +548,8 @@ // 'null_' must be the first object allocated as it is used in allocation to // clear the object. { - uword address = heap->Allocate(Instance::InstanceSize(), Heap::kOld); + uword address = + heap->Allocate(thread, Instance::InstanceSize(), Heap::kOld); null_ = static_cast<InstancePtr>(address + kHeapObjectTag); // The call below is using 'null_' to initialize itself. InitializeObject(address, kNullCid, Instance::InstanceSize(), @@ -561,14 +563,14 @@ // otherwise identical. { // Allocate a dummy bool object to give true the desired alignment. - uword address = heap->Allocate(Bool::InstanceSize(), Heap::kOld); + uword address = heap->Allocate(thread, Bool::InstanceSize(), Heap::kOld); InitializeObject(address, kBoolCid, Bool::InstanceSize(), Bool::ContainsCompressedPointers()); static_cast<BoolPtr>(address + kHeapObjectTag)->untag()->value_ = false; } { // Allocate true. - uword address = heap->Allocate(Bool::InstanceSize(), Heap::kOld); + uword address = heap->Allocate(thread, Bool::InstanceSize(), Heap::kOld); true_ = static_cast<BoolPtr>(address + kHeapObjectTag); InitializeObject(address, kBoolCid, Bool::InstanceSize(), Bool::ContainsCompressedPointers()); @@ -577,7 +579,7 @@ } { // Allocate false. - uword address = heap->Allocate(Bool::InstanceSize(), Heap::kOld); + uword address = heap->Allocate(thread, Bool::InstanceSize(), Heap::kOld); false_ = static_cast<BoolPtr>(address + kHeapObjectTag); InitializeObject(address, kBoolCid, Bool::InstanceSize(), Bool::ContainsCompressedPointers()); @@ -744,7 +746,7 @@ // Allocate and initialize the class class. { intptr_t size = Class::InstanceSize(); - uword address = heap->Allocate(size, Heap::kOld); + uword address = heap->Allocate(thread, size, Heap::kOld); class_class_ = static_cast<ClassPtr>(address + kHeapObjectTag); InitializeObject(address, Class::kClassId, size, Class::ContainsCompressedPointers()); @@ -979,7 +981,7 @@ // Allocate and initialize the empty_array instance. { - uword address = heap->Allocate(Array::InstanceSize(0), Heap::kOld); + uword address = heap->Allocate(thread, Array::InstanceSize(0), Heap::kOld); InitializeObject(address, kImmutableArrayCid, Array::InstanceSize(0), Array::ContainsCompressedPointers()); Array::initializeHandle(empty_array_, @@ -991,7 +993,7 @@ Smi& smi = Smi::Handle(); // Allocate and initialize the zero_array instance. { - uword address = heap->Allocate(Array::InstanceSize(1), Heap::kOld); + uword address = heap->Allocate(thread, Array::InstanceSize(1), Heap::kOld); InitializeObject(address, kImmutableArrayCid, Array::InstanceSize(1), Array::ContainsCompressedPointers()); Array::initializeHandle(zero_array_, @@ -1004,7 +1006,8 @@ // Allocate and initialize the canonical empty context scope object. { - uword address = heap->Allocate(ContextScope::InstanceSize(0), Heap::kOld); + uword address = + heap->Allocate(thread, ContextScope::InstanceSize(0), Heap::kOld); InitializeObject(address, kContextScopeCid, ContextScope::InstanceSize(0), ContextScope::ContainsCompressedPointers()); ContextScope::initializeHandle( @@ -1019,7 +1022,8 @@ // Allocate and initialize the canonical empty object pool object. { - uword address = heap->Allocate(ObjectPool::InstanceSize(0), Heap::kOld); + uword address = + heap->Allocate(thread, ObjectPool::InstanceSize(0), Heap::kOld); InitializeObject(address, kObjectPoolCid, ObjectPool::InstanceSize(0), ObjectPool::ContainsCompressedPointers()); ObjectPool::initializeHandle( @@ -1033,7 +1037,7 @@ // Allocate and initialize the empty_compressed_stackmaps instance. { const intptr_t instance_size = CompressedStackMaps::InstanceSize(0); - uword address = heap->Allocate(instance_size, Heap::kOld); + uword address = heap->Allocate(thread, instance_size, Heap::kOld); InitializeObject(address, kCompressedStackMapsCid, instance_size, CompressedStackMaps::ContainsCompressedPointers()); CompressedStackMaps::initializeHandle( @@ -1045,7 +1049,8 @@ // Allocate and initialize the empty_descriptors instance. { - uword address = heap->Allocate(PcDescriptors::InstanceSize(0), Heap::kOld); + uword address = + heap->Allocate(thread, PcDescriptors::InstanceSize(0), Heap::kOld); InitializeObject(address, kPcDescriptorsCid, PcDescriptors::InstanceSize(0), PcDescriptors::ContainsCompressedPointers()); PcDescriptors::initializeHandle( @@ -1058,8 +1063,8 @@ // Allocate and initialize the canonical empty variable descriptor object. { - uword address = - heap->Allocate(LocalVarDescriptors::InstanceSize(0), Heap::kOld); + uword address = heap->Allocate(thread, LocalVarDescriptors::InstanceSize(0), + Heap::kOld); InitializeObject(address, kLocalVarDescriptorsCid, LocalVarDescriptors::InstanceSize(0), LocalVarDescriptors::ContainsCompressedPointers()); @@ -1076,7 +1081,7 @@ // and can share this canonical descriptor. { uword address = - heap->Allocate(ExceptionHandlers::InstanceSize(0), Heap::kOld); + heap->Allocate(thread, ExceptionHandlers::InstanceSize(0), Heap::kOld); InitializeObject(address, kExceptionHandlersCid, ExceptionHandlers::InstanceSize(0), ExceptionHandlers::ContainsCompressedPointers()); @@ -1090,7 +1095,8 @@ // Allocate and initialize the canonical empty type arguments object. { - uword address = heap->Allocate(TypeArguments::InstanceSize(0), Heap::kOld); + uword address = + heap->Allocate(thread, TypeArguments::InstanceSize(0), Heap::kOld); InitializeObject(address, kTypeArgumentsCid, TypeArguments::InstanceSize(0), TypeArguments::ContainsCompressedPointers()); TypeArguments::initializeHandle( @@ -2666,7 +2672,7 @@ ASSERT(thread->no_callback_scope_depth() == 0); Heap* heap = thread->heap(); - uword address = heap->Allocate(size, space); + uword address = heap->Allocate(thread, size, space); if (UNLIKELY(address == 0)) { // SuspendLongJumpScope during Dart entry ensures that if a longjmp base is // available, it is the innermost error handler, so check for a longjmp base @@ -2684,7 +2690,7 @@ OUT_OF_MEMORY(); } } - NoSafepointScope no_safepoint; + NoSafepointScope no_safepoint(thread); ObjectPtr raw_obj; InitializeObject(address, cls_id, size, compressed); raw_obj = static_cast<ObjectPtr>(address + kHeapObjectTag); @@ -19714,6 +19720,11 @@ if (cls.EnsureIsAllocateFinalized(thread) != Error::null()) { return Instance::null(); } + return NewAlreadyFinalized(cls, space); +} + +InstancePtr Instance::NewAlreadyFinalized(const Class& cls, Heap::Space space) { + ASSERT(cls.is_allocate_finalized()); intptr_t instance_size = cls.host_instance_size(); ASSERT(instance_size > 0); ObjectPtr raw = Object::Allocate(cls.id(), instance_size, space, @@ -24306,7 +24317,8 @@ ArrayPtr Array::Grow(const Array& source, intptr_t new_length, Heap::Space space) { - Zone* zone = Thread::Current()->zone(); + Thread* thread = Thread::Current(); + Zone* zone = thread->zone(); const Array& result = Array::Handle(zone, Array::New(new_length, space)); intptr_t len = 0; if (!source.IsNull()) { @@ -24319,7 +24331,7 @@ PassiveObject& obj = PassiveObject::Handle(zone); for (int i = 0; i < len; i++) { obj = source.At(i); - result.SetAt(i, obj); + result.SetAt(i, obj, thread); } return result.ptr(); }
diff --git a/runtime/vm/object.h b/runtime/vm/object.h index 4d16d9c..7d9ffe0 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h
@@ -7714,6 +7714,8 @@ } static InstancePtr New(const Class& cls, Heap::Space space = Heap::kNew); + static InstancePtr NewAlreadyFinalized(const Class& cls, + Heap::Space space = Heap::kNew); // Array/list element address computations. static intptr_t DataOffsetFor(intptr_t cid); @@ -10342,9 +10344,12 @@ } template <std::memory_order order = std::memory_order_relaxed> void SetAt(intptr_t index, const Object& value) const { - // TODO(iposva): Add storing NoSafepointScope. untag()->set_element<order>(index, value.ptr()); } + template <std::memory_order order = std::memory_order_relaxed> + void SetAt(intptr_t index, const Object& value, Thread* thread) const { + untag()->set_element<order>(index, value.ptr(), thread); + } // Access to the array with acquire release semantics. ObjectPtr AtAcquire(intptr_t index) const { @@ -10479,9 +10484,11 @@ memmove(const_cast<CompressedObjectPtr*>(to), from, count * kBytesPerElement); } else { + Thread* thread = Thread::Current(); const uword heap_base = ptr()->heap_base(); for (intptr_t i = 0; i < count; ++i) { - StoreArrayPointer(&to[i], from[i].Decompress(heap_base)); + untag()->StoreArrayPointer(&to[i], from[i].Decompress(heap_base), + thread); } } }
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h index 89dae69..dc4fcf3 100644 --- a/runtime/vm/raw_object.h +++ b/runtime/vm/raw_object.h
@@ -601,9 +601,7 @@ } } - template <typename type, - typename compressed_type, - std::memory_order order = std::memory_order_relaxed> + template <typename type, typename compressed_type, std::memory_order order> void StoreCompressedArrayPointer(compressed_type const* addr, type value) { reinterpret_cast<std::atomic<compressed_type>*>( const_cast<compressed_type*>(addr)) @@ -613,6 +611,18 @@ } } + template <typename type, typename compressed_type, std::memory_order order> + void StoreCompressedArrayPointer(compressed_type const* addr, + type value, + Thread* thread) { + reinterpret_cast<std::atomic<compressed_type>*>( + const_cast<compressed_type*>(addr)) + ->store(static_cast<compressed_type>(value), order); + if (value->IsHeapObject()) { + CheckArrayPointerStore(addr, value, thread); + } + } + template <typename type, typename compressed_type> void StoreCompressedArrayPointer(compressed_type const* addr, type value, @@ -863,6 +873,10 @@ void set_##accessor_name(intptr_t index, type value) { \ StoreArrayPointer<type, order>(&array_name()[index], value); \ } \ + template <std::memory_order order = std::memory_order_relaxed> \ + void set_##accessor_name(intptr_t index, type value, Thread* thread) { \ + StoreArrayPointer<type, order>(&array_name()[index], value, thread); \ + } \ \ protected: \ type* array_name() { OPEN_ARRAY_START(type, type); } \ @@ -881,6 +895,11 @@ StoreCompressedArrayPointer<type, Compressed##type, order>( \ &array_name()[index], value); \ } \ + template <std::memory_order order = std::memory_order_relaxed> \ + void set_##accessor_name(intptr_t index, type value, Thread* thread) { \ + StoreCompressedArrayPointer<type, Compressed##type, order>( \ + &array_name()[index], value, thread); \ + } \ \ protected: \ Compressed##type* array_name() { \
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc index a249423..99601df 100644 --- a/runtime/vm/runtime_entry.cc +++ b/runtime/vm/runtime_entry.cc
@@ -460,11 +460,9 @@ // Return value: newly allocated object. DEFINE_RUNTIME_ENTRY(AllocateObject, 2) { const Class& cls = Class::CheckedHandle(zone, arguments.ArgAt(0)); - const Error& error = - Error::Handle(zone, cls.EnsureIsAllocateFinalized(thread)); - ThrowIfError(error); - const Instance& instance = - Instance::Handle(zone, Instance::New(cls, SpaceForRuntimeAllocation())); + ASSERT(cls.is_allocate_finalized()); + const Instance& instance = Instance::Handle( + zone, Instance::NewAlreadyFinalized(cls, SpaceForRuntimeAllocation())); arguments.SetReturn(instance); if (cls.NumTypeArguments() == 0) { @@ -1393,7 +1391,7 @@ #if !defined(PRODUCT) // Monomorphic/megamorphic do not check the isolate's stepping flag. - if (Isolate::Current()->has_attempted_stepping()) return; + if (thread->isolate()->has_attempted_stepping()) return; #endif // Monomorphic/megamorphic calls are only for unoptimized code.
diff --git a/runtime/vm/thread.cc b/runtime/vm/thread.cc index 9f3a5bf..e8ad780 100644 --- a/runtime/vm/thread.cc +++ b/runtime/vm/thread.cc
@@ -437,7 +437,10 @@ if ((interrupt_bits & kVMInterrupt) != 0) { CheckForSafepoint(); if (isolate_group()->store_buffer()->Overflowed()) { - heap()->CollectGarbage(this, GCType::kScavenge, GCReason::kStoreBuffer); + // Evacuate: If the popular store buffer targets are copied instead of + // promoted, the store buffer won't shrink and a second scavenge will + // occur that does promote them. + heap()->CollectGarbage(this, GCType::kEvacuate, GCReason::kStoreBuffer); } #if !defined(PRODUCT)
diff --git a/sdk/lib/_internal/js_runtime/lib/late_helper.dart b/sdk/lib/_internal/js_runtime/lib/late_helper.dart index 1643555..ceabcd7 100644 --- a/sdk/lib/_internal/js_runtime/lib/late_helper.dart +++ b/sdk/lib/_internal/js_runtime/lib/late_helper.dart
@@ -6,20 +6,8 @@ import 'dart:_internal' show LateError, createSentinel, isSentinel; -@pragma('dart2js:tryInline') -void throwLateFieldNI(String fieldName) => throw LateError.fieldNI(fieldName); -@pragma('dart2js:tryInline') -void throwLateFieldAI(String fieldName) => throw LateError.fieldAI(fieldName); -@pragma('dart2js:tryInline') void throwLateFieldADI(String fieldName) => throw LateError.fieldADI(fieldName); -@pragma('dart2js:tryInline') -void throwUnnamedLateFieldNI() => throw LateError.fieldNI(''); -@pragma('dart2js:tryInline') -void throwUnnamedLateFieldAI() => throw LateError.fieldAI(''); -@pragma('dart2js:tryInline') -void throwUnnamedLateFieldADI() => throw LateError.fieldADI(''); - /// A boxed variable used for lowering uninitialized `late` variables when they /// are locals or statics. class _Cell { @@ -125,8 +113,19 @@ // Helpers for lowering late instance fields: // TODO(fishythefish): Support specialization of sentinels based on type. -external T _lateReadCheck<T>(Object? value, String name); +@pragma('dart2js:noInline') +@pragma('dart2js:as:trust') +T _lateReadCheck<T>(Object? value, String name) { + if (isSentinel(value)) throw LateError.fieldNI(name); + return value as T; +} -external void _lateWriteOnceCheck(Object? value, String name); +@pragma('dart2js:noInline') +void _lateWriteOnceCheck(Object? value, String name) { + if (!isSentinel(value)) throw LateError.fieldAI(name); +} -external void _lateInitializeOnceCheck(Object? value, String name); +@pragma('dart2js:noInline') +void _lateInitializeOnceCheck(Object? value, String name) { + if (!isSentinel(value)) throw LateError.fieldADI(name); +}
diff --git a/sdk/lib/js_util/js_util.dart b/sdk/lib/js_util/js_util.dart index 9af9ea6..d0db609 100644 --- a/sdk/lib/js_util/js_util.dart +++ b/sdk/lib/js_util/js_util.dart
@@ -74,6 +74,10 @@ T getProperty<T>(Object o, Object name) => JS<dynamic>('Object|Null', '#[#]', o, name); +/// Similar to [getProperty] but introduces an unsound implicit cast to `T`. +T _getPropertyTrustType<T>(Object o, Object name) => + JS<T>('Object|Null', '#[#]', o, name); + // A CFE transformation may optimize calls to `setProperty`, when [value] is // statically known to be a non-function. T setProperty<T>(Object o, Object name, T? value) { @@ -95,18 +99,38 @@ return JS<dynamic>('Object|Null', '#[#].apply(#, #)', o, method, o, args); } +/// Similar to [callMethod] but introduces an unsound implicit cast to `T`. +T _callMethodTrustType<T>(Object o, String method, List<Object?> args) { + assertInteropArgs(args); + return JS<T>('Object|Null', '#[#].apply(#, #)', o, method, o, args); +} + /// Unchecked version for 0 arguments, only used in a CFE transformation. @pragma('dart2js:tryInline') T _callMethodUnchecked0<T>(Object o, String method) { return JS<dynamic>('Object|Null', '#[#]()', o, method); } +/// Similar to [_callMethodUnchecked] but introduces an unsound implicit cast +/// to `T`. +@pragma('dart2js:tryInline') +T _callMethodUncheckedTrustType0<T>(Object o, String method) { + return JS<T>('Object|Null', '#[#]()', o, method); +} + /// Unchecked version for 1 argument, only used in a CFE transformation. @pragma('dart2js:tryInline') T _callMethodUnchecked1<T>(Object o, String method, Object? arg1) { return JS<dynamic>('Object|Null', '#[#](#)', o, method, arg1); } +/// Similar to [_callMethodUnchecked1] but introduces an unsound implicit cast +/// to `T`. +@pragma('dart2js:tryInline') +T _callMethodUncheckedTrustType1<T>(Object o, String method, Object? arg1) { + return JS<T>('Object|Null', '#[#](#)', o, method, arg1); +} + /// Unchecked version for 2 arguments, only used in a CFE transformation. @pragma('dart2js:tryInline') T _callMethodUnchecked2<T>( @@ -114,6 +138,14 @@ return JS<dynamic>('Object|Null', '#[#](#, #)', o, method, arg1, arg2); } +/// Similar to [_callMethodUnchecked2] but introduces an unsound implicit cast +/// to `T`. +@pragma('dart2js:tryInline') +T _callMethodUncheckedTrustType2<T>( + Object o, String method, Object? arg1, Object? arg2) { + return JS<T>('Object|Null', '#[#](#, #)', o, method, arg1, arg2); +} + /// Unchecked version for 3 arguments, only used in a CFE transformation. @pragma('dart2js:tryInline') T _callMethodUnchecked3<T>( @@ -122,6 +154,14 @@ 'Object|Null', '#[#](#, #, #)', o, method, arg1, arg2, arg3); } +/// Similar to [_callMethodUnchecked3] but introduces an unsound implicit cast +/// to `T`. +@pragma('dart2js:tryInline') +T _callMethodUncheckedTrustType3<T>( + Object o, String method, Object? arg1, Object? arg2, Object? arg3) { + return JS<T>('Object|Null', '#[#](#, #, #)', o, method, arg1, arg2, arg3); +} + /// Unchecked version for 4 arguments, only used in a CFE transformation. @pragma('dart2js:tryInline') T _callMethodUnchecked4<T>(Object o, String method, Object? arg1, Object? arg2, @@ -130,6 +170,15 @@ 'Object|Null', '#[#](#, #, #, #)', o, method, arg1, arg2, arg3, arg4); } +/// Similar to [_callMethodUnchecked4] but introduces an unsound implicit cast +/// to `T`. +@pragma('dart2js:tryInline') +T _callMethodUncheckedTrustType4<T>(Object o, String method, Object? arg1, + Object? arg2, Object? arg3, Object? arg4) { + return JS<T>( + 'Object|Null', '#[#](#, #, #, #)', o, method, arg1, arg2, arg3, arg4); +} + /// Check whether [o] is an instance of [type]. /// /// The value in [type] is expected to be a JS-interop object that
diff --git a/tests/lib/js/static_interop_test/futurevaluetype_test.dart b/tests/lib/js/static_interop_test/futurevaluetype_test.dart new file mode 100644 index 0000000..ac2a5df --- /dev/null +++ b/tests/lib/js/static_interop_test/futurevaluetype_test.dart
@@ -0,0 +1,30 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Test that `FunctionNode`'s `futureValueType`s are correctly transformed. +// See https://github.com/dart-lang/sdk/issues/48835 for more details. + +@JS() +library futurevaluetype_test; + +import 'dart:html'; + +import 'package:js/js.dart'; + +@JS() +@staticInterop +class JSWindow {} + +// `futureValueType` corresponds to the `JSWindow` type parameter in the return +// value here. If this isn't correctly erased, we should see a runtime type +// error when using this method, as we'll be attemting to return a `@Native` +// type (`Window`) where a `package:js` type is expected instead of a +// `JavaScriptObject`. +Future<JSWindow> returnInteropType() async { + return window as JSWindow; +} + +void main() async { + await returnInteropType(); +}
diff --git a/tests/lib/js/trust_types_annotation_test.dart b/tests/lib/js/trust_types_annotation_test.dart new file mode 100644 index 0000000..7248ad5 --- /dev/null +++ b/tests/lib/js/trust_types_annotation_test.dart
@@ -0,0 +1,11 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:js/js.dart'; + +@JS() +@trustTypes +class Incorrect {} +// ^ +// [web] JS interop class 'Incorrect' has an `@trustTypes` annotation, but no `@staticInterop` annotation.
diff --git a/tests/lib/js/trust_types_test.dart b/tests/lib/js/trust_types_test.dart new file mode 100644 index 0000000..2cbfb37 --- /dev/null +++ b/tests/lib/js/trust_types_test.dart
@@ -0,0 +1,77 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Test that the [trustTypes] annotation has the same semantics as the older +// style of js interop. + +import 'package:js/js.dart'; +import 'package:expect/expect.dart'; + +class NonExistent {} + +@JS() +@staticInterop +@trustTypes +class TrustMe { + external factory TrustMe(); +} + +extension TrustMeExtension on TrustMe { + external List<NonExistent> get foos; + external List<NonExistent> callFoos(); + external List<NonExistent> callFoos1(int ignored); + external List<NonExistent> callFoos2(int ignored1, int ignored2); + external List<NonExistent> callFoos3( + int ignored1, int ignored2, int ignored3); + external List<NonExistent> callFoos4( + int ignored1, int ignored2, int ignored3, int ignored4); + external List<NonExistent> callFoos5( + int ignored1, int ignored2, int ignored3, int ignored4, int ignored5); + external String get fooPrimitive; +} + +@JS() +external void eval(String code); + +void main() { + eval(r''' + TrustMe = function TrustMe() { + this.foos = 'not a list 1'; + this.fooPrimitive = 5; + this.callFoos = function() { + return 'not a list 2'; + } + this.callFoos1 = function(a) { + return 'not a list 3'; + } + this.callFoos2 = function(a, b) { + return 'not a list 4'; + } + this.callFoos3 = function(a, b, c) { + return 'not a list 5'; + } + this.callFoos4 = function(a, b, c, d) { + return 'not a list 6'; + } + this.callFoos5 = function(a, b, c, d, e) { + return 'not a list 7'; + } + } + '''); + final trusted = TrustMe(); + Expect.equals('not a list 1', trusted.foos.toString()); + Expect.equals('not a list 2', trusted.callFoos().toString()); + Expect.equals('not a list 3', trusted.callFoos1(1).toString()); + Expect.equals('not a list 4', trusted.callFoos2(1, 1).toString()); + Expect.equals('not a list 5', trusted.callFoos3(1, 1, 1).toString()); + Expect.equals('not a list 6', trusted.callFoos4(1, 1, 1, 1).toString()); + + final falseList = trusted.callFoos5(1, 1, 1, 1, 1); + Expect.equals('not a list 7', falseList.toString()); + Expect.throws(() => falseList.removeAt(0)); + + final falseString = trusted.fooPrimitive; + Expect.equals(5, falseString); + Expect.throws(() => falseString.codeUnitAt(0)); +}
diff --git a/tests/lib/lib.status b/tests/lib/lib.status index 0b262df..2f3fffb 100644 --- a/tests/lib/lib.status +++ b/tests/lib/lib.status
@@ -12,7 +12,8 @@ isolate/issue_24243_parent_isolate_test: Skip # Requires checked mode [ $runtime == d8 ] -js/js_util/javascriptobject_extensions_test.dart: SkipByDesign # Uses dart:html. +js/js_util/javascriptobject_extensions_test: SkipByDesign # Uses dart:html. +js/static_interop_test/futurevaluetype_test: SkipByDesign # Uses dart:html. [ $runtime == dart_precompiled ] isolate/package_config_getter_test: SkipByDesign # AOT mode doesn't preserve package structure. @@ -54,6 +55,7 @@ js/method_call_on_object_test: SkipByDesign # Issue 42085. js/mock_test/*: SkipByDesign # Issue 42085. js/parameters_test: SkipByDesign # Issue 42085. +js/trust_types_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code [ $compiler != dart2js && $compiler != dartdevk ] web/*: SkipByDesign
diff --git a/tests/lib_2/js/static_interop_test/futurevaluetype_test.dart b/tests/lib_2/js/static_interop_test/futurevaluetype_test.dart new file mode 100644 index 0000000..ac2a5df --- /dev/null +++ b/tests/lib_2/js/static_interop_test/futurevaluetype_test.dart
@@ -0,0 +1,30 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Test that `FunctionNode`'s `futureValueType`s are correctly transformed. +// See https://github.com/dart-lang/sdk/issues/48835 for more details. + +@JS() +library futurevaluetype_test; + +import 'dart:html'; + +import 'package:js/js.dart'; + +@JS() +@staticInterop +class JSWindow {} + +// `futureValueType` corresponds to the `JSWindow` type parameter in the return +// value here. If this isn't correctly erased, we should see a runtime type +// error when using this method, as we'll be attemting to return a `@Native` +// type (`Window`) where a `package:js` type is expected instead of a +// `JavaScriptObject`. +Future<JSWindow> returnInteropType() async { + return window as JSWindow; +} + +void main() async { + await returnInteropType(); +}
diff --git a/tests/lib_2/js/trust_types_annotation_test.dart b/tests/lib_2/js/trust_types_annotation_test.dart new file mode 100644 index 0000000..7248ad5 --- /dev/null +++ b/tests/lib_2/js/trust_types_annotation_test.dart
@@ -0,0 +1,11 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:js/js.dart'; + +@JS() +@trustTypes +class Incorrect {} +// ^ +// [web] JS interop class 'Incorrect' has an `@trustTypes` annotation, but no `@staticInterop` annotation.
diff --git a/tests/lib_2/js/trust_types_test.dart b/tests/lib_2/js/trust_types_test.dart new file mode 100644 index 0000000..2cbfb37 --- /dev/null +++ b/tests/lib_2/js/trust_types_test.dart
@@ -0,0 +1,77 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Test that the [trustTypes] annotation has the same semantics as the older +// style of js interop. + +import 'package:js/js.dart'; +import 'package:expect/expect.dart'; + +class NonExistent {} + +@JS() +@staticInterop +@trustTypes +class TrustMe { + external factory TrustMe(); +} + +extension TrustMeExtension on TrustMe { + external List<NonExistent> get foos; + external List<NonExistent> callFoos(); + external List<NonExistent> callFoos1(int ignored); + external List<NonExistent> callFoos2(int ignored1, int ignored2); + external List<NonExistent> callFoos3( + int ignored1, int ignored2, int ignored3); + external List<NonExistent> callFoos4( + int ignored1, int ignored2, int ignored3, int ignored4); + external List<NonExistent> callFoos5( + int ignored1, int ignored2, int ignored3, int ignored4, int ignored5); + external String get fooPrimitive; +} + +@JS() +external void eval(String code); + +void main() { + eval(r''' + TrustMe = function TrustMe() { + this.foos = 'not a list 1'; + this.fooPrimitive = 5; + this.callFoos = function() { + return 'not a list 2'; + } + this.callFoos1 = function(a) { + return 'not a list 3'; + } + this.callFoos2 = function(a, b) { + return 'not a list 4'; + } + this.callFoos3 = function(a, b, c) { + return 'not a list 5'; + } + this.callFoos4 = function(a, b, c, d) { + return 'not a list 6'; + } + this.callFoos5 = function(a, b, c, d, e) { + return 'not a list 7'; + } + } + '''); + final trusted = TrustMe(); + Expect.equals('not a list 1', trusted.foos.toString()); + Expect.equals('not a list 2', trusted.callFoos().toString()); + Expect.equals('not a list 3', trusted.callFoos1(1).toString()); + Expect.equals('not a list 4', trusted.callFoos2(1, 1).toString()); + Expect.equals('not a list 5', trusted.callFoos3(1, 1, 1).toString()); + Expect.equals('not a list 6', trusted.callFoos4(1, 1, 1, 1).toString()); + + final falseList = trusted.callFoos5(1, 1, 1, 1, 1); + Expect.equals('not a list 7', falseList.toString()); + Expect.throws(() => falseList.removeAt(0)); + + final falseString = trusted.fooPrimitive; + Expect.equals(5, falseString); + Expect.throws(() => falseString.codeUnitAt(0)); +}
diff --git a/tests/lib_2/lib_2.status b/tests/lib_2/lib_2.status index cb20d17..1feee84 100644 --- a/tests/lib_2/lib_2.status +++ b/tests/lib_2/lib_2.status
@@ -12,7 +12,8 @@ isolate/issue_24243_parent_isolate_test: Skip # Requires checked mode [ $runtime == d8 ] -js/js_util/javascriptobject_extensions_test.dart: SkipByDesign # Uses dart:html. +js/js_util/javascriptobject_extensions_test: SkipByDesign # Uses dart:html. +js/static_interop_test/futurevaluetype_test: SkipByDesign # Uses dart:html. [ $runtime == dart_precompiled ] isolate/package_config_getter_test: SkipByDesign # AOT mode doesn't preserve package structure. @@ -54,6 +55,7 @@ js/method_call_on_object_test: SkipByDesign # Issue 42085. js/mock_test/*: SkipByDesign # Issue 42085. js/parameters_test: SkipByDesign # Issue 42085. +js/trust_types_test: SkipByDesign # Issue 42085. CSP policy disallows injected JS code [ $compiler != dart2js && $compiler != dartdevk ] web/*: SkipByDesign
diff --git a/tests/modular/js_interop/modules.yaml b/tests/modular/js_interop/modules.yaml index 79290b9..9e9b3b6 100644 --- a/tests/modular/js_interop/modules.yaml +++ b/tests/modular/js_interop/modules.yaml
@@ -6,5 +6,6 @@ dependencies: main: log log: js + js: meta packages: js: ../../../pkg/js/lib
diff --git a/tests/modular/static_interop_erasure/modules.yaml b/tests/modular/static_interop_erasure/modules.yaml index e356ba9..af71351 100644 --- a/tests/modular/static_interop_erasure/modules.yaml +++ b/tests/modular/static_interop_erasure/modules.yaml
@@ -7,5 +7,6 @@ dependencies: main: static_interop static_interop: js + js: meta packages: js: ../../../pkg/js/lib
diff --git a/tests/modular/unused_library_and_module/main.dart b/tests/modular/unused_library_and_module/main.dart new file mode 100644 index 0000000..b808acc --- /dev/null +++ b/tests/modular/unused_library_and_module/main.dart
@@ -0,0 +1,11 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:expect/expect.dart'; + +import 'used_module/used_library.dart'; + +main() { + Expect.equals(usedConstant, 1); +}
diff --git a/tests/modular/unused_library_and_module/modules.yaml b/tests/modular/unused_library_and_module/modules.yaml new file mode 100644 index 0000000..1e0c315 --- /dev/null +++ b/tests/modular/unused_library_and_module/modules.yaml
@@ -0,0 +1,8 @@ +# Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. +# +# Regression test: integral numbers should be treated as int and not double +# after serialization across modules. +dependencies: + main: [expect, used_module, unused_module]
diff --git a/tests/modular/unused_library_and_module/unused_module.dart b/tests/modular/unused_library_and_module/unused_module.dart new file mode 100644 index 0000000..06d84dd --- /dev/null +++ b/tests/modular/unused_library_and_module/unused_module.dart
@@ -0,0 +1,4 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +const int unusedConstant2 = 3;
diff --git a/tests/modular/unused_library_and_module/used_module/unused_library.dart b/tests/modular/unused_library_and_module/used_module/unused_library.dart new file mode 100644 index 0000000..5b0b178 --- /dev/null +++ b/tests/modular/unused_library_and_module/used_module/unused_library.dart
@@ -0,0 +1,4 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +const int unusedConstant1 = 2;
diff --git a/tests/modular/unused_library_and_module/used_module/used_library.dart b/tests/modular/unused_library_and_module/used_module/used_library.dart new file mode 100644 index 0000000..ea47365 --- /dev/null +++ b/tests/modular/unused_library_and_module/used_module/used_library.dart
@@ -0,0 +1,4 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +const int usedConstant = 1;
diff --git a/tests/web/js_trust_types_disallowed_test.dart b/tests/web/js_trust_types_disallowed_test.dart new file mode 100644 index 0000000..13dd1c6 --- /dev/null +++ b/tests/web/js_trust_types_disallowed_test.dart
@@ -0,0 +1,12 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:js/js.dart'; + +@JS() +@staticInterop +@trustTypes +class Incorrect {} +// ^ +// [web] JS interop class 'Incorrect' has an `@trustTypes` annotation, but `@trustTypes` is only supported within the sdk.
diff --git a/tests/web_2/js_trust_types_disallowed_test.dart b/tests/web_2/js_trust_types_disallowed_test.dart new file mode 100644 index 0000000..13dd1c6 --- /dev/null +++ b/tests/web_2/js_trust_types_disallowed_test.dart
@@ -0,0 +1,12 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:js/js.dart'; + +@JS() +@staticInterop +@trustTypes +class Incorrect {} +// ^ +// [web] JS interop class 'Incorrect' has an `@trustTypes` annotation, but `@trustTypes` is only supported within the sdk.
diff --git a/tools/VERSION b/tools/VERSION index 370264a..d376cc8 100644 --- a/tools/VERSION +++ b/tools/VERSION
@@ -27,5 +27,5 @@ MAJOR 2 MINOR 18 PATCH 0 -PRERELEASE 40 +PRERELEASE 41 PRERELEASE_PATCH 0 \ No newline at end of file
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json index 1feea04..459362c 100644 --- a/tools/bots/test_matrix.json +++ b/tools/bots/test_matrix.json
@@ -1783,10 +1783,75 @@ }, "steps": [ { - "name": "build dart x64", + "name": "build dart debug arm64", "script": "tools/build.py", "arguments": [ - "--mode=all", + "--mode=debug", + "--arch=arm64", + "--no-clang", + "--no-goma", + "dart_precompiled_runtime", + "gen_snapshot", + "runtime" + ] + }, + { + "name": "build dart release arm64", + "script": "tools/build.py", + "arguments": [ + "--mode=release", + "--arch=arm64", + "--no-clang", + "--no-goma", + "dart_precompiled_runtime", + "gen_snapshot", + "runtime" + ] + }, + { + "name": "build dart product arm64", + "script": "tools/build.py", + "arguments": [ + "--mode=product", + "--arch=arm64", + "--no-clang", + "--no-goma", + "dart_precompiled_runtime", + "gen_snapshot", + "runtime" + ] + }, + { + "name": "build dart debug x64", + "script": "tools/build.py", + "arguments": [ + "--mode=debug", + "--arch=x64", + "--no-clang", + "--no-goma", + "dart_precompiled_runtime", + "gen_snapshot", + "runtime" + ] + }, + { + "name": "build dart release x64", + "script": "tools/build.py", + "arguments": [ + "--mode=release", + "--arch=x64", + "--no-clang", + "--no-goma", + "dart_precompiled_runtime", + "gen_snapshot", + "runtime" + ] + }, + { + "name": "build dart product x64", + "script": "tools/build.py", + "arguments": [ + "--mode=product", "--arch=x64", "--no-clang", "--no-goma", @@ -3064,7 +3129,7 @@ "name": "build dart", "script": "tools/build.py", "arguments": [ - "--arch=ia32,x64", + "--arch=ia32,x64,arm64", "--mode=release", "--check-clean", "create_sdk", @@ -3075,7 +3140,7 @@ "name": "upload sdk", "script": "tools/bots/dart_sdk.py", "arguments": [ - "--arch=ia32,x64" + "--arch=ia32,x64,arm64" ] } ]
diff --git a/tools/gn.py b/tools/gn.py index f5c01ad..a40302a 100755 --- a/tools/gn.py +++ b/tools/gn.py
@@ -67,61 +67,76 @@ return [merge(x, y) for x, y in gn_args.items()] -# Runs true if the currently executing python interpreter is running under -# Rosetta. I.e., python3 is an x64 executable and we're on an arm64 Mac. -def IsRosetta(): - if platform.system() == 'Darwin': - p = subprocess.Popen(['sysctl', '-in', 'sysctl.proc_translated'], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - output, _ = p.communicate() - return output.decode('utf-8').strip() == '1' - return False - - +# The C compiler's host. def HostCpuForArch(arch): - # Check for Rosetta before checking platform.machine(), as the latter - # returns 'x86_64' when running under Rosetta. - if IsRosetta(): - if arch in ['x64', 'x64c']: - # Without this case, we would try to build with - # host_cpu="arm64" - # target_cpu="x64" - # dart_target_arch="x64" - # Which requires the VM to use an x64 simulator in the host - # arm64 binaries, and this simulator is unimplemented. - return 'x64' - else: - return 'arm64' - - m = platform.machine() - if m == 'aarch64' or m == 'arm64': - return 'arm64' - if m == 'armv7l': - return 'arm' - - if arch in ['ia32', 'arm', 'simarm', 'simarm_x64', 'riscv32', 'simriscv32']: - return 'x86' - if arch in [ - 'x64', 'arm64', 'simarm64', 'arm_x64', 'x64c', 'arm64c', - 'simarm64c', 'riscv64', 'simriscv64' - ]: + if arch.endswith('_x64'): return 'x64' + if arch.endswith('_arm64'): + return 'arm64' + if arch.endswith('_riscv64'): + return 'riscv64' + + # For each target architecture, we prefer in descending order + # - using the same architecture for the host (supports all architectures) + # - using a host architecture with the same word size (supports arm and riscv, which have simulators) + # - using a host architecture with a different word size (supports only AOT and only 32-bit target on 64-bit host) + if arch in ['ia32']: + candidates = ['x86'] + elif arch in ['x64', 'x64c']: + candidates = ['x64'] + elif arch in ['arm', 'simarm']: + candidates = ['arm', 'x86', 'riscv32', 'arm64', 'x64', 'riscv64'] + elif arch in ['arm64', 'arm64c', 'simarm64', 'simarm64c']: + candidates = ['arm64', 'x64', 'riscv64'] + elif arch in ['riscv32', 'simriscv32']: + candidates = ['riscv32', 'arm', 'x86', 'riscv64', 'arm64', 'x64'] + elif arch in ['riscv64', 'simriscv64']: + candidates = ['riscv64', 'arm64', 'x64'] + else: + raise Exception("Unknown Dart architecture: %s" % arch) + + available = utils.HostArchitectures() + for candidate in candidates: + if candidate in available: + return candidate + + raise Exception( + "Failed to find a C host architecture for %s. Need one of %s but only %s are available." + % (arch, candidates, available)) # The C compiler's target. def TargetCpuForArch(arch, target_os): - if arch in ['ia32', 'simarm', 'simriscv32']: + # Real target architectures + if arch in ['ia32']: return 'x86' - if arch in [ - 'x64', 'simarm64', 'simarm_x64', 'simriscv64', 'x64c', 'simarm64c' - ]: + elif arch in ['x64', 'x64c']: return 'x64' - if arch == 'arm_x64': + elif arch in ['arm', 'arm_x64', 'arm_arm64', 'arm_riscv64']: return 'arm' - if arch == 'arm64c': + elif arch in ['arm64', 'arm64c']: return 'arm64' - return arch + elif arch in ['riscv32', 'riscv32_x64', 'riscv32_arm64', 'riscv32_riscv64']: + return 'riscv32' + elif arch in ['riscv64']: + return 'riscv64' + + # Simulators + if arch in ['simarm', 'simriscv32']: + candidates = ['arm', 'riscv32', 'x86'] + elif arch in ['simarm64', 'simarm64c', 'simriscv64']: + candidates = ['arm64', 'riscv64', 'x64'] + else: + raise Exception("Unknown Dart architecture: %s" % arch) + + available = utils.HostArchitectures() + for candidate in candidates: + if candidate in available: + return candidate + + raise Exception( + "Failed to find a C target architecture for %s. Need one of %s but only %s are available." + % (arch, candidates, available)) # The Dart compiler's target. @@ -198,8 +213,9 @@ gn_args['dart_use_compressed_pointers'] = IsCompressedPointerArch(arch) # Configure Crashpad library if it is used. - gn_args['dart_use_crashpad'] = (args.use_crashpad or - DART_USE_CRASHPAD in os.environ) + gn_args['dart_use_crashpad'] = ((args.use_crashpad or + DART_USE_CRASHPAD in os.environ) and + gn_args['target_cpu'] in ['x86', 'x64']) if gn_args['dart_use_crashpad']: # Tell Crashpad's BUILD files which checkout layout to use. gn_args['crashpad_dependencies'] = 'dart'
diff --git a/tools/utils.py b/tools/utils.py index 2f42263..6209073 100644 --- a/tools/utils.py +++ b/tools/utils.py
@@ -67,9 +67,11 @@ 'arm': 'arm', 'arm64': 'arm', 'arm_x64': 'arm', + 'arm_arm64': 'arm', 'simarm': 'ia32', 'simarm64': 'ia32', 'simarm_x64': 'ia32', + 'simarm_arm64': 'arm', 'x64c': 'ia32', 'arm64c': 'arm', 'simarm64c': 'ia32', @@ -149,27 +151,44 @@ return None +# Runs true if the currently executing python interpreter is running under +# Rosetta. I.e., python3 is an x64 executable and we're on an arm64 Mac. +def IsRosetta(): + if platform.system() == 'Darwin': + p = subprocess.Popen(['sysctl', '-in', 'sysctl.proc_translated'], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + output, _ = p.communicate() + return output.decode('utf-8').strip() == '1' + return False + + +# Returns the architectures that can run on the current machine. +def HostArchitectures(): + m = platform.machine() + if platform.system() == 'Darwin': + if m == 'arm64' or IsRosetta(): + # ARM64 Macs also support X64. + return ['arm64', 'x64'] + if m == 'x86_64': + # X64 Macs no longer support IA32. + return ['x64'] + else: + if m in ['aarch64', 'arm64', 'arm64e']: + return ['arm64'] + if m in ['armv7l']: + return ['arm'] + if m in ['i386', 'i686', 'ia32', 'x86']: + return ['x86'] + if m in ['x64', 'x86-64', 'x86_64', 'AMD64']: + return ['x64', 'x86'] + raise Exception('Failed to determine host architectures for %s %s', + platform.machine(), platform.system()) + + # Try to guess the host architecture. def GuessArchitecture(): - os_id = platform.machine() - if os_id.startswith('aarch64') or os_id == 'arm64': - return 'arm64' - elif os_id.startswith('arm'): - return 'arm' - elif '64' in os_id: - return 'x64' - elif (not os_id) or (not re.match('(x|i[3-6])86', os_id) is None): - return 'ia32' - elif os_id == 'i86pc': - return 'ia32' - - guess_os = GuessOS() - print('Warning: Guessing architecture {} based on os {}\n'.format( - os_id, guess_os)) - if guess_os == 'win32': - return 'ia32' - return None - + return HostArchitectures()[0] # Try to guess the number of cpus on this machine. def GuessCpus(): @@ -242,9 +261,15 @@ def IsCrossBuild(target_os, arch): - host_arch = GuessArchitecture() - return ((GetArchFamily(host_arch) != GetArchFamily(arch)) or - (target_os != GuessOS())) + if target_os in [None, 'host']: + return False + if target_os != GuessOS(): + return True + if arch.startswith('sim'): + return False + if arch in HostArchitectures(): + return False + return True def GetBuildConf(mode, arch, conf_os=None, sanitizer=None): @@ -253,9 +278,8 @@ arch.upper()) # Ask for a cross build if the host and target architectures don't match. - host_arch = GuessArchitecture() cross_build = '' - if GetArchFamily(host_arch) != GetArchFamily(arch): + if IsCrossBuild(conf_os, arch): cross_build = 'X' return '{}{}{}{}'.format(GetBuildMode(mode), GetBuildSanitizer(sanitizer), cross_build, arch.upper())