[promises] Port promiseAll to torque.
Bug: v8:9838 Change-Id: I04383cba6dcb5fc61c82cb8018160aff6fed3b15 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1988794 Commit-Queue: Joshua Litt <joshualitt@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Cr-Commit-Position: refs/heads/master@{#65899}
This commit is contained in:
parent
365d7c80e2
commit
182b43d4d5
241
BUILD.gn
241
BUILD.gn
@ -834,9 +834,15 @@ template("asm_to_inline_asm") {
|
||||
assert(emit_builtins_as_inline_asm)
|
||||
|
||||
script = "tools/snapshot/asm_to_inline_asm.py"
|
||||
deps = [ ":run_mksnapshot_" + name ]
|
||||
sources = [ "$target_gen_dir/embedded${suffix}.S" ]
|
||||
outputs = [ "$target_gen_dir/embedded${suffix}.cc" ]
|
||||
deps = [
|
||||
":run_mksnapshot_" + name,
|
||||
]
|
||||
sources = [
|
||||
"$target_gen_dir/embedded${suffix}.S",
|
||||
]
|
||||
outputs = [
|
||||
"$target_gen_dir/embedded${suffix}.cc",
|
||||
]
|
||||
args = invoker.args
|
||||
args += [
|
||||
rebase_path("$target_gen_dir/embedded${suffix}.S", root_build_dir),
|
||||
@ -850,7 +856,9 @@ if (is_android && enable_java_templates) {
|
||||
if (v8_use_external_startup_data) {
|
||||
# We don't support side-by-side snapshots on Android within Chromium.
|
||||
assert(!v8_use_multi_snapshots)
|
||||
deps = [ "//v8" ]
|
||||
deps = [
|
||||
"//v8",
|
||||
]
|
||||
renaming_sources = [ "$root_out_dir/snapshot_blob.bin" ]
|
||||
if (current_cpu == "arm" || current_cpu == "x86" ||
|
||||
current_cpu == "mipsel") {
|
||||
@ -934,12 +942,16 @@ action("postmortem-metadata") {
|
||||
"$target_gen_dir/torque-generated/instance-types-tq.h",
|
||||
]
|
||||
|
||||
outputs = [ "$target_gen_dir/debug-support.cc" ]
|
||||
outputs = [
|
||||
"$target_gen_dir/debug-support.cc",
|
||||
]
|
||||
|
||||
args = rebase_path(outputs, root_build_dir) +
|
||||
rebase_path(sources, root_build_dir)
|
||||
|
||||
deps = [ ":run_torque" ]
|
||||
deps = [
|
||||
":run_torque",
|
||||
]
|
||||
}
|
||||
|
||||
torque_files = [
|
||||
@ -983,6 +995,7 @@ torque_files = [
|
||||
"src/builtins/object-fromentries.tq",
|
||||
"src/builtins/object.tq",
|
||||
"src/builtins/promise-abstract-operations.tq",
|
||||
"src/builtins/promise-all.tq",
|
||||
"src/builtins/promise-all-element-closure.tq",
|
||||
"src/builtins/promise-constructor.tq",
|
||||
"src/builtins/promise-finally.tq",
|
||||
@ -1124,7 +1137,9 @@ template("run_torque") {
|
||||
"test/cctest/:*",
|
||||
]
|
||||
|
||||
deps = [ ":torque($toolchain)" ]
|
||||
deps = [
|
||||
":torque($toolchain)",
|
||||
]
|
||||
|
||||
script = "tools/run.py"
|
||||
|
||||
@ -1210,13 +1225,17 @@ if (v8_verify_torque_generation_invariance) {
|
||||
rebase_path("$target_gen_dir/torque-generated_x86", root_build_dir),
|
||||
rebase_path(report_file, root_build_dir),
|
||||
]
|
||||
outputs = [ report_file ]
|
||||
outputs = [
|
||||
report_file,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
group("v8_maybe_icu") {
|
||||
if (v8_enable_i18n_support) {
|
||||
public_deps = [ "//third_party/icu" ]
|
||||
public_deps = [
|
||||
"//third_party/icu",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -1228,7 +1247,9 @@ v8_source_set("torque_generated_initializers") {
|
||||
":run_torque",
|
||||
]
|
||||
|
||||
public_deps = [ ":v8_maybe_icu" ]
|
||||
public_deps = [
|
||||
":v8_maybe_icu",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"$target_gen_dir/torque-generated/csa-types-tq.h",
|
||||
@ -1256,7 +1277,9 @@ v8_source_set("torque_generated_definitions") {
|
||||
":run_torque",
|
||||
]
|
||||
|
||||
public_deps = [ ":v8_maybe_icu" ]
|
||||
public_deps = [
|
||||
":v8_maybe_icu",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"$target_gen_dir/torque-generated/class-definitions-tq.cc",
|
||||
@ -1270,8 +1293,12 @@ v8_source_set("torque_generated_definitions") {
|
||||
|
||||
action("generate_bytecode_builtins_list") {
|
||||
script = "tools/run.py"
|
||||
outputs = [ "$target_gen_dir/builtins-generated/bytecodes-builtins-list.h" ]
|
||||
deps = [ ":bytecode_builtins_list_generator($v8_generator_toolchain)" ]
|
||||
outputs = [
|
||||
"$target_gen_dir/builtins-generated/bytecodes-builtins-list.h",
|
||||
]
|
||||
deps = [
|
||||
":bytecode_builtins_list_generator($v8_generator_toolchain)",
|
||||
]
|
||||
args = [
|
||||
"./" + rebase_path(
|
||||
get_label_info(
|
||||
@ -1303,7 +1330,9 @@ template("run_mksnapshot") {
|
||||
action("run_mksnapshot_" + name) {
|
||||
visibility = [ ":*" ] # Only targets in this file can depend on this.
|
||||
|
||||
deps = [ ":mksnapshot($v8_snapshot_toolchain)" ]
|
||||
deps = [
|
||||
":mksnapshot($v8_snapshot_toolchain)",
|
||||
]
|
||||
|
||||
script = "tools/run.py"
|
||||
|
||||
@ -1434,7 +1463,9 @@ if (v8_use_multi_snapshots) {
|
||||
|
||||
action("v8_dump_build_config") {
|
||||
script = "tools/testrunner/utils/dump_build_config.py"
|
||||
outputs = [ "$root_out_dir/v8_build_config.json" ]
|
||||
outputs = [
|
||||
"$root_out_dir/v8_build_config.json",
|
||||
]
|
||||
is_gcov_coverage = v8_code_coverage && !is_clang
|
||||
is_full_debug = v8_enable_debugging_features && !v8_optimized_debug
|
||||
args = [
|
||||
@ -1488,7 +1519,9 @@ v8_source_set("v8_snapshot") {
|
||||
# Do not publicize any header to remove build dependency.
|
||||
public = []
|
||||
|
||||
sources = [ "src/init/setup-isolate-deserialize.cc" ]
|
||||
sources = [
|
||||
"src/init/setup-isolate-deserialize.cc",
|
||||
]
|
||||
if (emit_builtins_as_inline_asm) {
|
||||
deps += [ ":asm_to_inline_asm_default" ]
|
||||
sources += [ "$target_gen_dir/embedded.cc" ]
|
||||
@ -1528,7 +1561,9 @@ v8_source_set("v8_initializers") {
|
||||
"test/cctest:*",
|
||||
]
|
||||
|
||||
deps = [ ":torque_generated_initializers" ]
|
||||
deps = [
|
||||
":torque_generated_initializers",
|
||||
]
|
||||
|
||||
sources = [
|
||||
### gcmole(all) ###
|
||||
@ -1651,14 +1686,18 @@ v8_source_set("v8_initializers") {
|
||||
v8_source_set("v8_init") {
|
||||
visibility = [ ":*" ] # Only targets in this file can depend on this.
|
||||
|
||||
deps = [ ":v8_initializers" ]
|
||||
deps = [
|
||||
":v8_initializers",
|
||||
]
|
||||
|
||||
sources = [
|
||||
### gcmole(all) ###
|
||||
"src/init/setup-isolate-full.cc",
|
||||
]
|
||||
|
||||
public_deps = [ ":v8_maybe_icu" ]
|
||||
public_deps = [
|
||||
":v8_maybe_icu",
|
||||
]
|
||||
|
||||
configs = [ ":internal_config" ]
|
||||
}
|
||||
@ -1695,7 +1734,9 @@ v8_header_set("v8_headers") {
|
||||
"include/v8-wasm-trap-handler-win.h",
|
||||
]
|
||||
|
||||
deps = [ ":v8_version" ]
|
||||
deps = [
|
||||
":v8_version",
|
||||
]
|
||||
}
|
||||
|
||||
# This is split out to share basic headers with Torque.
|
||||
@ -1703,9 +1744,13 @@ v8_header_set("v8_shared_internal_headers") {
|
||||
visibility = [ ":*" ] # Only targets in this file can depend on this.
|
||||
configs = [ ":internal_config" ]
|
||||
|
||||
sources = [ "src/common/globals.h" ]
|
||||
sources = [
|
||||
"src/common/globals.h",
|
||||
]
|
||||
|
||||
deps = [ ":v8_headers" ]
|
||||
deps = [
|
||||
":v8_headers",
|
||||
]
|
||||
}
|
||||
|
||||
v8_compiler_sources = [
|
||||
@ -1986,9 +2031,13 @@ v8_source_set("v8_compiler") {
|
||||
|
||||
group("v8_compiler_for_mksnapshot") {
|
||||
if (is_debug && !v8_optimized_debug && v8_enable_fast_mksnapshot) {
|
||||
deps = [ ":v8_compiler_opt" ]
|
||||
deps = [
|
||||
":v8_compiler_opt",
|
||||
]
|
||||
} else {
|
||||
deps = [ ":v8_compiler" ]
|
||||
deps = [
|
||||
":v8_compiler",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -3439,9 +3488,13 @@ v8_source_set("torque_base") {
|
||||
"src/torque/utils.h",
|
||||
]
|
||||
|
||||
deps = [ ":v8_shared_internal_headers" ]
|
||||
deps = [
|
||||
":v8_shared_internal_headers",
|
||||
]
|
||||
|
||||
public_deps = [ ":v8_libbase" ]
|
||||
public_deps = [
|
||||
":v8_libbase",
|
||||
]
|
||||
|
||||
# The use of exceptions for Torque in violation of the Chromium style-guide
|
||||
# is justified by the fact that it is only used from the non-essential
|
||||
@ -3484,7 +3537,9 @@ v8_source_set("torque_ls_base") {
|
||||
"src/torque/ls/message.h",
|
||||
]
|
||||
|
||||
public_deps = [ ":torque_base" ]
|
||||
public_deps = [
|
||||
":torque_base",
|
||||
]
|
||||
|
||||
# The use of exceptions for Torque in violation of the Chromium style-guide
|
||||
# is justified by the fact that it is only used from the non-essential
|
||||
@ -3585,7 +3640,9 @@ v8_component("v8_libbase") {
|
||||
|
||||
public_configs = [ ":libbase_config" ]
|
||||
|
||||
deps = [ ":v8_headers" ]
|
||||
deps = [
|
||||
":v8_headers",
|
||||
]
|
||||
|
||||
public_deps = []
|
||||
|
||||
@ -3771,7 +3828,9 @@ v8_source_set("v8_libsampler") {
|
||||
|
||||
public_configs = [ ":libsampler_config" ]
|
||||
|
||||
deps = [ ":v8_libbase" ]
|
||||
deps = [
|
||||
":v8_libbase",
|
||||
]
|
||||
}
|
||||
|
||||
v8_source_set("fuzzer_support") {
|
||||
@ -3784,7 +3843,9 @@ v8_source_set("fuzzer_support") {
|
||||
|
||||
configs = [ ":internal_config_base" ]
|
||||
|
||||
deps = [ ":v8" ]
|
||||
deps = [
|
||||
":v8",
|
||||
]
|
||||
|
||||
public_deps = [
|
||||
":v8_libbase",
|
||||
@ -3906,7 +3967,9 @@ if (current_toolchain == v8_snapshot_toolchain) {
|
||||
v8_executable("torque") {
|
||||
visibility = [ ":*" ] # Only targets in this file can depend on this.
|
||||
|
||||
sources = [ "src/torque/torque.cc" ]
|
||||
sources = [
|
||||
"src/torque/torque.cc",
|
||||
]
|
||||
|
||||
deps = [
|
||||
":torque_base",
|
||||
@ -3936,7 +3999,9 @@ if (current_toolchain == v8_snapshot_toolchain) {
|
||||
v8_executable("torque-language-server") {
|
||||
visibility = [ ":*" ] # Only targets in this file can depend on this.
|
||||
|
||||
sources = [ "src/torque/ls/torque-language-server.cc" ]
|
||||
sources = [
|
||||
"src/torque/ls/torque-language-server.cc",
|
||||
]
|
||||
|
||||
deps = [
|
||||
":torque_base",
|
||||
@ -3968,7 +4033,9 @@ if (v8_enable_i18n_support) {
|
||||
v8_executable("gen-regexp-special-case") {
|
||||
visibility = [ ":*" ] # Only targets in this file can depend on this.
|
||||
|
||||
sources = [ "src/regexp/gen-regexp-special-case.cc" ]
|
||||
sources = [
|
||||
"src/regexp/gen-regexp-special-case.cc",
|
||||
]
|
||||
|
||||
deps = [
|
||||
":v8_libbase",
|
||||
@ -3985,11 +4052,15 @@ if (v8_enable_i18n_support) {
|
||||
|
||||
script = "tools/run.py"
|
||||
|
||||
deps = [ ":gen-regexp-special-case($v8_generator_toolchain)" ]
|
||||
deps = [
|
||||
":gen-regexp-special-case($v8_generator_toolchain)",
|
||||
]
|
||||
|
||||
output_file = "$target_gen_dir/src/regexp/special-case.cc"
|
||||
|
||||
outputs = [ output_file ]
|
||||
outputs = [
|
||||
output_file,
|
||||
]
|
||||
|
||||
args = [
|
||||
"./" + rebase_path(
|
||||
@ -4035,13 +4106,17 @@ group("gn_all") {
|
||||
}
|
||||
|
||||
group("v8_python_base") {
|
||||
data = [ ".vpython" ]
|
||||
data = [
|
||||
".vpython",
|
||||
]
|
||||
}
|
||||
|
||||
group("v8_clusterfuzz") {
|
||||
testonly = true
|
||||
|
||||
deps = [ ":d8" ]
|
||||
deps = [
|
||||
":d8",
|
||||
]
|
||||
|
||||
if (v8_multi_arch_build) {
|
||||
deps += [
|
||||
@ -4057,7 +4132,9 @@ group("v8_clusterfuzz") {
|
||||
group("v8_archive") {
|
||||
testonly = true
|
||||
|
||||
deps = [ ":d8" ]
|
||||
deps = [
|
||||
":d8",
|
||||
]
|
||||
|
||||
if (!is_win) {
|
||||
# On windows, cctest doesn't link with v8_static_library.
|
||||
@ -4100,7 +4177,9 @@ group("v8_fuzzers") {
|
||||
|
||||
if (is_component_build) {
|
||||
v8_component("v8") {
|
||||
sources = [ "src/utils/v8dll-main.cc" ]
|
||||
sources = [
|
||||
"src/utils/v8dll-main.cc",
|
||||
]
|
||||
|
||||
public_deps = [
|
||||
":v8_base",
|
||||
@ -4115,7 +4194,9 @@ if (is_component_build) {
|
||||
v8_component("v8_for_testing") {
|
||||
testonly = true
|
||||
|
||||
sources = [ "src/utils/v8dll-main.cc" ]
|
||||
sources = [
|
||||
"src/utils/v8dll-main.cc",
|
||||
]
|
||||
|
||||
public_deps = [
|
||||
":torque_base",
|
||||
@ -4204,7 +4285,9 @@ v8_executable("d8") {
|
||||
}
|
||||
|
||||
v8_executable("v8_hello_world") {
|
||||
sources = [ "samples/hello-world.cc" ]
|
||||
sources = [
|
||||
"samples/hello-world.cc",
|
||||
]
|
||||
|
||||
configs = [
|
||||
# Note: don't use :internal_config here because this target will get
|
||||
@ -4222,7 +4305,9 @@ v8_executable("v8_hello_world") {
|
||||
}
|
||||
|
||||
v8_executable("v8_sample_process") {
|
||||
sources = [ "samples/process.cc" ]
|
||||
sources = [
|
||||
"samples/process.cc",
|
||||
]
|
||||
|
||||
configs = [
|
||||
# Note: don't use :internal_config here because this target will get
|
||||
@ -4241,7 +4326,9 @@ v8_executable("v8_sample_process") {
|
||||
|
||||
if (want_v8_shell) {
|
||||
v8_executable("v8_shell") {
|
||||
sources = [ "samples/shell.cc" ]
|
||||
sources = [
|
||||
"samples/shell.cc",
|
||||
]
|
||||
|
||||
configs = [
|
||||
# Note: don't use :internal_config here because this target will get
|
||||
@ -4268,16 +4355,22 @@ template("v8_fuzzer") {
|
||||
"//build/win:default_exe_manifest",
|
||||
]
|
||||
|
||||
sources = [ "test/fuzzer/fuzzer.cc" ]
|
||||
sources = [
|
||||
"test/fuzzer/fuzzer.cc",
|
||||
]
|
||||
|
||||
configs = [ ":external_config" ]
|
||||
}
|
||||
}
|
||||
|
||||
v8_source_set("json_fuzzer") {
|
||||
sources = [ "test/fuzzer/json.cc" ]
|
||||
sources = [
|
||||
"test/fuzzer/json.cc",
|
||||
]
|
||||
|
||||
deps = [ ":fuzzer_support" ]
|
||||
deps = [
|
||||
":fuzzer_support",
|
||||
]
|
||||
|
||||
configs = [
|
||||
":external_config",
|
||||
@ -4289,9 +4382,13 @@ v8_fuzzer("json_fuzzer") {
|
||||
}
|
||||
|
||||
v8_source_set("multi_return_fuzzer") {
|
||||
sources = [ "test/fuzzer/multi-return.cc" ]
|
||||
sources = [
|
||||
"test/fuzzer/multi-return.cc",
|
||||
]
|
||||
|
||||
deps = [ ":fuzzer_support" ]
|
||||
deps = [
|
||||
":fuzzer_support",
|
||||
]
|
||||
|
||||
configs = [
|
||||
":external_config",
|
||||
@ -4303,9 +4400,13 @@ v8_fuzzer("multi_return_fuzzer") {
|
||||
}
|
||||
|
||||
v8_source_set("parser_fuzzer") {
|
||||
sources = [ "test/fuzzer/parser.cc" ]
|
||||
sources = [
|
||||
"test/fuzzer/parser.cc",
|
||||
]
|
||||
|
||||
deps = [ ":fuzzer_support" ]
|
||||
deps = [
|
||||
":fuzzer_support",
|
||||
]
|
||||
|
||||
configs = [
|
||||
":external_config",
|
||||
@ -4322,7 +4423,9 @@ v8_source_set("regexp_builtins_fuzzer") {
|
||||
"test/fuzzer/regexp_builtins/mjsunit.js.h",
|
||||
]
|
||||
|
||||
deps = [ ":fuzzer_support" ]
|
||||
deps = [
|
||||
":fuzzer_support",
|
||||
]
|
||||
|
||||
configs = [
|
||||
":external_config",
|
||||
@ -4334,9 +4437,13 @@ v8_fuzzer("regexp_builtins_fuzzer") {
|
||||
}
|
||||
|
||||
v8_source_set("regexp_fuzzer") {
|
||||
sources = [ "test/fuzzer/regexp.cc" ]
|
||||
sources = [
|
||||
"test/fuzzer/regexp.cc",
|
||||
]
|
||||
|
||||
deps = [ ":fuzzer_support" ]
|
||||
deps = [
|
||||
":fuzzer_support",
|
||||
]
|
||||
|
||||
configs = [
|
||||
":external_config",
|
||||
@ -4358,7 +4465,9 @@ v8_source_set("wasm_module_runner") {
|
||||
":run_torque",
|
||||
]
|
||||
|
||||
public_deps = [ ":v8_maybe_icu" ]
|
||||
public_deps = [
|
||||
":v8_maybe_icu",
|
||||
]
|
||||
|
||||
configs = [
|
||||
":external_config",
|
||||
@ -4367,7 +4476,9 @@ v8_source_set("wasm_module_runner") {
|
||||
}
|
||||
|
||||
v8_source_set("wasm_fuzzer") {
|
||||
sources = [ "test/fuzzer/wasm.cc" ]
|
||||
sources = [
|
||||
"test/fuzzer/wasm.cc",
|
||||
]
|
||||
|
||||
deps = [
|
||||
":fuzzer_support",
|
||||
@ -4385,7 +4496,9 @@ v8_fuzzer("wasm_fuzzer") {
|
||||
}
|
||||
|
||||
v8_source_set("wasm_async_fuzzer") {
|
||||
sources = [ "test/fuzzer/wasm-async.cc" ]
|
||||
sources = [
|
||||
"test/fuzzer/wasm-async.cc",
|
||||
]
|
||||
|
||||
deps = [
|
||||
":fuzzer_support",
|
||||
@ -4434,7 +4547,9 @@ v8_source_set("lib_wasm_fuzzer_common") {
|
||||
":run_torque",
|
||||
]
|
||||
|
||||
public_deps = [ ":v8_maybe_icu" ]
|
||||
public_deps = [
|
||||
":v8_maybe_icu",
|
||||
]
|
||||
|
||||
configs = [
|
||||
":external_config",
|
||||
@ -4559,7 +4674,9 @@ if (!build_with_chromium && v8_use_perfetto) {
|
||||
|
||||
# This target should be used only by the protoc compiler and by test targets.
|
||||
source_set("protobuf_full") {
|
||||
deps = [ ":protobuf_lite" ]
|
||||
deps = [
|
||||
":protobuf_lite",
|
||||
]
|
||||
sources = [
|
||||
"third_party/protobuf/src/google/protobuf/any.cc",
|
||||
"third_party/protobuf/src/google/protobuf/any.pb.cc",
|
||||
@ -4629,7 +4746,9 @@ if (!build_with_chromium && v8_use_perfetto) {
|
||||
|
||||
if (current_toolchain == host_toolchain) {
|
||||
source_set("protoc_lib") {
|
||||
deps = [ ":protobuf_full" ]
|
||||
deps = [
|
||||
":protobuf_full",
|
||||
]
|
||||
sources = [
|
||||
"third_party/protobuf/src/google/protobuf/compiler/code_generator.cc",
|
||||
"third_party/protobuf/src/google/protobuf/compiler/command_line_interface.cc",
|
||||
@ -4668,7 +4787,9 @@ if (!build_with_chromium && v8_use_perfetto) {
|
||||
":protoc_lib",
|
||||
"//build/win:default_exe_manifest",
|
||||
]
|
||||
sources = [ "src/protobuf/protobuf-compiler-main.cc" ]
|
||||
sources = [
|
||||
"src/protobuf/protobuf-compiler-main.cc",
|
||||
]
|
||||
configs -= [ "//build/config/compiler:chromium_code" ]
|
||||
configs += [ "//build/config/compiler:no_chromium_code" ]
|
||||
}
|
||||
|
@ -285,6 +285,7 @@ extern enum MessageTemplate {
|
||||
kPromiseNonCallable,
|
||||
kNotAPromise,
|
||||
kResolverNotAFunction,
|
||||
kTooManyElementsInPromiseAll,
|
||||
kToRadixFormatRange,
|
||||
kCalledOnNonObject,
|
||||
kRegExpGlobalInvokedOnNonGlobal,
|
||||
|
@ -710,10 +710,6 @@ namespace internal {
|
||||
\
|
||||
/* Promise */ \
|
||||
CPP(IsPromise) \
|
||||
/* ES #sec-promise.all */ \
|
||||
TFJ(PromiseAll, 1, kReceiver, kIterable) \
|
||||
/* ES #sec-promise.allsettled */ \
|
||||
TFJ(PromiseAllSettled, 1, kReceiver, kIterable) \
|
||||
\
|
||||
/* Reflect */ \
|
||||
ASM(ReflectApply, JSTrampoline) \
|
||||
|
@ -99,67 +99,6 @@ TNode<JSPromise> PromiseBuiltinsAssembler::AllocateAndSetJSPromise(
|
||||
return instance;
|
||||
}
|
||||
|
||||
Node* PromiseBuiltinsAssembler::CreatePromiseAllResolveElementContext(
|
||||
Node* promise_capability, Node* native_context) {
|
||||
CSA_ASSERT(this, IsNativeContext(native_context));
|
||||
|
||||
// TODO(bmeurer): Manually fold this into a single allocation.
|
||||
TNode<Map> array_map = CAST(LoadContextElement(
|
||||
native_context, Context::JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX));
|
||||
TNode<JSArray> values_array = AllocateJSArray(
|
||||
PACKED_ELEMENTS, array_map, IntPtrConstant(0), SmiConstant(0));
|
||||
|
||||
const TNode<Context> context = AllocateSyntheticFunctionContext(
|
||||
CAST(native_context), PromiseBuiltins::kPromiseAllResolveElementLength);
|
||||
StoreContextElementNoWriteBarrier(
|
||||
context, PromiseBuiltins::kPromiseAllResolveElementRemainingSlot,
|
||||
SmiConstant(1));
|
||||
StoreContextElementNoWriteBarrier(
|
||||
context, PromiseBuiltins::kPromiseAllResolveElementCapabilitySlot,
|
||||
promise_capability);
|
||||
StoreContextElementNoWriteBarrier(
|
||||
context, PromiseBuiltins::kPromiseAllResolveElementValuesArraySlot,
|
||||
values_array);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
TNode<JSFunction>
|
||||
PromiseBuiltinsAssembler::CreatePromiseAllResolveElementFunction(
|
||||
Node* context, TNode<Smi> index, Node* native_context, int slot_index) {
|
||||
CSA_ASSERT(this, SmiGreaterThan(index, SmiConstant(0)));
|
||||
CSA_ASSERT(this, SmiLessThanOrEqual(
|
||||
index, SmiConstant(PropertyArray::HashField::kMax)));
|
||||
CSA_ASSERT(this, IsNativeContext(native_context));
|
||||
|
||||
const TNode<Map> map = CAST(LoadContextElement(
|
||||
native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX));
|
||||
const TNode<SharedFunctionInfo> resolve_info =
|
||||
CAST(LoadContextElement(native_context, slot_index));
|
||||
TNode<JSFunction> resolve =
|
||||
AllocateFunctionWithMapAndContext(map, resolve_info, CAST(context));
|
||||
|
||||
STATIC_ASSERT(PropertyArray::kNoHashSentinel == 0);
|
||||
StoreObjectFieldNoWriteBarrier(resolve, JSFunction::kPropertiesOrHashOffset,
|
||||
index);
|
||||
|
||||
return resolve;
|
||||
}
|
||||
|
||||
TNode<Context> PromiseBuiltinsAssembler::CreatePromiseResolvingFunctionsContext(
|
||||
TNode<JSPromise> promise, TNode<Object> debug_event,
|
||||
TNode<NativeContext> native_context) {
|
||||
const TNode<Context> context = AllocateSyntheticFunctionContext(
|
||||
native_context, PromiseBuiltins::kPromiseContextLength);
|
||||
StoreContextElementNoWriteBarrier(context, PromiseBuiltins::kPromiseSlot,
|
||||
promise);
|
||||
StoreContextElementNoWriteBarrier(
|
||||
context, PromiseBuiltins::kAlreadyResolvedSlot, FalseConstant());
|
||||
StoreContextElementNoWriteBarrier(context, PromiseBuiltins::kDebugEventSlot,
|
||||
debug_event);
|
||||
return context;
|
||||
}
|
||||
|
||||
Node* PromiseBuiltinsAssembler::PromiseHasHandler(Node* promise) {
|
||||
const TNode<Smi> flags =
|
||||
CAST(LoadObjectField(promise, JSPromise::kFlagsOffset));
|
||||
@ -221,46 +160,6 @@ PromiseBuiltinsAssembler::AllocatePromiseResolveThenableJobTask(
|
||||
return CAST(microtask);
|
||||
}
|
||||
|
||||
Node* PromiseBuiltinsAssembler::CallResolve(Node* native_context,
|
||||
Node* constructor, Node* resolve,
|
||||
Node* value, Label* if_exception,
|
||||
Variable* var_exception) {
|
||||
CSA_ASSERT(this, IsNativeContext(native_context));
|
||||
CSA_ASSERT(this, IsConstructor(constructor));
|
||||
VARIABLE(var_result, MachineRepresentation::kTagged);
|
||||
Label if_fast(this), if_slow(this, Label::kDeferred), done(this, &var_result);
|
||||
|
||||
// Undefined can never be a valid value for the resolve function,
|
||||
// instead it is used as a special marker for the fast path.
|
||||
Branch(IsUndefined(resolve), &if_fast, &if_slow);
|
||||
|
||||
BIND(&if_fast);
|
||||
{
|
||||
const TNode<Object> result = CallBuiltin(
|
||||
Builtins::kPromiseResolve, native_context, constructor, value);
|
||||
GotoIfException(result, if_exception, var_exception);
|
||||
|
||||
var_result.Bind(result);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&if_slow);
|
||||
{
|
||||
CSA_ASSERT(this, IsCallable(resolve));
|
||||
|
||||
Node* const result = CallJS(
|
||||
CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
|
||||
native_context, resolve, constructor, value);
|
||||
GotoIfException(result, if_exception, var_exception);
|
||||
|
||||
var_result.Bind(result);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&done);
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
void PromiseBuiltinsAssembler::BranchIfPromiseResolveLookupChainIntact(
|
||||
Node* native_context, SloppyTNode<Object> constructor, Label* if_fast,
|
||||
Label* if_slow) {
|
||||
@ -353,392 +252,5 @@ void PromiseBuiltinsAssembler::BranchIfAccessCheckFailed(
|
||||
BIND(&has_access);
|
||||
}
|
||||
|
||||
void PromiseBuiltinsAssembler::SetForwardingHandlerIfTrue(Node* context,
|
||||
Node* condition,
|
||||
Node* object) {
|
||||
Label done(this);
|
||||
GotoIfNot(condition, &done);
|
||||
SetPropertyStrict(
|
||||
CAST(context), CAST(object),
|
||||
HeapConstant(factory()->promise_forwarding_handler_symbol()),
|
||||
TrueConstant());
|
||||
Goto(&done);
|
||||
BIND(&done);
|
||||
}
|
||||
|
||||
void PromiseBuiltinsAssembler::SetPromiseHandledByIfTrue(
|
||||
Node* context, Node* condition, Node* promise,
|
||||
const NodeGenerator<Object>& handled_by) {
|
||||
Label done(this);
|
||||
GotoIfNot(condition, &done);
|
||||
GotoIf(TaggedIsSmi(promise), &done);
|
||||
GotoIfNot(HasInstanceType(promise, JS_PROMISE_TYPE), &done);
|
||||
SetPropertyStrict(CAST(context), CAST(promise),
|
||||
HeapConstant(factory()->promise_handled_by_symbol()),
|
||||
handled_by());
|
||||
Goto(&done);
|
||||
BIND(&done);
|
||||
}
|
||||
|
||||
|
||||
TNode<Object> PromiseBuiltinsAssembler::PerformPromiseAll(
|
||||
Node* context, Node* constructor, Node* capability,
|
||||
const IteratorRecord& iterator,
|
||||
const PromiseAllResolvingElementFunction& create_resolve_element_function,
|
||||
const PromiseAllResolvingElementFunction& create_reject_element_function,
|
||||
Label* if_exception, TVariable<Object>* var_exception) {
|
||||
IteratorBuiltinsAssembler iter_assembler(state());
|
||||
|
||||
TNode<NativeContext> native_context = LoadNativeContext(context);
|
||||
|
||||
// For catch prediction, don't treat the .then calls as handling it;
|
||||
// instead, recurse outwards.
|
||||
SetForwardingHandlerIfTrue(
|
||||
native_context, IsDebugActive(),
|
||||
LoadObjectField(capability, PromiseCapability::kRejectOffset));
|
||||
|
||||
TNode<Context> resolve_element_context =
|
||||
Cast(CreatePromiseAllResolveElementContext(capability, native_context));
|
||||
|
||||
TVARIABLE(Smi, var_index, SmiConstant(1));
|
||||
Label loop(this, &var_index), done_loop(this),
|
||||
too_many_elements(this, Label::kDeferred),
|
||||
close_iterator(this, Label::kDeferred), if_slow(this, Label::kDeferred);
|
||||
|
||||
// We can skip the "resolve" lookup on {constructor} if it's the
|
||||
// Promise constructor and the Promise.resolve protector is intact,
|
||||
// as that guards the lookup path for the "resolve" property on the
|
||||
// Promise constructor.
|
||||
TVARIABLE(Object, var_promise_resolve_function, UndefinedConstant());
|
||||
GotoIfNotPromiseResolveLookupChainIntact(native_context, constructor,
|
||||
&if_slow);
|
||||
Goto(&loop);
|
||||
|
||||
BIND(&if_slow);
|
||||
{
|
||||
// 5. Let _promiseResolve_ be ? Get(_constructor_, `"resolve"`).
|
||||
TNode<Object> resolve =
|
||||
GetProperty(native_context, constructor, factory()->resolve_string());
|
||||
GotoIfException(resolve, &close_iterator, var_exception);
|
||||
|
||||
// 6. If IsCallable(_promiseResolve_) is *false*, throw a *TypeError*
|
||||
// exception.
|
||||
ThrowIfNotCallable(CAST(context), resolve, "resolve");
|
||||
|
||||
var_promise_resolve_function = resolve;
|
||||
Goto(&loop);
|
||||
}
|
||||
|
||||
BIND(&loop);
|
||||
{
|
||||
// Let next be IteratorStep(iteratorRecord.[[Iterator]]).
|
||||
// If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
|
||||
// ReturnIfAbrupt(next).
|
||||
const TNode<Map> fast_iterator_result_map = CAST(
|
||||
LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX));
|
||||
const TNode<JSReceiver> next = iter_assembler.IteratorStep(
|
||||
native_context, iterator, &done_loop, fast_iterator_result_map,
|
||||
if_exception, var_exception);
|
||||
|
||||
// Let nextValue be IteratorValue(next).
|
||||
// If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to
|
||||
// true.
|
||||
// ReturnIfAbrupt(nextValue).
|
||||
const TNode<Object> next_value = iter_assembler.IteratorValue(
|
||||
native_context, next, fast_iterator_result_map, if_exception,
|
||||
var_exception);
|
||||
|
||||
// Check if we reached the limit.
|
||||
const TNode<Smi> index = var_index.value();
|
||||
GotoIf(SmiEqual(index, SmiConstant(PropertyArray::HashField::kMax)),
|
||||
&too_many_elements);
|
||||
|
||||
// Set index to index + 1.
|
||||
var_index = SmiAdd(index, SmiConstant(1));
|
||||
|
||||
// Set remainingElementsCount.[[Value]] to
|
||||
// remainingElementsCount.[[Value]] + 1.
|
||||
const TNode<Smi> remaining_elements_count = CAST(LoadContextElement(
|
||||
resolve_element_context,
|
||||
PromiseBuiltins::kPromiseAllResolveElementRemainingSlot));
|
||||
StoreContextElementNoWriteBarrier(
|
||||
resolve_element_context,
|
||||
PromiseBuiltins::kPromiseAllResolveElementRemainingSlot,
|
||||
SmiAdd(remaining_elements_count, SmiConstant(1)));
|
||||
|
||||
// Let resolveElement be CreateBuiltinFunction(steps,
|
||||
// « [[AlreadyCalled]],
|
||||
// [[Index]],
|
||||
// [[Values]],
|
||||
// [[Capability]],
|
||||
// [[RemainingElements]] »).
|
||||
// Set resolveElement.[[AlreadyCalled]] to a Record { [[Value]]: false }.
|
||||
// Set resolveElement.[[Index]] to index.
|
||||
// Set resolveElement.[[Values]] to values.
|
||||
// Set resolveElement.[[Capability]] to resultCapability.
|
||||
// Set resolveElement.[[RemainingElements]] to remainingElementsCount.
|
||||
const TNode<Object> resolve_element_fun = create_resolve_element_function(
|
||||
resolve_element_context, index, native_context, Cast(capability));
|
||||
const TNode<Object> reject_element_fun = create_reject_element_function(
|
||||
resolve_element_context, index, native_context, Cast(capability));
|
||||
|
||||
// We can skip the "resolve" lookup on the {constructor} as well as the
|
||||
// "then" lookup on the result of the "resolve" call, and immediately
|
||||
// chain continuation onto the {next_value} if:
|
||||
//
|
||||
// (a) The {constructor} is the intrinsic %Promise% function, and
|
||||
// looking up "resolve" on {constructor} yields the initial
|
||||
// Promise.resolve() builtin, and
|
||||
// (b) the promise @@species protector cell is valid, meaning that
|
||||
// no one messed with the Symbol.species property on any
|
||||
// intrinsic promise or on the Promise.prototype, and
|
||||
// (c) the {next_value} is a JSPromise whose [[Prototype]] field
|
||||
// contains the intrinsic %PromisePrototype%, and
|
||||
// (d) we're not running with async_hooks or DevTools enabled.
|
||||
//
|
||||
// In that case we also don't need to allocate a chained promise for
|
||||
// the PromiseReaction (aka we can pass undefined to PerformPromiseThen),
|
||||
// since this is only necessary for DevTools and PromiseHooks.
|
||||
Label if_fast(this), if_slow(this);
|
||||
GotoIfNot(IsUndefined(var_promise_resolve_function.value()), &if_slow);
|
||||
GotoIf(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(),
|
||||
&if_slow);
|
||||
GotoIf(IsPromiseSpeciesProtectorCellInvalid(), &if_slow);
|
||||
GotoIf(TaggedIsSmi(next_value), &if_slow);
|
||||
const TNode<Map> next_value_map = LoadMap(CAST(next_value));
|
||||
BranchIfPromiseThenLookupChainIntact(native_context, next_value_map,
|
||||
&if_fast, &if_slow);
|
||||
|
||||
BIND(&if_fast);
|
||||
{
|
||||
// Register the PromiseReaction immediately on the {next_value}, not
|
||||
// passing any chained promise since neither async_hooks nor DevTools
|
||||
// are enabled, so there's no use of the resulting promise.
|
||||
PerformPromiseThenImpl(native_context, CAST(next_value),
|
||||
CAST(resolve_element_fun),
|
||||
CAST(reject_element_fun), UndefinedConstant());
|
||||
Goto(&loop);
|
||||
}
|
||||
|
||||
BIND(&if_slow);
|
||||
{
|
||||
// Let nextPromise be ? Call(constructor, _promiseResolve_, « nextValue
|
||||
// »).
|
||||
Node* const next_promise = CallResolve(
|
||||
native_context, constructor, var_promise_resolve_function.value(),
|
||||
next_value, &close_iterator, var_exception);
|
||||
|
||||
// Perform ? Invoke(nextPromise, "then", « resolveElement,
|
||||
// resultCapability.[[Reject]] »).
|
||||
const TNode<Object> then =
|
||||
GetProperty(native_context, next_promise, factory()->then_string());
|
||||
GotoIfException(then, &close_iterator, var_exception);
|
||||
|
||||
Node* const then_call =
|
||||
CallJS(CodeFactory::Call(isolate(),
|
||||
ConvertReceiverMode::kNotNullOrUndefined),
|
||||
native_context, then, next_promise, resolve_element_fun,
|
||||
reject_element_fun);
|
||||
GotoIfException(then_call, &close_iterator, var_exception);
|
||||
|
||||
// For catch prediction, mark that rejections here are semantically
|
||||
// handled by the combined Promise.
|
||||
SetPromiseHandledByIfTrue(
|
||||
native_context, IsDebugActive(), then_call, [=]() {
|
||||
// Load promiseCapability.[[Promise]]
|
||||
return LoadObjectField(capability,
|
||||
PromiseCapability::kPromiseOffset);
|
||||
});
|
||||
|
||||
Goto(&loop);
|
||||
}
|
||||
}
|
||||
|
||||
BIND(&too_many_elements);
|
||||
{
|
||||
// If there are too many elements (currently more than 2**21-1), raise a
|
||||
// RangeError here (which is caught directly and turned into a rejection)
|
||||
// of the resulting promise. We could gracefully handle this case as well
|
||||
// and support more than this number of elements by going to a separate
|
||||
// function and pass the larger indices via a separate context, but it
|
||||
// doesn't seem likely that we need this, and it's unclear how the rest
|
||||
// of the system deals with 2**21 live Promises anyways.
|
||||
const TNode<Object> result =
|
||||
CallRuntime(Runtime::kThrowRangeError, native_context,
|
||||
SmiConstant(MessageTemplate::kTooManyElementsInPromiseAll));
|
||||
GotoIfException(result, &close_iterator, var_exception);
|
||||
Unreachable();
|
||||
}
|
||||
|
||||
BIND(&close_iterator);
|
||||
{
|
||||
// Exception must be bound to a JS value.
|
||||
CSA_ASSERT(this, IsNotTheHole(var_exception->value()));
|
||||
iter_assembler.IteratorCloseOnException(native_context, iterator,
|
||||
if_exception, var_exception);
|
||||
}
|
||||
|
||||
BIND(&done_loop);
|
||||
{
|
||||
Label resolve_promise(this, Label::kDeferred), return_promise(this);
|
||||
// Set iteratorRecord.[[Done]] to true.
|
||||
// Set remainingElementsCount.[[Value]] to
|
||||
// remainingElementsCount.[[Value]] - 1.
|
||||
TNode<Smi> remaining_elements_count = CAST(LoadContextElement(
|
||||
resolve_element_context,
|
||||
PromiseBuiltins::kPromiseAllResolveElementRemainingSlot));
|
||||
remaining_elements_count = SmiSub(remaining_elements_count, SmiConstant(1));
|
||||
StoreContextElementNoWriteBarrier(
|
||||
resolve_element_context,
|
||||
PromiseBuiltins::kPromiseAllResolveElementRemainingSlot,
|
||||
remaining_elements_count);
|
||||
GotoIf(SmiEqual(remaining_elements_count, SmiConstant(0)),
|
||||
&resolve_promise);
|
||||
|
||||
// Pre-allocate the backing store for the {values_array} to the desired
|
||||
// capacity here. We may already have elements here in case of some
|
||||
// fancy Thenable that calls the resolve callback immediately, so we need
|
||||
// to handle that correctly here.
|
||||
const TNode<JSArray> values_array = CAST(LoadContextElement(
|
||||
resolve_element_context,
|
||||
PromiseBuiltins::kPromiseAllResolveElementValuesArraySlot));
|
||||
const TNode<FixedArrayBase> old_elements = LoadElements(values_array);
|
||||
const TNode<Smi> old_capacity = LoadFixedArrayBaseLength(old_elements);
|
||||
const TNode<Smi> new_capacity = var_index.value();
|
||||
GotoIf(SmiGreaterThanOrEqual(old_capacity, new_capacity), &return_promise);
|
||||
const TNode<FixedArrayBase> new_elements =
|
||||
AllocateFixedArray(PACKED_ELEMENTS, new_capacity,
|
||||
AllocationFlag::kAllowLargeObjectAllocation);
|
||||
CopyFixedArrayElements(PACKED_ELEMENTS, old_elements, PACKED_ELEMENTS,
|
||||
new_elements, SmiConstant(0), old_capacity,
|
||||
new_capacity, UPDATE_WRITE_BARRIER, SMI_PARAMETERS);
|
||||
StoreObjectField(values_array, JSArray::kElementsOffset, new_elements);
|
||||
Goto(&return_promise);
|
||||
|
||||
// If remainingElementsCount.[[Value]] is 0, then
|
||||
// Let valuesArray be CreateArrayFromList(values).
|
||||
// Perform ? Call(resultCapability.[[Resolve]], undefined,
|
||||
// « valuesArray »).
|
||||
BIND(&resolve_promise);
|
||||
{
|
||||
const TNode<Object> resolve =
|
||||
LoadObjectField(capability, PromiseCapability::kResolveOffset);
|
||||
const TNode<Object> values_array = LoadContextElement(
|
||||
resolve_element_context,
|
||||
PromiseBuiltins::kPromiseAllResolveElementValuesArraySlot);
|
||||
Node* const resolve_call = CallJS(
|
||||
CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
|
||||
native_context, resolve, UndefinedConstant(), values_array);
|
||||
GotoIfException(resolve_call, if_exception, var_exception);
|
||||
Goto(&return_promise);
|
||||
}
|
||||
|
||||
// Return resultCapability.[[Promise]].
|
||||
BIND(&return_promise);
|
||||
}
|
||||
|
||||
const TNode<Object> promise =
|
||||
LoadObjectField(capability, PromiseCapability::kPromiseOffset);
|
||||
return promise;
|
||||
}
|
||||
|
||||
void PromiseBuiltinsAssembler::Generate_PromiseAll(
|
||||
TNode<Context> context, TNode<Object> receiver, TNode<Object> iterable,
|
||||
const PromiseAllResolvingElementFunction& create_resolve_element_function,
|
||||
const PromiseAllResolvingElementFunction& create_reject_element_function) {
|
||||
IteratorBuiltinsAssembler iter_assembler(state());
|
||||
|
||||
// Let C be the this value.
|
||||
// If Type(C) is not Object, throw a TypeError exception.
|
||||
ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject,
|
||||
"Promise.all");
|
||||
|
||||
// Let promiseCapability be ? NewPromiseCapability(C).
|
||||
// Don't fire debugEvent so that forwarding the rejection through all does not
|
||||
// trigger redundant ExceptionEvents
|
||||
const TNode<Oddball> debug_event = FalseConstant();
|
||||
const TNode<PromiseCapability> capability = CAST(CallBuiltin(
|
||||
Builtins::kNewPromiseCapability, context, receiver, debug_event));
|
||||
|
||||
TVARIABLE(Object, var_exception, TheHoleConstant());
|
||||
Label reject_promise(this, &var_exception, Label::kDeferred);
|
||||
|
||||
// Let iterator be GetIterator(iterable).
|
||||
// IfAbruptRejectPromise(iterator, promiseCapability).
|
||||
IteratorRecord iterator = iter_assembler.GetIterator(
|
||||
context, iterable, &reject_promise, &var_exception);
|
||||
|
||||
// Let result be PerformPromiseAll(iteratorRecord, C, promiseCapability).
|
||||
// If result is an abrupt completion, then
|
||||
// If iteratorRecord.[[Done]] is false, let result be
|
||||
// IteratorClose(iterator, result).
|
||||
// IfAbruptRejectPromise(result, promiseCapability).
|
||||
const TNode<Object> result = PerformPromiseAll(
|
||||
context, receiver, capability, iterator, create_resolve_element_function,
|
||||
create_reject_element_function, &reject_promise, &var_exception);
|
||||
|
||||
Return(result);
|
||||
|
||||
BIND(&reject_promise);
|
||||
{
|
||||
// Exception must be bound to a JS value.
|
||||
CSA_SLOW_ASSERT(this, IsNotTheHole(var_exception.value()));
|
||||
const TNode<Object> reject =
|
||||
LoadObjectField(capability, PromiseCapability::kRejectOffset);
|
||||
CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
|
||||
context, reject, UndefinedConstant(), var_exception.value());
|
||||
|
||||
const TNode<Object> promise =
|
||||
LoadObjectField(capability, PromiseCapability::kPromiseOffset);
|
||||
Return(promise);
|
||||
}
|
||||
}
|
||||
|
||||
// ES#sec-promise.all
|
||||
// Promise.all ( iterable )
|
||||
TF_BUILTIN(PromiseAll, PromiseBuiltinsAssembler) {
|
||||
TNode<Object> receiver = Cast(Parameter(Descriptor::kReceiver));
|
||||
TNode<Context> context = Cast(Parameter(Descriptor::kContext));
|
||||
TNode<Object> iterable = Cast(Parameter(Descriptor::kIterable));
|
||||
Generate_PromiseAll(
|
||||
context, receiver, iterable,
|
||||
[this](TNode<Context> context, TNode<Smi> index,
|
||||
TNode<NativeContext> native_context,
|
||||
TNode<PromiseCapability> capability) {
|
||||
return CreatePromiseAllResolveElementFunction(
|
||||
context, index, native_context,
|
||||
Context::PROMISE_ALL_RESOLVE_ELEMENT_SHARED_FUN);
|
||||
},
|
||||
[this](TNode<Context> context, TNode<Smi> index,
|
||||
TNode<NativeContext> native_context,
|
||||
TNode<PromiseCapability> capability) {
|
||||
return LoadObjectField(capability, PromiseCapability::kRejectOffset);
|
||||
});
|
||||
}
|
||||
|
||||
// ES#sec-promise.allsettled
|
||||
// Promise.allSettled ( iterable )
|
||||
TF_BUILTIN(PromiseAllSettled, PromiseBuiltinsAssembler) {
|
||||
TNode<Object> receiver = Cast(Parameter(Descriptor::kReceiver));
|
||||
TNode<Context> context = Cast(Parameter(Descriptor::kContext));
|
||||
TNode<Object> iterable = Cast(Parameter(Descriptor::kIterable));
|
||||
Generate_PromiseAll(
|
||||
context, receiver, iterable,
|
||||
[this](TNode<Context> context, TNode<Smi> index,
|
||||
TNode<NativeContext> native_context,
|
||||
TNode<PromiseCapability> capability) {
|
||||
return CreatePromiseAllResolveElementFunction(
|
||||
context, index, native_context,
|
||||
Context::PROMISE_ALL_SETTLED_RESOLVE_ELEMENT_SHARED_FUN);
|
||||
},
|
||||
[this](TNode<Context> context, TNode<Smi> index,
|
||||
TNode<NativeContext> native_context,
|
||||
TNode<PromiseCapability> capability) {
|
||||
return CreatePromiseAllResolveElementFunction(
|
||||
context, index, native_context,
|
||||
Context::PROMISE_ALL_SETTLED_REJECT_ELEMENT_SHARED_FUN);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -47,24 +47,6 @@ class V8_EXPORT_PRIVATE PromiseBuiltinsAssembler : public CodeStubAssembler {
|
||||
TNode<JSReceiver> thenable, TNode<Context> context);
|
||||
Node* PromiseHasHandler(Node* promise);
|
||||
|
||||
// Creates the context used by all Promise.all resolve element closures,
|
||||
// together with the values array. Since all closures for a single Promise.all
|
||||
// call use the same context, we need to store the indices for the individual
|
||||
// closures somewhere else (we put them into the identity hash field of the
|
||||
// closures), and we also need to have a separate marker for when the closure
|
||||
// was called already (we slap the native context onto the closure in that
|
||||
// case to mark it's done).
|
||||
Node* CreatePromiseAllResolveElementContext(Node* promise_capability,
|
||||
Node* native_context);
|
||||
TNode<JSFunction> CreatePromiseAllResolveElementFunction(Node* context,
|
||||
TNode<Smi> index,
|
||||
Node* native_context,
|
||||
int slot_index);
|
||||
|
||||
TNode<Context> CreatePromiseResolvingFunctionsContext(
|
||||
TNode<JSPromise> promise, TNode<Object> debug_event,
|
||||
TNode<NativeContext> native_context);
|
||||
|
||||
void BranchIfAccessCheckFailed(SloppyTNode<Context> context,
|
||||
SloppyTNode<Context> native_context,
|
||||
TNode<Object> promise_constructor,
|
||||
@ -140,33 +122,7 @@ class V8_EXPORT_PRIVATE PromiseBuiltinsAssembler : public CodeStubAssembler {
|
||||
Node* receiver_map, Label* if_fast,
|
||||
Label* if_slow);
|
||||
|
||||
// If resolve is Undefined, we use the builtin %PromiseResolve%
|
||||
// intrinsic, otherwise we use the given resolve function.
|
||||
Node* CallResolve(Node* native_context, Node* constructor, Node* resolve,
|
||||
Node* value, Label* if_exception, Variable* var_exception);
|
||||
|
||||
using PromiseAllResolvingElementFunction =
|
||||
std::function<TNode<Object>(TNode<Context> context, TNode<Smi> index,
|
||||
TNode<NativeContext> native_context,
|
||||
TNode<PromiseCapability> capability)>;
|
||||
|
||||
TNode<Object> PerformPromiseAll(
|
||||
Node* context, Node* constructor, Node* capability,
|
||||
const TorqueStructIteratorRecord& record,
|
||||
const PromiseAllResolvingElementFunction& create_resolve_element_function,
|
||||
const PromiseAllResolvingElementFunction& create_reject_element_function,
|
||||
Label* if_exception, TVariable<Object>* var_exception);
|
||||
|
||||
void SetForwardingHandlerIfTrue(Node* context, Node* condition, Node* object);
|
||||
void SetPromiseHandledByIfTrue(Node* context, Node* condition, Node* promise,
|
||||
const NodeGenerator<Object>& handled_by);
|
||||
|
||||
TNode<JSPromise> AllocateJSPromise(TNode<Context> context);
|
||||
|
||||
void Generate_PromiseAll(
|
||||
TNode<Context> context, TNode<Object> receiver, TNode<Object> iterable,
|
||||
const PromiseAllResolvingElementFunction& create_resolve_element_function,
|
||||
const PromiseAllResolvingElementFunction& create_reject_element_function);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
@ -248,9 +248,6 @@ namespace promise {
|
||||
|
||||
extern macro
|
||||
PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Context): JSPromise;
|
||||
extern macro
|
||||
PromiseBuiltinsAssembler::CreatePromiseResolvingFunctionsContext(
|
||||
JSPromise, Object, NativeContext): Context;
|
||||
|
||||
@export
|
||||
macro CreatePromiseCapabilitiesExecutorContext(
|
||||
|
384
src/builtins/promise-all.tq
Normal file
384
src/builtins/promise-all.tq
Normal file
@ -0,0 +1,384 @@
|
||||
// Copyright 2019 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include 'src/builtins/builtins-promise.h'
|
||||
#include 'src/builtins/builtins-promise-gen.h'
|
||||
|
||||
namespace promise {
|
||||
const kPromiseBuiltinsPromiseContextLength: constexpr int31
|
||||
generates 'PromiseBuiltins::kPromiseContextLength';
|
||||
|
||||
// Creates the context used by all Promise.all resolve element closures,
|
||||
// together with the values array. Since all closures for a single Promise.all
|
||||
// call use the same context, we need to store the indices for the individual
|
||||
// closures somewhere else (we put them into the identity hash field of the
|
||||
// closures), and we also need to have a separate marker for when the closure
|
||||
// was called already (we slap the native context onto the closure in that
|
||||
// case to mark it's done).
|
||||
macro CreatePromiseAllResolveElementContext(implicit context: Context)(
|
||||
capability: PromiseCapability, nativeContext: NativeContext): Context {
|
||||
// TODO(bmeurer): Manually fold this into a single allocation.
|
||||
const arrayMap = UnsafeCast<Map>(
|
||||
nativeContext[NativeContextSlot::JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX]);
|
||||
const valuesArray = AllocateJSArray(
|
||||
ElementsKind::PACKED_ELEMENTS, arrayMap, IntPtrConstant(0),
|
||||
SmiConstant(0));
|
||||
const resolveContext = AllocateSyntheticFunctionContext(
|
||||
nativeContext,
|
||||
PromiseAllResolveElementContextSlots::kPromiseAllResolveElementLength);
|
||||
resolveContext[PromiseAllResolveElementContextSlots::
|
||||
kPromiseAllResolveElementRemainingSlot] = SmiConstant(1);
|
||||
resolveContext[PromiseAllResolveElementContextSlots::
|
||||
kPromiseAllResolveElementCapabilitySlot] = capability;
|
||||
resolveContext[PromiseAllResolveElementContextSlots::
|
||||
kPromiseAllResolveElementValuesArraySlot] = valuesArray;
|
||||
return resolveContext;
|
||||
}
|
||||
|
||||
macro CreatePromiseAllResolveElementFunction(implicit context: Context)(
|
||||
resolveElementContext: Context, index: Smi, nativeContext: NativeContext,
|
||||
slotIndex: constexpr NativeContextSlot): JSFunction {
|
||||
assert(index > 0);
|
||||
assert(index < kPropertyArrayHashFieldMax);
|
||||
|
||||
const map = UnsafeCast<Map>(
|
||||
nativeContext
|
||||
[NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]);
|
||||
const resolveInfo =
|
||||
UnsafeCast<SharedFunctionInfo>(nativeContext[slotIndex]);
|
||||
const resolve = AllocateFunctionWithMapAndContext(
|
||||
map, resolveInfo, resolveElementContext);
|
||||
|
||||
assert(kPropertyArrayNoHashSentinel == 0);
|
||||
resolve.properties_or_hash = index;
|
||||
return resolve;
|
||||
}
|
||||
|
||||
@export
|
||||
macro CreatePromiseResolvingFunctionsContext(implicit context: Context)(
|
||||
promise: JSPromise, debugEvent: Object, nativeContext: NativeContext):
|
||||
Context {
|
||||
const resolveContext = AllocateSyntheticFunctionContext(
|
||||
nativeContext, kPromiseBuiltinsPromiseContextLength);
|
||||
resolveContext[kPromiseBuiltinsPromiseSlot] = promise;
|
||||
resolveContext[kPromiseBuiltinsAlreadyResolvedSlot] = False;
|
||||
resolveContext[kPromiseBuiltinsDebugEventSlot] = debugEvent;
|
||||
return resolveContext;
|
||||
}
|
||||
|
||||
macro IsPromiseThenLookupChainIntact(implicit context: Context)(
|
||||
nativeContext: NativeContext, receiverMap: Map): bool {
|
||||
if (IsForceSlowPath()) return false;
|
||||
if (!IsJSPromiseMap(receiverMap)) return false;
|
||||
if (receiverMap.prototype !=
|
||||
nativeContext[NativeContextSlot::PROMISE_PROTOTYPE_INDEX])
|
||||
return false;
|
||||
return !IsPromiseThenProtectorCellInvalid();
|
||||
}
|
||||
|
||||
struct PromiseAllResolveElementFunctor {
|
||||
macro Call(implicit context: Context)(
|
||||
resolveElementContext: Context, nativeContext: NativeContext,
|
||||
index: Smi, _capability: PromiseCapability): Callable {
|
||||
return CreatePromiseAllResolveElementFunction(
|
||||
resolveElementContext, index, nativeContext,
|
||||
NativeContextSlot::PROMISE_ALL_RESOLVE_ELEMENT_SHARED_FUN);
|
||||
}
|
||||
}
|
||||
|
||||
struct PromiseAllRejectElementFunctor {
|
||||
macro Call(implicit context: Context)(
|
||||
_resolveElementContext: Context, _nativeContext: NativeContext,
|
||||
_index: Smi, capability: PromiseCapability): Callable {
|
||||
return UnsafeCast<Callable>(capability.reject);
|
||||
}
|
||||
}
|
||||
|
||||
struct PromiseAllSettledResolveElementFunctor {
|
||||
macro Call(implicit context: Context)(
|
||||
resolveElementContext: Context, nativeContext: NativeContext,
|
||||
index: Smi, _capability: PromiseCapability): Callable {
|
||||
return CreatePromiseAllResolveElementFunction(
|
||||
resolveElementContext, index, nativeContext,
|
||||
NativeContextSlot::PROMISE_ALL_SETTLED_RESOLVE_ELEMENT_SHARED_FUN);
|
||||
}
|
||||
}
|
||||
|
||||
struct PromiseAllSettledRejectElementFunctor {
|
||||
macro Call(implicit context: Context)(
|
||||
resolveElementContext: Context, nativeContext: NativeContext,
|
||||
index: Smi, _capability: PromiseCapability): Callable {
|
||||
return CreatePromiseAllResolveElementFunction(
|
||||
resolveElementContext, index, nativeContext,
|
||||
NativeContextSlot::PROMISE_ALL_SETTLED_REJECT_ELEMENT_SHARED_FUN);
|
||||
}
|
||||
}
|
||||
|
||||
transitioning macro PerformPromiseAll<F1: type, F2: type>(implicit context:
|
||||
Context)(
|
||||
constructor: JSReceiver, capability: PromiseCapability,
|
||||
iter: iterator::IteratorRecord, createResolveElementFunctor: F1,
|
||||
createRejectElementFunctor: F2): JSAny labels Reject(Object) {
|
||||
const nativeContext = LoadNativeContext(context);
|
||||
const promise = capability.promise;
|
||||
const resolve = capability.resolve;
|
||||
const reject = capability.reject;
|
||||
|
||||
// For catch prediction, don't treat the .then calls as handling it;
|
||||
// instead, recurse outwards.
|
||||
if (IsDebugActive()) deferred {
|
||||
SetPropertyStrict(
|
||||
context, reject, kPromiseForwardingHandlerSymbol, True);
|
||||
}
|
||||
|
||||
const resolveElementContext =
|
||||
CreatePromiseAllResolveElementContext(capability, nativeContext);
|
||||
|
||||
let index: Smi = 1;
|
||||
|
||||
// We can skip the "resolve" lookup on {constructor} if it's the
|
||||
// Promise constructor and the Promise.resolve protector is intact,
|
||||
// as that guards the lookup path for the "resolve" property on the
|
||||
// Promise constructor.
|
||||
let promiseResolveFunction: JSAny = Undefined;
|
||||
try {
|
||||
try {
|
||||
if (!IsPromiseResolveLookupChainIntact(nativeContext, constructor)) {
|
||||
// 5. Let _promiseResolve_ be ? Get(_constructor_, `"resolve"`).
|
||||
let promiseResolve: JSAny;
|
||||
try {
|
||||
promiseResolve = GetProperty(constructor, kResolveString);
|
||||
} catch (e) deferred {
|
||||
iterator::IteratorCloseOnException(iter, e) otherwise Reject;
|
||||
}
|
||||
|
||||
// 6. If IsCallable(_promiseResolve_) is *false*, throw a *TypeError*
|
||||
// exception.
|
||||
promiseResolveFunction = Cast<Callable>(promiseResolve)
|
||||
otherwise ThrowTypeError(
|
||||
MessageTemplate::kCalledNonCallable, 'resolve');
|
||||
}
|
||||
|
||||
const fastIteratorResultMap = UnsafeCast<Map>(
|
||||
nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]);
|
||||
while (true) {
|
||||
let nextValue: JSAny;
|
||||
|
||||
// Let next be IteratorStep(iteratorRecord.[[Iterator]]).
|
||||
// If next is an abrupt completion, set iteratorRecord.[[Done]] to
|
||||
// true. ReturnIfAbrupt(next).
|
||||
const next: JSReceiver = iterator::IteratorStep(
|
||||
iter, fastIteratorResultMap) otherwise goto Done;
|
||||
|
||||
// Let nextValue be IteratorValue(next).
|
||||
// If nextValue is an abrupt completion, set iteratorRecord.[[Done]]
|
||||
// to true.
|
||||
// ReturnIfAbrupt(nextValue).
|
||||
nextValue = iterator::IteratorValue(next, fastIteratorResultMap);
|
||||
|
||||
// Check if we reached the limit.
|
||||
if (index == kPropertyArrayHashFieldMax) {
|
||||
// If there are too many elements (currently more than 2**21-1),
|
||||
// raise a RangeError here (which is caught directly and turned into
|
||||
// a rejection) of the resulting promise. We could gracefully handle
|
||||
// this case as well and support more than this number of elements
|
||||
// by going to a separate function and pass the larger indices via a
|
||||
// separate context, but it doesn't seem likely that we need this,
|
||||
// and it's unclear how the rest of the system deals with 2**21 live
|
||||
// Promises anyways.
|
||||
try {
|
||||
ThrowRangeError(MessageTemplate::kTooManyElementsInPromiseAll);
|
||||
} catch (e) deferred {
|
||||
iterator::IteratorCloseOnException(iter, e) otherwise Reject;
|
||||
}
|
||||
}
|
||||
|
||||
// Set remainingElementsCount.[[Value]] to
|
||||
// remainingElementsCount.[[Value]] + 1.
|
||||
const remainingElementsCount =
|
||||
UnsafeCast<Smi>(resolveElementContext
|
||||
[PromiseAllResolveElementContextSlots::
|
||||
kPromiseAllResolveElementRemainingSlot]);
|
||||
resolveElementContext[PromiseAllResolveElementContextSlots::
|
||||
kPromiseAllResolveElementRemainingSlot] =
|
||||
remainingElementsCount + 1;
|
||||
|
||||
// Let resolveElement be CreateBuiltinFunction(steps,
|
||||
// « [[AlreadyCalled]],
|
||||
// [[Index]],
|
||||
// [[Values]],
|
||||
// [[Capability]],
|
||||
// [[RemainingElements]]
|
||||
// »).
|
||||
// Set resolveElement.[[AlreadyCalled]] to a Record { [[Value]]: false
|
||||
// }. Set resolveElement.[[Index]] to index. Set
|
||||
// resolveElement.[[Values]] to values. Set
|
||||
// resolveElement.[[Capability]] to resultCapability. Set
|
||||
// resolveElement.[[RemainingElements]] to remainingElementsCount.
|
||||
const resolveElementFun = createResolveElementFunctor.Call(
|
||||
resolveElementContext, nativeContext, index, capability);
|
||||
const rejectElementFun = createRejectElementFunctor.Call(
|
||||
resolveElementContext, nativeContext, index, capability);
|
||||
|
||||
// We can skip the "resolve" lookup on the {constructor} as well as
|
||||
// the "then" lookup on the result of the "resolve" call, and
|
||||
// immediately chain continuation onto the {next_value} if:
|
||||
//
|
||||
// (a) The {constructor} is the intrinsic %Promise% function, and
|
||||
// looking up "resolve" on {constructor} yields the initial
|
||||
// Promise.resolve() builtin, and
|
||||
// (b) the promise @@species protector cell is valid, meaning that
|
||||
// no one messed with the Symbol.species property on any
|
||||
// intrinsic promise or on the Promise.prototype, and
|
||||
// (c) the {next_value} is a JSPromise whose [[Prototype]] field
|
||||
// contains the intrinsic %PromisePrototype%, and
|
||||
// (d) we're not running with async_hooks or DevTools enabled.
|
||||
//
|
||||
// In that case we also don't need to allocate a chained promise for
|
||||
// the PromiseReaction (aka we can pass undefined to
|
||||
// PerformPromiseThen), since this is only necessary for DevTools and
|
||||
// PromiseHooks.
|
||||
if (promiseResolveFunction != Undefined ||
|
||||
IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() ||
|
||||
IsPromiseSpeciesProtectorCellInvalid() || Is<Smi>(nextValue) ||
|
||||
!IsPromiseThenLookupChainIntact(
|
||||
nativeContext, UnsafeCast<HeapObject>(nextValue).map)) {
|
||||
try {
|
||||
// Let nextPromise be ? Call(constructor, _promiseResolve_, «
|
||||
// nextValue »).
|
||||
const nextPromise = CallResolve(
|
||||
UnsafeCast<Constructor>(constructor), promiseResolveFunction,
|
||||
nextValue);
|
||||
|
||||
// Perform ? Invoke(nextPromise, "then", « resolveElement,
|
||||
// resultCapability.[[Reject]] »).
|
||||
const then = GetProperty(nextPromise, kThenString);
|
||||
const thenResult = Call(
|
||||
nativeContext, then, nextPromise, resolveElementFun,
|
||||
rejectElementFun);
|
||||
|
||||
// For catch prediction, mark that rejections here are
|
||||
// semantically handled by the combined Promise.
|
||||
if (IsDebugActive() && Is<JSPromise>(thenResult)) deferred {
|
||||
SetPropertyStrict(
|
||||
context, thenResult, kPromiseHandledBySymbol, promise);
|
||||
}
|
||||
} catch (e) deferred {
|
||||
iterator::IteratorCloseOnException(iter, e) otherwise Reject;
|
||||
}
|
||||
} else {
|
||||
PerformPromiseThenImpl(
|
||||
UnsafeCast<JSPromise>(nextValue), resolveElementFun,
|
||||
rejectElementFun, Undefined);
|
||||
}
|
||||
|
||||
// Set index to index + 1.
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
label Done {}
|
||||
|
||||
// Set iteratorRecord.[[Done]] to true.
|
||||
// Set remainingElementsCount.[[Value]] to
|
||||
// remainingElementsCount.[[Value]] - 1.
|
||||
let remainingElementsCount = UnsafeCast<Smi>(
|
||||
resolveElementContext[PromiseAllResolveElementContextSlots::
|
||||
kPromiseAllResolveElementRemainingSlot]);
|
||||
remainingElementsCount -= 1;
|
||||
resolveElementContext[PromiseAllResolveElementContextSlots::
|
||||
kPromiseAllResolveElementRemainingSlot] =
|
||||
remainingElementsCount;
|
||||
if (remainingElementsCount > 0) {
|
||||
// Pre-allocate the backing store for the {values_array} to the desired
|
||||
// capacity here. We may already have elements here in case of some
|
||||
// fancy Thenable that calls the resolve callback immediately, so we need
|
||||
// to handle that correctly here.
|
||||
const valuesArray = UnsafeCast<JSArray>(
|
||||
resolveElementContext[PromiseAllResolveElementContextSlots::
|
||||
kPromiseAllResolveElementValuesArraySlot]);
|
||||
const oldElements = UnsafeCast<FixedArray>(valuesArray.elements);
|
||||
const oldCapacity = oldElements.length_intptr;
|
||||
const newCapacity = SmiUntag(index);
|
||||
if (oldCapacity < newCapacity) {
|
||||
valuesArray.elements =
|
||||
ExtractFixedArray(oldElements, 0, oldCapacity, newCapacity);
|
||||
}
|
||||
} else
|
||||
deferred {
|
||||
// If remainingElementsCount.[[Value]] is 0, then
|
||||
// Let valuesArray be CreateArrayFromList(values).
|
||||
// Perform ? Call(resultCapability.[[Resolve]], undefined,
|
||||
// « valuesArray »).
|
||||
assert(remainingElementsCount == 0);
|
||||
const valuesArray = UnsafeCast<JSAny>(
|
||||
resolveElementContext
|
||||
[PromiseAllResolveElementContextSlots::
|
||||
kPromiseAllResolveElementValuesArraySlot]);
|
||||
Call(nativeContext, UnsafeCast<JSAny>(resolve), Undefined, valuesArray);
|
||||
}
|
||||
|
||||
// Return resultCapability.[[Promise]].
|
||||
return promise;
|
||||
}
|
||||
|
||||
transitioning macro GeneratePromiseAll<F1: type, F2: type>(implicit context:
|
||||
Context)(
|
||||
receiver: JSAny, iterable: JSAny, createResolveElementFunctor: F1,
|
||||
createRejectElementFunctor: F2): JSAny {
|
||||
// Let C be the this value.
|
||||
// If Type(C) is not Object, throw a TypeError exception.
|
||||
const receiver = Cast<JSReceiver>(receiver)
|
||||
otherwise ThrowTypeError(
|
||||
MessageTemplate::kCalledOnNonObject, 'Promise.all');
|
||||
|
||||
// Let promiseCapability be ? NewPromiseCapability(C).
|
||||
// Don't fire debugEvent so that forwarding the rejection through all does
|
||||
// not trigger redundant ExceptionEvents
|
||||
const capability = NewPromiseCapability(receiver, False);
|
||||
|
||||
try {
|
||||
try {
|
||||
// Let iterator be GetIterator(iterable).
|
||||
// IfAbruptRejectPromise(iterator, promiseCapability).
|
||||
let i = iterator::GetIterator(iterable);
|
||||
|
||||
// Let result be PerformPromiseAll(iteratorRecord, C,
|
||||
// promiseCapability). If result is an abrupt completion, then
|
||||
// If iteratorRecord.[[Done]] is false, let result be
|
||||
// IteratorClose(iterator, result).
|
||||
// IfAbruptRejectPromise(result, promiseCapability).
|
||||
return PerformPromiseAll(
|
||||
receiver, capability, i, createResolveElementFunctor,
|
||||
createRejectElementFunctor) otherwise Reject;
|
||||
} catch (e) deferred {
|
||||
goto Reject(e);
|
||||
}
|
||||
}
|
||||
label Reject(e: Object) deferred {
|
||||
// Exception must be bound to a JS value.
|
||||
const e = UnsafeCast<JSAny>(e);
|
||||
const reject = UnsafeCast<JSAny>(capability.reject);
|
||||
Call(context, reject, Undefined, e);
|
||||
return capability.promise;
|
||||
}
|
||||
}
|
||||
|
||||
// ES#sec-promise.all
|
||||
transitioning javascript builtin PromiseAll(
|
||||
js-implicit context: Context, receiver: JSAny)(iterable: JSAny): JSAny {
|
||||
return GeneratePromiseAll(
|
||||
receiver, iterable, PromiseAllResolveElementFunctor{},
|
||||
PromiseAllRejectElementFunctor{});
|
||||
}
|
||||
|
||||
// ES#sec-promise.allsettled
|
||||
// Promise.allSettled ( iterable )
|
||||
transitioning javascript builtin PromiseAllSettled(
|
||||
js-implicit context: Context, receiver: JSAny)(iterable: JSAny): JSAny {
|
||||
return GeneratePromiseAll(
|
||||
receiver, iterable, PromiseAllSettledResolveElementFunctor{},
|
||||
PromiseAllSettledRejectElementFunctor{});
|
||||
}
|
||||
}
|
@ -47,7 +47,9 @@ extern enum NativeContextSlot extends intptr constexpr 'Context::Field' {
|
||||
|
||||
PROMISE_FUNCTION_INDEX,
|
||||
PROMISE_THEN_INDEX,
|
||||
STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX,
|
||||
PROMISE_ALL_RESOLVE_ELEMENT_SHARED_FUN,
|
||||
PROMISE_ALL_SETTLED_REJECT_ELEMENT_SHARED_FUN,
|
||||
PROMISE_ALL_SETTLED_RESOLVE_ELEMENT_SHARED_FUN,
|
||||
PROMISE_CAPABILITY_DEFAULT_RESOLVE_SHARED_FUN_INDEX,
|
||||
PROMISE_CAPABILITY_DEFAULT_REJECT_SHARED_FUN_INDEX,
|
||||
PROMISE_CATCH_FINALLY_SHARED_FUN,
|
||||
@ -56,6 +58,7 @@ extern enum NativeContextSlot extends intptr constexpr 'Context::Field' {
|
||||
PROMISE_THROWER_FINALLY_SHARED_FUN,
|
||||
PROMISE_THEN_FINALLY_SHARED_FUN,
|
||||
PROMISE_VALUE_THUNK_FINALLY_SHARED_FUN,
|
||||
STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX,
|
||||
...
|
||||
}
|
||||
|
||||
|
@ -2554,13 +2554,13 @@ TEST(CreatePromiseResolvingFunctionsContext) {
|
||||
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
||||
PromiseBuiltinsAssembler m(asm_tester.state());
|
||||
|
||||
Node* const context = m.Parameter(kNumParams + 2);
|
||||
const TNode<Context> context = m.CAST(m.Parameter(kNumParams + 2));
|
||||
const TNode<NativeContext> native_context = m.LoadNativeContext(context);
|
||||
const TNode<JSPromise> promise =
|
||||
m.AllocateAndInitJSPromise(m.CAST(context), m.UndefinedConstant());
|
||||
m.AllocateAndInitJSPromise(context, m.UndefinedConstant());
|
||||
const TNode<Context> promise_context =
|
||||
m.CreatePromiseResolvingFunctionsContext(
|
||||
promise, m.BooleanConstant(false), native_context);
|
||||
context, promise, m.BooleanConstant(false), native_context);
|
||||
m.Return(promise_context);
|
||||
|
||||
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
||||
@ -2672,12 +2672,12 @@ TEST(AllocateFunctionWithMapAndContext) {
|
||||
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
||||
PromiseBuiltinsAssembler m(asm_tester.state());
|
||||
|
||||
Node* const context = m.Parameter(kNumParams + 2);
|
||||
const TNode<Context> context = m.CAST(m.Parameter(kNumParams + 2));
|
||||
const TNode<NativeContext> native_context = m.LoadNativeContext(context);
|
||||
const TNode<JSPromise> promise =
|
||||
m.AllocateAndInitJSPromise(m.CAST(context), m.UndefinedConstant());
|
||||
m.AllocateAndInitJSPromise(context, m.UndefinedConstant());
|
||||
TNode<Context> promise_context = m.CreatePromiseResolvingFunctionsContext(
|
||||
promise, m.BooleanConstant(false), native_context);
|
||||
context, promise, m.BooleanConstant(false), native_context);
|
||||
TNode<Object> resolve_info = m.LoadContextElement(
|
||||
native_context,
|
||||
Context::PROMISE_CAPABILITY_DEFAULT_RESOLVE_SHARED_FUN_INDEX);
|
||||
|
Loading…
Reference in New Issue
Block a user