diff --git a/deps/v8/.gitignore b/deps/v8/.gitignore index 1afbd765d3e6c2..6d2cf1077a522a 100644 --- a/deps/v8/.gitignore +++ b/deps/v8/.gitignore @@ -61,6 +61,8 @@ !/third_party/antlr4 !/third_party/binutils !/third_party/inspector_protocol +!/third_party/jsoncpp +/third_party/jsoncpp/source !/third_party/colorama /third_party/colorama/src !/third_party/googletest diff --git a/deps/v8/AUTHORS b/deps/v8/AUTHORS index 7036ecd42bc461..47a83c5ff1c905 100644 --- a/deps/v8/AUTHORS +++ b/deps/v8/AUTHORS @@ -43,6 +43,7 @@ Julia Computing, Inc. <*@juliacomputing.com> Aaron Bieber Abdulla Kamar +Adam Kallai Akinori MUSHA Alessandro Pignotti Alex Kodat @@ -112,6 +113,7 @@ James Pike James M Snell Jianghua Yang Jiawen Geng +Jiaxun Yang Joel Stanley Johan Bergström Jonathan Liu @@ -211,3 +213,4 @@ Zhao Jiazhong Zhongping Wang 柳荣一 Yanbo Li +Gilang Mentari Hamidy \ No newline at end of file diff --git a/deps/v8/BUILD.gn b/deps/v8/BUILD.gn index b2dde3f9d70312..167e63503c5535 100644 --- a/deps/v8/BUILD.gn +++ b/deps/v8/BUILD.gn @@ -218,6 +218,17 @@ declare_args() { # Enable control-flow integrity features, such as pointer authentication for # ARM64. v8_control_flow_integrity = false + + # Enable object names in cppgc for debug purposes. + cppgc_enable_object_names = false + + # Enable V8 heap sandbox experimental feature. + # Sets -DV8_HEAP_SANDBOX. + v8_enable_heap_sandbox = "" + + # Experimental support for native context independent code. + # https://crbug.com/v8/8888 + v8_enable_nci_code = false } # Derived defaults. @@ -254,7 +265,9 @@ if (v8_enable_pointer_compression == "") { if (v8_enable_fast_torque == "") { v8_enable_fast_torque = v8_enable_fast_mksnapshot } - +if (v8_enable_heap_sandbox == "") { + v8_enable_heap_sandbox = false +} if (v8_enable_single_generation == "") { v8_enable_single_generation = v8_disable_write_barriers } @@ -284,6 +297,9 @@ assert( !v8_enable_pointer_compression || !v8_enable_shared_ro_heap, "Pointer compression is not supported with shared read-only heap enabled") +assert(!v8_enable_heap_sandbox || v8_enable_pointer_compression, + "V8 Heap Sandbox requires pointer compression") + v8_random_seed = "314159265" v8_toolset_for_shell = "host" @@ -294,8 +310,11 @@ v8_toolset_for_shell = "host" config("internal_config_base") { visibility = [ ":*" ] # Only targets in this file can depend on this. + configs = [ ":v8_tracing_config" ] + include_dirs = [ ".", + "include", "$target_gen_dir", ] } @@ -308,7 +327,6 @@ config("internal_config") { "//build/config/compiler:wexit_time_destructors", ":internal_config_base", ":v8_header_features", - ":v8_tracing_config", ] if (is_component_build) { @@ -346,6 +364,14 @@ config("libbase_config") { } } +# This config should be applied to code using the cppgc_base. +config("cppgc_base_config") { + defines = [] + if (cppgc_enable_object_names) { + defines += [ "CPPGC_SUPPORTS_OBJECT_NAMES" ] + } +} + # This config should be applied to code using the libsampler. config("libsampler_config") { include_dirs = [ "include" ] @@ -389,6 +415,9 @@ config("v8_header_features") { if (v8_enable_pointer_compression || v8_enable_31bit_smis_on_64bit_arch) { defines += [ "V8_31BIT_SMIS_ON_64BIT_ARCH" ] } + if (v8_enable_heap_sandbox) { + defines += [ "V8_HEAP_SANDBOX" ] + } if (v8_deprecation_warnings) { defines += [ "V8_DEPRECATION_WARNINGS" ] } @@ -428,10 +457,6 @@ config("features") { } if (v8_enable_lite_mode) { defines += [ "V8_LITE_MODE" ] - - # TODO(v8:7777): Remove the define once the --jitless runtime flag does - # everything we need. - defines += [ "V8_JITLESS_MODE" ] } if (v8_enable_gdbjit) { defines += [ "ENABLE_GDB_JIT_INTERFACE" ] @@ -501,10 +526,6 @@ config("features") { if (v8_check_microtasks_scopes_consistency) { defines += [ "V8_CHECK_MICROTASKS_SCOPES_CONSISTENCY" ] } - - # TODO(v8:8519): Remove the define once all use-sites in - # the code are removed/fixed - defines += [ "V8_EMBEDDED_BUILTINS" ] if (v8_use_multi_snapshots) { defines += [ "V8_MULTI_SNAPSHOTS" ] } @@ -532,6 +553,9 @@ config("features") { if (v8_enable_wasm_gdb_remote_debugging) { defines += [ "V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING" ] } + if (v8_enable_nci_code) { + defines += [ "V8_ENABLE_NCI_CODE" ] + } } config("toolchain") { @@ -630,7 +654,9 @@ config("toolchain") { if (v8_can_use_fpu_instructions) { defines += [ "CAN_USE_FPU_INSTRUCTIONS" ] } - + if (mips_use_msa) { + defines += [ "_MIPS_MSA" ] + } if (host_byteorder == "little") { defines += [ "V8_TARGET_ARCH_MIPS64_LE" ] } else if (host_byteorder == "big") { @@ -646,9 +672,6 @@ config("toolchain") { } if (mips_arch_variant == "r6") { defines += [ "_MIPS_ARCH_MIPS64R6" ] - if (mips_use_msa) { - defines += [ "_MIPS_MSA" ] - } } else if (mips_arch_variant == "r2") { defines += [ "_MIPS_ARCH_MIPS64R2" ] } @@ -1012,9 +1035,12 @@ torque_files = [ "src/builtins/convert.tq", "src/builtins/console.tq", "src/builtins/data-view.tq", + "src/builtins/finalization-registry.tq", "src/builtins/frames.tq", "src/builtins/frame-arguments.tq", "src/builtins/growable-fixed-array.tq", + "src/builtins/ic-callable.tq", + "src/builtins/ic.tq", "src/builtins/internal-coverage.tq", "src/builtins/iterator.tq", "src/builtins/math.tq", @@ -1024,6 +1050,7 @@ torque_files = [ "src/builtins/promise-abstract-operations.tq", "src/builtins/promise-all.tq", "src/builtins/promise-all-element-closure.tq", + "src/builtins/promise-any.tq", "src/builtins/promise-constructor.tq", "src/builtins/promise-finally.tq", "src/builtins/promise-misc.tq", @@ -1082,6 +1109,7 @@ torque_files = [ "src/builtins/typed-array-sort.tq", "src/builtins/typed-array-subarray.tq", "src/builtins/typed-array.tq", + "src/builtins/wasm.tq", "src/ic/handler-configuration.tq", "src/objects/allocation-site.tq", "src/objects/api-callbacks.tq", @@ -1101,6 +1129,7 @@ torque_files = [ "src/objects/heap-number.tq", "src/objects/heap-object.tq", "src/objects/intl-objects.tq", + "src/objects/js-aggregate-error.tq", "src/objects/js-array-buffer.tq", "src/objects/js-array.tq", "src/objects/js-collection-iterator.tq", @@ -1184,7 +1213,7 @@ template("run_torque") { "class-verifiers-tq.h", "enum-verifiers-tq.cc", "objects-printer-tq.cc", - "objects-body-descriptors-tq-inl.h", + "objects-body-descriptors-tq-inl.inc", "class-definitions-tq.cc", "class-definitions-tq-inl.h", "class-definitions-tq.h", @@ -1196,6 +1225,8 @@ template("run_torque") { "instance-types-tq.h", "internal-class-definitions-tq.h", "internal-class-definitions-tq-inl.h", + "exported-class-definitions-tq.h", + "exported-class-definitions-tq-inl.h", ] outputs = [] @@ -1270,6 +1301,7 @@ v8_source_set("torque_generated_initializers") { deps = [ ":generate_bytecode_builtins_list", ":run_torque", + ":v8_tracing", ] public_deps = [ ":v8_maybe_icu" ] @@ -1298,6 +1330,7 @@ v8_source_set("torque_generated_definitions") { deps = [ ":generate_bytecode_builtins_list", ":run_torque", + ":v8_tracing", ] public_deps = [ ":v8_maybe_icu" ] @@ -1573,7 +1606,10 @@ v8_source_set("v8_initializers") { "test/cctest:*", ] - deps = [ ":torque_generated_initializers" ] + deps = [ + ":torque_generated_initializers", + ":v8_tracing", + ] sources = [ ### gcmole(all) ### @@ -1624,6 +1660,7 @@ v8_source_set("v8_initializers") { "src/builtins/builtins-typed-array-gen.h", "src/builtins/builtins-utils-gen.h", "src/builtins/builtins-wasm-gen.cc", + "src/builtins/builtins-wasm-gen.h", "src/builtins/growable-fixed-array-gen.cc", "src/builtins/growable-fixed-array-gen.h", "src/builtins/setup-builtins-internal.cc", @@ -1701,7 +1738,10 @@ 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", + ":v8_tracing", + ] sources = [ ### gcmole(all) ### @@ -2005,6 +2045,7 @@ v8_source_set("v8_compiler_opt") { ":generate_bytecode_builtins_list", ":run_torque", ":v8_maybe_icu", + ":v8_tracing", ] if (is_debug && !v8_optimized_debug && v8_enable_fast_mksnapshot) { @@ -2029,6 +2070,7 @@ v8_source_set("v8_compiler") { ":generate_bytecode_builtins_list", ":run_torque", ":v8_maybe_icu", + ":v8_tracing", ] configs = [ ":internal_config" ] @@ -2042,6 +2084,18 @@ group("v8_compiler_for_mksnapshot") { } } +# Any target using trace events must directly or indirectly depend on +# v8_tracing. +group("v8_tracing") { + if (v8_use_perfetto) { + if (build_with_chromium) { + public_deps = [ "//third_party/perfetto:libperfetto" ] + } else { + public_deps = [ ":v8_libperfetto" ] + } + } +} + v8_source_set("v8_base_without_compiler") { visibility = [ ":*" ] # Only targets in this file can depend on this. @@ -2053,6 +2107,7 @@ v8_source_set("v8_base_without_compiler") { ### gcmole(all) ### "$target_gen_dir/builtins-generated/bytecodes-builtins-list.h", + "include/cppgc/common.h", "include/v8-fast-api-calls.h", "include/v8-inspector-protocol.h", "include/v8-inspector.h", @@ -2121,7 +2176,6 @@ v8_source_set("v8_base_without_compiler") { "src/builtins/builtins-json.cc", "src/builtins/builtins-number.cc", "src/builtins/builtins-object.cc", - "src/builtins/builtins-promise.cc", "src/builtins/builtins-promise.h", "src/builtins/builtins-reflect.cc", "src/builtins/builtins-regexp.cc", @@ -2160,6 +2214,8 @@ v8_source_set("v8_base_without_compiler") { "src/codegen/constant-pool.h", "src/codegen/constants-arch.h", "src/codegen/cpu-features.h", + "src/codegen/external-reference-encoder.cc", + "src/codegen/external-reference-encoder.h", "src/codegen/external-reference-table.cc", "src/codegen/external-reference-table.h", "src/codegen/external-reference.cc", @@ -2207,6 +2263,8 @@ v8_source_set("v8_base_without_compiler") { "src/common/assert-scope.cc", "src/common/assert-scope.h", "src/common/checks.h", + "src/common/external-pointer-inl.h", + "src/common/external-pointer.h", "src/common/message-template.h", "src/common/ptr-compr-inl.h", "src/common/ptr-compr.h", @@ -2284,6 +2342,7 @@ v8_source_set("v8_base_without_compiler") { "src/execution/messages.h", "src/execution/microtask-queue.cc", "src/execution/microtask-queue.h", + "src/execution/off-thread-isolate-inl.h", "src/execution/off-thread-isolate.cc", "src/execution/off-thread-isolate.h", "src/execution/pointer-authentication.h", @@ -2330,6 +2389,8 @@ v8_source_set("v8_base_without_compiler") { "src/handles/local-handles.h", "src/handles/maybe-handles-inl.h", "src/handles/maybe-handles.h", + "src/handles/persistent-handles.cc", + "src/handles/persistent-handles.h", "src/heap/array-buffer-collector.cc", "src/heap/array-buffer-collector.h", "src/heap/array-buffer-sweeper.cc", @@ -2344,6 +2405,9 @@ v8_source_set("v8_base_without_compiler") { "src/heap/code-stats.h", "src/heap/combined-heap.cc", "src/heap/combined-heap.h", + "src/heap/concurrent-allocator-inl.h", + "src/heap/concurrent-allocator.cc", + "src/heap/concurrent-allocator.h", "src/heap/concurrent-marking.cc", "src/heap/concurrent-marking.h", "src/heap/embedder-tracing.cc", @@ -2376,6 +2440,9 @@ v8_source_set("v8_base_without_compiler") { "src/heap/invalidated-slots.h", "src/heap/item-parallel-job.cc", "src/heap/item-parallel-job.h", + "src/heap/large-spaces.cc", + "src/heap/large-spaces.h", + "src/heap/list.h", "src/heap/local-allocator-inl.h", "src/heap/local-allocator.h", "src/heap/local-heap.cc", @@ -2389,6 +2456,9 @@ v8_source_set("v8_base_without_compiler") { "src/heap/marking-worklist.h", "src/heap/marking.cc", "src/heap/marking.h", + "src/heap/memory-chunk-inl.h", + "src/heap/memory-chunk.cc", + "src/heap/memory-chunk.h", "src/heap/memory-measurement-inl.h", "src/heap/memory-measurement.cc", "src/heap/memory-measurement.h", @@ -2401,9 +2471,13 @@ v8_source_set("v8_base_without_compiler") { "src/heap/objects-visiting.h", "src/heap/off-thread-factory.cc", "src/heap/off-thread-factory.h", + "src/heap/off-thread-heap.cc", + "src/heap/off-thread-heap.h", "src/heap/read-only-heap-inl.h", "src/heap/read-only-heap.cc", "src/heap/read-only-heap.h", + "src/heap/read-only-spaces.cc", + "src/heap/read-only-spaces.h", "src/heap/remembered-set.h", "src/heap/safepoint.cc", "src/heap/safepoint.h", @@ -2598,6 +2672,8 @@ v8_source_set("v8_base_without_compiler") { "src/objects/internal-index.h", "src/objects/intl-objects.cc", "src/objects/intl-objects.h", + "src/objects/js-aggregate-error-inl.h", + "src/objects/js-aggregate-error.h", "src/objects/js-array-buffer-inl.h", "src/objects/js-array-buffer.cc", "src/objects/js-array-buffer.h", @@ -2888,6 +2964,7 @@ v8_source_set("v8_base_without_compiler") { "src/runtime/runtime-typedarray.cc", "src/runtime/runtime-utils.h", "src/runtime/runtime-wasm.cc", + "src/runtime/runtime-weak-refs.cc", "src/runtime/runtime.cc", "src/runtime/runtime.h", "src/sanitizer/asan.h", @@ -2897,6 +2974,10 @@ v8_source_set("v8_base_without_compiler") { "src/sanitizer/tsan.h", "src/snapshot/code-serializer.cc", "src/snapshot/code-serializer.h", + "src/snapshot/context-deserializer.cc", + "src/snapshot/context-deserializer.h", + "src/snapshot/context-serializer.cc", + "src/snapshot/context-serializer.h", "src/snapshot/deserializer-allocator.cc", "src/snapshot/deserializer-allocator.h", "src/snapshot/deserializer.cc", @@ -2905,10 +2986,6 @@ v8_source_set("v8_base_without_compiler") { "src/snapshot/embedded/embedded-data.h", "src/snapshot/object-deserializer.cc", "src/snapshot/object-deserializer.h", - "src/snapshot/partial-deserializer.cc", - "src/snapshot/partial-deserializer.h", - "src/snapshot/partial-serializer.cc", - "src/snapshot/partial-serializer.h", "src/snapshot/read-only-deserializer.cc", "src/snapshot/read-only-deserializer.h", "src/snapshot/read-only-serializer.cc", @@ -2918,15 +2995,19 @@ v8_source_set("v8_base_without_compiler") { "src/snapshot/roots-serializer.h", "src/snapshot/serializer-allocator.cc", "src/snapshot/serializer-allocator.h", - "src/snapshot/serializer-common.cc", - "src/snapshot/serializer-common.h", + "src/snapshot/serializer-deserializer.cc", + "src/snapshot/serializer-deserializer.h", "src/snapshot/serializer.cc", "src/snapshot/serializer.h", - "src/snapshot/snapshot-common.cc", "src/snapshot/snapshot-compression.cc", "src/snapshot/snapshot-compression.h", + "src/snapshot/snapshot-data.cc", + "src/snapshot/snapshot-data.h", "src/snapshot/snapshot-source-sink.cc", "src/snapshot/snapshot-source-sink.h", + "src/snapshot/snapshot-utils.cc", + "src/snapshot/snapshot-utils.h", + "src/snapshot/snapshot.cc", "src/snapshot/snapshot.h", "src/snapshot/startup-deserializer.cc", "src/snapshot/startup-deserializer.h", @@ -3026,6 +3107,7 @@ v8_source_set("v8_base_without_compiler") { "src/wasm/signature-map.h", "src/wasm/streaming-decoder.cc", "src/wasm/streaming-decoder.h", + "src/wasm/struct-types.h", "src/wasm/value-type.h", "src/wasm/wasm-arguments.h", "src/wasm/wasm-code-manager.cc", @@ -3085,21 +3167,28 @@ v8_source_set("v8_base_without_compiler") { if (v8_enable_third_party_heap) { sources += v8_third_party_heap_files + } else { + sources += [ "src/heap/third-party/heap-api-stub.cc" ] } if (v8_enable_wasm_gdb_remote_debugging) { sources += [ + "src/debug/wasm/gdb-server/gdb-remote-util.cc", + "src/debug/wasm/gdb-server/gdb-remote-util.h", "src/debug/wasm/gdb-server/gdb-server-thread.cc", "src/debug/wasm/gdb-server/gdb-server-thread.h", "src/debug/wasm/gdb-server/gdb-server.cc", "src/debug/wasm/gdb-server/gdb-server.h", + "src/debug/wasm/gdb-server/packet.cc", + "src/debug/wasm/gdb-server/packet.h", "src/debug/wasm/gdb-server/session.cc", "src/debug/wasm/gdb-server/session.h", "src/debug/wasm/gdb-server/target.cc", "src/debug/wasm/gdb-server/target.h", "src/debug/wasm/gdb-server/transport.cc", "src/debug/wasm/gdb-server/transport.h", - "src/debug/wasm/gdb-server/util.h", + "src/debug/wasm/gdb-server/wasm-module-debug.cc", + "src/debug/wasm/gdb-server/wasm-module-debug.h", ] } @@ -3420,6 +3509,7 @@ v8_source_set("v8_base_without_compiler") { ":v8_libbase", ":v8_libsampler", ":v8_shared_internal_headers", + ":v8_tracing", ":v8_version", "src/inspector:inspector", ] @@ -3511,6 +3601,14 @@ v8_source_set("v8_base_without_compiler") { ] deps += [ "src/third_party/vtune:v8_vtune_trace_mark" ] } + + if (v8_use_perfetto) { + sources -= [ "//base/trace_event/common/trace_event_common.h" ] + sources += [ + "src/tracing/trace-categories.cc", + "src/tracing/trace-categories.h", + ] + } } group("v8_base") { @@ -3672,7 +3770,6 @@ v8_component("v8_libbase") { "src/base/ieee754.h", "src/base/iterator.h", "src/base/lazy-instance.h", - "src/base/list.h", "src/base/logging.cc", "src/base/logging.h", "src/base/lsan.h", @@ -3843,6 +3940,8 @@ v8_component("v8_libplatform") { "include/libplatform/v8-tracing.h", "src/libplatform/default-foreground-task-runner.cc", "src/libplatform/default-foreground-task-runner.h", + "src/libplatform/default-job.cc", + "src/libplatform/default-job.h", "src/libplatform/default-platform.cc", "src/libplatform/default-platform.h", "src/libplatform/default-worker-threads-task-runner.cc", @@ -3873,20 +3972,25 @@ v8_component("v8_libplatform") { deps = [ ":v8_headers", ":v8_libbase", + ":v8_tracing", ] + if (v8_use_perfetto) { + sources -= [ + "//base/trace_event/common/trace_event_common.h", + "src/libplatform/tracing/trace-buffer.cc", + "src/libplatform/tracing/trace-buffer.h", + "src/libplatform/tracing/trace-object.cc", + "src/libplatform/tracing/trace-writer.cc", + "src/libplatform/tracing/trace-writer.h", + ] sources += [ - "src/libplatform/tracing/json-trace-event-listener.cc", - "src/libplatform/tracing/json-trace-event-listener.h", "src/libplatform/tracing/trace-event-listener.cc", "src/libplatform/tracing/trace-event-listener.h", ] deps += [ + # TODO(skyostil): Switch TraceEventListener to protozero. "//third_party/perfetto/protos/perfetto/trace:lite", - "//third_party/perfetto/protos/perfetto/trace/chrome:minimal_complete_lite", - "//third_party/perfetto/protos/perfetto/trace/chrome:zero", - "//third_party/perfetto/src/tracing:client_api", - "//third_party/perfetto/src/tracing:platform_posix", ] } } @@ -3914,9 +4018,8 @@ v8_source_set("fuzzer_support") { configs = [ ":internal_config_base" ] - deps = [ ":v8" ] - public_deps = [ + ":v8", ":v8_libbase", ":v8_libplatform", ":v8_maybe_icu", @@ -3928,14 +4031,33 @@ v8_source_set("cppgc_base") { sources = [ "include/cppgc/allocation.h", - "include/cppgc/api-constants.h", - "include/cppgc/finalizer-trait.h", + "include/cppgc/common.h", + "include/cppgc/custom-space.h", "include/cppgc/garbage-collected.h", - "include/cppgc/gc-info.h", "include/cppgc/heap.h", + "include/cppgc/internal/accessors.h", + "include/cppgc/internal/api-contants.h", + "include/cppgc/internal/compiler-specific.h", + "include/cppgc/internal/finalizer-traits.h", + "include/cppgc/internal/gc-info.h", + "include/cppgc/internal/persistent-node.h", + "include/cppgc/internal/pointer-policies.h", + "include/cppgc/internal/prefinalizer-handler.h", + "include/cppgc/liveness-broker.h", + "include/cppgc/liveness-broker.h", + "include/cppgc/macros.h", + "include/cppgc/member.h", + "include/cppgc/persistent.h", "include/cppgc/platform.h", + "include/cppgc/prefinalizer.h", + "include/cppgc/source-location.h", + "include/cppgc/trace-trait.h", + "include/cppgc/type-traits.h", + "include/cppgc/visitor.h", "include/v8config.h", "src/heap/cppgc/allocation.cc", + "src/heap/cppgc/free-list.cc", + "src/heap/cppgc/free-list.h", "src/heap/cppgc/gc-info-table.cc", "src/heap/cppgc/gc-info-table.h", "src/heap/cppgc/gc-info.cc", @@ -3943,23 +4065,75 @@ v8_source_set("cppgc_base") { "src/heap/cppgc/heap-object-header-inl.h", "src/heap/cppgc/heap-object-header.cc", "src/heap/cppgc/heap-object-header.h", + "src/heap/cppgc/heap-page.cc", + "src/heap/cppgc/heap-page.h", + "src/heap/cppgc/heap-space.cc", + "src/heap/cppgc/heap-space.h", + "src/heap/cppgc/heap-visitor.h", "src/heap/cppgc/heap.cc", "src/heap/cppgc/heap.h", + "src/heap/cppgc/liveness-broker.cc", + "src/heap/cppgc/logging.cc", + "src/heap/cppgc/marker.cc", + "src/heap/cppgc/marker.h", + "src/heap/cppgc/marking-visitor.cc", + "src/heap/cppgc/marking-visitor.h", + "src/heap/cppgc/object-allocator-inl.h", + "src/heap/cppgc/object-allocator.cc", + "src/heap/cppgc/object-allocator.h", + "src/heap/cppgc/object-start-bitmap-inl.h", + "src/heap/cppgc/object-start-bitmap.h", + "src/heap/cppgc/page-memory-inl.h", + "src/heap/cppgc/page-memory.cc", + "src/heap/cppgc/page-memory.h", + "src/heap/cppgc/persistent-node.cc", "src/heap/cppgc/platform.cc", + "src/heap/cppgc/pointer-policies.cc", + "src/heap/cppgc/prefinalizer-handler.cc", + "src/heap/cppgc/prefinalizer-handler.h", + "src/heap/cppgc/raw-heap.cc", + "src/heap/cppgc/raw-heap.h", "src/heap/cppgc/sanitizers.h", + "src/heap/cppgc/source-location.cc", "src/heap/cppgc/stack.cc", "src/heap/cppgc/stack.h", + "src/heap/cppgc/sweeper.cc", + "src/heap/cppgc/sweeper.h", + "src/heap/cppgc/worklist.h", ] - if (target_cpu == "x64") { - if (is_win) { - sources += [ "src/heap/cppgc/asm/x64/push_registers_win.S" ] - } else { - sources += [ "src/heap/cppgc/asm/x64/push_registers.S" ] + if (is_clang || !is_win) { + if (target_cpu == "x64") { + sources += [ "src/heap/cppgc/asm/x64/push_registers_asm.cc" ] + } else if (target_cpu == "x86") { + sources += [ "src/heap/cppgc/asm/ia32/push_registers_asm.cc" ] + } else if (target_cpu == "arm") { + sources += [ "src/heap/cppgc/asm/arm/push_registers_asm.cc" ] + } else if (target_cpu == "arm64") { + sources += [ "src/heap/cppgc/asm/arm64/push_registers_asm.cc" ] + } else if (target_cpu == "ppc64") { + sources += [ "src/heap/cppgc/asm/ppc/push_registers_asm.cc" ] + } else if (target_cpu == "s390x") { + sources += [ "src/heap/cppgc/asm/s390/push_registers_asm.cc" ] + } else if (target_cpu == "mipsel") { + sources += [ "src/heap/cppgc/asm/mips/push_registers_asm.cc" ] + } else if (target_cpu == "mips64el") { + sources += [ "src/heap/cppgc/asm/mips64/push_registers_asm.cc" ] + } + } else if (is_win) { + if (target_cpu == "x64") { + sources += [ "src/heap/cppgc/asm/x64/push_registers_masm.S" ] + } else if (target_cpu == "x86") { + sources += [ "src/heap/cppgc/asm/ia32/push_registers_masm.S" ] + } else if (target_cpu == "arm64") { + sources += [ "src/heap/cppgc/asm/arm64/push_registers_masm.S" ] } } - configs = [ ":internal_config" ] + configs = [ + ":internal_config", + ":cppgc_base_config", + ] public_deps = [ ":v8_libbase" ] } @@ -4010,12 +4184,6 @@ v8_static_library("wee8") { ] } -v8_static_library("cppgc") { - deps = [ ":cppgc_base" ] - - configs = [ ":internal_config" ] -} - ############################################################################### # Executables # @@ -4074,6 +4242,7 @@ if (current_toolchain == v8_snapshot_toolchain) { ":v8_libbase", ":v8_libplatform", ":v8_maybe_icu", + ":v8_tracing", "//build/win:default_exe_manifest", ] } @@ -4308,6 +4477,14 @@ if (is_component_build) { public_configs = [ ":external_config" ] } + v8_component("cppgc") { + public_deps = [ ":cppgc_base" ] + + configs = [ ":internal_config" ] + + public_configs = [ ":external_config" ] + } + v8_component("cppgc_for_testing") { testonly = true @@ -4340,6 +4517,12 @@ if (is_component_build) { public_configs = [ ":external_config" ] } + group("cppgc") { + public_deps = [ ":cppgc_base" ] + + public_configs = [ ":external_config" ] + } + group("cppgc_for_testing") { testonly = true @@ -4374,6 +4557,7 @@ v8_executable("d8") { ":v8", ":v8_libbase", ":v8_libplatform", + ":v8_tracing", "//build/win:default_exe_manifest", ] @@ -4392,10 +4576,6 @@ v8_executable("d8") { if (v8_enable_vtunejit) { deps += [ "src/third_party/vtune:v8_vtune" ] } - - if (v8_use_perfetto) { - deps += [ "//third_party/perfetto/src/tracing:in_process_backend" ] - } } v8_executable("v8_hello_world") { @@ -4551,6 +4731,7 @@ v8_source_set("wasm_module_runner") { deps = [ ":generate_bytecode_builtins_list", ":run_torque", + ":v8_tracing", ] public_deps = [ ":v8_maybe_icu" ] @@ -4627,6 +4808,7 @@ v8_source_set("lib_wasm_fuzzer_common") { deps = [ ":generate_bytecode_builtins_list", ":run_torque", + ":v8_tracing", ] public_deps = [ ":v8_maybe_icu" ] @@ -4709,7 +4891,7 @@ if (!build_with_chromium && v8_use_perfetto) { "-Wno-tautological-constant-compare", ] } - if (is_win) { + if (is_win && is_clang) { cflags += [ "-Wno-microsoft-unqualified-friend" ] } } @@ -4868,4 +5050,21 @@ if (!build_with_chromium && v8_use_perfetto) { configs += [ "//build/config/compiler:no_chromium_code" ] } } # host_toolchain + + v8_component("v8_libperfetto") { + configs = [ ":v8_tracing_config" ] + public_configs = [ "//third_party/perfetto/gn:public_config" ] + deps = [ + "//third_party/perfetto/src/trace_processor:export_json", + "//third_party/perfetto/src/trace_processor:storage_minimal", + "//third_party/perfetto/src/tracing:client_api", + "//third_party/perfetto/src/tracing/core", + + # TODO(skyostil): Support non-POSIX platforms. + "//third_party/perfetto/protos/perfetto/config:cpp", + "//third_party/perfetto/protos/perfetto/trace/track_event:zero", + "//third_party/perfetto/src/tracing:in_process_backend", + "//third_party/perfetto/src/tracing:platform_posix", + ] + } } # if (!build_with_chromium && v8_use_perfetto) diff --git a/deps/v8/COMMON_OWNERS b/deps/v8/COMMON_OWNERS index 1eee48173a1576..1319a579173c3c 100644 --- a/deps/v8/COMMON_OWNERS +++ b/deps/v8/COMMON_OWNERS @@ -2,6 +2,7 @@ adamk@chromium.org ahaas@chromium.org bbudge@chromium.org binji@chromium.org +bikineev@chromium.org bmeurer@chromium.org cbruni@chromium.org clemensb@chromium.org @@ -25,6 +26,7 @@ mslekova@chromium.org mvstanton@chromium.org mythria@chromium.org neis@chromium.org +omerkatz@chromium.org petermarshall@chromium.org rmcilroy@chromium.org sigurds@chromium.org diff --git a/deps/v8/DEPS b/deps/v8/DEPS index 1bc687beaf6ead..7b38c3dcd07b17 100644 --- a/deps/v8/DEPS +++ b/deps/v8/DEPS @@ -34,7 +34,7 @@ vars = { 'gn_version': 'git_revision:5ed3c9cc67b090d5e311e4bd2aba072173e82db9', # luci-go CIPD package version. - 'luci_go': 'git_revision:de73cf6c4bde86f0a9c8d54151b69b0154a398f1', + 'luci_go': 'git_revision:56ae79476e3caf14da59d75118408aa778637936', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling android_sdk_build-tools_version @@ -55,7 +55,7 @@ vars = { # Three lines of non-changing comments so that # the commit queue can handle CLs rolling android_sdk_platform-tools_version # and whatever else without interference from each other. - 'android_sdk_platform-tools_version': 'Jxtur3_L9RzY4q79K-AwIahwFW4oi5uYVD5URx9h62wC', + 'android_sdk_platform-tools_version': 'zMVtBEihXp2Z0NYFNjLLmNrwy6252b_YWG6sh2l0QAcC', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling android_sdk_platforms_version # and whatever else without interference from each other. @@ -67,20 +67,20 @@ vars = { # Three lines of non-changing comments so that # the commit queue can handle CLs rolling android_sdk_tools-lint_version # and whatever else without interference from each other. - 'android_sdk_tools-lint_version': '89hXqZYzCum3delB5RV7J_QyWkaRodqdtQS0s3LMh3wC', + 'android_sdk_cmdline-tools_version': 'CR25ixsRhwuRnhdgDpGFyl9S0C_0HO9SUgFrwX46zq8C', } deps = { 'v8/build': - Var('chromium_url') + '/chromium/src/build.git' + '@' + '26e9d485d01d6e0eb9dadd21df767a63494c8fea', + Var('chromium_url') + '/chromium/src/build.git' + '@' + '1b904cc30093c25d5fd48389bd58e3f7409bcf80', 'v8/third_party/depot_tools': - Var('chromium_url') + '/chromium/tools/depot_tools.git' + '@' + '2b2aec6506a810f8d7bd018609de2c2450b3c121', + Var('chromium_url') + '/chromium/tools/depot_tools.git' + '@' + '454f4ba4b3a69feb03c73f93d789062033433b4c', 'v8/third_party/icu': - Var('chromium_url') + '/chromium/deps/icu.git' + '@' + 'd7aff76cf6bb0fbef3afa6c07718f78a80a70f8f', + Var('chromium_url') + '/chromium/deps/icu.git' + '@' + 'f2223961702f00a8833874b0560d615a2cc42738', 'v8/third_party/instrumented_libraries': Var('chromium_url') + '/chromium/src/third_party/instrumented_libraries.git' + '@' + 'bb3f1802c237dd19105dd0f7919f99e536a39d10', 'v8/buildtools': - Var('chromium_url') + '/chromium/src/buildtools.git' + '@' + '7977eb176752aeec29d888cfe8e677ac12ed1c41', + Var('chromium_url') + '/chromium/src/buildtools.git' + '@' + '204a35a2a64f7179f8b76d7a0385653690839e21', 'v8/buildtools/clang_format/script': Var('chromium_url') + '/chromium/llvm-project/cfe/tools/clang-format.git' + '@' + '96636aa0e9f047f17447f2d45a094d0b59ed7917', 'v8/buildtools/linux64': { @@ -108,7 +108,7 @@ deps = { 'v8/buildtools/third_party/libc++abi/trunk': Var('chromium_url') + '/external/github.com/llvm/llvm-project/libcxxabi.git' + '@' + '196ba1aaa8ac285d94f4ea8d9836390a45360533', 'v8/buildtools/third_party/libunwind/trunk': - Var('chromium_url') + '/external/github.com/llvm/llvm-project/libunwind.git' + '@' + '43bb9f872232f531bac80093ceb4de61c64b9ab7', + Var('chromium_url') + '/external/github.com/llvm/llvm-project/libunwind.git' + '@' + 'd999d54f4bca789543a2eb6c995af2d9b5a1f3ed', 'v8/buildtools/win': { 'packages': [ { @@ -126,7 +126,7 @@ deps = { 'condition': 'checkout_android', }, 'v8/third_party/android_platform': { - 'url': Var('chromium_url') + '/chromium/src/third_party/android_platform.git' + '@' + '2244b5ea295f8fda3179bef160c84ef8fa0ec9fc', + 'url': Var('chromium_url') + '/chromium/src/third_party/android_platform.git' + '@' + '716366f5685ad8aaf1208c64941e440e8e117441', 'condition': 'checkout_android', }, 'v8/third_party/android_sdk/public': { @@ -160,15 +160,15 @@ deps = { 'version': Var('android_sdk_sources_version'), }, { - 'package': 'chromium/third_party/android_sdk/public/tools-lint', - 'version': Var('android_sdk_tools-lint_version'), + 'package': 'chromium/third_party/android_sdk/public/cmdline-tools', + 'version': Var('android_sdk_cmdline-tools_version'), }, ], 'condition': 'checkout_android', 'dep_type': 'cipd', }, 'v8/third_party/catapult': { - 'url': Var('chromium_url') + '/catapult.git' + '@' + '032c78376792ef343ea361bca2181ba6dec6b95f', + 'url': Var('chromium_url') + '/catapult.git' + '@' + 'e9a8d378c950ee44beec5dd5207e151f48e5b5be', 'condition': 'checkout_android', }, 'v8/third_party/colorama/src': { @@ -176,23 +176,23 @@ deps = { 'condition': 'checkout_android', }, 'v8/third_party/fuchsia-sdk': { - 'url': Var('chromium_url') + '/chromium/src/third_party/fuchsia-sdk.git' + '@' + '2457e41d8dc379f74662d3157e76339ba92cee06', + 'url': Var('chromium_url') + '/chromium/src/third_party/fuchsia-sdk.git' + '@' + '277fe9120cce5f7a42d43554646fa447f88a1598', 'condition': 'checkout_fuchsia', }, 'v8/third_party/googletest/src': - Var('chromium_url') + '/external/github.com/google/googletest.git' + '@' + '10b1902d893ea8cc43c69541d70868f91af3646b', + Var('chromium_url') + '/external/github.com/google/googletest.git' + '@' + 'a09ea700d32bab83325aff9ff34d0582e50e3997', 'v8/third_party/jinja2': - Var('chromium_url') + '/chromium/src/third_party/jinja2.git' + '@' + 'b41863e42637544c2941b574c7877d3e1f663e25', + Var('chromium_url') + '/chromium/src/third_party/jinja2.git' + '@' + '3f90fa05c85718505e28c9c3426c1ba52843b9b7', 'v8/third_party/markupsafe': Var('chromium_url') + '/chromium/src/third_party/markupsafe.git' + '@' + '8f45f5cfa0009d2a70589bcda0349b8cb2b72783', 'v8/tools/swarming_client': - Var('chromium_url') + '/infra/luci/client-py.git' + '@' + 'cc958279ffd6853e0a1b227a7e957ca334fe56af', + Var('chromium_url') + '/infra/luci/client-py.git' + '@' + '160b445a44e0daacf6f3f8570ca2707ec451f374', 'v8/test/benchmarks/data': Var('chromium_url') + '/v8/deps/third_party/benchmarks.git' + '@' + '05d7188267b4560491ff9155c5ee13e207ecd65f', 'v8/test/mozilla/data': Var('chromium_url') + '/v8/deps/third_party/mozilla-tests.git' + '@' + 'f6c578a10ea707b1a8ab0b88943fe5115ce2b9be', 'v8/test/test262/data': - Var('chromium_url') + '/external/github.com/tc39/test262.git' + '@' + 'f6b2ccdd091ff82da54150796297c3a96d7edb41', + Var('chromium_url') + '/external/github.com/tc39/test262.git' + '@' + 'd2f7d4285c4a5267f5be37a9c823a397daadad1b', 'v8/test/test262/harness': Var('chromium_url') + '/external/github.com/test262-utils/test262-harness-py.git' + '@' + '4555345a943d0c99a9461182705543fb171dda4b', 'v8/third_party/qemu-linux-x64': { @@ -219,7 +219,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/third_party/aemu/linux-amd64', - 'version': '7YlCgase5GlIanqHn-nZClSlZ5kQETJyVUYRF7Jjy6UC' + 'version': '5LzaFiFYMxwWXcgus5JjF74yr90M5oz9IMo29pTdoLgC' }, ], 'condition': 'host_os == "linux" and checkout_fuchsia', @@ -236,7 +236,7 @@ deps = { 'dep_type': 'cipd', }, 'v8/tools/clang': - Var('chromium_url') + '/chromium/src/tools/clang.git' + '@' + '105a8460911176861a422738eee4daad8dfe88a2', + Var('chromium_url') + '/chromium/src/tools/clang.git' + '@' + 'de3e20662b84f0ee361a5ae11c99a9513df7c8e8', 'v8/tools/luci-go': { 'packages': [ { @@ -266,11 +266,13 @@ deps = { 'dep_type': 'cipd', }, 'v8/third_party/perfetto': - Var('android_url') + '/platform/external/perfetto.git' + '@' + 'b9b24d1b0b80aafec393af085067e9eae829412f', + Var('android_url') + '/platform/external/perfetto.git' + '@' + 'ff70e0d273ed10995866c803f23e11250eb3dc52', 'v8/third_party/protobuf': Var('chromium_url') + '/external/github.com/google/protobuf'+ '@' + 'b68a347f56137b4b1a746e8c7438495a6ac1bd91', 'v8/third_party/zlib': - Var('chromium_url') + '/chromium/src/third_party/zlib.git'+ '@' + '156be8c52f80cde343088b4a69a80579101b6e67', + Var('chromium_url') + '/chromium/src/third_party/zlib.git'+ '@' + '90fc47e6eed7bd1a59ad1603761303ef24705593', + 'v8/third_party/jsoncpp/source': + Var('chromium_url') + '/external/github.com/open-source-parsers/jsoncpp.git'+ '@' + '645250b6690785be60ab6780ce4b58698d884d11', 'v8/third_party/ittapi': { # Force checkout ittapi libraries to pass v8 header includes check on # bots that has check_v8_header_includes enabled. diff --git a/deps/v8/INTL_OWNERS b/deps/v8/INTL_OWNERS index dbe6f3b7b54292..6e9f2cedb98c4d 100644 --- a/deps/v8/INTL_OWNERS +++ b/deps/v8/INTL_OWNERS @@ -1,3 +1,4 @@ cira@chromium.org mnita@google.com jshin@chromium.org +ftang@chromium.org diff --git a/deps/v8/WATCHLISTS b/deps/v8/WATCHLISTS index 47470f49e42308..54d6bbec1c51ff 100644 --- a/deps/v8/WATCHLISTS +++ b/deps/v8/WATCHLISTS @@ -33,10 +33,6 @@ { 'WATCHLIST_DEFINITIONS': { - 'api': { - 'filepath': 'include/' \ - '|src/api\.(cc|h)$', - }, 'snapshot': { 'filepath': 'src/snapshot/', }, @@ -94,9 +90,6 @@ }, 'WATCHLISTS': { - 'api': [ - 'yangguo+watch@chromium.org', - ], 'csa': [ 'jgruber+watch@chromium.org', ], diff --git a/deps/v8/build_overrides/build.gni b/deps/v8/build_overrides/build.gni index 5b99eb94022596..dde92c46eaa78d 100644 --- a/deps/v8/build_overrides/build.gni +++ b/deps/v8/build_overrides/build.gni @@ -16,6 +16,14 @@ perfetto_build_with_embedder = true perfetto_protobuf_target_prefix = "//" perfetto_protobuf_gni = "//gni/proto_library.gni" +# We use Perfetto's Trace Processor to convert traces to the legacy JSON +# format. +enable_perfetto_trace_processor = true + +# When building with chromium, determines whether we want to also use the +# perfetto library from chromium instead declaring our own. +use_perfetto_client_library = false + # Uncomment these to specify a different NDK location and version in # non-Chromium builds. # default_android_ndk_root = "//third_party/android_ndk" diff --git a/deps/v8/gni/v8.gni b/deps/v8/gni/v8.gni index 0b2806ca949b77..9d286ebbfc0226 100644 --- a/deps/v8/gni/v8.gni +++ b/deps/v8/gni/v8.gni @@ -54,8 +54,7 @@ declare_args() { # Expose symbols for dynamic linking. v8_expose_symbols = false - # Use Perfetto (https://perfetto.dev) as the default TracingController. Not - # currently implemented. + # Implement tracing using Perfetto (https://perfetto.dev). v8_use_perfetto = false # Override global symbol level setting for v8 @@ -82,6 +81,12 @@ if (v8_enable_backtrace == "") { v8_enable_backtrace = is_debug && !v8_optimized_debug } +# If chromium is configured to use the perfetto client library, v8 should also +# use perfetto for tracing. +if (build_with_chromium && use_perfetto_client_library) { + v8_use_perfetto = true +} + # Points to // in v8 stand-alone or to //v8/ in chromium. We need absolute # paths for all configs in templates as they are shared in different # subdirectories. diff --git a/deps/v8/include/DEPS b/deps/v8/include/DEPS index ca60f841f530ab..7305ff51125503 100644 --- a/deps/v8/include/DEPS +++ b/deps/v8/include/DEPS @@ -1,4 +1,5 @@ include_rules = [ # v8-inspector-protocol.h depends on generated files under include/inspector. "+inspector", + "+cppgc/common.h", ] diff --git a/deps/v8/include/cppgc/DEPS b/deps/v8/include/cppgc/DEPS new file mode 100644 index 00000000000000..04c343de27c329 --- /dev/null +++ b/deps/v8/include/cppgc/DEPS @@ -0,0 +1,7 @@ +include_rules = [ + "-include", + "+v8config.h", + "+v8-platform.h", + "+cppgc", + "-src", +] diff --git a/deps/v8/include/cppgc/allocation.h b/deps/v8/include/cppgc/allocation.h index 3e717ad7d428f8..49ad49c34d6bc9 100644 --- a/deps/v8/include/cppgc/allocation.h +++ b/deps/v8/include/cppgc/allocation.h @@ -6,12 +6,14 @@ #define INCLUDE_CPPGC_ALLOCATION_H_ #include + #include -#include "include/cppgc/garbage-collected.h" -#include "include/cppgc/gc-info.h" -#include "include/cppgc/heap.h" -#include "include/cppgc/internals.h" +#include "cppgc/custom-space.h" +#include "cppgc/garbage-collected.h" +#include "cppgc/heap.h" +#include "cppgc/internal/api-constants.h" +#include "cppgc/internal/gc-info.h" namespace cppgc { @@ -35,36 +37,80 @@ class V8_EXPORT MakeGarbageCollectedTraitInternal { } static void* Allocate(cppgc::Heap* heap, size_t size, GCInfoIndex index); + static void* Allocate(cppgc::Heap* heapx, size_t size, GCInfoIndex index, + CustomSpaceIndex space_inde); friend class HeapObjectHeader; }; } // namespace internal -// Users with custom allocation needs (e.g. overriding size) should override -// MakeGarbageCollectedTrait (see below) and inherit their trait from -// MakeGarbageCollectedTraitBase to get access to low-level primitives. +/** + * Base trait that provides utilities for advancers users that have custom + * allocation needs (e.g., overriding size). It's expected that users override + * MakeGarbageCollectedTrait (see below) and inherit from + * MakeGarbageCollectedTraitBase and make use of the low-level primitives + * offered to allocate and construct an object. + */ template class MakeGarbageCollectedTraitBase : private internal::MakeGarbageCollectedTraitInternal { + private: + template + struct SpacePolicy { + static void* Allocate(Heap* heap, size_t size) { + // Custom space. + static_assert(std::is_base_of::value, + "Custom space must inherit from CustomSpaceBase."); + return internal::MakeGarbageCollectedTraitInternal::Allocate( + heap, size, internal::GCInfoTrait::Index(), + CustomSpace::kSpaceIndex); + } + }; + + template + struct SpacePolicy { + static void* Allocate(Heap* heap, size_t size) { + // Default space. + return internal::MakeGarbageCollectedTraitInternal::Allocate( + heap, size, internal::GCInfoTrait::Index()); + } + }; + protected: - // Allocates an object of |size| bytes on |heap|. - // - // TODO(mlippautz): Allow specifying arena for specific embedder uses. + /** + * Allocates memory for an object of type T. + * + * \param heap The heap to allocate this object on. + * \param size The size that should be reserved for the object. + * \returns the memory to construct an object of type T on. + */ static void* Allocate(Heap* heap, size_t size) { - return internal::MakeGarbageCollectedTraitInternal::Allocate( - heap, size, internal::GCInfoTrait::Index()); + return SpacePolicy::Space>::Allocate(heap, size); } - // Marks an object as being fully constructed, resulting in precise handling - // by the garbage collector. + /** + * Marks an object as fully constructed, resulting in precise handling by the + * garbage collector. + * + * \param payload The base pointer the object is allocated at. + */ static void MarkObjectAsFullyConstructed(const void* payload) { - // internal::MarkObjectAsFullyConstructed(payload); internal::MakeGarbageCollectedTraitInternal::MarkObjectAsFullyConstructed( payload); } }; +/** + * Default trait class that specifies how to construct an object of type T. + * Advanced users may override how an object is constructed using the utilities + * that are provided through MakeGarbageCollectedTraitBase. + * + * Any trait overriding construction must + * - allocate through MakeGarbageCollectedTraitBase::Allocate; + * - mark the object as fully constructed using + * MakeGarbageCollectedTraitBase::MarkObjectAsFullyConstructed; + */ template class MakeGarbageCollectedTrait : public MakeGarbageCollectedTraitBase { public: @@ -72,6 +118,10 @@ class MakeGarbageCollectedTrait : public MakeGarbageCollectedTraitBase { static T* Call(Heap* heap, Args&&... args) { static_assert(internal::IsGarbageCollectedType::value, "T needs to be a garbage collected object"); + static_assert( + !internal::IsGarbageCollectedMixinType::value || + sizeof(T) <= internal::api_constants::kLargeObjectSizeThreshold, + "GarbageCollectedMixin may not be a large object"); void* memory = MakeGarbageCollectedTraitBase::Allocate(heap, sizeof(T)); T* object = ::new (memory) T(std::forward(args)...); MakeGarbageCollectedTraitBase::MarkObjectAsFullyConstructed(object); @@ -79,11 +129,31 @@ class MakeGarbageCollectedTrait : public MakeGarbageCollectedTraitBase { } }; -// Default MakeGarbageCollected: Constructs an instance of T, which is a garbage -// collected type. +/** + * Allows users to specify a post-construction callback for specific types. The + * callback is invoked on the instance of type T right after it has been + * constructed. This can be useful when the callback requires a + * fully-constructed object to be able to dispatch to virtual methods. + */ +template +struct PostConstructionCallbackTrait { + static void Call(T*) {} +}; + +/** + * Constructs a managed object of type T where T transitively inherits from + * GarbageCollected. + * + * \param args List of arguments with which an instance of T will be + * constructed. + * \returns an instance of type T. + */ template T* MakeGarbageCollected(Heap* heap, Args&&... args) { - return MakeGarbageCollectedTrait::Call(heap, std::forward(args)...); + T* object = + MakeGarbageCollectedTrait::Call(heap, std::forward(args)...); + PostConstructionCallbackTrait::Call(object); + return object; } } // namespace cppgc diff --git a/deps/v8/include/cppgc/common.h b/deps/v8/include/cppgc/common.h new file mode 100644 index 00000000000000..228b9abb74e763 --- /dev/null +++ b/deps/v8/include/cppgc/common.h @@ -0,0 +1,26 @@ +// Copyright 2020 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. + +#ifndef INCLUDE_CPPGC_COMMON_H_ +#define INCLUDE_CPPGC_COMMON_H_ + +// TODO(chromium:1056170): Remove dependency on v8. +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +// Indicator for the stack state of the embedder. +enum class EmbedderStackState { + kMayContainHeapPointers, + kNoHeapPointers, + kUnknown V8_ENUM_DEPRECATE_SOON("Use kMayContainHeapPointers") = + kMayContainHeapPointers, + kNonEmpty V8_ENUM_DEPRECATE_SOON("Use kMayContainHeapPointers") = + kMayContainHeapPointers, + kEmpty V8_ENUM_DEPRECATE_SOON("Use kNoHeapPointers") = kNoHeapPointers, +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_COMMON_H_ diff --git a/deps/v8/include/cppgc/custom-space.h b/deps/v8/include/cppgc/custom-space.h new file mode 100644 index 00000000000000..2597a5bdef7a41 --- /dev/null +++ b/deps/v8/include/cppgc/custom-space.h @@ -0,0 +1,62 @@ +// Copyright 2020 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. + +#ifndef INCLUDE_CPPGC_CUSTOM_SPACE_H_ +#define INCLUDE_CPPGC_CUSTOM_SPACE_H_ + +#include + +namespace cppgc { + +struct CustomSpaceIndex { + CustomSpaceIndex(size_t value) : value(value) {} // NOLINT + size_t value; +}; + +/** + * Top-level base class for custom spaces. Users must inherit from CustomSpace + * below. + */ +class CustomSpaceBase { + public: + virtual ~CustomSpaceBase() = default; + virtual CustomSpaceIndex GetCustomSpaceIndex() const = 0; +}; + +/** + * Base class custom spaces should directly inherit from. The class inheriting + * from CustomSpace must define kSpaceIndex as unique space index. These + * indices need for form a sequence starting at 0. + * + * Example: + * \code + * class CustomSpace1 : public CustomSpace { + * public: + * static constexpr CustomSpaceIndex kSpaceIndex = 0; + * }; + * class CustomSpace2 : public CustomSpace { + * public: + * static constexpr CustomSpaceIndex kSpaceIndex = 1; + * }; + * \endcode + */ +template +class CustomSpace : public CustomSpaceBase { + public: + CustomSpaceIndex GetCustomSpaceIndex() const final { + return ConcreteCustomSpace::kSpaceIndex; + } +}; + +/** + * User-overridable trait that allows pinning types to custom spaces. + */ +template +struct SpaceTrait { + using Space = void; +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_CUSTOM_SPACE_H_ diff --git a/deps/v8/include/cppgc/garbage-collected.h b/deps/v8/include/cppgc/garbage-collected.h index 6c62daafdc5d8d..c263a9fecf0d96 100644 --- a/deps/v8/include/cppgc/garbage-collected.h +++ b/deps/v8/include/cppgc/garbage-collected.h @@ -7,31 +7,20 @@ #include -#include "include/cppgc/internals.h" -#include "include/cppgc/platform.h" +#include "cppgc/internal/api-constants.h" +#include "cppgc/macros.h" +#include "cppgc/platform.h" +#include "cppgc/trace-trait.h" +#include "cppgc/type-traits.h" namespace cppgc { -namespace internal { - -template -struct IsGarbageCollectedType : std::false_type { - static_assert(sizeof(T), "T must be fully defined"); -}; -template -struct IsGarbageCollectedType< - T, void_t::IsGarbageCollectedTypeMarker>> - : std::true_type { - static_assert(sizeof(T), "T must be fully defined"); -}; +class Visitor; -} // namespace internal +namespace internal { -template -class GarbageCollected { +class GarbageCollectedBase { public: - using IsGarbageCollectedTypeMarker = void; - // Must use MakeGarbageCollected. void* operator new(size_t) = delete; void* operator new[](size_t) = delete; @@ -44,10 +33,160 @@ class GarbageCollected { } void operator delete[](void*) = delete; + protected: + GarbageCollectedBase() = default; +}; + +} // namespace internal + +/** + * Base class for managed objects. Only descendent types of GarbageCollected + * can be constructed using MakeGarbageCollected. Must be inherited from as + * left-most base class. + * + * Types inheriting from GarbageCollected must provide a method of + * signature `void Trace(cppgc::Visitor*) const` that dispatchs all managed + * pointers to the visitor and delegates to garbage-collected base classes. + * The method must be virtual if the type is not directly a child of + * GarbageCollected and marked as final. + * + * \code + * // Example using final class. + * class FinalType final : public GarbageCollected { + * public: + * void Trace(cppgc::Visitor* visitor) const { + * // Dispatch using visitor->Trace(...); + * } + * }; + * + * // Example using non-final base class. + * class NonFinalBase : public GarbageCollected { + * public: + * virtual void Trace(cppgc::Visitor*) const {} + * }; + * + * class FinalChild final : public NonFinalBase { + * public: + * void Trace(cppgc::Visitor* visitor) const final { + * // Dispatch using visitor->Trace(...); + * NonFinalBase::Trace(visitor); + * } + * }; + * \endcode + */ +template +class GarbageCollected : public internal::GarbageCollectedBase { + public: + using IsGarbageCollectedTypeMarker = void; + protected: GarbageCollected() = default; }; +/** + * Base class for managed mixin objects. Such objects cannot be constructed + * directly but must be mixed into the inheritance hierarchy of a + * GarbageCollected object. + * + * Types inheriting from GarbageCollectedMixin must override a virtual method + * of signature `void Trace(cppgc::Visitor*) const` that dispatchs all managed + * pointers to the visitor and delegates to base classes. + * + * \code + * class Mixin : public GarbageCollectedMixin { + * public: + * void Trace(cppgc::Visitor* visitor) const override { + * // Dispatch using visitor->Trace(...); + * } + * }; + * \endcode + */ +class GarbageCollectedMixin : public internal::GarbageCollectedBase { + public: + using IsGarbageCollectedMixinTypeMarker = void; + + // Sentinel used to mark not-fully-constructed mixins. + static constexpr void* kNotFullyConstructedObject = nullptr; + + // Provide default implementation that indicate that the vtable is not yet + // set up properly. This is used to to get GCInfo objects for mixins so that + // these objects can be processed later on. + virtual TraceDescriptor GetTraceDescriptor() const { + return {kNotFullyConstructedObject, nullptr}; + } + + /** + * This Trace method must be overriden by objects inheriting from + * GarbageCollectedMixin. + */ + virtual void Trace(cppgc::Visitor*) const {} +}; + +/** + * Macro defines all methods and markers needed for handling mixins. Must be + * used on the type that is inheriting from GarbageCollected *and* + * GarbageCollectedMixin. + * + * \code + * class Mixin : public GarbageCollectedMixin { + * public: + * void Trace(cppgc::Visitor* visitor) const override { + * // Dispatch using visitor->Trace(...); + * } + * }; + * + * class Foo : public GarbageCollected, public Mixin { + * USING_GARBAGE_COLLECTED_MIXIN(); + * public: + * void Trace(cppgc::Visitor* visitor) const override { + * // Dispatch using visitor->Trace(...); + * Mixin::Trace(visitor); + * } + * }; + * \endcode + */ +#define USING_GARBAGE_COLLECTED_MIXIN() \ + public: \ + /* Marker is used by clang to check for proper usages of the macro. */ \ + typedef int HasUsingGarbageCollectedMixinMacro; \ + \ + TraceDescriptor GetTraceDescriptor() const override { \ + static_assert( \ + internal::IsSubclassOfTemplate< \ + std::remove_const_t>, \ + cppgc::GarbageCollected>::value, \ + "Only garbage collected objects can have garbage collected mixins"); \ + return {this, TraceTrait>>::Trace}; \ + } \ + \ + private: \ + friend class internal::__thisIsHereToForceASemicolonAfterThisMacro + +/** + * Merge two or more Mixins into one. + * + * \code + * class A : public GarbageCollectedMixin {}; + * class B : public GarbageCollectedMixin {}; + * class C : public A, public B { + * MERGE_GARBAGE_COLLECTED_MIXINS(); + * public: + * }; + * \endcode + */ +#define MERGE_GARBAGE_COLLECTED_MIXINS() \ + public: \ + /* When using multiple mixins the methods become */ \ + /* ambigous. Providing additional implementations */ \ + /* disambiguate them again. */ \ + TraceDescriptor GetTraceDescriptor() const override { \ + return {kNotFullyConstructedObject, nullptr}; \ + } \ + \ + private: \ + friend class internal::__thisIsHereToForceASemicolonAfterThisMacro + } // namespace cppgc #endif // INCLUDE_CPPGC_GARBAGE_COLLECTED_H_ diff --git a/deps/v8/include/cppgc/heap.h b/deps/v8/include/cppgc/heap.h index a0568d534fbee2..90046c35055e2e 100644 --- a/deps/v8/include/cppgc/heap.h +++ b/deps/v8/include/cppgc/heap.h @@ -6,8 +6,11 @@ #define INCLUDE_CPPGC_HEAP_H_ #include +#include -#include "include/v8config.h" +#include "cppgc/common.h" +#include "cppgc/custom-space.h" +#include "v8config.h" // NOLINT(build/include_directory) namespace cppgc { namespace internal { @@ -16,10 +19,39 @@ class Heap; class V8_EXPORT Heap { public: - static std::unique_ptr Create(); + /** + * Specifies the stack state the embedder is in. + */ + using StackState = EmbedderStackState; + + struct HeapOptions { + static HeapOptions Default() { return {}; } + + /** + * Custom spaces added to heap are required to have indices forming a + * numbered sequence starting at 0, i.e., their kSpaceIndex must correspond + * to the index they reside in the vector. + */ + std::vector> custom_spaces; + }; + + static std::unique_ptr Create(HeapOptions = HeapOptions::Default()); virtual ~Heap() = default; + /** + * Forces garbage collection. + * + * \param source String specifying the source (or caller) triggering a + * forced garbage collection. + * \param reason String specifying the reason for the forced garbage + * collection. + * \param stack_state The embedder stack state, see StackState. + */ + void ForceGarbageCollectionSlow( + const char* source, const char* reason, + StackState stack_state = StackState::kMayContainHeapPointers); + private: Heap() = default; diff --git a/deps/v8/include/cppgc/internal/accessors.h b/deps/v8/include/cppgc/internal/accessors.h new file mode 100644 index 00000000000000..ee0a0042fe07b7 --- /dev/null +++ b/deps/v8/include/cppgc/internal/accessors.h @@ -0,0 +1,26 @@ +// Copyright 2020 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. + +#ifndef INCLUDE_CPPGC_INTERNAL_ACCESSORS_H_ +#define INCLUDE_CPPGC_INTERNAL_ACCESSORS_H_ + +#include "cppgc/internal/api-constants.h" + +namespace cppgc { + +class Heap; + +namespace internal { + +inline cppgc::Heap* GetHeapFromPayload(const void* payload) { + return *reinterpret_cast( + ((reinterpret_cast(payload) & api_constants::kPageBaseMask) + + api_constants::kGuardPageSize) + + api_constants::kHeapOffset); +} + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_ACCESSORS_H_ diff --git a/deps/v8/include/cppgc/internals.h b/deps/v8/include/cppgc/internal/api-constants.h similarity index 57% rename from deps/v8/include/cppgc/internals.h rename to deps/v8/include/cppgc/internal/api-constants.h index 1e57779758b6c7..ef910a48571f46 100644 --- a/deps/v8/include/cppgc/internals.h +++ b/deps/v8/include/cppgc/internal/api-constants.h @@ -2,25 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef INCLUDE_CPPGC_INTERNALS_H_ -#define INCLUDE_CPPGC_INTERNALS_H_ +#ifndef INCLUDE_CPPGC_INTERNAL_API_CONSTANTS_H_ +#define INCLUDE_CPPGC_INTERNAL_API_CONSTANTS_H_ #include #include -#include "include/v8config.h" +#include "v8config.h" // NOLINT(build/include_directory) namespace cppgc { namespace internal { -// Pre-C++17 custom implementation of std::void_t. -template -struct make_void { - typedef void type; -}; -template -using void_t = typename make_void::type; - // Embedders should not rely on this code! // Internal constants to avoid exposing internal types on the API surface. @@ -33,9 +25,20 @@ static constexpr size_t kFullyConstructedBitFieldOffsetFromPayload = // Mask for in-construction bit. static constexpr size_t kFullyConstructedBitMask = size_t{1}; +// Page constants used to align pointers to page begin. +static constexpr size_t kPageSize = size_t{1} << 17; +static constexpr size_t kPageAlignment = kPageSize; +static constexpr size_t kPageBaseMask = ~(kPageAlignment - 1); +static constexpr size_t kGuardPageSize = 4096; + +// Offset of the Heap backref. +static constexpr size_t kHeapOffset = 0; + +static constexpr size_t kLargeObjectSizeThreshold = kPageSize / 2; + } // namespace api_constants } // namespace internal } // namespace cppgc -#endif // INCLUDE_CPPGC_INTERNALS_H_ +#endif // INCLUDE_CPPGC_INTERNAL_API_CONSTANTS_H_ diff --git a/deps/v8/include/cppgc/internal/compiler-specific.h b/deps/v8/include/cppgc/internal/compiler-specific.h new file mode 100644 index 00000000000000..e1f5c1d57fb850 --- /dev/null +++ b/deps/v8/include/cppgc/internal/compiler-specific.h @@ -0,0 +1,26 @@ +// Copyright 2020 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. + +#ifndef INCLUDE_CPPGC_INTERNAL_COMPILER_SPECIFIC_H_ +#define INCLUDE_CPPGC_INTERNAL_COMPILER_SPECIFIC_H_ + +namespace cppgc { + +#if defined(__has_cpp_attribute) +#define CPPGC_HAS_CPP_ATTRIBUTE(FEATURE) __has_cpp_attribute(FEATURE) +#else +#define CPPGC_HAS_CPP_ATTRIBUTE(FEATURE) 0 +#endif + +// [[no_unique_address]] comes in C++20 but supported in clang with -std >= +// c++11. +#if CPPGC_HAS_CPP_ATTRIBUTE(no_unique_address) // NOLINTNEXTLINE +#define CPPGC_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else +#define CPPGC_NO_UNIQUE_ADDRESS +#endif + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_COMPILER_SPECIFIC_H_ diff --git a/deps/v8/include/cppgc/finalizer-trait.h b/deps/v8/include/cppgc/internal/finalizer-trait.h similarity index 92% rename from deps/v8/include/cppgc/finalizer-trait.h rename to deps/v8/include/cppgc/internal/finalizer-trait.h index 12216ed84ed9f6..a95126591cb72d 100644 --- a/deps/v8/include/cppgc/finalizer-trait.h +++ b/deps/v8/include/cppgc/internal/finalizer-trait.h @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef INCLUDE_CPPGC_FINALIZER_TRAIT_H_ -#define INCLUDE_CPPGC_FINALIZER_TRAIT_H_ +#ifndef INCLUDE_CPPGC_INTERNAL_FINALIZER_TRAIT_H_ +#define INCLUDE_CPPGC_INTERNAL_FINALIZER_TRAIT_H_ #include -#include "include/cppgc/internals.h" +#include "cppgc/type-traits.h" namespace cppgc { namespace internal { @@ -87,4 +87,4 @@ constexpr FinalizationCallback FinalizerTrait::kCallback; } // namespace internal } // namespace cppgc -#endif // INCLUDE_CPPGC_FINALIZER_TRAIT_H_ +#endif // INCLUDE_CPPGC_INTERNAL_FINALIZER_TRAIT_H_ diff --git a/deps/v8/include/cppgc/gc-info.h b/deps/v8/include/cppgc/internal/gc-info.h similarity index 81% rename from deps/v8/include/cppgc/gc-info.h rename to deps/v8/include/cppgc/internal/gc-info.h index 987ba34fa4254c..9aac1361c61afd 100644 --- a/deps/v8/include/cppgc/gc-info.h +++ b/deps/v8/include/cppgc/internal/gc-info.h @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef INCLUDE_CPPGC_GC_INFO_H_ -#define INCLUDE_CPPGC_GC_INFO_H_ +#ifndef INCLUDE_CPPGC_INTERNAL_GC_INFO_H_ +#define INCLUDE_CPPGC_INTERNAL_GC_INFO_H_ #include -#include "include/cppgc/finalizer-trait.h" -#include "include/v8config.h" +#include "cppgc/internal/finalizer-trait.h" +#include "v8config.h" // NOLINT(build/include_directory) namespace cppgc { namespace internal { @@ -40,4 +40,4 @@ struct GCInfoTrait { } // namespace internal } // namespace cppgc -#endif // INCLUDE_CPPGC_GC_INFO_H_ +#endif // INCLUDE_CPPGC_INTERNAL_GC_INFO_H_ diff --git a/deps/v8/include/cppgc/internal/logging.h b/deps/v8/include/cppgc/internal/logging.h new file mode 100644 index 00000000000000..79beaef7d4f80d --- /dev/null +++ b/deps/v8/include/cppgc/internal/logging.h @@ -0,0 +1,50 @@ +// Copyright 2020 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. + +#ifndef INCLUDE_CPPGC_INTERNAL_LOGGING_H_ +#define INCLUDE_CPPGC_INTERNAL_LOGGING_H_ + +#include "cppgc/source-location.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { +namespace internal { + +void V8_EXPORT DCheckImpl(const char*, + const SourceLocation& = SourceLocation::Current()); +[[noreturn]] void V8_EXPORT +FatalImpl(const char*, const SourceLocation& = SourceLocation::Current()); + +// Used to ignore -Wunused-variable. +template +struct EatParams {}; + +#if DEBUG +#define CPPGC_DCHECK_MSG(condition, message) \ + do { \ + if (V8_UNLIKELY(!(condition))) { \ + ::cppgc::internal::DCheckImpl(message); \ + } \ + } while (false) +#else +#define CPPGC_DCHECK_MSG(condition, message) \ + (static_cast(::cppgc::internal::EatParams(condition), message)>{})) +#endif + +#define CPPGC_DCHECK(condition) CPPGC_DCHECK_MSG(condition, #condition) + +#define CPPGC_CHECK_MSG(condition, message) \ + do { \ + if (V8_UNLIKELY(!(condition))) { \ + ::cppgc::internal::FatalImpl(message); \ + } \ + } while (false) + +#define CPPGC_CHECK(condition) CPPGC_CHECK_MSG(condition, #condition) + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_LOGGING_H_ diff --git a/deps/v8/include/cppgc/internal/persistent-node.h b/deps/v8/include/cppgc/internal/persistent-node.h new file mode 100644 index 00000000000000..11cf69623e8dad --- /dev/null +++ b/deps/v8/include/cppgc/internal/persistent-node.h @@ -0,0 +1,109 @@ +// Copyright 2020 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. + +#ifndef INCLUDE_CPPGC_INTERNAL_PERSISTENT_NODE_H_ +#define INCLUDE_CPPGC_INTERNAL_PERSISTENT_NODE_H_ + +#include +#include +#include + +#include "cppgc/internal/logging.h" +#include "cppgc/trace-trait.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +class Visitor; + +namespace internal { + +// PersistentNode represesents a variant of two states: +// 1) traceable node with a back pointer to the Persistent object; +// 2) freelist entry. +class PersistentNode final { + public: + PersistentNode() = default; + + PersistentNode(const PersistentNode&) = delete; + PersistentNode& operator=(const PersistentNode&) = delete; + + void InitializeAsUsedNode(void* owner, TraceCallback trace) { + owner_ = owner; + trace_ = trace; + } + + void InitializeAsFreeNode(PersistentNode* next) { + next_ = next; + trace_ = nullptr; + } + + void UpdateOwner(void* owner) { + CPPGC_DCHECK(IsUsed()); + owner_ = owner; + } + + PersistentNode* FreeListNext() const { + CPPGC_DCHECK(!IsUsed()); + return next_; + } + + void Trace(Visitor* visitor) const { + CPPGC_DCHECK(IsUsed()); + trace_(visitor, owner_); + } + + bool IsUsed() const { return trace_; } + + private: + // PersistentNode acts as a designated union: + // If trace_ != nullptr, owner_ points to the corresponding Persistent handle. + // Otherwise, next_ points to the next freed PersistentNode. + union { + void* owner_ = nullptr; + PersistentNode* next_; + }; + TraceCallback trace_ = nullptr; +}; + +class V8_EXPORT PersistentRegion { + using PersistentNodeSlots = std::array; + + public: + PersistentRegion() = default; + + PersistentRegion(const PersistentRegion&) = delete; + PersistentRegion& operator=(const PersistentRegion&) = delete; + + PersistentNode* AllocateNode(void* owner, TraceCallback trace) { + if (!free_list_head_) { + EnsureNodeSlots(); + } + PersistentNode* node = free_list_head_; + free_list_head_ = free_list_head_->FreeListNext(); + node->InitializeAsUsedNode(owner, trace); + return node; + } + + void FreeNode(PersistentNode* node) { + node->InitializeAsFreeNode(free_list_head_); + free_list_head_ = node; + } + + void Trace(Visitor*); + + size_t NodesInUse() const; + + private: + void EnsureNodeSlots(); + + std::vector> nodes_; + PersistentNode* free_list_head_ = nullptr; +}; + +} // namespace internal + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_PERSISTENT_NODE_H_ diff --git a/deps/v8/include/cppgc/internal/pointer-policies.h b/deps/v8/include/cppgc/internal/pointer-policies.h new file mode 100644 index 00000000000000..fe8d94b57a68bb --- /dev/null +++ b/deps/v8/include/cppgc/internal/pointer-policies.h @@ -0,0 +1,133 @@ +// Copyright 2020 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. + +#ifndef INCLUDE_CPPGC_INTERNAL_POINTER_POLICIES_H_ +#define INCLUDE_CPPGC_INTERNAL_POINTER_POLICIES_H_ + +#include +#include + +#include "cppgc/source-location.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { +namespace internal { + +class PersistentRegion; + +// Tags to distinguish between strong and weak member types. +class StrongMemberTag; +class WeakMemberTag; +class UntracedMemberTag; + +struct DijkstraWriteBarrierPolicy { + static void InitializingBarrier(const void*, const void*) { + // Since in initializing writes the source object is always white, having no + // barrier doesn't break the tri-color invariant. + } + static void AssigningBarrier(const void*, const void*) { + // TODO(chromium:1056170): Add actual implementation. + } +}; + +struct NoWriteBarrierPolicy { + static void InitializingBarrier(const void*, const void*) {} + static void AssigningBarrier(const void*, const void*) {} +}; + +class V8_EXPORT EnabledCheckingPolicy { + protected: + EnabledCheckingPolicy(); + void CheckPointer(const void* ptr); + + private: + void* impl_; +}; + +class DisabledCheckingPolicy { + protected: + void CheckPointer(const void* raw) {} +}; + +#if V8_ENABLE_CHECKS +using DefaultCheckingPolicy = EnabledCheckingPolicy; +#else +using DefaultCheckingPolicy = DisabledCheckingPolicy; +#endif + +class KeepLocationPolicy { + public: + constexpr const SourceLocation& Location() const { return location_; } + + protected: + constexpr explicit KeepLocationPolicy(const SourceLocation& location) + : location_(location) {} + + // KeepLocationPolicy must not copy underlying source locations. + KeepLocationPolicy(const KeepLocationPolicy&) = delete; + KeepLocationPolicy& operator=(const KeepLocationPolicy&) = delete; + + // Location of the original moved from object should be preserved. + KeepLocationPolicy(KeepLocationPolicy&&) = default; + KeepLocationPolicy& operator=(KeepLocationPolicy&&) = default; + + private: + SourceLocation location_; +}; + +class IgnoreLocationPolicy { + public: + constexpr SourceLocation Location() const { return {}; } + + protected: + constexpr explicit IgnoreLocationPolicy(const SourceLocation&) {} +}; + +#if CPPGC_SUPPORTS_OBJECT_NAMES +using DefaultLocationPolicy = KeepLocationPolicy; +#else +using DefaultLocationPolicy = IgnoreLocationPolicy; +#endif + +struct StrongPersistentPolicy { + using IsStrongPersistent = std::true_type; + + static V8_EXPORT PersistentRegion& GetPersistentRegion(void* object); +}; + +struct WeakPersistentPolicy { + using IsStrongPersistent = std::false_type; + + static V8_EXPORT PersistentRegion& GetPersistentRegion(void* object); +}; + +// Persistent/Member forward declarations. +template +class BasicPersistent; +template +class BasicMember; + +// Special tag type used to denote some sentinel member. The semantics of the +// sentinel is defined by the embedder. +struct SentinelPointer { + template + operator T*() const { // NOLINT + static constexpr intptr_t kSentinelValue = -1; + return reinterpret_cast(kSentinelValue); + } + // Hidden friends. + friend bool operator==(SentinelPointer, SentinelPointer) { return true; } + friend bool operator!=(SentinelPointer, SentinelPointer) { return false; } +}; + +} // namespace internal + +constexpr internal::SentinelPointer kSentinelPointer; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_POINTER_POLICIES_H_ diff --git a/deps/v8/include/cppgc/internal/prefinalizer-handler.h b/deps/v8/include/cppgc/internal/prefinalizer-handler.h new file mode 100644 index 00000000000000..939a9b8ff0a8fd --- /dev/null +++ b/deps/v8/include/cppgc/internal/prefinalizer-handler.h @@ -0,0 +1,31 @@ +// Copyright 2020 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. + +#ifndef INCLUDE_CPPGC_INTERNAL_PREFINALIZER_HANDLER_H_ +#define INCLUDE_CPPGC_INTERNAL_PREFINALIZER_HANDLER_H_ + +#include "cppgc/heap.h" +#include "cppgc/liveness-broker.h" + +namespace cppgc { +namespace internal { + +class V8_EXPORT PreFinalizerRegistrationDispatcher final { + public: + using PreFinalizerCallback = bool (*)(const LivenessBroker&, void*); + struct PreFinalizer { + void* object_; + PreFinalizerCallback callback_; + + bool operator==(const PreFinalizer& other); + }; + + static void RegisterPrefinalizer(cppgc::Heap* heap, + PreFinalizer prefinalzier); +}; + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_PREFINALIZER_HANDLER_H_ diff --git a/deps/v8/include/cppgc/liveness-broker.h b/deps/v8/include/cppgc/liveness-broker.h new file mode 100644 index 00000000000000..69dbc11f1f4a95 --- /dev/null +++ b/deps/v8/include/cppgc/liveness-broker.h @@ -0,0 +1,50 @@ +// Copyright 2020 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. + +#ifndef INCLUDE_CPPGC_LIVENESS_BROKER_H_ +#define INCLUDE_CPPGC_LIVENESS_BROKER_H_ + +#include "cppgc/heap.h" +#include "cppgc/member.h" +#include "cppgc/trace-trait.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +namespace internal { +class LivenessBrokerFactory; +} // namespace internal + +class V8_EXPORT LivenessBroker final { + public: + template + bool IsHeapObjectAlive(const T* object) const { + return object && + IsHeapObjectAliveImpl( + TraceTrait::GetTraceDescriptor(object).base_object_payload); + } + + template + bool IsHeapObjectAlive(const WeakMember& weak_member) const { + return (weak_member != kSentinelPointer) && + IsHeapObjectAlive(weak_member.Get()); + } + + template + bool IsHeapObjectAlive(const UntracedMember& untraced_member) const { + return (untraced_member != kSentinelPointer) && + IsHeapObjectAlive(untraced_member.Get()); + } + + private: + LivenessBroker() = default; + + bool IsHeapObjectAliveImpl(const void*) const; + + friend class internal::LivenessBrokerFactory; +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_LIVENESS_BROKER_H_ diff --git a/deps/v8/include/cppgc/macros.h b/deps/v8/include/cppgc/macros.h new file mode 100644 index 00000000000000..7c7a10e433a894 --- /dev/null +++ b/deps/v8/include/cppgc/macros.h @@ -0,0 +1,26 @@ +// Copyright 2020 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. + +#ifndef INCLUDE_CPPGC_MACROS_H_ +#define INCLUDE_CPPGC_MACROS_H_ + +namespace cppgc { + +namespace internal { +class __thisIsHereToForceASemicolonAfterThisMacro {}; +} // namespace internal + +// Use if the object is only stack allocated. +#define CPPGC_STACK_ALLOCATED() \ + public: \ + using IsStackAllocatedTypeMarker = int; \ + \ + private: \ + void* operator new(size_t) = delete; \ + void* operator new(size_t, void*) = delete; \ + friend class internal::__thisIsHereToForceASemicolonAfterThisMacro + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_MACROS_H_ diff --git a/deps/v8/include/cppgc/member.h b/deps/v8/include/cppgc/member.h new file mode 100644 index 00000000000000..a183edb96fd030 --- /dev/null +++ b/deps/v8/include/cppgc/member.h @@ -0,0 +1,206 @@ +// Copyright 2020 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. + +#ifndef INCLUDE_CPPGC_MEMBER_H_ +#define INCLUDE_CPPGC_MEMBER_H_ + +#include +#include +#include + +#include "cppgc/internal/pointer-policies.h" +#include "cppgc/type-traits.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +class Visitor; + +namespace internal { + +// The basic class from which all Member classes are 'generated'. +template +class BasicMember : private CheckingPolicy { + public: + using PointeeType = T; + + constexpr BasicMember() = default; + constexpr BasicMember(std::nullptr_t) {} // NOLINT + BasicMember(SentinelPointer s) : raw_(s) {} // NOLINT + BasicMember(T* raw) : raw_(raw) { // NOLINT + InitializingWriteBarrier(); + this->CheckPointer(raw_); + } + BasicMember(T& raw) : BasicMember(&raw) {} // NOLINT + BasicMember(const BasicMember& other) : BasicMember(other.Get()) {} + // Allow heterogeneous construction. + template ::value>> + BasicMember( // NOLINT + const BasicMember& other) + : BasicMember(other.Get()) {} + // Construction from Persistent. + template ::value>> + BasicMember( // NOLINT + const BasicPersistent& + p) + : BasicMember(p.Get()) {} + + BasicMember& operator=(const BasicMember& other) { + return operator=(other.Get()); + } + // Allow heterogeneous assignment. + template ::value>> + BasicMember& operator=( + const BasicMember& other) { + return operator=(other.Get()); + } + // Assignment from Persistent. + template ::value>> + BasicMember& operator=( + const BasicPersistent& + other) { + return operator=(other.Get()); + } + BasicMember& operator=(T* other) { + SetRawAtomic(other); + AssigningWriteBarrier(); + this->CheckPointer(Get()); + return *this; + } + BasicMember& operator=(std::nullptr_t) { + Clear(); + return *this; + } + BasicMember& operator=(SentinelPointer s) { + SetRawAtomic(s); + return *this; + } + + template + void Swap(BasicMember& other) { + T* tmp = Get(); + *this = other; + other = tmp; + } + + explicit operator bool() const { return Get(); } + operator T*() const { return Get(); } // NOLINT + T* operator->() const { return Get(); } + T& operator*() const { return *Get(); } + + T* Get() const { + // Executed by the mutator, hence non atomic load. + return raw_; + } + + void Clear() { SetRawAtomic(nullptr); } + + T* Release() { + T* result = Get(); + Clear(); + return result; + } + + private: + void SetRawAtomic(T* raw) { + reinterpret_cast*>(&raw_)->store(raw, + std::memory_order_relaxed); + } + T* GetRawAtomic() const { + return reinterpret_cast*>(&raw_)->load( + std::memory_order_relaxed); + } + + void InitializingWriteBarrier() const { + WriteBarrierPolicy::InitializingBarrier( + reinterpret_cast(&raw_), static_cast(raw_)); + } + void AssigningWriteBarrier() const { + WriteBarrierPolicy::AssigningBarrier(reinterpret_cast(&raw_), + static_cast(raw_)); + } + + T* raw_ = nullptr; + + friend class cppgc::Visitor; +}; + +template +bool operator==( + BasicMember member1, + BasicMember + member2) { + return member1.Get() == member2.Get(); +} + +template +bool operator!=( + BasicMember member1, + BasicMember + member2) { + return !(member1 == member2); +} + +template +struct IsWeak< + internal::BasicMember> + : std::true_type {}; + +} // namespace internal + +/** + * Members are used in classes to contain strong pointers to other garbage + * collected objects. All Member fields of a class must be traced in the class' + * trace method. + */ +template +using Member = internal::BasicMember; + +/** + * WeakMember is similar to Member in that it is used to point to other garbage + * collected objects. However instead of creating a strong pointer to the + * object, the WeakMember creates a weak pointer, which does not keep the + * pointee alive. Hence if all pointers to to a heap allocated object are weak + * the object will be garbage collected. At the time of GC the weak pointers + * will automatically be set to null. + */ +template +using WeakMember = internal::BasicMember; + +/** + * UntracedMember is a pointer to an on-heap object that is not traced for some + * reason. Do not use this unless you know what you are doing. Keeping raw + * pointers to on-heap objects is prohibited unless used from stack. Pointee + * must be kept alive through other means. + */ +template +using UntracedMember = internal::BasicMember; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_MEMBER_H_ diff --git a/deps/v8/include/cppgc/persistent.h b/deps/v8/include/cppgc/persistent.h new file mode 100644 index 00000000000000..fc6b0b9d92efa1 --- /dev/null +++ b/deps/v8/include/cppgc/persistent.h @@ -0,0 +1,304 @@ +// Copyright 2020 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. + +#ifndef INCLUDE_CPPGC_PERSISTENT_H_ +#define INCLUDE_CPPGC_PERSISTENT_H_ + +#include + +#include "cppgc/internal/persistent-node.h" +#include "cppgc/internal/pointer-policies.h" +#include "cppgc/source-location.h" +#include "cppgc/type-traits.h" +#include "cppgc/visitor.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { +namespace internal { + +// The basic class from which all Persistent classes are generated. +template +class BasicPersistent : public LocationPolicy, + private WeaknessPolicy, + private CheckingPolicy { + public: + using typename WeaknessPolicy::IsStrongPersistent; + using PointeeType = T; + + // Null-state/sentinel constructors. + BasicPersistent( // NOLINT + const SourceLocation& loc = SourceLocation::Current()) + : LocationPolicy(loc) {} + + BasicPersistent(std::nullptr_t, // NOLINT + const SourceLocation& loc = SourceLocation::Current()) + : LocationPolicy(loc) {} + + BasicPersistent( // NOLINT + SentinelPointer s, const SourceLocation& loc = SourceLocation::Current()) + : LocationPolicy(loc), raw_(s) {} + + // Raw value contstructors. + BasicPersistent(T* raw, // NOLINT + const SourceLocation& loc = SourceLocation::Current()) + : LocationPolicy(loc), raw_(raw) { + if (!IsValid()) return; + node_ = WeaknessPolicy::GetPersistentRegion(raw_).AllocateNode( + this, &BasicPersistent::Trace); + this->CheckPointer(Get()); + } + + BasicPersistent(T& raw, // NOLINT + const SourceLocation& loc = SourceLocation::Current()) + : BasicPersistent(&raw, loc) {} + + // Copy ctor. + BasicPersistent(const BasicPersistent& other, + const SourceLocation& loc = SourceLocation::Current()) + : BasicPersistent(other.Get(), loc) {} + + // Heterogeneous ctor. + template ::value>> + BasicPersistent( // NOLINT + const BasicPersistent& other, + const SourceLocation& loc = SourceLocation::Current()) + : BasicPersistent(other.Get(), loc) {} + + // Move ctor. The heterogeneous move ctor is not supported since e.g. + // persistent can't reuse persistent node from weak persistent. + BasicPersistent( + BasicPersistent&& other, + const SourceLocation& loc = SourceLocation::Current()) noexcept + : LocationPolicy(std::move(other)), + raw_(std::move(other.raw_)), + node_(std::move(other.node_)) { + if (!IsValid()) return; + node_->UpdateOwner(this); + other.raw_ = nullptr; + other.node_ = nullptr; + this->CheckPointer(Get()); + } + + // Constructor from member. + template ::value>> + BasicPersistent(internal::BasicMember + member, + const SourceLocation& loc = SourceLocation::Current()) + : BasicPersistent(member.Get(), loc) {} + + ~BasicPersistent() { Clear(); } + + // Copy assignment. + BasicPersistent& operator=(const BasicPersistent& other) { + return operator=(other.Get()); + } + + template ::value>> + BasicPersistent& operator=( + const BasicPersistent& other) { + return operator=(other.Get()); + } + + // Move assignment. + BasicPersistent& operator=(BasicPersistent&& other) { + if (this == &other) return *this; + Clear(); + LocationPolicy::operator=(std::move(other)); + raw_ = std::move(other.raw_); + node_ = std::move(other.node_); + if (!IsValid()) return *this; + node_->UpdateOwner(this); + other.raw_ = nullptr; + other.node_ = nullptr; + this->CheckPointer(Get()); + return *this; + } + + // Assignment from member. + template ::value>> + BasicPersistent& operator=( + internal::BasicMember + member) { + return operator=(member.Get()); + } + + BasicPersistent& operator=(T* other) { + Assign(other); + return *this; + } + + BasicPersistent& operator=(std::nullptr_t) { + Clear(); + return *this; + } + + BasicPersistent& operator=(SentinelPointer s) { + Assign(s); + return *this; + } + + explicit operator bool() const { return Get(); } + operator T*() const { return Get(); } + T* operator->() const { return Get(); } + T& operator*() const { return *Get(); } + + T* Get() const { return raw_; } + + void Clear() { Assign(nullptr); } + + T* Release() { + T* result = Get(); + Clear(); + return result; + } + + private: + static void Trace(Visitor* v, const void* ptr) { + const auto* persistent = static_cast(ptr); + v->TraceRoot(*persistent, persistent->Location()); + } + + bool IsValid() const { + // Ideally, handling kSentinelPointer would be done by the embedder. On the + // other hand, having Persistent aware of it is beneficial since no node + // gets wasted. + return raw_ != nullptr && raw_ != kSentinelPointer; + } + + void Assign(T* ptr) { + if (IsValid()) { + if (ptr && ptr != kSentinelPointer) { + // Simply assign the pointer reusing the existing node. + raw_ = ptr; + this->CheckPointer(ptr); + return; + } + WeaknessPolicy::GetPersistentRegion(raw_).FreeNode(node_); + node_ = nullptr; + } + raw_ = ptr; + if (!IsValid()) return; + node_ = WeaknessPolicy::GetPersistentRegion(raw_).AllocateNode( + this, &BasicPersistent::Trace); + this->CheckPointer(Get()); + } + + T* raw_ = nullptr; + PersistentNode* node_ = nullptr; +}; + +template +bool operator==(const BasicPersistent& p1, + const BasicPersistent& p2) { + return p1.Get() == p2.Get(); +} + +template +bool operator!=(const BasicPersistent& p1, + const BasicPersistent& p2) { + return !(p1 == p2); +} + +template +bool operator==(const BasicPersistent& p, + BasicMember + m) { + return p.Get() == m.Get(); +} + +template +bool operator!=(const BasicPersistent& p, + BasicMember + m) { + return !(p == m); +} + +template +bool operator==(BasicMember + m, + const BasicPersistent& p) { + return m.Get() == p.Get(); +} + +template +bool operator!=(BasicMember + m, + const BasicPersistent& p) { + return !(m == p); +} + +template +struct IsWeak> : std::true_type {}; +} // namespace internal + +/** + * Persistent is a way to create a strong pointer from an off-heap object to + * another on-heap object. As long as the Persistent handle is alive the GC will + * keep the object pointed to alive. The Persistent handle is always a GC root + * from the point of view of the GC. Persistent must be constructed and + * destructed in the same thread. + */ +template +using Persistent = + internal::BasicPersistent; + +/** + * WeakPersistent is a way to create a weak pointer from an off-heap object to + * an on-heap object. The pointer is automatically cleared when the pointee gets + * collected. WeakPersistent must be constructed and destructed in the same + * thread. + */ +template +using WeakPersistent = + internal::BasicPersistent; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_PERSISTENT_H_ diff --git a/deps/v8/include/cppgc/platform.h b/deps/v8/include/cppgc/platform.h index f216c2730a4dea..8dc5e14a7d6f07 100644 --- a/deps/v8/include/cppgc/platform.h +++ b/deps/v8/include/cppgc/platform.h @@ -5,8 +5,8 @@ #ifndef INCLUDE_CPPGC_PLATFORM_H_ #define INCLUDE_CPPGC_PLATFORM_H_ -#include "include/v8-platform.h" -#include "include/v8config.h" +#include "v8-platform.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) namespace cppgc { diff --git a/deps/v8/include/cppgc/prefinalizer.h b/deps/v8/include/cppgc/prefinalizer.h new file mode 100644 index 00000000000000..2f6d68a1dac808 --- /dev/null +++ b/deps/v8/include/cppgc/prefinalizer.h @@ -0,0 +1,54 @@ +// Copyright 2020 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. + +#ifndef INCLUDE_CPPGC_PREFINALIZER_H_ +#define INCLUDE_CPPGC_PREFINALIZER_H_ + +#include "cppgc/internal/accessors.h" +#include "cppgc/internal/compiler-specific.h" +#include "cppgc/internal/prefinalizer-handler.h" +#include "cppgc/liveness-broker.h" +#include "cppgc/macros.h" + +namespace cppgc { + +namespace internal { + +template +class PrefinalizerRegistration final { + public: + explicit PrefinalizerRegistration(T* self) { + static_assert(sizeof(&T::InvokePreFinalizer) > 0, + "USING_PRE_FINALIZER(T) must be defined."); + + cppgc::internal::PreFinalizerRegistrationDispatcher::RegisterPrefinalizer( + internal::GetHeapFromPayload(self), {self, T::InvokePreFinalizer}); + } + + void* operator new(size_t, void* location) = delete; + void* operator new(size_t) = delete; +}; + +} // namespace internal + +#define CPPGC_USING_PRE_FINALIZER(Class, PreFinalizer) \ + public: \ + static bool InvokePreFinalizer(const LivenessBroker& liveness_broker, \ + void* object) { \ + static_assert(internal::IsGarbageCollectedTypeV, \ + "Only garbage collected objects can have prefinalizers"); \ + Class* self = static_cast(object); \ + if (liveness_broker.IsHeapObjectAlive(self)) return false; \ + self->Class::PreFinalizer(); \ + return true; \ + } \ + \ + private: \ + CPPGC_NO_UNIQUE_ADDRESS internal::PrefinalizerRegistration \ + prefinalizer_dummy_{this}; \ + friend class internal::__thisIsHereToForceASemicolonAfterThisMacro + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_PREFINALIZER_H_ diff --git a/deps/v8/include/cppgc/source-location.h b/deps/v8/include/cppgc/source-location.h new file mode 100644 index 00000000000000..8cc52d6a539c2f --- /dev/null +++ b/deps/v8/include/cppgc/source-location.h @@ -0,0 +1,59 @@ +// Copyright 2020 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. + +#ifndef INCLUDE_CPPGC_SOURCE_LOCATION_H_ +#define INCLUDE_CPPGC_SOURCE_LOCATION_H_ + +#include + +#include "v8config.h" // NOLINT(build/include_directory) + +#if defined(__has_builtin) +#define CPPGC_SUPPORTS_SOURCE_LOCATION \ + (__has_builtin(__builtin_FUNCTION) && __has_builtin(__builtin_FILE) && \ + __has_builtin(__builtin_LINE)) // NOLINT +#elif defined(V8_CC_GNU) && __GNUC__ >= 7 +#define CPPGC_SUPPORTS_SOURCE_LOCATION 1 +#elif defined(V8_CC_INTEL) && __ICC >= 1800 +#define CPPGC_SUPPORTS_SOURCE_LOCATION 1 +#else +#define CPPGC_SUPPORTS_SOURCE_LOCATION 0 +#endif + +namespace cppgc { + +// Encapsulates source location information. Mimics C++20's +// std::source_location. +class V8_EXPORT SourceLocation final { + public: +#if CPPGC_SUPPORTS_SOURCE_LOCATION + static constexpr SourceLocation Current( + const char* function = __builtin_FUNCTION(), + const char* file = __builtin_FILE(), size_t line = __builtin_LINE()) { + return SourceLocation(function, file, line); + } +#else + static constexpr SourceLocation Current() { return SourceLocation(); } +#endif // CPPGC_SUPPORTS_SOURCE_LOCATION + + constexpr SourceLocation() = default; + + constexpr const char* Function() const { return function_; } + constexpr const char* FileName() const { return file_; } + constexpr size_t Line() const { return line_; } + + std::string ToString() const; + + private: + constexpr SourceLocation(const char* function, const char* file, size_t line) + : function_(function), file_(file), line_(line) {} + + const char* function_ = nullptr; + const char* file_ = nullptr; + size_t line_ = 0u; +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_SOURCE_LOCATION_H_ diff --git a/deps/v8/include/cppgc/trace-trait.h b/deps/v8/include/cppgc/trace-trait.h new file mode 100644 index 00000000000000..e246bc53b7d9aa --- /dev/null +++ b/deps/v8/include/cppgc/trace-trait.h @@ -0,0 +1,67 @@ +// Copyright 2020 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. + +#ifndef INCLUDE_CPPGC_TRACE_TRAIT_H_ +#define INCLUDE_CPPGC_TRACE_TRAIT_H_ + +#include +#include "cppgc/type-traits.h" + +namespace cppgc { + +class Visitor; + +namespace internal { + +template ::type>> +struct TraceTraitImpl; + +} // namespace internal + +using TraceCallback = void (*)(Visitor*, const void*); + +// TraceDescriptor is used to describe how to trace an object. +struct TraceDescriptor { + // The adjusted base pointer of the object that should be traced. + const void* base_object_payload; + // A callback for tracing the object. + TraceCallback callback; +}; + +template +struct TraceTrait { + static_assert(internal::IsTraceableV, "T must have a Trace() method"); + + static TraceDescriptor GetTraceDescriptor(const void* self) { + return internal::TraceTraitImpl::GetTraceDescriptor( + static_cast(self)); + } + + static void Trace(Visitor* visitor, const void* self) { + static_cast(self)->Trace(visitor); + } +}; + +namespace internal { + +template +struct TraceTraitImpl { + static TraceDescriptor GetTraceDescriptor(const void* self) { + return {self, TraceTrait::Trace}; + } +}; + +template +struct TraceTraitImpl { + static TraceDescriptor GetTraceDescriptor(const void* self) { + return static_cast(self)->GetTraceDescriptor(); + } +}; + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_TRACE_TRAIT_H_ diff --git a/deps/v8/include/cppgc/type-traits.h b/deps/v8/include/cppgc/type-traits.h new file mode 100644 index 00000000000000..4d8ab809c8d439 --- /dev/null +++ b/deps/v8/include/cppgc/type-traits.h @@ -0,0 +1,109 @@ +// Copyright 2020 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. + +#ifndef INCLUDE_CPPGC_TYPE_TRAITS_H_ +#define INCLUDE_CPPGC_TYPE_TRAITS_H_ + +#include + +namespace cppgc { + +class Visitor; + +namespace internal { + +// Pre-C++17 custom implementation of std::void_t. +template +struct make_void { + typedef void type; +}; +template +using void_t = typename make_void::type; + +// Not supposed to be specialized by the user. +template +struct IsWeak : std::false_type {}; + +template class U> +struct IsSubclassOfTemplate { + private: + template + static std::true_type SubclassCheck(U*); + static std::false_type SubclassCheck(...); + + public: + static constexpr bool value = + decltype(SubclassCheck(std::declval()))::value; +}; + +// IsTraceMethodConst is used to verify that all Trace methods are marked as +// const. It is equivalent to IsTraceable but for a non-const object. +template +struct IsTraceMethodConst : std::false_type {}; + +template +struct IsTraceMethodConst().Trace( + std::declval()))>> : std::true_type { +}; + +template +struct IsTraceable : std::false_type { + static_assert(sizeof(T), "T must be fully defined"); +}; + +template +struct IsTraceable< + T, void_t().Trace(std::declval()))>> + : std::true_type { + // All Trace methods should be marked as const. If an object of type + // 'T' is traceable then any object of type 'const T' should also + // be traceable. + static_assert(IsTraceMethodConst(), + "Trace methods should be marked as const."); +}; + +template +constexpr bool IsTraceableV = IsTraceable::value; + +template +struct IsGarbageCollectedMixinType : std::false_type { + static_assert(sizeof(T), "T must be fully defined"); +}; + +template +struct IsGarbageCollectedMixinType< + T, + void_t::IsGarbageCollectedMixinTypeMarker>> + : std::true_type { + static_assert(sizeof(T), "T must be fully defined"); +}; + +template +struct IsGarbageCollectedType : IsGarbageCollectedMixinType { + static_assert(sizeof(T), "T must be fully defined"); +}; + +template +struct IsGarbageCollectedType< + T, void_t::IsGarbageCollectedTypeMarker>> + : std::true_type { + static_assert(sizeof(T), "T must be fully defined"); +}; + +template +constexpr bool IsGarbageCollectedTypeV = + internal::IsGarbageCollectedType::value; + +template +constexpr bool IsGarbageCollectedMixinTypeV = + internal::IsGarbageCollectedMixinType::value; + +} // namespace internal + +template +constexpr bool IsWeakV = internal::IsWeak::value; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_TYPE_TRAITS_H_ diff --git a/deps/v8/include/cppgc/visitor.h b/deps/v8/include/cppgc/visitor.h new file mode 100644 index 00000000000000..a73a4abb2bdb7c --- /dev/null +++ b/deps/v8/include/cppgc/visitor.h @@ -0,0 +1,138 @@ +// Copyright 2020 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. + +#ifndef INCLUDE_CPPGC_VISITOR_H_ +#define INCLUDE_CPPGC_VISITOR_H_ + +#include "cppgc/garbage-collected.h" +#include "cppgc/internal/logging.h" +#include "cppgc/internal/pointer-policies.h" +#include "cppgc/liveness-broker.h" +#include "cppgc/member.h" +#include "cppgc/source-location.h" +#include "cppgc/trace-trait.h" + +namespace cppgc { +namespace internal { +class VisitorBase; +} // namespace internal + +using WeakCallback = void (*)(const LivenessBroker&, const void*); + +/** + * Visitor passed to trace methods. All managed pointers must have called the + * visitor's trace method on them. + */ +class Visitor { + public: + template + void Trace(const Member& member) { + const T* value = member.GetRawAtomic(); + CPPGC_DCHECK(value != kSentinelPointer); + Trace(value); + } + + template + void Trace(const WeakMember& weak_member) { + static_assert(sizeof(T), "T must be fully defined"); + static_assert(internal::IsGarbageCollectedType::value, + "T must be GarabgeCollected or GarbageCollectedMixin type"); + + const T* value = weak_member.GetRawAtomic(); + + // Bailout assumes that WeakMember emits write barrier. + if (!value) { + return; + } + + // TODO(chromium:1056170): DCHECK (or similar) for deleted values as they + // should come in at a different path. + VisitWeak(value, TraceTrait::GetTraceDescriptor(value), + &HandleWeak>, &weak_member); + } + + template * = nullptr> + void TraceRoot(const Persistent& p, const SourceLocation& loc) { + using PointeeType = typename Persistent::PointeeType; + static_assert(sizeof(PointeeType), + "Persistent's pointee type must be fully defined"); + static_assert(internal::IsGarbageCollectedType::value, + "Persisent's pointee type must be GarabgeCollected or " + "GarbageCollectedMixin"); + if (!p.Get()) { + return; + } + VisitRoot(p.Get(), TraceTrait::GetTraceDescriptor(p.Get())); + } + + template < + typename WeakPersistent, + std::enable_if_t* = nullptr> + void TraceRoot(const WeakPersistent& p, const SourceLocation& loc) { + using PointeeType = typename WeakPersistent::PointeeType; + static_assert(sizeof(PointeeType), + "Persistent's pointee type must be fully defined"); + static_assert(internal::IsGarbageCollectedType::value, + "Persisent's pointee type must be GarabgeCollected or " + "GarbageCollectedMixin"); + VisitWeakRoot(p.Get(), TraceTrait::GetTraceDescriptor(p.Get()), + &HandleWeak, &p); + } + + template + void RegisterWeakCallbackMethod(const T* obj) { + RegisterWeakCallback(&WeakCallbackMethodDelegate, obj); + } + + virtual void RegisterWeakCallback(WeakCallback, const void*) {} + + protected: + virtual void Visit(const void* self, TraceDescriptor) {} + virtual void VisitWeak(const void* self, TraceDescriptor, WeakCallback, + const void* weak_member) {} + virtual void VisitRoot(const void*, TraceDescriptor) {} + virtual void VisitWeakRoot(const void* self, TraceDescriptor, WeakCallback, + const void* weak_root) {} + + private: + template + static void WeakCallbackMethodDelegate(const LivenessBroker& info, + const void* self) { + // Callback is registered through a potential const Trace method but needs + // to be able to modify fields. See HandleWeak. + (const_cast(static_cast(self))->*method)(info); + } + + template + static void HandleWeak(const LivenessBroker& info, const void* object) { + const PointerType* weak = static_cast(object); + const auto* raw = weak->Get(); + if (raw && !info.IsHeapObjectAlive(raw)) { + // Object is passed down through the marker as const. Alternatives are + // - non-const Trace method; + // - mutable pointer in MemberBase; + const_cast(weak)->Clear(); + } + } + + Visitor() = default; + + template + void Trace(const T* t) { + static_assert(sizeof(T), "T must be fully defined"); + static_assert(internal::IsGarbageCollectedType::value, + "T must be GarabgeCollected or GarbageCollectedMixin type"); + if (!t) { + return; + } + Visit(t, TraceTrait::GetTraceDescriptor(t)); + } + + friend class internal::VisitorBase; +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_VISITOR_H_ diff --git a/deps/v8/include/js_protocol.pdl b/deps/v8/include/js_protocol.pdl index 3f5410d1e1f787..706c37f958e4a0 100644 --- a/deps/v8/include/js_protocol.pdl +++ b/deps/v8/include/js_protocol.pdl @@ -204,6 +204,21 @@ domain Debugger # Exception details. optional Runtime.ExceptionDetails exceptionDetails + # Execute a Wasm Evaluator module on a given call frame. + experimental command executeWasmEvaluator + parameters + # WebAssembly call frame identifier to evaluate on. + CallFrameId callFrameId + # Code of the evaluator module. + binary evaluator + # Terminate execution after timing out (number of milliseconds). + experimental optional Runtime.TimeDelta timeout + returns + # Object wrapper for the evaluation result. + Runtime.RemoteObject result + # Exception details. + optional Runtime.ExceptionDetails exceptionDetails + # Returns possible locations for breakpoint. scriptId in start and end range locations should be # the same. command getPossibleBreakpoints @@ -510,6 +525,18 @@ domain Debugger JavaScript WebAssembly + # Debug symbols available for a wasm script. + type DebugSymbols extends object + properties + # Type of the debug symbols. + enum type + None + SourceMap + EmbeddedDWARF + ExternalDWARF + # URL of the external symbol source. + optional string externalURL + # Fired when virtual machine fails to parse the script. event scriptFailedToParse parameters @@ -584,6 +611,8 @@ domain Debugger experimental optional integer codeOffset # The language of the script. experimental optional Debugger.ScriptLanguage scriptLanguage + # If the scriptLanguage is WebASsembly, the source of debug symbols for the module. + experimental optional Debugger.DebugSymbols debugSymbols experimental domain HeapProfiler depends on Runtime @@ -980,6 +1009,7 @@ domain Runtime f32 f64 v128 + anyref # Object class (constructor) name. Specified for `object` type values only. optional string className # Remote object value in case of primitive values or JSON values (if it was requested). diff --git a/deps/v8/include/libplatform/libplatform.h b/deps/v8/include/libplatform/libplatform.h index 6051b644fb9b59..c7ea4c2bd38575 100644 --- a/deps/v8/include/libplatform/libplatform.h +++ b/deps/v8/include/libplatform/libplatform.h @@ -9,8 +9,8 @@ #include "libplatform/libplatform-export.h" #include "libplatform/v8-tracing.h" -#include "v8-platform.h" // NOLINT(build/include) -#include "v8config.h" // NOLINT(build/include) +#include "v8-platform.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) namespace v8 { namespace platform { diff --git a/deps/v8/include/libplatform/v8-tracing.h b/deps/v8/include/libplatform/v8-tracing.h index 79e6f62d23e1bb..45822d00f371d4 100644 --- a/deps/v8/include/libplatform/v8-tracing.h +++ b/deps/v8/include/libplatform/v8-tracing.h @@ -12,9 +12,12 @@ #include #include "libplatform/libplatform-export.h" -#include "v8-platform.h" // NOLINT(build/include) +#include "v8-platform.h" // NOLINT(build/include_directory) namespace perfetto { +namespace trace_processor { +class TraceProcessorStorage; +} class TracingSession; } @@ -28,7 +31,6 @@ namespace platform { namespace tracing { class TraceEventListener; -class JSONTraceEventListener; const int kTraceMaxNumArgs = 2; @@ -197,6 +199,9 @@ class V8_PLATFORM_EXPORT TraceConfig { TraceConfig() : enable_systrace_(false), enable_argument_filter_(false) {} TraceRecordMode GetTraceRecordMode() const { return record_mode_; } + const StringList& GetEnabledCategories() const { + return included_categories_; + } bool IsSystraceEnabled() const { return enable_systrace_; } bool IsArgumentFilterEnabled() const { return enable_argument_filter_; } @@ -229,6 +234,17 @@ class V8_PLATFORM_EXPORT TraceConfig { class V8_PLATFORM_EXPORT TracingController : public V8_PLATFORM_NON_EXPORTED_BASE(v8::TracingController) { public: + TracingController(); + ~TracingController() override; + +#if defined(V8_USE_PERFETTO) + // Must be called before StartTracing() if V8_USE_PERFETTO is true. Provides + // the output stream for the JSON trace data. + void InitializeForPerfetto(std::ostream* output_stream); + // Provide an optional listener for testing that will receive trace events. + // Must be called before StartTracing(). + void SetTraceEventListenerForTesting(TraceEventListener* listener); +#else // defined(V8_USE_PERFETTO) // The pointer returned from GetCategoryGroupEnabled() points to a value with // zero or more of the following bits. Used in this class only. The // TRACE_EVENT macros should only use the value as a bool. These values must @@ -242,19 +258,8 @@ class V8_PLATFORM_EXPORT TracingController ENABLED_FOR_ETW_EXPORT = 1 << 3 }; - TracingController(); - ~TracingController() override; - // Takes ownership of |trace_buffer|. void Initialize(TraceBuffer* trace_buffer); -#ifdef V8_USE_PERFETTO - // Must be called before StartTracing() if V8_USE_PERFETTO is true. Provides - // the output stream for the JSON trace data. - void InitializeForPerfetto(std::ostream* output_stream); - // Provide an optional listener for testing that will receive trace events. - // Must be called before StartTracing(). - void SetTraceEventListenerForTesting(TraceEventListener* listener); -#endif // v8::TracingController implementation. const uint8_t* GetCategoryGroupEnabled(const char* category_group) override; @@ -274,6 +279,10 @@ class V8_PLATFORM_EXPORT TracingController unsigned int flags, int64_t timestamp) override; void UpdateTraceEventDuration(const uint8_t* category_enabled_flag, const char* name, uint64_t handle) override; + + static const char* GetCategoryGroupName(const uint8_t* category_enabled_flag); +#endif // !defined(V8_USE_PERFETTO) + void AddTraceStateObserver( v8::TracingController::TraceStateObserver* observer) override; void RemoveTraceStateObserver( @@ -282,27 +291,32 @@ class V8_PLATFORM_EXPORT TracingController void StartTracing(TraceConfig* trace_config); void StopTracing(); - static const char* GetCategoryGroupName(const uint8_t* category_enabled_flag); - protected: +#if !defined(V8_USE_PERFETTO) virtual int64_t CurrentTimestampMicroseconds(); virtual int64_t CurrentCpuTimestampMicroseconds(); +#endif // !defined(V8_USE_PERFETTO) private: +#if !defined(V8_USE_PERFETTO) void UpdateCategoryGroupEnabledFlag(size_t category_index); void UpdateCategoryGroupEnabledFlags(); +#endif // !defined(V8_USE_PERFETTO) - std::unique_ptr trace_buffer_; - std::unique_ptr trace_config_; std::unique_ptr mutex_; - std::unordered_set observers_; + std::unique_ptr trace_config_; std::atomic_bool recording_{false}; -#ifdef V8_USE_PERFETTO + std::unordered_set observers_; + +#if defined(V8_USE_PERFETTO) std::ostream* output_stream_ = nullptr; - std::unique_ptr json_listener_; + std::unique_ptr + trace_processor_; TraceEventListener* listener_for_testing_ = nullptr; std::unique_ptr tracing_session_; -#endif +#else // !defined(V8_USE_PERFETTO) + std::unique_ptr trace_buffer_; +#endif // !defined(V8_USE_PERFETTO) // Disallow copy and assign TracingController(const TracingController&) = delete; diff --git a/deps/v8/include/v8-fast-api-calls.h b/deps/v8/include/v8-fast-api-calls.h index 79a5d4d82a764d..f74406493bcf2a 100644 --- a/deps/v8/include/v8-fast-api-calls.h +++ b/deps/v8/include/v8-fast-api-calls.h @@ -165,7 +165,7 @@ #include #include -#include "v8config.h" // NOLINT(build/include) +#include "v8config.h" // NOLINT(build/include_directory) namespace v8 { diff --git a/deps/v8/include/v8-inspector-protocol.h b/deps/v8/include/v8-inspector-protocol.h index 612a2ebc3911f5..a5ffb7d6954bcf 100644 --- a/deps/v8/include/v8-inspector-protocol.h +++ b/deps/v8/include/v8-inspector-protocol.h @@ -5,9 +5,9 @@ #ifndef V8_V8_INSPECTOR_PROTOCOL_H_ #define V8_V8_INSPECTOR_PROTOCOL_H_ -#include "inspector/Debugger.h" // NOLINT(build/include) -#include "inspector/Runtime.h" // NOLINT(build/include) -#include "inspector/Schema.h" // NOLINT(build/include) -#include "v8-inspector.h" // NOLINT(build/include) +#include "inspector/Debugger.h" // NOLINT(build/include_directory) +#include "inspector/Runtime.h" // NOLINT(build/include_directory) +#include "inspector/Schema.h" // NOLINT(build/include_directory) +#include "v8-inspector.h" // NOLINT(build/include_directory) #endif // V8_V8_INSPECTOR_PROTOCOL_H_ diff --git a/deps/v8/include/v8-inspector.h b/deps/v8/include/v8-inspector.h index 01274625c1f476..6573940e2fe3c3 100644 --- a/deps/v8/include/v8-inspector.h +++ b/deps/v8/include/v8-inspector.h @@ -11,7 +11,7 @@ #include #include -#include "v8.h" // NOLINT(build/include) +#include "v8.h" // NOLINT(build/include_directory) namespace v8_inspector { diff --git a/deps/v8/include/v8-internal.h b/deps/v8/include/v8-internal.h index 52ee403f526d06..127a77dbfca070 100644 --- a/deps/v8/include/v8-internal.h +++ b/deps/v8/include/v8-internal.h @@ -10,8 +10,8 @@ #include #include -#include "v8-version.h" // NOLINT(build/include) -#include "v8config.h" // NOLINT(build/include) +#include "v8-version.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) namespace v8 { @@ -110,6 +110,16 @@ constexpr bool PointerCompressionIsEnabled() { return kApiTaggedSize != kApiSystemPointerSize; } +constexpr bool HeapSandboxIsEnabled() { +#ifdef V8_HEAP_SANDBOX + return true; +#else + return false; +#endif +} + +using ExternalPointer_t = Address; + #ifdef V8_31BIT_SMIS_ON_64BIT_ARCH using PlatformSmiTagging = SmiTagging; #else @@ -130,6 +140,15 @@ V8_INLINE static constexpr internal::Address IntToSmi(int value) { kSmiTag; } +// {obj} must be the raw tagged pointer representation of a HeapObject +// that's guaranteed to never be in ReadOnlySpace. +V8_EXPORT internal::Isolate* IsolateFromNeverReadOnlySpaceObject(Address obj); + +// Returns if we need to throw when an error occurs. This infers the language +// mode based on the current context and the closure. This returns true if the +// language mode is strict. +V8_EXPORT bool ShouldThrowOnError(v8::internal::Isolate* isolate); + /** * This class exports constants and functionality from within v8 that * is necessary to implement inline functions in the v8 api. Don't @@ -145,7 +164,6 @@ class Internals { 1 * kApiTaggedSize + 2 * kApiInt32Size; static const int kOddballKindOffset = 4 * kApiTaggedSize + kApiDoubleSize; - static const int kForeignAddressOffset = kApiTaggedSize; static const int kJSObjectHeaderSize = 3 * kApiTaggedSize; static const int kFixedArrayHeaderSize = 2 * kApiTaggedSize; static const int kEmbedderDataArrayHeaderSize = 2 * kApiTaggedSize; @@ -330,11 +348,36 @@ class Internals { #endif } + V8_INLINE static internal::Isolate* GetIsolateForHeapSandbox( + internal::Address obj) { +#ifdef V8_HEAP_SANDBOX + return internal::IsolateFromNeverReadOnlySpaceObject(obj); +#else + // Not used in non-sandbox mode. + return nullptr; +#endif + } + + V8_INLINE static internal::Address ReadExternalPointerField( + internal::Isolate* isolate, internal::Address heap_object_ptr, + int offset) { + internal::Address value = ReadRawField
(heap_object_ptr, offset); +#ifdef V8_HEAP_SANDBOX + // We currently have to treat zero as nullptr in embedder slots. + if (value) value = DecodeExternalPointer(isolate, value); +#endif + return value; + } + #ifdef V8_COMPRESS_POINTERS // See v8:7703 or src/ptr-compr.* for details about pointer compression. static constexpr size_t kPtrComprHeapReservationSize = size_t{1} << 32; static constexpr size_t kPtrComprIsolateRootAlignment = size_t{1} << 32; + // See v8:10391 for details about V8 heap sandbox. + static constexpr uint32_t kExternalPointerSalt = + 0x7fffffff & ~static_cast(kHeapObjectTagMask); + V8_INLINE static internal::Address GetRootFromOnHeapAddress( internal::Address addr) { return addr & -static_cast(kPtrComprIsolateRootAlignment); @@ -345,6 +388,15 @@ class Internals { internal::Address root = GetRootFromOnHeapAddress(heap_object_ptr); return root + static_cast(static_cast(value)); } + + V8_INLINE static Address DecodeExternalPointer( + const Isolate* isolate, ExternalPointer_t encoded_pointer) { +#ifndef V8_HEAP_SANDBOX + return encoded_pointer; +#else + return encoded_pointer ^ kExternalPointerSalt; +#endif + } #endif // V8_COMPRESS_POINTERS }; @@ -371,15 +423,6 @@ V8_INLINE void PerformCastCheck(T* data) { CastCheck::value>::Perform(data); } -// {obj} must be the raw tagged pointer representation of a HeapObject -// that's guaranteed to never be in ReadOnlySpace. -V8_EXPORT internal::Isolate* IsolateFromNeverReadOnlySpaceObject(Address obj); - -// Returns if we need to throw when an error occurs. This infers the language -// mode based on the current context and the closure. This returns true if the -// language mode is strict. -V8_EXPORT bool ShouldThrowOnError(v8::internal::Isolate* isolate); - // A base class for backing stores, which is needed due to vagaries of // how static casts work with std::shared_ptr. class BackingStoreBase {}; diff --git a/deps/v8/include/v8-platform.h b/deps/v8/include/v8-platform.h index 5d23cd665ed399..7cfd18b5708d57 100644 --- a/deps/v8/include/v8-platform.h +++ b/deps/v8/include/v8-platform.h @@ -11,12 +11,34 @@ #include #include -#include "v8config.h" // NOLINT(build/include) +#include "v8config.h" // NOLINT(build/include_directory) namespace v8 { class Isolate; +// Valid priorities supported by the task scheduling infrastructure. +enum class TaskPriority : uint8_t { + /** + * Best effort tasks are not critical for performance of the application. The + * platform implementation should preempt such tasks if higher priority tasks + * arrive. + */ + kBestEffort, + /** + * User visible tasks are long running background tasks that will + * improve performance and memory usage of the application upon completion. + * Example: background compilation and garbage collection. + */ + kUserVisible, + /** + * User blocking tasks are highest priority tasks that block the execution + * thread (e.g. major garbage collection). They must be finished as soon as + * possible. + */ + kUserBlocking, +}; + /** * A Task represents a unit of work. */ @@ -113,6 +135,82 @@ class TaskRunner { TaskRunner& operator=(const TaskRunner&) = delete; }; +/** + * Delegate that's passed to Job's worker task, providing an entry point to + * communicate with the scheduler. + */ +class JobDelegate { + public: + /** + * Returns true if this thread should return from the worker task on the + * current thread ASAP. Workers should periodically invoke ShouldYield (or + * YieldIfNeeded()) as often as is reasonable. + */ + virtual bool ShouldYield() = 0; + + /** + * Notifies the scheduler that max concurrency was increased, and the number + * of worker should be adjusted accordingly. See Platform::PostJob() for more + * details. + */ + virtual void NotifyConcurrencyIncrease() = 0; +}; + +/** + * Handle returned when posting a Job. Provides methods to control execution of + * the posted Job. + */ +class JobHandle { + public: + virtual ~JobHandle() = default; + + /** + * Notifies the scheduler that max concurrency was increased, and the number + * of worker should be adjusted accordingly. See Platform::PostJob() for more + * details. + */ + virtual void NotifyConcurrencyIncrease() = 0; + + /** + * Contributes to the job on this thread. Doesn't return until all tasks have + * completed and max concurrency becomes 0. When Join() is called and max + * concurrency reaches 0, it should not increase again. This also promotes + * this Job's priority to be at least as high as the calling thread's + * priority. + */ + virtual void Join() = 0; + + /** + * Forces all existing workers to yield ASAP. Waits until they have all + * returned from the Job's callback before returning. + */ + virtual void Cancel() = 0; + + /** + * Returns true if associated with a Job and other methods may be called. + * Returns false after Join() or Cancel() was called. + */ + virtual bool IsRunning() = 0; +}; + +/** + * A JobTask represents work to run in parallel from Platform::PostJob(). + */ +class JobTask { + public: + virtual ~JobTask() = default; + + virtual void Run(JobDelegate* delegate) = 0; + + /** + * Controls the maximum number of threads calling Run() concurrently. Run() is + * only invoked if the number of threads previously running Run() was less + * than the value returned. Since GetMaxConcurrency() is a leaf function, it + * must not call back any JobHandle methods. + */ + virtual size_t GetMaxConcurrency() const = 0; +}; + /** * The interface represents complex arguments to trace events. */ @@ -138,6 +236,10 @@ class TracingController { public: virtual ~TracingController() = default; + // In Perfetto mode, trace events are written using Perfetto's Track Event + // API directly without going through the embedder. However, it is still + // possible to observe tracing being enabled and disabled. +#if !defined(V8_USE_PERFETTO) /** * Called by TRACE_EVENT* macros, don't call this directly. * The name parameter is a category group for example: @@ -183,6 +285,7 @@ class TracingController { **/ virtual void UpdateTraceEventDuration(const uint8_t* category_enabled_flag, const char* name, uint64_t handle) {} +#endif // !defined(V8_USE_PERFETTO) class TraceStateObserver { public: @@ -368,6 +471,64 @@ class Platform { */ virtual bool IdleTasksEnabled(Isolate* isolate) { return false; } + /** + * Posts |job_task| to run in parallel. Returns a JobHandle associated with + * the Job, which can be joined or canceled. + * This avoids degenerate cases: + * - Calling CallOnWorkerThread() for each work item, causing significant + * overhead. + * - Fixed number of CallOnWorkerThread() calls that split the work and might + * run for a long time. This is problematic when many components post + * "num cores" tasks and all expect to use all the cores. In these cases, + * the scheduler lacks context to be fair to multiple same-priority requests + * and/or ability to request lower priority work to yield when high priority + * work comes in. + * A canonical implementation of |job_task| looks like: + * class MyJobTask : public JobTask { + * public: + * MyJobTask(...) : worker_queue_(...) {} + * // JobTask: + * void Run(JobDelegate* delegate) override { + * while (!delegate->ShouldYield()) { + * // Smallest unit of work. + * auto work_item = worker_queue_.TakeWorkItem(); // Thread safe. + * if (!work_item) return; + * ProcessWork(work_item); + * } + * } + * + * size_t GetMaxConcurrency() const override { + * return worker_queue_.GetSize(); // Thread safe. + * } + * }; + * auto handle = PostJob(TaskPriority::kUserVisible, + * std::make_unique(...)); + * handle->Join(); + * + * PostJob() and methods of the returned JobHandle/JobDelegate, must never be + * called while holding a lock that could be acquired by JobTask::Run or + * JobTask::GetMaxConcurrency -- that could result in a deadlock. This is + * because [1] JobTask::GetMaxConcurrency may be invoked while holding + * internal lock (A), hence JobTask::GetMaxConcurrency can only use a lock (B) + * if that lock is *never* held while calling back into JobHandle from any + * thread (A=>B/B=>A deadlock) and [2] JobTask::Run or + * JobTask::GetMaxConcurrency may be invoked synchronously from JobHandle + * (B=>JobHandle::foo=>B deadlock). + * + * A sufficient PostJob() implementation that uses the default Job provided in + * libplatform looks like: + * std::unique_ptr PostJob( + * TaskPriority priority, std::unique_ptr job_task) override { + * return std::make_unique( + * std::make_shared( + * this, std::move(job_task), kNumThreads)); + * } + */ + virtual std::unique_ptr PostJob( + TaskPriority priority, std::unique_ptr job_task) { + return nullptr; + } + /** * Monotonically increasing time in seconds from an arbitrary fixed point in * the past. This function is expected to return at least diff --git a/deps/v8/include/v8-profiler.h b/deps/v8/include/v8-profiler.h index 866d799076e9db..c3b25e8d6a6302 100644 --- a/deps/v8/include/v8-profiler.h +++ b/deps/v8/include/v8-profiler.h @@ -10,7 +10,7 @@ #include #include -#include "v8.h" // NOLINT(build/include) +#include "v8.h" // NOLINT(build/include_directory) /** * Profiler support for the V8 JavaScript engine. diff --git a/deps/v8/include/v8-util.h b/deps/v8/include/v8-util.h index 29d813e4274d16..89ec4f6a789c03 100644 --- a/deps/v8/include/v8-util.h +++ b/deps/v8/include/v8-util.h @@ -5,7 +5,7 @@ #ifndef V8_UTIL_H_ #define V8_UTIL_H_ -#include "v8.h" // NOLINT(build/include) +#include "v8.h" // NOLINT(build/include_directory) #include #include #include diff --git a/deps/v8/include/v8-version-string.h b/deps/v8/include/v8-version-string.h index fb84144d544106..8faed2a740592b 100644 --- a/deps/v8/include/v8-version-string.h +++ b/deps/v8/include/v8-version-string.h @@ -5,7 +5,7 @@ #ifndef V8_VERSION_STRING_H_ #define V8_VERSION_STRING_H_ -#include "v8-version.h" // NOLINT(build/include) +#include "v8-version.h" // NOLINT(build/include_directory) // This is here rather than v8-version.h to keep that file simple and // machine-processable. diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h index 64f184866537b5..cee7990e4bc193 100644 --- a/deps/v8/include/v8-version.h +++ b/deps/v8/include/v8-version.h @@ -9,9 +9,9 @@ // NOTE these macros are used by some of the tool scripts and the build // system so their names cannot be changed without changing the scripts. #define V8_MAJOR_VERSION 8 -#define V8_MINOR_VERSION 3 -#define V8_BUILD_NUMBER 110 -#define V8_PATCH_LEVEL 9 +#define V8_MINOR_VERSION 4 +#define V8_BUILD_NUMBER 371 +#define V8_PATCH_LEVEL 19 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/deps/v8/include/v8-wasm-trap-handler-posix.h b/deps/v8/include/v8-wasm-trap-handler-posix.h index 998d0a41bb73d8..9b8e8a5b496aa0 100644 --- a/deps/v8/include/v8-wasm-trap-handler-posix.h +++ b/deps/v8/include/v8-wasm-trap-handler-posix.h @@ -7,7 +7,7 @@ #include -#include "v8config.h" // NOLINT(build/include) +#include "v8config.h" // NOLINT(build/include_directory) namespace v8 { /** diff --git a/deps/v8/include/v8-wasm-trap-handler-win.h b/deps/v8/include/v8-wasm-trap-handler-win.h index 0185df6401c24b..9d3cad58483285 100644 --- a/deps/v8/include/v8-wasm-trap-handler-win.h +++ b/deps/v8/include/v8-wasm-trap-handler-win.h @@ -7,7 +7,7 @@ #include -#include "v8config.h" // NOLINT(build/include) +#include "v8config.h" // NOLINT(build/include_directory) namespace v8 { /** diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index 406c47383b714d..18d72f1630ccd5 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -18,15 +18,17 @@ #include #include #include + #include #include #include #include #include -#include "v8-internal.h" // NOLINT(build/include) -#include "v8-version.h" // NOLINT(build/include) -#include "v8config.h" // NOLINT(build/include) +#include "cppgc/common.h" +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8-version.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) // We reserve the V8_* prefix for macros defined in V8 public API and // assume there are no name conflicts with the embedder's code. @@ -123,19 +125,21 @@ namespace internal { enum class ArgumentsType; template class Arguments; +template +class CustomArguments; class DeferredHandles; +class FunctionCallbackArguments; +class GlobalHandles; class Heap; class HeapObject; class ExternalString; class Isolate; class LocalEmbedderHeapTracer; class MicrotaskQueue; -struct ScriptStreamingData; -template class CustomArguments; class PropertyCallbackArguments; -class FunctionCallbackArguments; -class GlobalHandles; +class ReadOnlyHeap; class ScopedExternalStringLock; +struct ScriptStreamingData; class ThreadLocalTop; namespace wasm { @@ -1752,11 +1756,9 @@ class V8_EXPORT ScriptCompiler { public: enum Encoding { ONE_BYTE, TWO_BYTE, UTF8 }; -#if defined(_MSC_VER) && _MSC_VER >= 1910 /* Disable on VS2015 */ V8_DEPRECATE_SOON( "This class takes ownership of source_stream, so use the constructor " "taking a unique_ptr to make these semantics clearer") -#endif StreamedSource(ExternalSourceStream* source_stream, Encoding encoding); StreamedSource(std::unique_ptr source_stream, Encoding encoding); @@ -4764,11 +4766,17 @@ class V8_EXPORT CompiledWasmModule { */ MemorySpan GetWireBytesRef(); + const std::string& source_url() const { return source_url_; } + private: - explicit CompiledWasmModule(std::shared_ptr); - friend class Utils; + friend class WasmModuleObject; + friend class WasmStreaming; + + explicit CompiledWasmModule(std::shared_ptr, + const char* source_url, size_t url_length); const std::shared_ptr native_module_; + const std::string source_url_; }; // An instance of WebAssembly.Module. @@ -5935,37 +5943,6 @@ class V8_EXPORT RegExp : public Object { static void CheckCast(Value* obj); }; -/** - * An instance of the built-in FinalizationRegistry constructor. - * - * The C++ name is FinalizationGroup for backwards compatibility. This API is - * experimental and deprecated. - */ -class V8_EXPORT FinalizationGroup : public Object { - public: - /** - * Runs the cleanup callback of the given FinalizationRegistry. - * - * V8 will inform the embedder that there are finalizer callbacks be - * called through HostCleanupFinalizationGroupCallback. - * - * HostCleanupFinalizationGroupCallback should schedule a task to - * call FinalizationGroup::Cleanup() at some point in the - * future. It's the embedders responsiblity to make this call at a - * time which does not interrupt synchronous ECMAScript code - * execution. - * - * If the result is Nothing then an exception has - * occurred. Otherwise the result is |true| if the cleanup callback - * was called successfully. The result is never |false|. - */ - V8_DEPRECATED( - "FinalizationGroup cleanup is automatic if " - "HostCleanupFinalizationGroupCallback is not set") - static V8_WARN_UNUSED_RESULT Maybe Cleanup( - Local finalization_group); -}; - /** * A JavaScript value that wraps a C++ void*. This type of value is mainly used * to associate C++ data structures with JavaScript objects. @@ -7172,6 +7149,9 @@ class V8_EXPORT Exception { static Local ReferenceError(Local message); static Local SyntaxError(Local message); static Local TypeError(Local message); + static Local WasmCompileError(Local message); + static Local WasmLinkError(Local message); + static Local WasmRuntimeError(Local message); static Local Error(Local message); /** @@ -7215,20 +7195,6 @@ typedef void (*AddCrashKeyCallback)(CrashKeyId id, const std::string& value); typedef void (*BeforeCallEnteredCallback)(Isolate*); typedef void (*CallCompletedCallback)(Isolate*); -/** - * HostCleanupFinalizationGroupCallback is called when we require the - * embedder to enqueue a task that would call - * FinalizationGroup::Cleanup(). - * - * The FinalizationGroup is the one for which the embedder needs to - * call FinalizationGroup::Cleanup() on. - * - * The context provided is the one in which the FinalizationGroup was - * created in. - */ -typedef void (*HostCleanupFinalizationGroupCallback)( - Local context, Local fg); - /** * HostImportModuleDynamicallyCallback is called when we require the * embedder to load a module. This is used as part of the dynamic @@ -7255,7 +7221,8 @@ typedef MaybeLocal (*HostImportModuleDynamicallyCallback)( /** * HostInitializeImportMetaObjectCallback is called the first time import.meta - * is accessed for a module. Subsequent access will reuse the same value. + * is accessed for a module. Subsequent access will reuse the same value. The + * callback must not throw. * * The method combines two implementation-defined abstract operations into one: * HostGetImportMetaProperties and HostFinalizeImportMeta. @@ -7326,6 +7293,7 @@ class PromiseRejectMessage { typedef void (*PromiseRejectCallback)(PromiseRejectMessage message); // --- Microtasks Callbacks --- +V8_DEPRECATE_SOON("Use *WithData version.") typedef void (*MicrotasksCompletedCallback)(Isolate*); typedef void (*MicrotasksCompletedCallbackWithData)(Isolate*, void*); typedef void (*MicrotaskCallback)(void* data); @@ -7517,6 +7485,9 @@ typedef bool (*WasmThreadsEnabledCallback)(Local context); typedef Local (*WasmLoadSourceMapCallback)(Isolate* isolate, const char* name); +// --- Callback for checking if WebAssembly Simd is enabled --- +typedef bool (*WasmSimdEnabledCallback)(Local context); + // --- Garbage Collection Callbacks --- /** @@ -7594,6 +7565,7 @@ class V8_EXPORT SharedMemoryStatistics { size_t read_only_space_physical_size_; friend class V8; + friend class internal::ReadOnlyHeap; }; /** @@ -7873,16 +7845,12 @@ enum class MemoryPressureLevel { kNone, kModerate, kCritical }; */ class V8_EXPORT EmbedderHeapTracer { public: + using EmbedderStackState = cppgc::EmbedderStackState; + enum TraceFlags : uint64_t { kNoFlags = 0, kReduceMemory = 1 << 0, - }; - - // Indicator for the stack state of the embedder. - enum EmbedderStackState { - kUnknown, - kNonEmpty, - kEmpty, + kForced = 1 << 2, }; /** @@ -8445,7 +8413,7 @@ class V8_EXPORT Isolate { kOptimizedFunctionWithOneShotBytecode = 71, kRegExpMatchIsTrueishOnNonJSRegExp = 72, kRegExpMatchIsFalseishOnJSRegExp = 73, - kDateGetTimezoneOffset = 74, + kDateGetTimezoneOffset = 74, // Unused. kStringNormalize = 75, kCallSiteAPIGetFunctionSloppyCall = 76, kCallSiteAPIGetThisSloppyCall = 77, @@ -8461,6 +8429,24 @@ class V8_EXPORT Isolate { kDateTimeFormatDateTimeStyle = 87, kBreakIteratorTypeWord = 88, kBreakIteratorTypeLine = 89, + kInvalidatedArrayBufferDetachingProtector = 90, + kInvalidatedArrayConstructorProtector = 91, + kInvalidatedArrayIteratorLookupChainProtector = 92, + kInvalidatedArraySpeciesLookupChainProtector = 93, + kInvalidatedIsConcatSpreadableLookupChainProtector = 94, + kInvalidatedMapIteratorLookupChainProtector = 95, + kInvalidatedNoElementsProtector = 96, + kInvalidatedPromiseHookProtector = 97, + kInvalidatedPromiseResolveLookupChainProtector = 98, + kInvalidatedPromiseSpeciesLookupChainProtector = 99, + kInvalidatedPromiseThenLookupChainProtector = 100, + kInvalidatedRegExpSpeciesLookupChainProtector = 101, + kInvalidatedSetIteratorLookupChainProtector = 102, + kInvalidatedStringIteratorLookupChainProtector = 103, + kInvalidatedStringLengthOverflowLookupChainProtector = 104, + kInvalidatedTypedArraySpeciesLookupChainProtector = 105, + kWasmSimdOpcodes = 106, + kVarRedeclaredCatchBinding = 107, // If you add new values here, you'll also need to update Chromium's: // web_feature.mojom, use_counter_callback.cc, and enums.xml. V8 changes to @@ -8549,17 +8535,6 @@ class V8_EXPORT Isolate { void SetAbortOnUncaughtExceptionCallback( AbortOnUncaughtExceptionCallback callback); - /** - * This specifies the callback to be called when FinalizationRegistries - * are ready to be cleaned up and require FinalizationGroup::Cleanup() - * to be called in a future task. - */ - V8_DEPRECATED( - "FinalizationRegistry cleanup is automatic if " - "HostCleanupFinalizationGroupCallback is not set") - void SetHostCleanupFinalizationGroupCallback( - HostCleanupFinalizationGroupCallback callback); - /** * This specifies the callback called by the upcoming dynamic * import() language feature to load modules. @@ -9374,6 +9349,8 @@ class V8_EXPORT Isolate { void SetWasmLoadSourceMapCallback(WasmLoadSourceMapCallback callback); + void SetWasmSimdEnabledCallback(WasmSimdEnabledCallback callback); + /** * Check if V8 is dead and therefore unusable. This is the case after * fatal errors such as out-of-memory situations. @@ -9512,7 +9489,6 @@ class V8_EXPORT Isolate { internal::Address* GetDataFromSnapshotOnce(size_t index); void ReportExternalAllocationLimitReached(); - void CheckMemoryPressure(); }; class V8_EXPORT StartupData { @@ -9601,7 +9577,8 @@ class V8_EXPORT V8 { V8_INLINE static bool Initialize() { const int kBuildConfiguration = (internal::PointerCompressionIsEnabled() ? kPointerCompression : 0) | - (internal::SmiValuesAre31Bits() ? k31BitSmis : 0); + (internal::SmiValuesAre31Bits() ? k31BitSmis : 0) | + (internal::HeapSandboxIsEnabled() ? kHeapSandbox : 0); return Initialize(kBuildConfiguration); } @@ -9740,6 +9717,7 @@ class V8_EXPORT V8 { enum BuildConfigurationFeatures { kPointerCompression = 1 << 0, k31BitSmis = 1 << 1, + kHeapSandbox = 1 << 2, }; /** @@ -11381,7 +11359,9 @@ void* Object::GetAlignedPointerFromInternalField(int index) { instance_type == I::kJSApiObjectType || instance_type == I::kJSSpecialApiObjectType)) { int offset = I::kJSObjectHeaderSize + (I::kEmbedderDataSlotSize * index); - return I::ReadRawField(obj, offset); + internal::Isolate* isolate = I::GetIsolateForHeapSandbox(obj); + A value = I::ReadExternalPointerField(isolate, obj, offset); + return reinterpret_cast(value); } #endif return SlowGetAlignedPointerFromInternalField(index); @@ -11411,7 +11391,9 @@ String::ExternalStringResource* String::GetExternalStringResource() const { ExternalStringResource* result; if (I::IsExternalTwoByteString(I::GetInstanceType(obj))) { - void* value = I::ReadRawField(obj, I::kStringResourceOffset); + internal::Isolate* isolate = I::GetIsolateForHeapSandbox(obj); + A value = + I::ReadExternalPointerField(isolate, obj, I::kStringResourceOffset); result = reinterpret_cast(value); } else { result = GetExternalStringResourceSlow(); @@ -11433,8 +11415,10 @@ String::ExternalStringResourceBase* String::GetExternalStringResourceBase( ExternalStringResourceBase* resource; if (type == I::kExternalOneByteRepresentationTag || type == I::kExternalTwoByteRepresentationTag) { - void* value = I::ReadRawField(obj, I::kStringResourceOffset); - resource = static_cast(value); + internal::Isolate* isolate = I::GetIsolateForHeapSandbox(obj); + A value = + I::ReadExternalPointerField(isolate, obj, I::kStringResourceOffset); + resource = reinterpret_cast(value); } else { resource = GetExternalStringResourceBaseSlow(encoding_out); } @@ -11950,7 +11934,6 @@ MaybeLocal Isolate::GetDataFromSnapshotOnce(size_t index) { int64_t Isolate::AdjustAmountOfExternalAllocatedMemory( int64_t change_in_bytes) { typedef internal::Internals I; - constexpr int64_t kMemoryReducerActivationLimit = 32 * 1024 * 1024; int64_t* external_memory = reinterpret_cast( reinterpret_cast(this) + I::kExternalMemoryOffset); int64_t* external_memory_limit = reinterpret_cast( @@ -11973,14 +11956,6 @@ int64_t Isolate::AdjustAmountOfExternalAllocatedMemory( if (change_in_bytes <= 0) return *external_memory; - int64_t allocation_diff_since_last_mc = static_cast( - static_cast(*external_memory) - - static_cast(*external_memory_low_since_mc)); - // Only check memory pressure and potentially trigger GC if the amount of - // external memory increased. - if (allocation_diff_since_last_mc > kMemoryReducerActivationLimit) { - CheckMemoryPressure(); - } if (amount > *external_memory_limit) { ReportExternalAllocationLimitReached(); } @@ -12022,7 +11997,9 @@ void* Context::GetAlignedPointerFromEmbedderData(int index) { I::ReadTaggedPointerField(ctx, I::kNativeContextEmbedderDataOffset); int value_offset = I::kEmbedderDataArrayHeaderSize + (I::kEmbedderDataSlotSize * index); - return I::ReadRawField(embedder_data, value_offset); + internal::Isolate* isolate = I::GetIsolateForHeapSandbox(ctx); + return reinterpret_cast( + I::ReadExternalPointerField(isolate, embedder_data, value_offset)); #else return SlowGetAlignedPointerFromEmbedderData(index); #endif diff --git a/deps/v8/include/v8config.h b/deps/v8/include/v8config.h index 40d23c35c186e4..9825232d6a10ac 100644 --- a/deps/v8/include/v8config.h +++ b/deps/v8/include/v8config.h @@ -405,6 +405,15 @@ #endif +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 6) +# define V8_ENUM_DEPRECATED(message) +# define V8_ENUM_DEPRECATE_SOON(message) +#else +# define V8_ENUM_DEPRECATED(message) V8_DEPRECATED(message) +# define V8_ENUM_DEPRECATE_SOON(message) V8_DEPRECATE_SOON(message) +#endif + + // A macro to provide the compiler with branch prediction information. #if V8_HAS_BUILTIN_EXPECT # define V8_UNLIKELY(condition) (__builtin_expect(!!(condition), 0)) diff --git a/deps/v8/infra/mb/mb_config.pyl b/deps/v8/infra/mb/mb_config.pyl index 8677333c2f8cdb..d4abcf89eb15e9 100644 --- a/deps/v8/infra/mb/mb_config.pyl +++ b/deps/v8/infra/mb/mb_config.pyl @@ -78,6 +78,7 @@ # Sanitizers. 'V8 Linux64 ASAN': 'release_x64_asan', 'V8 Linux64 TSAN - builder': 'release_x64_tsan', + 'V8 Linux - arm64 - sim - CFI': 'release_simulate_arm64_cfi', 'V8 Linux - arm64 - sim - MSAN': 'release_simulate_arm64_msan', # Misc. 'V8 Linux gcc': 'release_x86_gcc', @@ -98,6 +99,7 @@ 'V8 Linux64 - gcov coverage': 'release_x64_gcc_coverage', 'V8 Linux - predictable': 'release_x86_predictable', 'V8 Linux - full debug': 'full_debug_x86', + 'V8 Mac64 - full debug': 'full_debug_x64', 'V8 Random Deopt Fuzzer - debug': 'debug_x64', }, 'client.v8.clusterfuzz': { @@ -251,6 +253,7 @@ 'v8_mac64_rel_ng': 'release_x64_trybot', 'v8_mac64_dbg': 'debug_x64', 'v8_mac64_dbg_ng': 'debug_x64', + 'v8_mac64_compile_full_dbg_ng': 'full_debug_x64', 'v8_mac64_asan_rel': 'release_x64_asan_no_lsan', 'v8_linux_arm_rel_ng': 'release_simulate_arm_trybot', 'v8_linux_arm_lite_rel_ng': 'release_simulate_arm_lite_trybot', @@ -258,6 +261,7 @@ 'v8_linux_arm_armv8a_rel': 'release_simulate_arm_trybot', 'v8_linux_arm_armv8a_dbg': 'debug_simulate_arm', 'v8_linux_arm64_rel_ng': 'release_simulate_arm64_trybot', + 'v8_linux_arm64_cfi_rel_ng' : 'release_simulate_arm64_cfi', 'v8_linux_arm64_dbg': 'debug_simulate_arm64', 'v8_linux_arm64_gc_stress_dbg': 'debug_simulate_arm64', 'v8_linux_mipsel_compile_rel': 'release_simulate_mipsel', @@ -363,6 +367,8 @@ 'release_trybot', 'simulate_arm', 'v8_enable_lite_mode'], 'release_simulate_arm64': [ 'release_bot', 'simulate_arm64'], + 'release_simulate_arm64_cfi': [ + 'release_bot', 'simulate_arm64', 'v8_control_flow_integrity'], 'release_simulate_arm64_pointer_compression': [ # TODO(v8:v7703): Make pointer compression bots testing non pointer # compression mode while pointer compression is temporarily enabled @@ -452,7 +458,7 @@ 'release_x64_fuchsia_trybot': [ 'release_trybot', 'x64', 'fuchsia'], 'release_x64_gcc_coverage': [ - 'release_bot', 'x64', 'coverage', 'gcc', 'no_custom_libcxx', + 'release_bot_no_goma', 'x64', 'coverage', 'gcc', 'no_custom_libcxx', 'no_sysroot'], 'release_x64_ios_simulator': [ 'release_bot', 'x64', 'ios_simulator'], @@ -502,7 +508,7 @@ 'debug_x64_fuchsia': [ 'debug_bot', 'x64', 'fuchsia'], 'debug_x64_gcc': [ - 'debug_bot', 'x64', 'gcc', 'v8_check_header_includes'], + 'debug_bot_no_goma', 'x64', 'gcc', 'v8_check_header_includes'], 'debug_x64_header_includes': [ 'debug_bot', 'x64', 'v8_check_header_includes'], 'debug_x64_minimal_symbols': [ @@ -513,6 +519,8 @@ 'debug_trybot', 'x64'], 'debug_x64_trybot_custom': [ 'debug_trybot', 'x64', 'v8_snapshot_custom'], + 'full_debug_x64': [ + 'debug_bot', 'x64', 'v8_full_debug'], # Debug configs for x86. 'debug_x86': [ @@ -532,9 +540,9 @@ 'release_x86': [ 'release_bot', 'x86'], 'release_x86_gcc': [ - 'release_bot', 'x86', 'gcc', 'v8_check_header_includes'], + 'release_bot_no_goma', 'x86', 'gcc', 'v8_check_header_includes'], 'release_x86_gcc_minimal_symbols': [ - 'release_bot', 'x86', 'gcc', 'minimal_symbols', + 'release_bot_no_goma', 'x86', 'gcc', 'minimal_symbols', 'v8_check_header_includes'], 'release_x86_gcmole': [ 'release_bot', 'x86', 'gcmole'], @@ -623,6 +631,12 @@ 'v8_optimized_debug'], }, + 'debug_bot_no_goma': { + 'mixins': [ + 'debug', 'shared', 'no_goma', 'v8_enable_slow_dchecks', + 'v8_optimized_debug'], + }, + 'debug_trybot': { 'mixins': ['debug_bot', 'minimal_symbols'], }, @@ -776,6 +790,10 @@ 'gn_args': 'v8_correctness_fuzzer=true v8_multi_arch_build=true', }, + 'v8_control_flow_integrity' : { + 'gn_args': 'v8_control_flow_integrity=true', + }, + 'v8_enable_lite_mode': { 'gn_args': 'v8_enable_lite_mode=true', }, diff --git a/deps/v8/infra/testing/builders.pyl b/deps/v8/infra/testing/builders.pyl index 6c3379b5cbc34e..72f739487ccec3 100644 --- a/deps/v8/infra/testing/builders.pyl +++ b/deps/v8/infra/testing/builders.pyl @@ -310,12 +310,15 @@ 'os': 'Ubuntu-16.04', }, 'tests': [ + # Infra staging. + {'name': 'v8testing', 'variant': 'infra_staging', 'shards': 2}, + # Native context independent code. + {'name': 'v8testing', 'variant': 'nci'}, # Stress sampling. {'name': 'mjsunit', 'variant': 'stress_sampling'}, {'name': 'webkit', 'variant': 'stress_sampling'}, - # Infra staging. - {'name': 'test262', 'variant': 'infra_staging', 'shards': 2}, - {'name': 'v8testing', 'variant': 'infra_staging', 'shards': 2}, + # Stress snapshot. + {'name': 'mjsunit', 'variant': 'stress_snapshot'}, ], }, 'v8_linux64_msan_rel': { @@ -481,6 +484,15 @@ {'name': 'v8testing', 'variant': 'trusted', 'shards': 5}, ], }, + 'v8_linux_arm64_cfi_rel_ng_triggered': { + 'swarming_dimensions' : { + 'os': 'Ubuntu-16.04', + }, + 'tests': [ + {'name': 'test262', 'variant': 'default', 'shards': 3}, + {'name': 'v8testing', 'shards': 4}, + ], + }, 'v8_linux64_arm64_pointer_compression_rel_ng_triggered': { 'swarming_dimensions' : { 'os': 'Ubuntu-16.04', @@ -679,6 +691,15 @@ }, ], }, + 'V8 Linux - arm64 - sim - CFI': { + 'swarming_dimensions': { + 'os': 'Ubuntu-16.04', + }, + 'tests': [ + {'name': 'test262', 'variant': 'default', 'shards': 3}, + {'name': 'v8testing', 'shards': 4}, + ], + }, 'V8 Linux - arm64 - sim - MSAN': { 'swarming_dimensions': { 'os': 'Ubuntu-16.04', @@ -941,9 +962,13 @@ 'tests': [ # Infra staging. {'name': 'v8testing', 'variant': 'infra_staging', 'shards': 2}, + # Native context independent code. + {'name': 'v8testing', 'variant': 'nci'}, # Stress sampling. - {'name': 'mjsunit', 'variant': 'stress_sampling', 'shards': 1}, - {'name': 'webkit', 'variant': 'stress_sampling', 'shards': 1}, + {'name': 'mjsunit', 'variant': 'stress_sampling'}, + {'name': 'webkit', 'variant': 'stress_sampling'}, + # Stress snapshot. + {'name': 'mjsunit', 'variant': 'stress_snapshot'}, ], }, 'V8 Linux64 - debug - perfetto': { @@ -965,10 +990,14 @@ }, 'tests': [ # Infra staging. - {'name': 'v8testing', 'variant': 'infra_staging', 'shards': 1}, + {'name': 'v8testing', 'variant': 'infra_staging'}, + # Native context independent code. + {'name': 'v8testing', 'variant': 'nci'}, # Stress sampling. - {'name': 'mjsunit', 'variant': 'stress_sampling', 'shards': 1}, - {'name': 'webkit', 'variant': 'stress_sampling', 'shards': 1}, + {'name': 'mjsunit', 'variant': 'stress_sampling'}, + {'name': 'webkit', 'variant': 'stress_sampling'}, + # Stress snapshot. + {'name': 'mjsunit', 'variant': 'stress_snapshot'}, ], }, 'V8 Linux64 - gcov coverage': { diff --git a/deps/v8/src/DEPS b/deps/v8/src/DEPS index 772ad53b326066..abea95558da2c2 100644 --- a/deps/v8/src/DEPS +++ b/deps/v8/src/DEPS @@ -12,15 +12,22 @@ include_rules = [ "+src/heap/embedder-tracing.h", "+src/heap/factory.h", "+src/heap/factory-inl.h", + # TODO(v8:10496): Don't expose so much (through transitive includes) outside + # of heap/. "+src/heap/heap.h", "+src/heap/heap-inl.h", "+src/heap/heap-write-barrier-inl.h", "+src/heap/heap-write-barrier.h", "+src/heap/local-heap.h", + # TODO(v8:10496): Don't expose memory chunk outside of heap/. + "+src/heap/memory-chunk.h", + "+src/heap/memory-chunk-inl.h", "+src/heap/off-thread-factory-inl.h", "+src/heap/off-thread-factory.h", + "+src/heap/off-thread-heap.h", "+src/heap/read-only-heap-inl.h", "+src/heap/read-only-heap.h", + "+src/heap/safepoint.h", "-src/inspector", "-src/interpreter", "+src/interpreter/bytecode-array-accessor.h", @@ -54,4 +61,7 @@ specific_include_rules = { "+include/libplatform/v8-tracing.h", "+perfetto/tracing.h" ], + "builtins-trace\.cc": [ + "+protos/perfetto", + ], } diff --git a/deps/v8/src/api/api-arguments.h b/deps/v8/src/api/api-arguments.h index 794681b71d0e60..18690b5db2b6d7 100644 --- a/deps/v8/src/api/api-arguments.h +++ b/deps/v8/src/api/api-arguments.h @@ -160,11 +160,9 @@ class FunctionCallbackArguments static const int kIsolateIndex = T::kIsolateIndex; static const int kNewTargetIndex = T::kNewTargetIndex; - FunctionCallbackArguments(internal::Isolate* isolate, internal::Object data, - internal::HeapObject callee, - internal::Object holder, - internal::HeapObject new_target, - internal::Address* argv, int argc); + FunctionCallbackArguments(Isolate* isolate, Object data, HeapObject callee, + Object holder, HeapObject new_target, Address* argv, + int argc); /* * The following Call function wraps the calling of all callbacks to handle diff --git a/deps/v8/src/api/api-inl.h b/deps/v8/src/api/api-inl.h index 0d2ad2f8a0f8ce..f686424286d8fe 100644 --- a/deps/v8/src/api/api-inl.h +++ b/deps/v8/src/api/api-inl.h @@ -86,7 +86,6 @@ MAKE_TO_LOCAL(ToLocal, JSArrayBufferView, ArrayBufferView) MAKE_TO_LOCAL(ToLocal, JSDataView, DataView) MAKE_TO_LOCAL(ToLocal, JSTypedArray, TypedArray) MAKE_TO_LOCAL(ToLocalShared, JSArrayBuffer, SharedArrayBuffer) -MAKE_TO_LOCAL(ToLocal, JSFinalizationRegistry, FinalizationGroup) TYPED_ARRAYS(MAKE_TO_LOCAL_TYPED_ARRAY) diff --git a/deps/v8/src/api/api.cc b/deps/v8/src/api/api.cc index 1b59f2cf6457b2..93780bceec4186 100644 --- a/deps/v8/src/api/api.cc +++ b/deps/v8/src/api/api.cc @@ -28,6 +28,7 @@ #include "src/codegen/compiler.h" #include "src/codegen/cpu-features.h" #include "src/common/assert-scope.h" +#include "src/common/external-pointer.h" #include "src/common/globals.h" #include "src/compiler-dispatcher/compiler-dispatcher.h" #include "src/date/date.h" @@ -96,10 +97,8 @@ #include "src/regexp/regexp-utils.h" #include "src/runtime/runtime.h" #include "src/snapshot/code-serializer.h" -#include "src/snapshot/partial-serializer.h" -#include "src/snapshot/read-only-serializer.h" #include "src/snapshot/snapshot.h" -#include "src/snapshot/startup-serializer.h" +#include "src/snapshot/startup-serializer.h" // For SerializedHandleChecker. #include "src/strings/char-predicates-inl.h" #include "src/strings/string-hasher.h" #include "src/strings/unicode-inl.h" @@ -328,6 +327,7 @@ class CallDepthScope { bool CheckKeptObjectsClearedAfterMicrotaskCheckpoint( i::MicrotaskQueue* microtask_queue) { bool did_perform_microtask_checkpoint = + isolate_->thread_local_top()->CallDepthIsZero() && do_callback && microtask_queue && microtask_queue->microtasks_policy() == MicrotasksPolicy::kAuto; return !did_perform_microtask_checkpoint || @@ -598,7 +598,6 @@ SnapshotCreator::SnapshotCreator(Isolate* isolate, const intptr_t* external_references, StartupData* existing_snapshot) { SnapshotCreatorData* data = new SnapshotCreatorData(isolate); - data->isolate_ = isolate; i::Isolate* internal_isolate = reinterpret_cast(isolate); internal_isolate->set_array_buffer_allocator(&data->allocator_); internal_isolate->set_api_external_references(external_references); @@ -733,8 +732,11 @@ StartupData SnapshotCreator::CreateBlob( DCHECK(!data->created_); DCHECK(!data->default_context_.IsEmpty()); - int num_additional_contexts = static_cast(data->contexts_.Size()); + const int num_additional_contexts = static_cast(data->contexts_.Size()); + const int num_contexts = num_additional_contexts + 1; // The default context. + // Create and store lists of embedder-provided data needed during + // serialization. { i::HandleScope scope(isolate); // Convert list of context-independent data to FixedArray. @@ -773,48 +775,15 @@ StartupData SnapshotCreator::CreateBlob( isolate->heap()->CompactWeakArrayLists(internal::AllocationType::kOld); } - if (function_code_handling == FunctionCodeHandling::kClear) { - // Clear out re-compilable data from all shared function infos. Any - // JSFunctions using these SFIs will have their code pointers reset by the - // partial serializer. - // - // We have to iterate the heap and collect handles to each clearable SFI, - // before we disable allocation, since we have to allocate UncompiledDatas - // to be able to recompile them. - // - // Compiled irregexp code is also flushed by collecting and clearing any - // seen JSRegExp objects. - i::HandleScope scope(isolate); - std::vector> sfis_to_clear; - - { // Heap allocation is disallowed within this scope. - i::HeapObjectIterator heap_iterator(isolate->heap()); - for (i::HeapObject current_obj = heap_iterator.Next(); - !current_obj.is_null(); current_obj = heap_iterator.Next()) { - if (current_obj.IsSharedFunctionInfo()) { - i::SharedFunctionInfo shared = - i::SharedFunctionInfo::cast(current_obj); - if (shared.CanDiscardCompiled()) { - sfis_to_clear.emplace_back(shared, isolate); - } - } else if (current_obj.IsJSRegExp()) { - i::JSRegExp regexp = i::JSRegExp::cast(current_obj); - if (regexp.HasCompiledCode()) { - regexp.DiscardCompiledCodeForSerialization(); - } - } - } - } - - // Must happen after heap iteration since SFI::DiscardCompiled may allocate. - for (i::Handle shared : sfis_to_clear) { - i::SharedFunctionInfo::DiscardCompiled(isolate, shared); - } - } + i::Snapshot::ClearReconstructableDataForSerialization( + isolate, function_code_handling == FunctionCodeHandling::kClear); i::DisallowHeapAllocation no_gc_from_here_on; - int num_contexts = num_additional_contexts + 1; + // Create a vector with all contexts and clear associated Persistent fields. + // Note these contexts may be dead after calling Clear(), but will not be + // collected until serialization completes and the DisallowHeapAllocation + // scope above goes out of scope. std::vector contexts; contexts.reserve(num_contexts); { @@ -834,82 +803,19 @@ StartupData SnapshotCreator::CreateBlob( i::SerializedHandleChecker handle_checker(isolate, &contexts); CHECK(handle_checker.CheckGlobalAndEternalHandles()); - i::HeapObjectIterator heap_iterator(isolate->heap()); - for (i::HeapObject current_obj = heap_iterator.Next(); !current_obj.is_null(); - current_obj = heap_iterator.Next()) { - if (current_obj.IsJSFunction()) { - i::JSFunction fun = i::JSFunction::cast(current_obj); - - // Complete in-object slack tracking for all functions. - fun.CompleteInobjectSlackTrackingIfActive(); - - // Also, clear out feedback vectors, or any optimized code. - // Note that checking for fun.IsOptimized() || fun.IsInterpreted() is not - // sufficient because the function can have a feedback vector even if it - // is not compiled (e.g. when the bytecode was flushed). On the other - // hand, only checking for the feedback vector is not sufficient because - // there can be multiple functions sharing the same feedback vector. So we - // need all these checks. - if (fun.IsOptimized() || fun.IsInterpreted() || - !fun.raw_feedback_cell().value().IsUndefined()) { - fun.raw_feedback_cell().set_value( - i::ReadOnlyRoots(isolate).undefined_value()); - fun.set_code(isolate->builtins()->builtin(i::Builtins::kCompileLazy)); - } - if (function_code_handling == FunctionCodeHandling::kClear) { - DCHECK(fun.shared().HasWasmExportedFunctionData() || - fun.shared().HasBuiltinId() || fun.shared().IsApiFunction() || - fun.shared().HasUncompiledDataWithoutPreparseData()); - } - } + // Create a vector with all embedder fields serializers. + std::vector embedder_fields_serializers; + embedder_fields_serializers.reserve(num_contexts); + embedder_fields_serializers.push_back( + data->default_embedder_fields_serializer_); + for (int i = 0; i < num_additional_contexts; i++) { + embedder_fields_serializers.push_back( + data->embedder_fields_serializers_[i]); } - i::ReadOnlySerializer read_only_serializer(isolate); - read_only_serializer.SerializeReadOnlyRoots(); - - i::StartupSerializer startup_serializer(isolate, &read_only_serializer); - startup_serializer.SerializeStrongReferences(); - - // Serialize each context with a new partial serializer. - std::vector context_snapshots; - context_snapshots.reserve(num_contexts); - - // TODO(6593): generalize rehashing, and remove this flag. - bool can_be_rehashed = true; - - for (int i = 0; i < num_contexts; i++) { - bool is_default_context = i == 0; - i::PartialSerializer partial_serializer( - isolate, &startup_serializer, - is_default_context ? data->default_embedder_fields_serializer_ - : data->embedder_fields_serializers_[i - 1]); - partial_serializer.Serialize(&contexts[i], !is_default_context); - can_be_rehashed = can_be_rehashed && partial_serializer.can_be_rehashed(); - context_snapshots.push_back(new i::SnapshotData(&partial_serializer)); - } - - startup_serializer.SerializeWeakReferencesAndDeferred(); - can_be_rehashed = can_be_rehashed && startup_serializer.can_be_rehashed(); - - startup_serializer.CheckNoDirtyFinalizationRegistries(); - - read_only_serializer.FinalizeSerialization(); - can_be_rehashed = can_be_rehashed && read_only_serializer.can_be_rehashed(); - - i::SnapshotData read_only_snapshot(&read_only_serializer); - i::SnapshotData startup_snapshot(&startup_serializer); - StartupData result = - i::Snapshot::CreateSnapshotBlob(&startup_snapshot, &read_only_snapshot, - context_snapshots, can_be_rehashed); - - // Delete heap-allocated context snapshot instances. - for (const auto context_snapshot : context_snapshots) { - delete context_snapshot; - } data->created_ = true; - - DCHECK(i::Snapshot::VerifyChecksum(&result)); - return result; + return i::Snapshot::Create(isolate, &contexts, embedder_fields_serializers, + no_gc_from_here_on); } bool StartupData::CanBeRehashed() const { @@ -1341,21 +1247,25 @@ void Context::SetEmbedderData(int index, v8::Local value) { void* Context::SlowGetAlignedPointerFromEmbedderData(int index) { const char* location = "v8::Context::GetAlignedPointerFromEmbedderData()"; - HandleScope handle_scope(GetIsolate()); + i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); + i::HandleScope handle_scope(isolate); i::Handle data = EmbedderDataFor(this, index, false, location); if (data.is_null()) return nullptr; void* result; - Utils::ApiCheck(i::EmbedderDataSlot(*data, index).ToAlignedPointer(&result), - location, "Pointer is not aligned"); + Utils::ApiCheck( + i::EmbedderDataSlot(*data, index).ToAlignedPointer(isolate, &result), + location, "Pointer is not aligned"); return result; } void Context::SetAlignedPointerInEmbedderData(int index, void* value) { const char* location = "v8::Context::SetAlignedPointerInEmbedderData()"; + i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); i::Handle data = EmbedderDataFor(this, index, true, location); - bool ok = i::EmbedderDataSlot(*data, index).store_aligned_pointer(value); + bool ok = + i::EmbedderDataSlot(*data, index).store_aligned_pointer(isolate, value); Utils::ApiCheck(ok, location, "Pointer is not aligned"); DCHECK_EQ(value, GetAlignedPointerFromEmbedderData(index)); } @@ -3776,6 +3686,12 @@ void v8::debug::AccessorPair::CheckCast(Value* that) { "Value is not a debug::AccessorPair"); } +void v8::debug::WasmValue::CheckCast(Value* that) { + i::Handle obj = Utils::OpenHandle(that); + Utils::ApiCheck(obj->IsWasmValue(), "v8::WasmValue::Cast", + "Value is not a debug::WasmValue"); +} + v8::BackingStore::~BackingStore() { auto i_this = reinterpret_cast(this); i_this->~BackingStore(); // manually call internal destructor @@ -5494,7 +5410,9 @@ String::ExternalStringResource* String::GetExternalStringResourceSlow() const { } if (i::StringShape(str).IsExternalTwoByte()) { - void* value = I::ReadRawField(str.ptr(), I::kStringResourceOffset); + internal::Isolate* isolate = I::GetIsolateForHeapSandbox(str.ptr()); + internal::Address value = I::ReadExternalPointerField( + isolate, str.ptr(), I::kStringResourceOffset); return reinterpret_cast(value); } return nullptr; @@ -5516,8 +5434,10 @@ String::ExternalStringResourceBase* String::GetExternalStringResourceBaseSlow( *encoding_out = static_cast(type & I::kStringEncodingMask); if (i::StringShape(str).IsExternalOneByte() || i::StringShape(str).IsExternalTwoByte()) { - void* value = I::ReadRawField(string, I::kStringResourceOffset); - resource = static_cast(value); + internal::Isolate* isolate = I::GetIsolateForHeapSandbox(string); + internal::Address value = + I::ReadExternalPointerField(isolate, string, I::kStringResourceOffset); + resource = reinterpret_cast(value); } return resource; } @@ -5635,7 +5555,7 @@ void* v8::Object::SlowGetAlignedPointerFromInternalField(int index) { if (!InternalFieldOK(obj, index, location)) return nullptr; void* result; Utils::ApiCheck(i::EmbedderDataSlot(i::JSObject::cast(*obj), index) - .ToAlignedPointer(&result), + .ToAlignedPointer(obj->GetIsolate(), &result), location, "Unaligned pointer"); return result; } @@ -5645,7 +5565,7 @@ void v8::Object::SetAlignedPointerInInternalField(int index, void* value) { const char* location = "v8::Object::SetAlignedPointerInInternalField()"; if (!InternalFieldOK(obj, index, location)) return; Utils::ApiCheck(i::EmbedderDataSlot(i::JSObject::cast(*obj), index) - .store_aligned_pointer(value), + .store_aligned_pointer(obj->GetIsolate(), value), location, "Unaligned pointer"); DCHECK_EQ(value, GetAlignedPointerFromInternalField(index)); } @@ -5664,9 +5584,9 @@ void v8::Object::SetAlignedPointerInInternalFields(int argc, int indices[], return; } void* value = values[i]; - Utils::ApiCheck( - i::EmbedderDataSlot(js_obj, index).store_aligned_pointer(value), - location, "Unaligned pointer"); + Utils::ApiCheck(i::EmbedderDataSlot(js_obj, index) + .store_aligned_pointer(obj->GetIsolate(), value), + location, "Unaligned pointer"); DCHECK_EQ(value, GetAlignedPointerFromInternalField(index)); } } @@ -5707,6 +5627,15 @@ bool v8::V8::Initialize(const int build_config) { kEmbedderSmiValueSize, internal::kSmiValueSize); } + const bool kEmbedderHeapSandbox = (build_config & kHeapSandbox) != 0; + if (kEmbedderHeapSandbox != V8_HEAP_SANDBOX_BOOL) { + FATAL( + "Embedder-vs-V8 build configuration mismatch. On embedder side " + "heap sandbox is %s while on V8 side it's %s.", + kEmbedderHeapSandbox ? "ENABLED" : "DISABLED", + V8_HEAP_SANDBOX_BOOL ? "ENABLED" : "DISABLED"); + } + i::V8::Initialize(); return true; } @@ -5824,17 +5753,7 @@ void v8::V8::InitializeExternalStartupDataFromFile(const char* snapshot_blob) { const char* v8::V8::GetVersion() { return i::Version::GetVersion(); } void V8::GetSharedMemoryStatistics(SharedMemoryStatistics* statistics) { -#ifdef V8_SHARED_RO_HEAP - i::ReadOnlySpace* ro_space = i::ReadOnlyHeap::Instance()->read_only_space(); - statistics->read_only_space_size_ = ro_space->CommittedMemory(); - statistics->read_only_space_used_size_ = ro_space->SizeOfObjects(); - statistics->read_only_space_physical_size_ = - ro_space->CommittedPhysicalMemory(); -#else - statistics->read_only_space_size_ = 0; - statistics->read_only_space_used_size_ = 0; - statistics->read_only_space_physical_size_ = 0; -#endif // V8_SHARED_RO_HEAP + i::ReadOnlyHeap::PopulateReadOnlySpaceStatistics(statistics); } template @@ -7247,8 +7166,10 @@ MaybeLocal Proxy::New(Local context, Local local_target, } CompiledWasmModule::CompiledWasmModule( - std::shared_ptr native_module) - : native_module_(std::move(native_module)) { + std::shared_ptr native_module, + const char* source_url, size_t url_length) + : native_module_(std::move(native_module)), + source_url_(source_url, url_length) { CHECK_NOT_NULL(native_module_); } @@ -7269,7 +7190,13 @@ MemorySpan CompiledWasmModule::GetWireBytesRef() { CompiledWasmModule WasmModuleObject::GetCompiledModule() { i::Handle obj = i::Handle::cast(Utils::OpenHandle(this)); - return Utils::Convert(obj->shared_native_module()); + auto source_url = i::String::cast(obj->script().source_url()); + int length; + std::unique_ptr cstring = source_url.ToCString( + i::DISALLOW_NULLS, i::FAST_STRING_TRAVERSAL, &length); + i::Handle url(source_url, obj->GetIsolate()); + return CompiledWasmModule(std::move(obj->shared_native_module()), + cstring.get(), length); } MaybeLocal WasmModuleObject::FromCompiledModule( @@ -7277,7 +7204,8 @@ MaybeLocal WasmModuleObject::FromCompiledModule( i::Isolate* i_isolate = reinterpret_cast(isolate); i::Handle module_object = i_isolate->wasm_engine()->ImportNativeModule( - i_isolate, Utils::Open(compiled_module)); + i_isolate, compiled_module.native_module_, + i::VectorOf(compiled_module.source_url())); return Local::Cast( Utils::ToLocal(i::Handle::cast(module_object))); } @@ -8059,12 +7987,6 @@ void Isolate::ReportExternalAllocationLimitReached() { heap->ReportExternalMemoryPressure(); } -void Isolate::CheckMemoryPressure() { - i::Heap* heap = reinterpret_cast(this)->heap(); - if (heap->gc_state() != i::Heap::NOT_IN_GC) return; - heap->CheckMemoryPressure(); -} - HeapProfiler* Isolate::GetHeapProfiler() { i::HeapProfiler* heap_profiler = reinterpret_cast(this)->heap_profiler(); @@ -8373,29 +8295,6 @@ void Isolate::SetAbortOnUncaughtExceptionCallback( isolate->SetAbortOnUncaughtExceptionCallback(callback); } -void Isolate::SetHostCleanupFinalizationGroupCallback( - HostCleanupFinalizationGroupCallback callback) { - i::Isolate* isolate = reinterpret_cast(this); - isolate->SetHostCleanupFinalizationGroupCallback(callback); -} - -Maybe FinalizationGroup::Cleanup( - Local finalization_group) { - i::Handle fr = - Utils::OpenHandle(*finalization_group); - i::Isolate* isolate = fr->native_context().GetIsolate(); - i::Handle i_context(fr->native_context(), isolate); - Local context = Utils::ToLocal(i_context); - ENTER_V8(isolate, context, FinalizationGroup, Cleanup, Nothing(), - i::HandleScope); - i::Handle callback(fr->cleanup(), isolate); - fr->set_scheduled_for_cleanup(false); - has_pending_exception = - i::JSFinalizationRegistry::Cleanup(isolate, fr, callback).IsNothing(); - RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool); - return Just(true); -} - void Isolate::SetHostImportModuleDynamicallyCallback( HostImportModuleDynamicallyCallback callback) { i::Isolate* isolate = reinterpret_cast(this); @@ -8730,11 +8629,7 @@ void Isolate::EnqueueMicrotask(Local v8_function) { void Isolate::EnqueueMicrotask(MicrotaskCallback callback, void* data) { i::Isolate* isolate = reinterpret_cast(this); - i::HandleScope scope(isolate); - i::Handle microtask = isolate->factory()->NewCallbackTask( - isolate->factory()->NewForeign(reinterpret_cast(callback)), - isolate->factory()->NewForeign(reinterpret_cast(data))); - isolate->default_microtask_queue()->EnqueueMicrotask(*microtask); + isolate->default_microtask_queue()->EnqueueMicrotask(this, callback, data); } void Isolate::SetMicrotasksPolicy(MicrotasksPolicy policy) { @@ -9011,6 +8906,9 @@ CALLBACK_SETTER(WasmThreadsEnabledCallback, WasmThreadsEnabledCallback, CALLBACK_SETTER(WasmLoadSourceMapCallback, WasmLoadSourceMapCallback, wasm_load_source_map_callback) +CALLBACK_SETTER(WasmSimdEnabledCallback, WasmSimdEnabledCallback, + wasm_simd_enabled_callback) + void Isolate::AddNearHeapLimitCallback(v8::NearHeapLimitCallback callback, void* data) { i::Isolate* isolate = reinterpret_cast(this); @@ -9138,6 +9036,7 @@ void v8::Isolate::LocaleConfigurationChangeNotification() { #ifdef V8_INTL_SUPPORT i_isolate->ResetDefaultLocale(); + i_isolate->ClearCachedIcuObjects(); #endif // V8_INTL_SUPPORT } @@ -9256,6 +9155,9 @@ DEFINE_ERROR(RangeError, range_error) DEFINE_ERROR(ReferenceError, reference_error) DEFINE_ERROR(SyntaxError, syntax_error) DEFINE_ERROR(TypeError, type_error) +DEFINE_ERROR(WasmCompileError, wasm_compile_error) +DEFINE_ERROR(WasmLinkError, wasm_link_error) +DEFINE_ERROR(WasmRuntimeError, wasm_runtime_error) DEFINE_ERROR(Error, error) #undef DEFINE_ERROR @@ -9767,6 +9669,37 @@ debug::WasmScript* debug::WasmScript::Cast(debug::Script* script) { return static_cast(script); } +debug::WasmScript::DebugSymbolsType debug::WasmScript::GetDebugSymbolType() + const { + i::Handle script = Utils::OpenHandle(this); + DCHECK_EQ(i::Script::TYPE_WASM, script->type()); + switch (script->wasm_native_module()->module()->debug_symbols.type) { + case i::wasm::WasmDebugSymbols::Type::None: + return debug::WasmScript::DebugSymbolsType::None; + case i::wasm::WasmDebugSymbols::Type::EmbeddedDWARF: + return debug::WasmScript::DebugSymbolsType::EmbeddedDWARF; + case i::wasm::WasmDebugSymbols::Type::ExternalDWARF: + return debug::WasmScript::DebugSymbolsType::ExternalDWARF; + case i::wasm::WasmDebugSymbols::Type::SourceMap: + return debug::WasmScript::DebugSymbolsType::SourceMap; + } +} + +MemorySpan debug::WasmScript::ExternalSymbolsURL() const { + i::Handle script = Utils::OpenHandle(this); + DCHECK_EQ(i::Script::TYPE_WASM, script->type()); + + const i::wasm::WasmDebugSymbols& symbols = + script->wasm_native_module()->module()->debug_symbols; + if (symbols.external_url.is_empty()) return {}; + + internal::wasm::ModuleWireBytes wire_bytes( + script->wasm_native_module()->wire_bytes()); + i::wasm::WasmName external_url = + wire_bytes.GetNameOrNull(symbols.external_url); + return {external_url.data(), external_url.size()}; +} + int debug::WasmScript::NumFunctions() const { i::DisallowHeapAllocation no_gc; i::Handle script = Utils::OpenHandle(this); @@ -10365,6 +10298,51 @@ bool debug::AccessorPair::IsAccessorPair(Local that) { return obj->IsAccessorPair(); } +int debug::WasmValue::value_type() { + i::Handle obj = Utils::OpenHandle(this); + return obj->value_type(); +} + +v8::Local debug::WasmValue::bytes() { + i::Handle obj = Utils::OpenHandle(this); + // Should only be called on i32, i64, f32, f64, s128. + DCHECK_GE(1, obj->value_type()); + DCHECK_LE(5, obj->value_type()); + + i::Isolate* isolate = obj->GetIsolate(); + i::Handle bytes_or_ref(obj->bytes_or_ref(), isolate); + i::Handle bytes(i::Handle::cast(bytes_or_ref)); + + int length = bytes->length(); + + i::Handle fa = isolate->factory()->NewFixedArray(length); + i::Handle arr = obj->GetIsolate()->factory()->NewJSArray( + i::PACKED_SMI_ELEMENTS, length, length); + i::JSArray::SetContent(arr, fa); + + for (int i = 0; i < length; i++) { + fa->set(i, i::Smi::FromInt(bytes->get(i))); + } + + return Utils::ToLocal(arr); +} + +v8::Local debug::WasmValue::ref() { + i::Handle obj = Utils::OpenHandle(this); + // Should only be called on anyref. + DCHECK_EQ(6, obj->value_type()); + + i::Isolate* isolate = obj->GetIsolate(); + i::Handle bytes_or_ref(obj->bytes_or_ref(), isolate); + + return Utils::ToLocal(bytes_or_ref); +} + +bool debug::WasmValue::IsWasmValue(Local that) { + i::Handle obj = Utils::OpenHandle(*that); + return obj->IsWasmValue(); +} + MaybeLocal debug::GetMessageFromPromise(Local p) { i::Handle promise = Utils::OpenHandle(*p); i::Isolate* isolate = promise->GetIsolate(); @@ -11174,8 +11152,11 @@ void InvokeFinalizationRegistryCleanupFromTask( Local api_context = Utils::ToLocal(context); CallDepthScope call_depth_scope(isolate, api_context); VMState state(isolate); - if (JSFinalizationRegistry::Cleanup(isolate, finalization_registry, callback) - .IsNothing()) { + Handle argv[] = {callback}; + if (Execution::CallBuiltin(isolate, + isolate->finalization_registry_cleanup_some(), + finalization_registry, arraysize(argv), argv) + .is_null()) { call_depth_scope.Escape(); } } diff --git a/deps/v8/src/api/api.h b/deps/v8/src/api/api.h index 4c383d3e43caf3..ad879657c99247 100644 --- a/deps/v8/src/api/api.h +++ b/deps/v8/src/api/api.h @@ -33,6 +33,7 @@ namespace debug { class AccessorPair; class GeneratorObject; class Script; +class WasmValue; class WeakMap; } // namespace debug @@ -93,7 +94,6 @@ class RegisteredExtension { V(Data, Object) \ V(RegExp, JSRegExp) \ V(Object, JSReceiver) \ - V(FinalizationGroup, JSFinalizationRegistry) \ V(Array, JSArray) \ V(Map, JSMap) \ V(Set, JSSet) \ @@ -129,6 +129,7 @@ class RegisteredExtension { V(debug::Script, Script) \ V(debug::WeakMap, JSWeakMap) \ V(debug::AccessorPair, AccessorPair) \ + V(debug::WasmValue, WasmValue) \ V(Promise, JSPromise) \ V(Primitive, Object) \ V(PrimitiveArray, FixedArray) \ @@ -205,8 +206,6 @@ class Utils { v8::internal::Handle obj); static inline Local ToLocalBigUint64Array( v8::internal::Handle obj); - static inline Local ToLocal( - v8::internal::Handle obj); static inline Local ToLocalShared( v8::internal::Handle obj); @@ -275,16 +274,6 @@ class Utils { return OpenHandle(*handle); } - static inline CompiledWasmModule Convert( - std::shared_ptr native_module) { - return CompiledWasmModule{std::move(native_module)}; - } - - static inline const std::shared_ptr& Open( - const CompiledWasmModule& compiled_module) { - return compiled_module.native_module_; - } - private: static void ReportApiFailure(const char* location, const char* message); }; diff --git a/deps/v8/src/asmjs/asm-js.cc b/deps/v8/src/asmjs/asm-js.cc index 5a6846c33f4e5c..17bf39c8538722 100644 --- a/deps/v8/src/asmjs/asm-js.cc +++ b/deps/v8/src/asmjs/asm-js.cc @@ -187,7 +187,8 @@ class AsmJsCompilationJob final : public UnoptimizedCompilationJob { explicit AsmJsCompilationJob(ParseInfo* parse_info, FunctionLiteral* literal, AccountingAllocator* allocator) : UnoptimizedCompilationJob(parse_info->stack_limit(), parse_info, - &compilation_info_), + &compilation_info_, + CanOffThreadFinalize::kNo), allocator_(allocator), zone_(allocator, ZONE_NAME), compilation_info_(&zone_, parse_info, literal), @@ -223,7 +224,7 @@ class AsmJsCompilationJob final : public UnoptimizedCompilationJob { UnoptimizedCompilationJob::Status AsmJsCompilationJob::ExecuteJobImpl() { // Step 1: Translate asm.js module to WebAssembly module. - Zone* compile_zone = compilation_info()->zone(); + Zone* compile_zone = &zone_; Zone translate_zone(allocator_, ZONE_NAME); Utf16CharacterStream* stream = parse_info()->character_stream(); @@ -332,6 +333,13 @@ MaybeHandle AsmJs::InstantiateAsmWasm(Isolate* isolate, // but should instead point to the instantiation site (more intuitive). int position = shared->StartPosition(); + // Check that the module is not instantiated as a generator or async function. + if (IsResumableFunction(shared->scope_info().function_kind())) { + ReportInstantiationFailure(script, position, + "Cannot be instantiated as resumable function"); + return MaybeHandle(); + } + // Check that all used stdlib members are valid. bool stdlib_use_of_typed_array_present = false; wasm::AsmJsParser::StdlibSet stdlib_uses = diff --git a/deps/v8/src/asmjs/asm-scanner.cc b/deps/v8/src/asmjs/asm-scanner.cc index eaff042d31cae5..73140867084c7a 100644 --- a/deps/v8/src/asmjs/asm-scanner.cc +++ b/deps/v8/src/asmjs/asm-scanner.cc @@ -329,7 +329,7 @@ void AsmJsScanner::ConsumeNumber(uc32 ch) { token_ = kParseError; return; } - if (has_dot) { + if (has_dot || trunc(double_value_) != double_value_) { token_ = kDouble; } else { // Exceeding safe integer range is an error. diff --git a/deps/v8/src/ast/ast-value-factory.cc b/deps/v8/src/ast/ast-value-factory.cc index 7e1be44da16ea2..23f28b834ac7ec 100644 --- a/deps/v8/src/ast/ast-value-factory.cc +++ b/deps/v8/src/ast/ast-value-factory.cc @@ -27,6 +27,7 @@ #include "src/ast/ast-value-factory.h" +#include "src/base/logging.h" #include "src/common/globals.h" #include "src/execution/off-thread-isolate.h" #include "src/heap/factory-inl.h" @@ -332,20 +333,22 @@ const AstRawString* AstValueFactory::CloneFromOtherFactory( } AstConsString* AstValueFactory::NewConsString() { - return new (zone_) AstConsString; + return new (zone()) AstConsString; } AstConsString* AstValueFactory::NewConsString(const AstRawString* str) { - return NewConsString()->AddString(zone_, str); + return NewConsString()->AddString(zone(), str); } AstConsString* AstValueFactory::NewConsString(const AstRawString* str1, const AstRawString* str2) { - return NewConsString()->AddString(zone_, str1)->AddString(zone_, str2); + return NewConsString()->AddString(zone(), str1)->AddString(zone(), str2); } template void AstValueFactory::Internalize(LocalIsolate* isolate) { + if (!zone_) return; + // Strings need to be internalized before values, because values refer to // strings. for (AstRawString* current = strings_; current != nullptr;) { @@ -355,6 +358,7 @@ void AstValueFactory::Internalize(LocalIsolate* isolate) { } ResetStrings(); + zone_ = nullptr; } template EXPORT_TEMPLATE_DEFINE( V8_EXPORT_PRIVATE) void AstValueFactory::Internalize(Isolate* @@ -373,9 +377,9 @@ AstRawString* AstValueFactory::GetString(uint32_t hash_field, bool is_one_byte, if (entry->value == nullptr) { // Copy literal contents for later comparison. int length = literal_bytes.length(); - byte* new_literal_bytes = zone_->NewArray(length); + byte* new_literal_bytes = zone()->NewArray(length); memcpy(new_literal_bytes, literal_bytes.begin(), length); - AstRawString* new_string = new (zone_) AstRawString( + AstRawString* new_string = new (zone()) AstRawString( is_one_byte, Vector(new_literal_bytes, length), hash_field); CHECK_NOT_NULL(new_string); AddString(new_string); diff --git a/deps/v8/src/ast/ast-value-factory.h b/deps/v8/src/ast/ast-value-factory.h index dce9de40697f29..134612f1fd02a5 100644 --- a/deps/v8/src/ast/ast-value-factory.h +++ b/deps/v8/src/ast/ast-value-factory.h @@ -31,6 +31,7 @@ #include #include "src/base/hashmap.h" +#include "src/base/logging.h" #include "src/common/globals.h" #include "src/heap/factory.h" #include "src/numbers/conversions.h" @@ -66,6 +67,8 @@ class AstRawString final : public ZoneObject { int byte_length() const { return literal_bytes_.length(); } const unsigned char* raw_data() const { return literal_bytes_.begin(); } + bool IsPrivateName() const { return length() > 0 && FirstCharacter() == '#'; } + // For storing AstRawStrings in a hash map. uint32_t hash_field() const { return hash_field_; } uint32_t Hash() const { return hash_field_ >> Name::kHashShift; } @@ -288,6 +291,7 @@ class AstValueFactory { empty_cons_string_(nullptr), zone_(zone), hash_seed_(hash_seed) { + DCHECK_NOT_NULL(zone_); DCHECK_EQ(hash_seed, string_constants->hash_seed()); std::fill(one_character_strings_, one_character_strings_ + arraysize(one_character_strings_), @@ -295,7 +299,10 @@ class AstValueFactory { empty_cons_string_ = NewConsString(); } - Zone* zone() const { return zone_; } + Zone* zone() const { + DCHECK_NOT_NULL(zone_); + return zone_; + } const AstRawString* GetOneByteString(Vector literal) { return GetOneByteStringInternal(literal); @@ -317,6 +324,9 @@ class AstValueFactory { V8_EXPORT_PRIVATE AstConsString* NewConsString(const AstRawString* str1, const AstRawString* str2); + // Internalize all the strings in the factory, and prevent any more from being + // allocated. Multiple calls to Internalize are allowed, for simplicity, where + // subsequent calls are a no-op. template void Internalize(LocalIsolate* isolate); diff --git a/deps/v8/src/ast/ast.h b/deps/v8/src/ast/ast.h index 5bf2d7e1928f9c..6fcf30499a5f0d 100644 --- a/deps/v8/src/ast/ast.h +++ b/deps/v8/src/ast/ast.h @@ -14,8 +14,6 @@ #include "src/codegen/bailout-reason.h" #include "src/codegen/label.h" #include "src/common/globals.h" -#include "src/execution/isolate.h" -#include "src/execution/off-thread-isolate.h" #include "src/heap/factory.h" #include "src/objects/elements-kind.h" #include "src/objects/function-syntax-kind.h" @@ -117,6 +115,9 @@ namespace internal { EXPRESSION_NODE_LIST(V) // Forward declarations +class Isolate; +class OffThreadIsolate; + class AstNode; class AstNodeFactory; class Declaration; @@ -1445,9 +1446,7 @@ class VariableProxy final : public Expression { HoleCheckModeField::update(bit_field_, HoleCheckMode::kRequired); } - bool IsPrivateName() const { - return raw_name()->length() > 0 && raw_name()->FirstCharacter() == '#'; - } + bool IsPrivateName() const { return raw_name()->IsPrivateName(); } // Bind this proxy to the variable var. void BindTo(Variable* var); @@ -2242,7 +2241,7 @@ class FunctionLiteral final : public Expression { private: friend class AstNodeFactory; - FunctionLiteral(Zone* zone, const AstRawString* name, + FunctionLiteral(Zone* zone, const AstConsString* name, AstValueFactory* ast_value_factory, DeclarationScope* scope, const ScopedPtrList& body, int expected_property_count, int parameter_count, @@ -2258,7 +2257,7 @@ class FunctionLiteral final : public Expression { function_token_position_(kNoSourcePosition), suspend_count_(0), function_literal_id_(function_literal_id), - raw_name_(name ? ast_value_factory->NewConsString(name) : nullptr), + raw_name_(name), scope_(scope), body_(0, nullptr), raw_inferred_name_(ast_value_factory->empty_cons_string()), @@ -3109,7 +3108,8 @@ class AstNodeFactory final { bool has_braces, int function_literal_id, ProducedPreparseData* produced_preparse_data = nullptr) { return new (zone_) FunctionLiteral( - zone_, name, ast_value_factory_, scope, body, expected_property_count, + zone_, name ? ast_value_factory_->NewConsString(name) : nullptr, + ast_value_factory_, scope, body, expected_property_count, parameter_count, function_length, function_syntax_kind, has_duplicate_parameters, eager_compile_hint, position, has_braces, function_literal_id, produced_preparse_data); @@ -3122,8 +3122,8 @@ class AstNodeFactory final { DeclarationScope* scope, const ScopedPtrList& body, int expected_property_count, int parameter_count) { return new (zone_) FunctionLiteral( - zone_, ast_value_factory_->empty_string(), ast_value_factory_, scope, - body, expected_property_count, parameter_count, parameter_count, + zone_, ast_value_factory_->empty_cons_string(), ast_value_factory_, + scope, body, expected_property_count, parameter_count, parameter_count, FunctionSyntaxKind::kAnonymousExpression, FunctionLiteral::kNoDuplicateParameters, FunctionLiteral::kShouldLazyCompile, 0, /* has_braces */ false, diff --git a/deps/v8/src/ast/scopes.cc b/deps/v8/src/ast/scopes.cc index 8c13556db915ee..3f0a1adbc3af95 100644 --- a/deps/v8/src/ast/scopes.cc +++ b/deps/v8/src/ast/scopes.cc @@ -598,7 +598,7 @@ bool DeclarationScope::Analyze(ParseInfo* info) { DCHECK_EQ(scope->scope_type_, ScopeType::FUNCTION_SCOPE); allow_deref.emplace(); info->consumed_preparse_data()->RestoreScopeAllocationData( - scope, info->ast_value_factory()); + scope, info->ast_value_factory(), info->zone()); } if (!scope->AllocateVariables(info)) return false; @@ -1138,7 +1138,8 @@ Variable* Scope::NewTemporary(const AstRawString* name, return var; } -Declaration* DeclarationScope::CheckConflictingVarDeclarations() { +Declaration* DeclarationScope::CheckConflictingVarDeclarations( + bool* allowed_catch_binding_var_redeclaration) { if (has_checked_syntax_) return nullptr; for (Declaration* decl : decls_) { // Lexical vs lexical conflicts within the same scope have already been @@ -1152,11 +1153,12 @@ Declaration* DeclarationScope::CheckConflictingVarDeclarations() { // Iterate through all scopes until the declaration scope. do { // There is a conflict if there exists a non-VAR binding. + Variable* other_var = current->LookupLocal(decl->var()->raw_name()); if (current->is_catch_scope()) { + *allowed_catch_binding_var_redeclaration |= other_var != nullptr; current = current->outer_scope(); continue; } - Variable* other_var = current->LookupLocal(decl->var()->raw_name()); if (other_var != nullptr) { DCHECK(IsLexicalVariableMode(other_var->mode())); return decl; @@ -2586,8 +2588,8 @@ Variable* ClassScope::DeclarePrivateName(const AstRawString* name, bool* was_added) { Variable* result = EnsureRareData()->private_name_map.Declare( zone(), this, name, mode, NORMAL_VARIABLE, - InitializationFlag::kNeedsInitialization, - MaybeAssignedFlag::kMaybeAssigned, is_static_flag, was_added); + InitializationFlag::kNeedsInitialization, MaybeAssignedFlag::kNotAssigned, + is_static_flag, was_added); if (*was_added) { locals_.Add(result); has_static_private_methods_ |= @@ -2683,7 +2685,7 @@ Variable* ClassScope::LookupPrivateNameInScopeInfo(const AstRawString* name) { DCHECK(IsConstVariableMode(mode)); DCHECK_EQ(init_flag, InitializationFlag::kNeedsInitialization); - DCHECK_EQ(maybe_assigned_flag, MaybeAssignedFlag::kMaybeAssigned); + DCHECK_EQ(maybe_assigned_flag, MaybeAssignedFlag::kNotAssigned); // Add the found private name to the map to speed up subsequent // lookups for the same name. @@ -2725,7 +2727,7 @@ bool ClassScope::ResolvePrivateNames(ParseInfo* info) { if (var == nullptr) { // It's only possible to fail to resolve private names here if // this is at the top level or the private name is accessed through eval. - DCHECK(info->is_eval() || outer_scope_->is_script_scope()); + DCHECK(info->flags().is_eval() || outer_scope_->is_script_scope()); Scanner::Location loc = proxy->location(); info->pending_error_handler()->ReportMessageAt( loc.beg_pos, loc.end_pos, @@ -2812,7 +2814,7 @@ Variable* ClassScope::DeclareBrandVariable(AstValueFactory* ast_value_factory, Variable* brand = Declare(zone(), ast_value_factory->dot_brand_string(), VariableMode::kConst, NORMAL_VARIABLE, InitializationFlag::kNeedsInitialization, - MaybeAssignedFlag::kMaybeAssigned, &was_added); + MaybeAssignedFlag::kNotAssigned, &was_added); DCHECK(was_added); brand->set_is_static_flag(is_static_flag); brand->ForceContextAllocation(); diff --git a/deps/v8/src/ast/scopes.h b/deps/v8/src/ast/scopes.h index 08bbc696d94193..11f44bb498436f 100644 --- a/deps/v8/src/ast/scopes.h +++ b/deps/v8/src/ast/scopes.h @@ -909,7 +909,8 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope { // Check if the scope has conflicting var // declarations, i.e. a var declaration that has been hoisted from a nested // scope over a let binding of the same name. - Declaration* CheckConflictingVarDeclarations(); + Declaration* CheckConflictingVarDeclarations( + bool* allowed_catch_binding_var_redeclaration); void set_has_checked_syntax(bool has_checked_syntax) { has_checked_syntax_ = has_checked_syntax; diff --git a/deps/v8/src/ast/variables.h b/deps/v8/src/ast/variables.h index a3a5199620ec29..7c6ee4324e8153 100644 --- a/deps/v8/src/ast/variables.h +++ b/deps/v8/src/ast/variables.h @@ -90,7 +90,10 @@ class Variable final : public ZoneObject { } void SetMaybeAssigned() { if (mode() == VariableMode::kConst) return; - + // Private names are only initialized once by us. + if (name_->IsPrivateName()) { + return; + } // If this variable is dynamically shadowing another variable, then that // variable could also be assigned (in the non-shadowing case). if (has_local_if_not_shadowed()) { diff --git a/deps/v8/src/base/address-region.h b/deps/v8/src/base/address-region.h index 9ef6160d2a79a4..44151606c0cc63 100644 --- a/deps/v8/src/base/address-region.h +++ b/deps/v8/src/base/address-region.h @@ -15,6 +15,14 @@ namespace base { // Helper class representing an address region of certain size. class AddressRegion { public: + // Function object that compares the start address of two regions. Usable as + // compare function on std data structures and algorithms. + struct StartAddressLess { + bool operator()(base::AddressRegion a, base::AddressRegion b) const { + return a.begin() < b.begin(); + } + }; + using Address = uintptr_t; AddressRegion() = default; diff --git a/deps/v8/src/base/cpu.cc b/deps/v8/src/base/cpu.cc index f1c48fa1356698..bbdae525e30b39 100644 --- a/deps/v8/src/base/cpu.cc +++ b/deps/v8/src/base/cpu.cc @@ -605,7 +605,11 @@ CPU::CPU() #endif #elif V8_HOST_ARCH_ARM64 -// Implementer, variant and part are currently unused under ARM64. +#ifdef V8_OS_WIN + // Windows makes high-resolution thread timing information available in + // user-space. + has_non_stop_time_stamp_counter_ = true; +#endif // V8_OS_WIN #elif V8_HOST_ARCH_PPC || V8_HOST_ARCH_PPC64 diff --git a/deps/v8/src/base/platform/mutex.h b/deps/v8/src/base/platform/mutex.h index 5b3b31ec1e5246..c3144f7ceb5365 100644 --- a/deps/v8/src/base/platform/mutex.h +++ b/deps/v8/src/base/platform/mutex.h @@ -67,7 +67,8 @@ class V8_BASE_EXPORT Mutex final { return native_handle_; } - V8_INLINE void AssertHeld() { DCHECK_EQ(1, level_); } + V8_INLINE void AssertHeld() const { DCHECK_EQ(1, level_); } + V8_INLINE void AssertUnheld() const { DCHECK_EQ(0, level_); } private: NativeHandle native_handle_; diff --git a/deps/v8/src/base/platform/platform-aix.cc b/deps/v8/src/base/platform/platform-aix.cc index e3d7c426b4ab9e..e1ccda2ab05601 100644 --- a/deps/v8/src/base/platform/platform-aix.cc +++ b/deps/v8/src/base/platform/platform-aix.cc @@ -129,5 +129,34 @@ void OS::SignalCodeMovingGC() {} void OS::AdjustSchedulingParams() {} +// static +void* Stack::GetStackStart() { + // pthread_getthrds_np creates 3 values: + // __pi_stackaddr, __pi_stacksize, __pi_stackend + + // higher address ----- __pi_stackend, stack base + // + // | + // | __pi_stacksize, stack grows downwards + // | + // V + // + // lower address ----- __pi_stackaddr, current sp + + pthread_t tid = pthread_self(); + struct __pthrdsinfo buf; + // clear buf + memset(&buf, 0, sizeof(buf)); + char regbuf[1]; + int regbufsize = sizeof(regbuf); + const int rc = pthread_getthrds_np(&tid, PTHRDSINFO_QUERY_ALL, &buf, + sizeof(buf), regbuf, ®bufsize); + CHECK(!rc); + if (buf.__pi_stackend == NULL || buf.__pi_stackaddr == NULL) { + return nullptr; + } + return reinterpret_cast(buf.__pi_stackend); +} + } // namespace base } // namespace v8 diff --git a/deps/v8/src/base/platform/platform-posix.cc b/deps/v8/src/base/platform/platform-posix.cc index 54f72e04e68372..c3f0b08ddde843 100644 --- a/deps/v8/src/base/platform/platform-posix.cc +++ b/deps/v8/src/base/platform/platform-posix.cc @@ -970,7 +970,7 @@ void Thread::SetThreadLocal(LocalStorageKey key, void* value) { // pthread_getattr_np used below is non portable (hence the _np suffix). We // keep this version in POSIX as most Linux-compatible derivatives will // support it. MacOS and FreeBSD are different here. -#if !defined(V8_OS_FREEBSD) && !defined(V8_OS_MACOSX) && !defined(V8_OS_SOLARIS) +#if !defined(V8_OS_FREEBSD) && !defined(V8_OS_MACOSX) && !defined(_AIX) // static void* Stack::GetStackStart() { @@ -996,7 +996,7 @@ void* Stack::GetStackStart() { return nullptr; } -#endif // !defined(V8_OS_FREEBSD) && !defined(V8_OS_MACOSX) && !defined(V8_OS_SOLARIS) +#endif // !defined(V8_OS_FREEBSD) && !defined(V8_OS_MACOSX) && !defined(_AIX) // static void* Stack::GetCurrentStackPosition() { return __builtin_frame_address(0); } diff --git a/deps/v8/src/base/platform/platform-solaris.cc b/deps/v8/src/base/platform/platform-solaris.cc index b4ac98ce73bdbb..b5b16dac568789 100644 --- a/deps/v8/src/base/platform/platform-solaris.cc +++ b/deps/v8/src/base/platform/platform-solaris.cc @@ -65,23 +65,5 @@ void OS::SignalCodeMovingGC() {} void OS::AdjustSchedulingParams() {} -// static -void* Stack::GetStackStart() { - pthread_attr_t attr; - int error; - pthread_attr_init(&attr); - error = pthread_attr_get_np(pthread_self(), &attr); - if (!error) { - void* base; - size_t size; - error = pthread_attr_getstack(&attr, &base, &size); - CHECK(!error); - pthread_attr_destroy(&attr); - return reinterpret_cast(base) + size; - } - pthread_attr_destroy(&attr); - return nullptr; -} - } // namespace base } // namespace v8 diff --git a/deps/v8/src/base/platform/time.cc b/deps/v8/src/base/platform/time.cc index f07fd8e595fc60..a12a5b0d0a7f9d 100644 --- a/deps/v8/src/base/platform/time.cc +++ b/deps/v8/src/base/platform/time.cc @@ -125,12 +125,6 @@ V8_INLINE bool IsHighResolutionTimer(clockid_t clk_id) { } #elif V8_OS_WIN -V8_INLINE bool IsQPCReliable() { - v8::base::CPU cpu; - // On Athlon X2 CPUs (e.g. model 15) QueryPerformanceCounter is unreliable. - return strcmp(cpu.vendor(), "AuthenticAMD") == 0 && cpu.family() == 15; -} - // Returns the current value of the performance counter. V8_INLINE uint64_t QPCNowRaw() { LARGE_INTEGER perf_counter_now = {}; @@ -645,11 +639,6 @@ TimeDelta QPCValueToTimeDelta(LONGLONG qpc_value) { TimeTicks QPCNow() { return TimeTicks() + QPCValueToTimeDelta(QPCNowRaw()); } -bool IsBuggyAthlon(const CPU& cpu) { - // On Athlon X2 CPUs (e.g. model 15) QueryPerformanceCounter is unreliable. - return strcmp(cpu.vendor(), "AuthenticAMD") == 0 && cpu.family() == 15; -} - void InitializeTimeTicksNowFunctionPointer() { LARGE_INTEGER ticks_per_sec = {}; if (!QueryPerformanceFrequency(&ticks_per_sec)) ticks_per_sec.QuadPart = 0; @@ -667,8 +656,7 @@ void InitializeTimeTicksNowFunctionPointer() { // ~72% of users fall within this category. TimeTicksNowFunction now_function; CPU cpu; - if (ticks_per_sec.QuadPart <= 0 || !cpu.has_non_stop_time_stamp_counter() || - IsBuggyAthlon(cpu)) { + if (ticks_per_sec.QuadPart <= 0 || !cpu.has_non_stop_time_stamp_counter()) { now_function = &RolloverProtectedNow; } else { now_function = &QPCNow; @@ -800,8 +788,7 @@ ThreadTicks ThreadTicks::GetForThread(const HANDLE& thread_handle) { // static bool ThreadTicks::IsSupportedWin() { - static bool is_supported = base::CPU().has_non_stop_time_stamp_counter() && - !IsQPCReliable(); + static bool is_supported = base::CPU().has_non_stop_time_stamp_counter(); return is_supported; } diff --git a/deps/v8/src/builtins/arm/builtins-arm.cc b/deps/v8/src/builtins/arm/builtins-arm.cc index 49f578d1fd11bf..d340fd20b0a72b 100644 --- a/deps/v8/src/builtins/arm/builtins-arm.cc +++ b/deps/v8/src/builtins/arm/builtins-arm.cc @@ -123,32 +123,23 @@ void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) { __ Push(cp, r0); __ SmiUntag(r0); +#ifdef V8_REVERSE_JSARGS + // Set up pointer to last argument (skip receiver). + __ add( + r4, fp, + Operand(StandardFrameConstants::kCallerSPOffset + kSystemPointerSize)); + // Copy arguments and receiver to the expression stack. + __ PushArray(r4, r0, r5); + // The receiver for the builtin/api call. + __ PushRoot(RootIndex::kTheHoleValue); +#else // The receiver for the builtin/api call. __ PushRoot(RootIndex::kTheHoleValue); - // Set up pointer to last argument. __ add(r4, fp, Operand(StandardFrameConstants::kCallerSPOffset)); - // Copy arguments and receiver to the expression stack. - Label loop, entry; - __ mov(r5, r0); - // ----------- S t a t e ------------- - // -- r0: number of arguments (untagged) - // -- r1: constructor function - // -- r3: new target - // -- r4: pointer to last argument - // -- r5: counter - // -- sp[0*kPointerSize]: the hole (receiver) - // -- sp[1*kPointerSize]: number of arguments (tagged) - // -- sp[2*kPointerSize]: context - // ----------------------------------- - __ b(&entry); - __ bind(&loop); - __ ldr(scratch, MemOperand(r4, r5, LSL, kPointerSizeLog2)); - __ push(scratch); - __ bind(&entry); - __ sub(r5, r5, Operand(1), SetCC); - __ b(ge, &loop); + __ PushArray(r4, r0, r5); +#endif // Call the function. // r0: number of arguments (untagged) @@ -239,29 +230,36 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { // Restore new target. __ Pop(r3); + +#ifdef V8_REVERSE_JSARGS + // Push the allocated receiver to the stack. + __ Push(r0); + // We need two copies because we may have to return the original one + // and the calling conventions dictate that the called function pops the + // receiver. The second copy is pushed after the arguments, we saved in r6 + // since r0 needs to store the number of arguments before + // InvokingFunction. + __ mov(r6, r0); + + // Set up pointer to first argument (skip receiver). + __ add( + r4, fp, + Operand(StandardFrameConstants::kCallerSPOffset + kSystemPointerSize)); +#else // Push the allocated receiver to the stack. We need two copies // because we may have to return the original one and the calling // conventions dictate that the called function pops the receiver. __ Push(r0, r0); - // ----------- S t a t e ------------- - // -- r3: new target - // -- sp[0*kPointerSize]: implicit receiver - // -- sp[1*kPointerSize]: implicit receiver - // -- sp[2*kPointerSize]: padding - // -- sp[3*kPointerSize]: constructor function - // -- sp[4*kPointerSize]: number of arguments (tagged) - // -- sp[5*kPointerSize]: context - // ----------------------------------- + // Set up pointer to last argument. + __ add(r4, fp, Operand(StandardFrameConstants::kCallerSPOffset)); +#endif // Restore constructor function and argument count. __ ldr(r1, MemOperand(fp, ConstructFrameConstants::kConstructorOffset)); __ ldr(r0, MemOperand(fp, ConstructFrameConstants::kLengthOffset)); __ SmiUntag(r0); - // Set up pointer to last argument. - __ add(r4, fp, Operand(StandardFrameConstants::kCallerSPOffset)); - Label enough_stack_space, stack_overflow; Generate_StackOverflowCheck(masm, r0, r5, &stack_overflow); __ b(&enough_stack_space); @@ -275,29 +273,13 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { __ bind(&enough_stack_space); - // Copy arguments and receiver to the expression stack. - Label loop, entry; - __ mov(r5, r0); - // ----------- S t a t e ------------- - // -- r0: number of arguments (untagged) - // -- r3: new target - // -- r4: pointer to last argument - // -- r5: counter - // -- sp[0*kPointerSize]: implicit receiver - // -- sp[1*kPointerSize]: implicit receiver - // -- sp[2*kPointerSize]: padding - // -- r1 and sp[3*kPointerSize]: constructor function - // -- sp[4*kPointerSize]: number of arguments (tagged) - // -- sp[5*kPointerSize]: context - // ----------------------------------- - __ b(&entry); + // Copy arguments to the expression stack. + __ PushArray(r4, r0, r5); - __ bind(&loop); - __ ldr(r6, MemOperand(r4, r5, LSL, kPointerSizeLog2)); - __ push(r6); - __ bind(&entry); - __ sub(r5, r5, Operand(1), SetCC); - __ b(ge, &loop); +#ifdef V8_REVERSE_JSARGS + // Push implicit receiver. + __ Push(r6); +#endif // Call the function. __ InvokeFunctionWithNewTarget(r1, r3, r0, CALL_FUNCTION); @@ -424,9 +406,11 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ cmp(sp, scratch); __ b(lo, &stack_overflow); +#ifndef V8_REVERSE_JSARGS // Push receiver. __ ldr(scratch, FieldMemOperand(r1, JSGeneratorObject::kReceiverOffset)); __ Push(scratch); +#endif // ----------- S t a t e ------------- // -- r1 : the JSGeneratorObject to resume @@ -443,19 +427,38 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ ldr(r2, FieldMemOperand(r1, JSGeneratorObject::kParametersAndRegistersOffset)); { +#ifdef V8_REVERSE_JSARGS + Label done_loop, loop; + __ mov(r6, r3); + + __ bind(&loop); + __ sub(r6, r6, Operand(1), SetCC); + __ b(lt, &done_loop); + __ add(scratch, r2, Operand(r6, LSL, kTaggedSizeLog2)); + __ ldr(scratch, FieldMemOperand(scratch, FixedArray::kHeaderSize)); + __ Push(scratch); + __ b(&loop); + + __ bind(&done_loop); + + // Push receiver. + __ ldr(scratch, FieldMemOperand(r1, JSGeneratorObject::kReceiverOffset)); + __ Push(scratch); +#else Label done_loop, loop; __ mov(r6, Operand(0)); __ bind(&loop); __ cmp(r6, r3); __ b(ge, &done_loop); - __ add(scratch, r2, Operand(r6, LSL, kPointerSizeLog2)); + __ add(scratch, r2, Operand(r6, LSL, kTaggedSizeLog2)); __ ldr(scratch, FieldMemOperand(scratch, FixedArray::kHeaderSize)); __ Push(scratch); __ add(r6, r6, Operand(1)); __ b(&loop); __ bind(&done_loop); +#endif } // Underlying function needs to have bytecode available. @@ -744,13 +747,14 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, __ Move(cp, context_address); __ ldr(cp, MemOperand(cp)); - // Push the function and the receiver onto the stack. - __ Push(r2, r3); + // Push the function. + __ Push(r2); - // Check if we have enough stack space to push all arguments. - // Clobbers r3. + // Check if we have enough stack space to push all arguments + receiver. + // Clobbers r5. Label enough_stack_space, stack_overflow; - Generate_StackOverflowCheck(masm, r0, r3, &stack_overflow); + __ add(r6, r0, Operand(1)); // Add one for receiver. + Generate_StackOverflowCheck(masm, r6, r5, &stack_overflow); __ b(&enough_stack_space); __ bind(&stack_overflow); __ CallRuntime(Runtime::kThrowStackOverflow); @@ -762,19 +766,42 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, // Copy arguments to the stack in a loop. // r1: new.target // r2: function + // r3: receiver // r0: argc // r4: argv, i.e. points to first arg +#ifdef V8_REVERSE_JSARGS + Label loop, entry; + __ add(r6, r4, Operand(r0, LSL, kSystemPointerSizeLog2)); + // r6 points past last arg. + __ b(&entry); + __ bind(&loop); + __ ldr(r5, MemOperand(r6, -kSystemPointerSize, + PreIndex)); // read next parameter + __ ldr(r5, MemOperand(r5)); // dereference handle + __ push(r5); // push parameter + __ bind(&entry); + __ cmp(r4, r6); + __ b(ne, &loop); + + // Push the receiver. + __ Push(r3); +#else + // Push the receiver. + __ Push(r3); + Label loop, entry; - __ add(r3, r4, Operand(r0, LSL, kPointerSizeLog2)); - // r1 points past last arg. + __ add(r3, r4, Operand(r0, LSL, kSystemPointerSizeLog2)); + // r3 points past last arg. __ b(&entry); __ bind(&loop); - __ ldr(r5, MemOperand(r4, kPointerSize, PostIndex)); // read next parameter + __ ldr(r5, MemOperand(r4, kSystemPointerSize, + PostIndex)); // read next parameter __ ldr(r5, MemOperand(r5)); // dereference handle __ push(r5); // push parameter __ bind(&entry); __ cmp(r4, r3); __ b(ne, &loop); +#endif // Setup new.target and function. __ mov(r3, r1); @@ -1237,21 +1264,20 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { } static void Generate_InterpreterPushArgs(MacroAssembler* masm, - Register num_args, Register index, - Register limit, Register scratch) { - // Find the address of the last argument. - __ mov(limit, num_args); - __ mov(limit, Operand(limit, LSL, kPointerSizeLog2)); - __ sub(limit, index, limit); - - Label loop_header, loop_check; - __ b(al, &loop_check); - __ bind(&loop_header); - __ ldr(scratch, MemOperand(index, -kPointerSize, PostIndex)); - __ push(scratch); - __ bind(&loop_check); - __ cmp(index, limit); - __ b(hi, &loop_header); + Register num_args, + Register start_address, + Register scratch) { + // Find the argument with lowest address. + __ sub(scratch, num_args, Operand(1)); + __ mov(scratch, Operand(scratch, LSL, kSystemPointerSizeLog2)); + __ sub(start_address, start_address, scratch); + // Push the arguments. +#ifdef V8_REVERSE_JSARGS + __ PushArray(start_address, num_args, scratch, + TurboAssembler::PushArrayOrder::kReverse); +#else + __ PushArray(start_address, num_args, scratch); +#endif } // static @@ -1268,23 +1294,53 @@ void Builtins::Generate_InterpreterPushArgsThenCallImpl( // ----------------------------------- Label stack_overflow; +#ifdef V8_REVERSE_JSARGS + if (mode == InterpreterPushArgsMode::kWithFinalSpread) { + // The spread argument should not be pushed. + __ sub(r0, r0, Operand(1)); + } +#endif + __ add(r3, r0, Operand(1)); // Add one for receiver. Generate_StackOverflowCheck(masm, r3, r4, &stack_overflow); +#ifdef V8_REVERSE_JSARGS + if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { + // Don't copy receiver. Argument count is correct. + __ mov(r3, r0); + } + + // Push the arguments. r2 and r4 will be modified. + Generate_InterpreterPushArgs(masm, r3, r2, r4); + + // Push "undefined" as the receiver arg if we need to. + if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { + __ PushRoot(RootIndex::kUndefinedValue); + } + + if (mode == InterpreterPushArgsMode::kWithFinalSpread) { + // Pass the spread in the register r2. + // r2 already points to the penultimate argument, the spread + // lies in the next interpreter register. + __ sub(r2, r2, Operand(kSystemPointerSize)); + __ ldr(r2, MemOperand(r2)); + } +#else // Push "undefined" as the receiver arg if we need to. if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { __ PushRoot(RootIndex::kUndefinedValue); __ mov(r3, r0); // Argument count is correct. } - // Push the arguments. r2, r4, r5 will be modified. - Generate_InterpreterPushArgs(masm, r3, r2, r4, r5); + // Push the arguments. r2 and r4 will be modified. + Generate_InterpreterPushArgs(masm, r3, r2, r4); if (mode == InterpreterPushArgsMode::kWithFinalSpread) { __ Pop(r2); // Pass the spread in a register __ sub(r0, r0, Operand(1)); // Subtract one for spread } +#endif // Call the target. if (mode == InterpreterPushArgsMode::kWithFinalSpread) { @@ -1315,14 +1371,39 @@ void Builtins::Generate_InterpreterPushArgsThenConstructImpl( // ----------------------------------- Label stack_overflow; + __ add(r5, r0, Operand(1)); // Add one for receiver. + + Generate_StackOverflowCheck(masm, r5, r6, &stack_overflow); + +#ifdef V8_REVERSE_JSARGS + if (mode == InterpreterPushArgsMode::kWithFinalSpread) { + // The spread argument should not be pushed. + __ sub(r0, r0, Operand(1)); + } + + // Push the arguments. r4 and r5 will be modified. + Generate_InterpreterPushArgs(masm, r0, r4, r5); + // Push a slot for the receiver to be constructed. __ mov(r5, Operand::Zero()); __ push(r5); - Generate_StackOverflowCheck(masm, r0, r5, &stack_overflow); + if (mode == InterpreterPushArgsMode::kWithFinalSpread) { + // Pass the spread in the register r2. + // r4 already points to the penultimate argument, the spread + // lies in the next interpreter register. + __ sub(r4, r4, Operand(kSystemPointerSize)); + __ ldr(r2, MemOperand(r4)); + } else { + __ AssertUndefinedOrAllocationSite(r2, r5); + } +#else + // Push a slot for the receiver to be constructed. + __ mov(r5, Operand::Zero()); + __ push(r5); - // Push the arguments. r5, r4, r6 will be modified. - Generate_InterpreterPushArgs(masm, r0, r4, r5, r6); + // Push the arguments. r4 and r5 will be modified. + Generate_InterpreterPushArgs(masm, r0, r4, r5); if (mode == InterpreterPushArgsMode::kWithFinalSpread) { __ Pop(r2); // Pass the spread in a register @@ -1330,6 +1411,7 @@ void Builtins::Generate_InterpreterPushArgsThenConstructImpl( } else { __ AssertUndefinedOrAllocationSite(r2, r5); } +#endif if (mode == InterpreterPushArgsMode::kArrayFunction) { __ AssertFunction(r1); @@ -1604,12 +1686,21 @@ void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) { { __ LoadRoot(r5, RootIndex::kUndefinedValue); __ mov(r2, r5); - __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); // receiver +#ifdef V8_REVERSE_JSARGS + __ ldr(r1, MemOperand(sp, 0)); // receiver + __ cmp(r0, Operand(1)); + __ ldr(r5, MemOperand(sp, kSystemPointerSize), ge); // thisArg + __ cmp(r0, Operand(2), ge); + __ ldr(r2, MemOperand(sp, 2 * kSystemPointerSize), ge); // argArray +#else + __ ldr(r1, MemOperand(sp, r0, LSL, kSystemPointerSizeLog2)); // receiver __ sub(r4, r0, Operand(1), SetCC); - __ ldr(r5, MemOperand(sp, r4, LSL, kPointerSizeLog2), ge); // thisArg + __ ldr(r5, MemOperand(sp, r4, LSL, kSystemPointerSizeLog2), ge); // thisArg __ sub(r4, r4, Operand(1), SetCC, ge); - __ ldr(r2, MemOperand(sp, r4, LSL, kPointerSizeLog2), ge); // argArray - __ add(sp, sp, Operand(r0, LSL, kPointerSizeLog2)); + __ ldr(r2, MemOperand(sp, r4, LSL, kSystemPointerSizeLog2), + ge); // argArray +#endif + __ add(sp, sp, Operand(r0, LSL, kSystemPointerSizeLog2)); __ str(r5, MemOperand(sp, 0)); } @@ -1643,6 +1734,24 @@ void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) { // static void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { +#ifdef V8_REVERSE_JSARGS + // 1. Get the callable to call (passed as receiver) from the stack. + __ Pop(r1); + + // 2. Make sure we have at least one argument. + // r0: actual number of arguments + { + Label done; + __ cmp(r0, Operand::Zero()); + __ b(ne, &done); + __ PushRoot(RootIndex::kUndefinedValue); + __ add(r0, r0, Operand(1)); + __ bind(&done); + } + + // 3. Adjust the actual number of arguments. + __ sub(r0, r0, Operand(1)); +#else // 1. Make sure we have at least one argument. // r0: actual number of arguments { @@ -1656,7 +1765,7 @@ void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { // 2. Get the callable to call (passed as receiver) from the stack. // r0: actual number of arguments - __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); + __ ldr(r1, __ ReceiverOperand(r0)); // 3. Shift arguments and return address one slot down on the stack // (overwriting the original receiver). Adjust argument count to make @@ -1667,12 +1776,12 @@ void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { Register scratch = r3; Label loop; // Calculate the copy start address (destination). Copy end address is sp. - __ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2)); + __ add(r2, sp, Operand(r0, LSL, kSystemPointerSizeLog2)); __ bind(&loop); - __ ldr(scratch, MemOperand(r2, -kPointerSize)); + __ ldr(scratch, MemOperand(r2, -kSystemPointerSize)); __ str(scratch, MemOperand(r2)); - __ sub(r2, r2, Operand(kPointerSize)); + __ sub(r2, r2, Operand(kSystemPointerSize)); __ cmp(r2, sp); __ b(ne, &loop); // Adjust the actual number of arguments and remove the top element @@ -1680,6 +1789,7 @@ void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { __ sub(r0, r0, Operand(1)); __ pop(); } +#endif // 4. Call the callable. __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); @@ -1693,6 +1803,7 @@ void Builtins::Generate_ReflectApply(MacroAssembler* masm) { // -- sp[8] : target // -- sp[12] : receiver // ----------------------------------- + // NOTE: The order of args in the stack are reversed if V8_REVERSE_JSARGS // 1. Load target into r1 (if present), argumentsList into r2 (if present), // remove all arguments from the stack (including the receiver), and push @@ -1701,13 +1812,24 @@ void Builtins::Generate_ReflectApply(MacroAssembler* masm) { __ LoadRoot(r1, RootIndex::kUndefinedValue); __ mov(r5, r1); __ mov(r2, r1); +#ifdef V8_REVERSE_JSARGS + __ cmp(r0, Operand(1)); + __ ldr(r1, MemOperand(sp, kSystemPointerSize), ge); // target + __ cmp(r0, Operand(2), ge); + __ ldr(r5, MemOperand(sp, 2 * kSystemPointerSize), ge); // thisArgument + __ cmp(r0, Operand(3), ge); + __ ldr(r2, MemOperand(sp, 3 * kSystemPointerSize), ge); // argumentsList +#else __ sub(r4, r0, Operand(1), SetCC); - __ ldr(r1, MemOperand(sp, r4, LSL, kPointerSizeLog2), ge); // target + __ ldr(r1, MemOperand(sp, r4, LSL, kSystemPointerSizeLog2), ge); // target __ sub(r4, r4, Operand(1), SetCC, ge); - __ ldr(r5, MemOperand(sp, r4, LSL, kPointerSizeLog2), ge); // thisArgument + __ ldr(r5, MemOperand(sp, r4, LSL, kSystemPointerSizeLog2), + ge); // thisArgument __ sub(r4, r4, Operand(1), SetCC, ge); - __ ldr(r2, MemOperand(sp, r4, LSL, kPointerSizeLog2), ge); // argumentsList - __ add(sp, sp, Operand(r0, LSL, kPointerSizeLog2)); + __ ldr(r2, MemOperand(sp, r4, LSL, kSystemPointerSizeLog2), + ge); // argumentsList +#endif + __ add(sp, sp, Operand(r0, LSL, kSystemPointerSizeLog2)); __ str(r5, MemOperand(sp, 0)); } @@ -1734,6 +1856,7 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { // -- sp[8] : target // -- sp[12] : receiver // ----------------------------------- + // NOTE: The order of args in the stack are reversed if V8_REVERSE_JSARGS // 1. Load target into r1 (if present), argumentsList into r2 (if present), // new.target into r3 (if present, otherwise use target), remove all @@ -1742,15 +1865,30 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { { __ LoadRoot(r1, RootIndex::kUndefinedValue); __ mov(r2, r1); - __ str(r2, MemOperand(sp, r0, LSL, kPointerSizeLog2)); // receiver +#ifdef V8_REVERSE_JSARGS + __ mov(r4, r1); + __ cmp(r0, Operand(1)); + __ ldr(r1, MemOperand(sp, kSystemPointerSize), ge); // target + __ mov(r3, r1); // new.target defaults to target + __ cmp(r0, Operand(2), ge); + __ ldr(r2, MemOperand(sp, 2 * kSystemPointerSize), ge); // argumentsList + __ cmp(r0, Operand(3), ge); + __ ldr(r3, MemOperand(sp, 3 * kSystemPointerSize), ge); // new.target + __ add(sp, sp, Operand(r0, LSL, kSystemPointerSizeLog2)); + __ str(r4, MemOperand(sp, 0)); // set undefined to the receiver +#else + __ str(r2, MemOperand(sp, r0, LSL, kSystemPointerSizeLog2)); // receiver __ sub(r4, r0, Operand(1), SetCC); - __ ldr(r1, MemOperand(sp, r4, LSL, kPointerSizeLog2), ge); // target + __ ldr(r1, MemOperand(sp, r4, LSL, kSystemPointerSizeLog2), ge); // target __ mov(r3, r1); // new.target defaults to target __ sub(r4, r4, Operand(1), SetCC, ge); - __ ldr(r2, MemOperand(sp, r4, LSL, kPointerSizeLog2), ge); // argumentsList + __ ldr(r2, MemOperand(sp, r4, LSL, kSystemPointerSizeLog2), + ge); // argumentsList __ sub(r4, r4, Operand(1), SetCC, ge); - __ ldr(r3, MemOperand(sp, r4, LSL, kPointerSizeLog2), ge); // new.target - __ add(sp, sp, Operand(r0, LSL, kPointerSizeLog2)); + __ ldr(r3, MemOperand(sp, r4, LSL, kSystemPointerSizeLog2), + ge); // new.target + __ add(sp, sp, Operand(r0, LSL, kSystemPointerSizeLog2)); +#endif } // ----------- S t a t e ------------- @@ -1830,7 +1968,29 @@ void Builtins::Generate_CallOrConstructVarargs(MacroAssembler* masm, Label stack_overflow; Generate_StackOverflowCheck(masm, r4, scratch, &stack_overflow); - // Push arguments onto the stack (thisArgument is already on the stack). +#ifdef V8_REVERSE_JSARGS + // Move the arguments already in the stack, + // including the receiver and the return address. + { + Label copy, check; + Register num = r5, src = r6, dest = r9; // r7 and r8 are context and root. + __ mov(src, sp); + // Update stack pointer. + __ lsl(scratch, r4, Operand(kSystemPointerSizeLog2)); + __ AllocateStackSpace(scratch); + __ mov(dest, sp); + __ mov(num, r0); + __ b(&check); + __ bind(©); + __ ldr(scratch, MemOperand(src, kSystemPointerSize, PostIndex)); + __ str(scratch, MemOperand(dest, kSystemPointerSize, PostIndex)); + __ sub(num, num, Operand(1), SetCC); + __ bind(&check); + __ b(ge, ©); + } +#endif + + // Copy arguments onto the stack (thisArgument is already on the stack). { __ mov(r6, Operand(0)); __ LoadRoot(r5, RootIndex::kTheHoleValue); @@ -1838,11 +1998,16 @@ void Builtins::Generate_CallOrConstructVarargs(MacroAssembler* masm, __ bind(&loop); __ cmp(r6, r4); __ b(eq, &done); - __ add(scratch, r2, Operand(r6, LSL, kPointerSizeLog2)); + __ add(scratch, r2, Operand(r6, LSL, kTaggedSizeLog2)); __ ldr(scratch, FieldMemOperand(scratch, FixedArray::kHeaderSize)); __ cmp(scratch, r5); + // Turn the hole into undefined as we go. __ LoadRoot(scratch, RootIndex::kUndefinedValue, eq); +#ifdef V8_REVERSE_JSARGS + __ str(scratch, MemOperand(r9, kSystemPointerSize, PostIndex)); +#else __ Push(scratch); +#endif __ add(r6, r6, Operand(1)); __ b(&loop); __ bind(&done); @@ -1981,7 +2146,7 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, __ LoadGlobalProxy(r3); } else { Label convert_to_object, convert_receiver; - __ ldr(r3, MemOperand(sp, r0, LSL, kPointerSizeLog2)); + __ ldr(r3, __ ReceiverOperand(r0)); __ JumpIfSmi(r3, &convert_to_object); STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); __ CompareObjectType(r3, r4, r4, FIRST_JS_RECEIVER_TYPE); @@ -2017,7 +2182,7 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); __ bind(&convert_receiver); } - __ str(r3, MemOperand(sp, r0, LSL, kPointerSizeLog2)); + __ str(r3, __ ReceiverOperand(r0)); } __ bind(&done_convert); @@ -2073,10 +2238,11 @@ void Generate_PushBoundArguments(MacroAssembler* masm) { // (i.e. debug break and preemption) here, so check the "real stack // limit". Label done; - __ mov(scratch, Operand(r4, LSL, kPointerSizeLog2)); + __ mov(scratch, Operand(r4, LSL, kSystemPointerSizeLog2)); { UseScratchRegisterScope temps(masm); Register remaining_stack_size = temps.Acquire(); + DCHECK(!AreAliased(r0, r1, r2, r3, r4, scratch, remaining_stack_size)); // Compute the space we have left. The stack might already be overflowed // here which will cause remaining_stack_size to become negative. @@ -2096,6 +2262,25 @@ void Generate_PushBoundArguments(MacroAssembler* masm) { __ bind(&done); } +#ifdef V8_REVERSE_JSARGS + // Pop receiver. + __ Pop(r5); + + // Push [[BoundArguments]]. + { + Label loop; + __ add(r0, r0, r4); // Adjust effective number of arguments. + __ add(r2, r2, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ bind(&loop); + __ sub(r4, r4, Operand(1), SetCC); + __ ldr(scratch, MemOperand(r2, r4, LSL, kTaggedSizeLog2)); + __ Push(scratch); + __ b(gt, &loop); + } + + // Push receiver. + __ Push(r5); +#else // Reserve stack space for the [[BoundArguments]]. __ AllocateStackSpace(scratch); @@ -2106,8 +2291,8 @@ void Generate_PushBoundArguments(MacroAssembler* masm) { __ bind(&loop); __ cmp(r5, r0); __ b(gt, &done_loop); - __ ldr(scratch, MemOperand(sp, r4, LSL, kPointerSizeLog2)); - __ str(scratch, MemOperand(sp, r5, LSL, kPointerSizeLog2)); + __ ldr(scratch, MemOperand(sp, r4, LSL, kSystemPointerSizeLog2)); + __ str(scratch, MemOperand(sp, r5, LSL, kSystemPointerSizeLog2)); __ add(r4, r4, Operand(1)); __ add(r5, r5, Operand(1)); __ b(&loop); @@ -2127,6 +2312,7 @@ void Generate_PushBoundArguments(MacroAssembler* masm) { __ add(r0, r0, Operand(1)); __ b(gt, &loop); } +#endif } __ bind(&no_bound_arguments); } @@ -2143,7 +2329,7 @@ void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm) { // Patch the receiver to [[BoundThis]]. __ ldr(r3, FieldMemOperand(r1, JSBoundFunction::kBoundThisOffset)); - __ str(r3, MemOperand(sp, r0, LSL, kPointerSizeLog2)); + __ str(r3, __ ReceiverOperand(r0)); // Push the [[BoundArguments]] onto the stack. Generate_PushBoundArguments(masm); @@ -2183,7 +2369,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) { // 2. Call to something else, which might have a [[Call]] internal method (if // not we raise an exception). // Overwrite the original receiver the (original) target. - __ str(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); + __ str(r1, __ ReceiverOperand(r0)); // Let the "call_as_function_delegate" take care of the rest. __ LoadNativeContextSlot(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, r1); __ Jump(masm->isolate()->builtins()->CallFunction( @@ -2292,7 +2478,7 @@ void Builtins::Generate_Construct(MacroAssembler* masm) { __ bind(&non_proxy); { // Overwrite the original receiver with the (original) target. - __ str(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); + __ str(r1, __ ReceiverOperand(r0)); // Let the "call_as_constructor_delegate" take care of the rest. __ LoadNativeContextSlot(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, r1); __ Jump(masm->isolate()->builtins()->CallFunction(), @@ -2319,9 +2505,13 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ b(eq, &dont_adapt_arguments); __ ldr(r4, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); __ ldr(r4, FieldMemOperand(r4, SharedFunctionInfo::kFlagsOffset)); + +#ifndef V8_REVERSE_JSARGS + // This optimization is disabled when the arguments are reversed. __ tst(r4, Operand(SharedFunctionInfo::IsSafeToSkipArgumentsAdaptorBit::kMask)); __ b(ne, &skip_adapt_arguments); +#endif // ------------------------------------------- // Adapt arguments. @@ -2342,10 +2532,14 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // r1: function // r2: expected number of arguments // r3: new target (passed through to callee) +#ifdef V8_REVERSE_JSARGS + __ add(r0, fp, Operand(r2, LSL, kSystemPointerSizeLog2)); +#else __ add(r0, fp, Operand::PointerOffsetFromSmiKey(r0)); +#endif // adjust for return address and receiver - __ add(r0, r0, Operand(2 * kPointerSize)); - __ sub(r4, r0, Operand(r2, LSL, kPointerSizeLog2)); + __ add(r0, r0, Operand(2 * kSystemPointerSize)); + __ sub(r4, r0, Operand(r2, LSL, kSystemPointerSizeLog2)); // Copy the arguments (including the receiver) to the new stack frame. // r0: copy start address @@ -2359,7 +2553,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ ldr(r5, MemOperand(r0, 0)); __ push(r5); __ cmp(r0, r4); // Compare before moving to next argument. - __ sub(r0, r0, Operand(kPointerSize)); + __ sub(r0, r0, Operand(kSystemPointerSize)); __ b(ne, ©); __ b(&invoke); @@ -2371,6 +2565,49 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { EnterArgumentsAdaptorFrame(masm); Generate_StackOverflowCheck(masm, r2, r5, &stack_overflow); +#ifdef V8_REVERSE_JSARGS + // Fill the remaining expected arguments with undefined. + // r0: actual number of arguments as a smi + // r1: function + // r2: expected number of arguments + // r3: new target (passed through to callee) + __ LoadRoot(r5, RootIndex::kUndefinedValue); + __ sub(r6, r2, Operand::SmiUntag(r0)); + __ sub(r4, fp, Operand(r6, LSL, kPointerSizeLog2)); + // Adjust for frame. + __ sub(r4, r4, + Operand(ArgumentsAdaptorFrameConstants::kFixedFrameSizeFromFp + + kPointerSize)); + + Label fill; + __ bind(&fill); + __ push(r5); + __ cmp(sp, r4); + __ b(ne, &fill); + + // Calculate copy start address into r0 and copy end address is fp. + // r0: actual number of arguments as a smi + // r1: function + // r2: expected number of arguments + // r3: new target (passed through to callee) + __ add(r0, fp, Operand::PointerOffsetFromSmiKey(r0)); + + // Copy the arguments (including the receiver) to the new stack frame. + // r0: copy start address + // r1: function + // r2: expected number of arguments + // r3: new target (passed through to callee) + Label copy; + __ bind(©); + + // Adjust load for return address and receiver. + __ ldr(r5, MemOperand(r0, 2 * kPointerSize)); + __ push(r5); + + __ cmp(r0, fp); // Compare before moving to next argument. + __ sub(r0, r0, Operand(kPointerSize)); + __ b(ne, ©); +#else // Calculate copy start address into r0 and copy end address is fp. // r0: actual number of arguments as a smi // r1: function @@ -2410,6 +2647,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ push(r5); __ cmp(sp, r4); __ b(ne, &fill); +#endif } // Call the entry point. @@ -2915,6 +3153,7 @@ void Builtins::Generate_CallApiCallback(MacroAssembler* masm) { // -- sp[(argc - 1) * 4] : first argument // -- sp[(argc + 0) * 4] : receiver // ----------------------------------- + // NOTE: The order of args are reversed if V8_REVERSE_JSARGS Register api_function_address = r1; Register argc = r2; @@ -2982,8 +3221,12 @@ void Builtins::Generate_CallApiCallback(MacroAssembler* masm) { // FunctionCallbackInfo::values_ (points at the first varargs argument passed // on the stack). +#ifdef V8_REVERSE_JSARGS + __ add(scratch, scratch, Operand((FCA::kArgsLength + 1) * kPointerSize)); +#else __ add(scratch, scratch, Operand((FCA::kArgsLength - 1) * kPointerSize)); __ add(scratch, scratch, Operand(argc, LSL, kPointerSizeLog2)); +#endif __ str(scratch, MemOperand(sp, 2 * kPointerSize)); // FunctionCallbackInfo::length_. diff --git a/deps/v8/src/builtins/arm64/builtins-arm64.cc b/deps/v8/src/builtins/arm64/builtins-arm64.cc index 9c38ae085ecc4f..46ab7a61fa88d2 100644 --- a/deps/v8/src/builtins/arm64/builtins-arm64.cc +++ b/deps/v8/src/builtins/arm64/builtins-arm64.cc @@ -143,17 +143,19 @@ void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) { __ Claim(slot_count); // Preserve the incoming parameters on the stack. - __ LoadRoot(x10, RootIndex::kTheHoleValue); + __ LoadRoot(x4, RootIndex::kTheHoleValue); // Compute a pointer to the slot immediately above the location on the // stack to which arguments will be later copied. __ SlotAddress(x2, argc); +#ifndef V8_REVERSE_JSARGS // Poke the hole (receiver) in the highest slot. - __ Str(x10, MemOperand(x2)); - __ Tbnz(slot_count_without_rounding, 0, &already_aligned); + __ Str(x4, MemOperand(x2)); +#endif // Store padding, if needed. + __ Tbnz(slot_count_without_rounding, 0, &already_aligned); __ Str(padreg, MemOperand(x2, 1 * kSystemPointerSize)); __ Bind(&already_aligned); @@ -162,9 +164,18 @@ void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) { Register count = x2; Register dst = x10; Register src = x11; - __ Mov(count, argc); __ SlotAddress(dst, 0); +#ifdef V8_REVERSE_JSARGS + // Poke the hole (receiver). + __ Str(x4, MemOperand(dst)); + __ Add(dst, dst, kSystemPointerSize); // Skip receiver. + __ Add(src, fp, + StandardFrameConstants::kCallerSPOffset + + kSystemPointerSize); // Skip receiver. +#else __ Add(src, fp, StandardFrameConstants::kCallerSPOffset); +#endif + __ Mov(count, argc); __ CopyDoubleWords(dst, src, count); } @@ -175,7 +186,7 @@ void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) { // If argc is odd: // -- sp[0*kSystemPointerSize]: argument n - 1 // -- ... - // -- sp[(n-1)*kSystemPointerSize]: argument 0 + // -- sp[(n-1)*kSystemPointerSize]: argument 1 // -- sp[(n+0)*kSystemPointerSize]: the hole (receiver) // -- sp[(n+1)*kSystemPointerSize]: padding // -- sp[(n+2)*kSystemPointerSize]: padding @@ -184,12 +195,13 @@ void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) { // If argc is even: // -- sp[0*kSystemPointerSize]: argument n - 1 // -- ... - // -- sp[(n-1)*kSystemPointerSize]: argument 0 + // -- sp[(n-1)*kSystemPointerSize]: argument 1 // -- sp[(n+0)*kSystemPointerSize]: the hole (receiver) // -- sp[(n+1)*kSystemPointerSize]: padding // -- sp[(n+2)*kSystemPointerSize]: number of arguments (tagged) // -- sp[(n+3)*kSystemPointerSize]: context (pushed by FrameScope) // ----------------------------------- + // NOTE: The order of args in the stack are reversed if V8_REVERSE_JSARGS // Call the function. __ InvokeFunctionWithNewTarget(x1, x3, argc, CALL_FUNCTION); @@ -264,8 +276,10 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { // If not derived class constructor: Allocate the new receiver object. __ IncrementCounter(masm->isolate()->counters()->constructed_objects(), 1, x4, x5); + __ Call(BUILTIN_CODE(masm->isolate(), FastNewObject), RelocInfo::CODE_TARGET); + __ B(&post_instantiation_deopt_entry); // Else: use TheHoleValue as receiver for constructor call @@ -346,8 +360,15 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { Register dst = x10; Register src = x11; __ Mov(count, x12); +#ifdef V8_REVERSE_JSARGS + __ Poke(x0, 0); // Add the receiver. + __ SlotAddress(dst, 1); // Skip receiver. + __ Add(src, fp, + StandardFrameConstants::kCallerSPOffset + kSystemPointerSize); +#else __ SlotAddress(dst, 0); __ Add(src, fp, StandardFrameConstants::kCallerSPOffset); +#endif __ CopyDoubleWords(dst, src, count); } @@ -496,7 +517,7 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { // Poke receiver into highest claimed slot. __ LoadTaggedPointerField( x5, FieldMemOperand(x1, JSGeneratorObject::kReceiverOffset)); - __ Poke(x5, Operand(x10, LSL, kSystemPointerSizeLog2)); + __ Poke(x5, __ ReceiverOperand(x10)); // ----------- S t a t e ------------- // -- x1 : the JSGeneratorObject to resume @@ -504,26 +525,33 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { // -- x10 : argument count // -- cp : generator context // -- lr : return address - // -- sp[arg count] : generator receiver - // -- sp[0 .. arg count - 1] : claimed for args + // -- sp[0 .. arg count] : claimed for receiver and args // ----------------------------------- // Copy the function arguments from the generator object's register file. - __ LoadTaggedPointerField( x5, FieldMemOperand(x1, JSGeneratorObject::kParametersAndRegistersOffset)); { Label loop, done; __ Cbz(x10, &done); +#ifdef V8_REVERSE_JSARGS + __ SlotAddress(x12, x10); + __ Add(x5, x5, Operand(x10, LSL, kTaggedSizeLog2)); + __ Add(x5, x5, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ Bind(&loop); + __ Sub(x10, x10, 1); + __ LoadAnyTaggedField(x11, MemOperand(x5, -kTaggedSize, PreIndex)); + __ Str(x11, MemOperand(x12, -kSystemPointerSize, PostIndex)); +#else __ Mov(x12, 0); - __ Bind(&loop); __ Sub(x10, x10, 1); __ Add(x11, x5, Operand(x12, LSL, kTaggedSizeLog2)); __ LoadAnyTaggedField(x11, FieldMemOperand(x11, FixedArray::kHeaderSize)); __ Poke(x11, Operand(x10, LSL, kSystemPointerSizeLog2)); __ Add(x12, x12, 1); +#endif __ Cbnz(x10, &loop); __ Bind(&done); } @@ -862,9 +890,17 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, __ SlotAddress(scratch, slots_to_claim); __ Str(padreg, MemOperand(scratch, -kSystemPointerSize)); +#ifdef V8_REVERSE_JSARGS + // Store receiver on the stack. + __ Poke(receiver, 0); + // Store function on the stack. + __ SlotAddress(scratch, argc); + __ Str(function, MemOperand(scratch, kSystemPointerSize)); +#else // Store receiver and function on the stack. __ SlotAddress(scratch, argc); __ Stp(receiver, function, MemOperand(scratch)); +#endif // Copy arguments to the stack in a loop, in reverse order. // x4: argc. @@ -874,9 +910,21 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, // Skip the argument set up if we have no arguments. __ Cbz(argc, &done); - // scratch has been set to point to the location of the receiver, which + // scratch has been set to point to the location of the function, which // marks the end of the argument copy. - +#ifdef V8_REVERSE_JSARGS + __ SlotAddress(x0, 1); // Skips receiver. + __ Bind(&loop); + // Load the handle. + __ Ldr(x11, MemOperand(argv, kSystemPointerSize, PostIndex)); + // Dereference the handle. + __ Ldr(x11, MemOperand(x11)); + // Poke the result into the stack. + __ Str(x11, MemOperand(x0, kSystemPointerSize, PostIndex)); + // Loop if we've not reached the end of copy marker. + __ Cmp(x0, scratch); + __ B(le, &loop); +#else __ Bind(&loop); // Load the handle. __ Ldr(x11, MemOperand(argv, kSystemPointerSize, PostIndex)); @@ -887,6 +935,7 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, // Loop if we've not reached the end of copy marker. __ Cmp(sp, scratch); __ B(lt, &loop); +#endif __ Bind(&done); @@ -1418,6 +1467,36 @@ static void Generate_InterpreterPushArgs(MacroAssembler* masm, __ Poke(padreg, Operand(scratch, LSL, kSystemPointerSizeLog2)); } +#ifdef V8_REVERSE_JSARGS + if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { + __ Mov(slots_to_copy, num_args); + __ SlotAddress(stack_addr, 1); + } else { + // If we're not given an explicit receiver to store, we'll need to copy it + // together with the rest of the arguments. + __ Add(slots_to_copy, num_args, 1); + __ SlotAddress(stack_addr, 0); + } + + __ Sub(last_arg_addr, first_arg_index, + Operand(slots_to_copy, LSL, kSystemPointerSizeLog2)); + __ Add(last_arg_addr, last_arg_addr, kSystemPointerSize); + + // Load the final spread argument into spread_arg_out, if necessary. + if (mode == InterpreterPushArgsMode::kWithFinalSpread) { + __ Ldr(spread_arg_out, MemOperand(last_arg_addr, -kSystemPointerSize)); + } + + __ CopyDoubleWords(stack_addr, last_arg_addr, slots_to_copy, + TurboAssembler::kDstLessThanSrcAndReverse); + + if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { + // Store "undefined" as the receiver arg if we need to. + Register receiver = x14; + __ LoadRoot(receiver, RootIndex::kUndefinedValue); + __ Poke(receiver, 0); + } +#else // !V8_REVERSE_JSARGS if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { // Store "undefined" as the receiver arg if we need to. Register receiver = x14; @@ -1443,6 +1522,7 @@ static void Generate_InterpreterPushArgs(MacroAssembler* masm, // Copy the rest of the arguments. __ SlotAddress(stack_addr, 0); __ CopyDoubleWords(stack_addr, last_arg_addr, slots_to_copy); +#endif // !V8_REVERSE_JSARGS } // static @@ -1778,14 +1858,16 @@ void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) { // -- sp[8] : thisArg (if argc >= 1) // -- sp[16] : receiver // ----------------------------------- + // NOTE: The order of args in the stack are reversed if V8_REVERSE_JSARGS + ASM_LOCATION("Builtins::Generate_FunctionPrototypeApply"); Register argc = x0; - Register arg_array = x2; Register receiver = x1; - Register this_arg = x0; - Register undefined_value = x3; - Register null_value = x4; + Register arg_array = x2; + Register this_arg = x3; + Register undefined_value = x4; + Register null_value = x5; __ LoadRoot(undefined_value, RootIndex::kUndefinedValue); __ LoadRoot(null_value, RootIndex::kNullValue); @@ -1793,8 +1875,21 @@ void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) { // 1. Load receiver into x1, argArray into x2 (if present), remove all // arguments from the stack (including the receiver), and push thisArg (if // present) instead. +#ifdef V8_REVERSE_JSARGS + { + Label done; + __ Mov(this_arg, undefined_value); + __ Mov(arg_array, undefined_value); + __ Peek(receiver, 0); + __ Cmp(argc, Immediate(1)); + __ B(lt, &done); + __ Peek(this_arg, kSystemPointerSize); + __ B(eq, &done); + __ Peek(arg_array, 2 * kSystemPointerSize); + __ bind(&done); + } +#else // !V8_REVERSE_JSARGS { - Register saved_argc = x10; Register scratch = x11; // Push two undefined values on the stack, to put it in a consistent state @@ -1814,16 +1909,13 @@ void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) { // There are now always three arguments to read, in the slots starting from // slot argc. __ SlotAddress(scratch, argc); - - __ Mov(saved_argc, argc); - __ Ldp(arg_array, this_arg, MemOperand(scratch)); // Overwrites argc. + __ Ldp(arg_array, this_arg, MemOperand(scratch)); __ Ldr(receiver, MemOperand(scratch, 2 * kSystemPointerSize)); - __ Drop(2); // Drop the undefined values we pushed above. - __ DropArguments(saved_argc, TurboAssembler::kCountExcludesReceiver); - - __ PushArgument(this_arg); } +#endif // !V8_REVERSE_JSARGS + __ DropArguments(argc, TurboAssembler::kCountExcludesReceiver); + __ PushArgument(this_arg); // ----------- S t a t e ------------- // -- x2 : argArray @@ -1863,7 +1955,7 @@ void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { ASM_LOCATION("Builtins::Generate_FunctionPrototypeCall"); // 1. Get the callable to call (passed as receiver) from the stack. - __ Peek(function, Operand(argc, LSL, kXRegSizeLog2)); + __ Peek(function, __ ReceiverOperand(argc)); // 2. Handle case with no arguments. { @@ -1879,9 +1971,39 @@ void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { __ Bind(&non_zero); } + Label arguments_ready; +#ifdef V8_REVERSE_JSARGS + // 3. Shift arguments. It depends if the arguments is even or odd. + // That is if padding exists or not. + { + Label even; + Register copy_from = x10; + Register copy_to = x11; + Register count = x12; + __ Mov(count, argc); // CopyDoubleWords changes the count argument. + __ Tbz(argc, 0, &even); + + // Shift arguments one slot down on the stack (overwriting the original + // receiver). + __ SlotAddress(copy_from, 1); + __ Sub(copy_to, copy_from, kSystemPointerSize); + __ CopyDoubleWords(copy_to, copy_from, count); + // Overwrite the duplicated remaining last argument. + __ Poke(padreg, Operand(argc, LSL, kXRegSizeLog2)); + __ B(&arguments_ready); + + // Copy arguments one slot higher in memory, overwriting the original + // receiver and padding. + __ Bind(&even); + __ SlotAddress(copy_from, count); + __ Add(copy_to, copy_from, kSystemPointerSize); + __ CopyDoubleWords(copy_to, copy_from, count, + TurboAssembler::kSrcLessThanDst); + __ Drop(2); + } +#else // !V8_REVERSE_JSARGS // 3. Overwrite the receiver with padding. If argc is odd, this is all we // need to do. - Label arguments_ready; __ Poke(padreg, Operand(argc, LSL, kXRegSizeLog2)); __ Tbnz(argc, 0, &arguments_ready); @@ -1902,6 +2024,7 @@ void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { // Drop two slots. These are copies of the last two arguments. __ Drop(2); } +#endif // !V8_REVERSE_JSARGS // 5. Adjust argument count to make the original first argument the new // receiver and call the callable. @@ -1918,6 +2041,8 @@ void Builtins::Generate_ReflectApply(MacroAssembler* masm) { // -- sp[16] : target (if argc >= 1) // -- sp[24] : receiver // ----------------------------------- + // NOTE: The order of args in the stack are reversed if V8_REVERSE_JSARGS + ASM_LOCATION("Builtins::Generate_ReflectApply"); Register argc = x0; @@ -1931,6 +2056,23 @@ void Builtins::Generate_ReflectApply(MacroAssembler* masm) { // 1. Load target into x1 (if present), argumentsList into x2 (if present), // remove all arguments from the stack (including the receiver), and push // thisArgument (if present) instead. +#ifdef V8_REVERSE_JSARGS + { + Label done; + __ Mov(target, undefined_value); + __ Mov(this_argument, undefined_value); + __ Mov(arguments_list, undefined_value); + __ Cmp(argc, Immediate(1)); + __ B(lt, &done); + __ Peek(target, kSystemPointerSize); + __ B(eq, &done); + __ Peek(this_argument, 2 * kSystemPointerSize); + __ Cmp(argc, Immediate(3)); + __ B(lt, &done); + __ Peek(arguments_list, 3 * kSystemPointerSize); + __ bind(&done); + } +#else // !V8_REVERSE_JSARGS { // Push four undefined values on the stack, to put it in a consistent state // so that we can always read the three arguments we need from it. The @@ -1967,10 +2109,10 @@ void Builtins::Generate_ReflectApply(MacroAssembler* masm) { __ Ldr(target, MemOperand(scratch, 3 * kSystemPointerSize)); __ Drop(4); // Drop the undefined values we pushed above. - __ DropArguments(argc, TurboAssembler::kCountExcludesReceiver); - - __ PushArgument(this_argument); } +#endif // !V8_REVERSE_JSARGS + __ DropArguments(argc, TurboAssembler::kCountExcludesReceiver); + __ PushArgument(this_argument); // ----------- S t a t e ------------- // -- x2 : argumentsList @@ -1995,6 +2137,8 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { // -- sp[16] : target // -- sp[24] : receiver // ----------------------------------- + // NOTE: The order of args in the stack are reversed if V8_REVERSE_JSARGS + ASM_LOCATION("Builtins::Generate_ReflectConstruct"); Register argc = x0; @@ -2009,6 +2153,24 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { // new.target into x3 (if present, otherwise use target), remove all // arguments from the stack (including the receiver), and push thisArgument // (if present) instead. +#ifdef V8_REVERSE_JSARGS + { + Label done; + __ Mov(target, undefined_value); + __ Mov(arguments_list, undefined_value); + __ Mov(new_target, undefined_value); + __ Cmp(argc, Immediate(1)); + __ B(lt, &done); + __ Peek(target, kSystemPointerSize); + __ B(eq, &done); + __ Peek(arguments_list, 2 * kSystemPointerSize); + __ Mov(new_target, target); // new.target defaults to target + __ Cmp(argc, Immediate(3)); + __ B(lt, &done); + __ Peek(new_target, 3 * kSystemPointerSize); + __ bind(&done); + } +#else // !V8_REVERSE_JSARGS { // Push four undefined values on the stack, to put it in a consistent state // so that we can always read the three arguments we need from it. The @@ -2048,11 +2210,13 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { __ CmovX(new_target, target, ls); // target if argc <= 2. __ Drop(4); // Drop the undefined values we pushed above. - __ DropArguments(argc, TurboAssembler::kCountExcludesReceiver); - - // Push receiver (undefined). - __ PushArgument(undefined_value); } +#endif // !V8_REVERSE_JSARGS + + __ DropArguments(argc, TurboAssembler::kCountExcludesReceiver); + + // Push receiver (undefined). + __ PushArgument(undefined_value); // ----------- S t a t e ------------- // -- x2 : argumentsList @@ -2105,6 +2269,39 @@ void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) { // one slot up or one slot down, as needed. void Generate_PrepareForCopyingVarargs(MacroAssembler* masm, Register argc, Register len) { +#ifdef V8_REVERSE_JSARGS + Label even; + Register slots_to_copy = x10; + Register slots_to_claim = x12; + + __ Add(slots_to_copy, argc, 1); // Copy with receiver. + __ Mov(slots_to_claim, len); + __ Tbz(slots_to_claim, 0, &even); + + // Claim space we need. If argc is even, slots_to_claim = len + 1, as we need + // one extra padding slot. If argc is odd, we know that the original arguments + // will have a padding slot we can reuse (since len is odd), so + // slots_to_claim = len - 1. + { + Register scratch = x11; + __ Add(slots_to_claim, len, 1); + __ And(scratch, argc, 1); + __ Eor(scratch, scratch, 1); + __ Sub(slots_to_claim, slots_to_claim, Operand(scratch, LSL, 1)); + } + + __ Bind(&even); + __ Claim(slots_to_claim); + + // Move the arguments already in the stack including the receiver. + { + Register src = x11; + Register dst = x12; + __ SlotAddress(src, slots_to_claim); + __ SlotAddress(dst, 0); + __ CopyDoubleWords(dst, src, slots_to_copy); + } +#else // !V8_REVERSE_JSARGS Label len_odd, exit; Register slots_to_copy = x10; // If needed. __ Add(slots_to_copy, argc, 1); @@ -2158,6 +2355,7 @@ void Generate_PrepareForCopyingVarargs(MacroAssembler* masm, Register argc, } __ Bind(&exit); +#endif // !V8_REVERSE_JSARGS } } // namespace @@ -2217,6 +2415,19 @@ void Builtins::Generate_CallOrConstructVarargs(MacroAssembler* masm, // We do not use the CompareRoot macro as it would do a LoadRoot behind the // scenes and we want to avoid that in a loop. // TODO(all): Consider using Ldp and Stp. +#ifdef V8_REVERSE_JSARGS + Register dst = x16; + __ Add(dst, argc, Immediate(1)); // Consider the receiver as well. + __ SlotAddress(dst, dst); + __ Add(argc, argc, len); // Update new argc. + __ Bind(&loop); + __ Sub(len, len, 1); + __ LoadAnyTaggedField(scratch, MemOperand(src, kTaggedSize, PostIndex)); + __ CmpTagged(scratch, the_hole_value); + __ Csel(scratch, scratch, undefined_value, ne); + __ Str(scratch, MemOperand(dst, kSystemPointerSize, PostIndex)); + __ Cbnz(len, &loop); +#else __ Bind(&loop); __ Sub(len, len, 1); __ LoadAnyTaggedField(scratch, MemOperand(src, kTaggedSize, PostIndex)); @@ -2224,9 +2435,9 @@ void Builtins::Generate_CallOrConstructVarargs(MacroAssembler* masm, __ Csel(scratch, scratch, undefined_value, ne); __ Poke(scratch, Operand(len, LSL, kSystemPointerSizeLog2)); __ Cbnz(len, &loop); +#endif } __ Bind(&done); - // Tail-call to the actual Call or Construct builtin. __ Jump(code, RelocInfo::CODE_TARGET); @@ -2368,7 +2579,7 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, __ LoadGlobalProxy(x3); } else { Label convert_to_object, convert_receiver; - __ Peek(x3, Operand(x0, LSL, kXRegSizeLog2)); + __ Peek(x3, __ ReceiverOperand(x0)); __ JumpIfSmi(x3, &convert_to_object); STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); __ CompareObjectType(x3, x4, x4, FIRST_JS_RECEIVER_TYPE); @@ -2403,7 +2614,7 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, x2, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset)); __ Bind(&convert_receiver); } - __ Poke(x3, Operand(x0, LSL, kXRegSizeLog2)); + __ Poke(x3, __ ReceiverOperand(x0)); } __ Bind(&done_convert); @@ -2474,6 +2685,83 @@ void Generate_PushBoundArguments(MacroAssembler* masm) { __ Bind(&done); } +#ifdef V8_REVERSE_JSARGS + Label copy_bound_args; + Register total_argc = x15; + Register slots_to_claim = x12; + Register scratch = x10; + Register receiver = x14; + + __ Add(total_argc, argc, bound_argc); + __ Peek(receiver, 0); + + // Round up slots_to_claim to an even number if it is odd. + __ Add(slots_to_claim, bound_argc, 1); + __ Bic(slots_to_claim, slots_to_claim, 1); + __ Claim(slots_to_claim, kSystemPointerSize); + + __ Tbz(bound_argc, 0, ©_bound_args); + { + Label argc_even; + __ Tbz(argc, 0, &argc_even); + // Arguments count is odd (with the receiver it's even), so there's no + // alignment padding above the arguments and we have to "add" it. We + // claimed bound_argc + 1, since it is odd and it was rounded up. +1 here + // is for stack alignment padding. + // 1. Shift args one slot down. + { + Register copy_from = x11; + Register copy_to = x12; + __ SlotAddress(copy_to, slots_to_claim); + __ Add(copy_from, copy_to, kSystemPointerSize); + __ CopyDoubleWords(copy_to, copy_from, argc); + } + // 2. Write a padding in the last slot. + __ Add(scratch, total_argc, 1); + __ Str(padreg, MemOperand(sp, scratch, LSL, kSystemPointerSizeLog2)); + __ B(©_bound_args); + + __ Bind(&argc_even); + // Arguments count is even (with the receiver it's odd), so there's an + // alignment padding above the arguments and we can reuse it. We need to + // claim bound_argc - 1, but we claimed bound_argc + 1, since it is odd + // and it was rounded up. + // 1. Drop 2. + __ Drop(2); + // 2. Shift args one slot up. + { + Register copy_from = x11; + Register copy_to = x12; + __ SlotAddress(copy_to, total_argc); + __ Sub(copy_from, copy_to, kSystemPointerSize); + __ CopyDoubleWords(copy_to, copy_from, argc, + TurboAssembler::kSrcLessThanDst); + } + } + + // If bound_argc is even, there is no alignment massage to do, and we have + // already claimed the correct number of slots (bound_argc). + __ Bind(©_bound_args); + + // Copy the receiver back. + __ Poke(receiver, 0); + // Copy [[BoundArguments]] to the stack (below the receiver). + { + Label loop; + Register counter = bound_argc; + Register copy_to = x12; + __ Add(bound_argv, bound_argv, FixedArray::kHeaderSize - kHeapObjectTag); + __ SlotAddress(copy_to, 1); + __ Bind(&loop); + __ Sub(counter, counter, 1); + __ LoadAnyTaggedField(scratch, + MemOperand(bound_argv, kTaggedSize, PostIndex)); + __ Str(scratch, MemOperand(copy_to, kSystemPointerSize, PostIndex)); + __ Cbnz(counter, &loop); + } + // Update argc. + __ Mov(argc, total_argc); +#else // !V8_REVERSE_JSARGS // Check if we need padding. Label copy_args, copy_bound_args; Register total_argc = x15; @@ -2546,6 +2834,7 @@ void Generate_PushBoundArguments(MacroAssembler* masm) { __ Bind(&done); } } +#endif // !V8_REVERSE_JSARGS } __ Bind(&no_bound_arguments); } @@ -2563,7 +2852,7 @@ void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm) { // Patch the receiver to [[BoundThis]]. __ LoadAnyTaggedField(x10, FieldMemOperand(x1, JSBoundFunction::kBoundThisOffset)); - __ Poke(x10, Operand(x0, LSL, kSystemPointerSizeLog2)); + __ Poke(x10, __ ReceiverOperand(x0)); // Push the [[BoundArguments]] onto the stack. Generate_PushBoundArguments(masm); @@ -2604,7 +2893,8 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) { // 2. Call to something else, which might have a [[Call]] internal method (if // not we raise an exception). // Overwrite the original receiver with the (original) target. - __ Poke(x1, Operand(x0, LSL, kXRegSizeLog2)); + __ Poke(x1, __ ReceiverOperand(x0)); + // Let the "call_as_function_delegate" take care of the rest. __ LoadNativeContextSlot(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, x1); __ Jump(masm->isolate()->builtins()->CallFunction( @@ -2720,7 +3010,8 @@ void Builtins::Generate_Construct(MacroAssembler* masm) { __ bind(&non_proxy); { // Overwrite the original receiver with the (original) target. - __ Poke(x1, Operand(x0, LSL, kXRegSizeLog2)); + __ Poke(x1, __ ReceiverOperand(x0)); + // Let the "call_as_constructor_delegate" take care of the rest. __ LoadNativeContextSlot(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, x1); __ Jump(masm->isolate()->builtins()->CallFunction(), @@ -2793,18 +3084,22 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // alignment of the arguments. // If the number of expected arguments is larger than the number of actual // arguments, the remaining expected slots will be filled with undefined. + // TODO(v8:10201) update comment once reversed arguments order sticks Register argc_actual = x0; // Excluding the receiver. Register argc_expected = x2; // Excluding the receiver. Register function = x1; - Register argc_actual_minus_expected = x5; - Label create_adaptor_frame, dont_adapt_arguments, stack_overflow, - adapt_arguments_in_place; + Label create_adaptor_frame, dont_adapt_arguments, stack_overflow; __ Cmp(argc_expected, kDontAdaptArgumentsSentinel); __ B(eq, &dont_adapt_arguments); +#ifndef V8_REVERSE_JSARGS + // This optimization is disabled when the arguments are reversed. + Label adapt_arguments_in_place; + Register argc_actual_minus_expected = x5; + // When the difference between argc_actual and argc_expected is odd, we // create an arguments adaptor frame. __ Sub(argc_actual_minus_expected, argc_actual, argc_expected); @@ -2818,6 +3113,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ TestAndBranchIfAnySet( w4, SharedFunctionInfo::IsSafeToSkipArgumentsAdaptorBit::kMask, &adapt_arguments_in_place); +#endif // ------------------------------------------- // Create an arguments adaptor frame. @@ -2828,11 +3124,10 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { EnterArgumentsAdaptorFrame(masm); Register copy_from = x10; - Register copy_end = x11; Register copy_to = x12; + Register copy_end = x11; Register argc_to_copy = x13; - Register argc_unused_actual = x14; - Register scratch1 = x15, scratch2 = x16; + Register scratch1 = x15; // We need slots for the expected arguments, with one extra slot for the // receiver. @@ -2846,12 +3141,61 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ Bic(scratch1, scratch1, 1); __ Claim(scratch1, kSystemPointerSize); +#ifdef V8_REVERSE_JSARGS + // If we don't have enough arguments, fill the remaining expected + // arguments with undefined, otherwise skip this step. + Label enough_arguments; + __ Cmp(argc_actual, argc_expected); + __ Csel(argc_to_copy, argc_expected, argc_actual, ge); + __ Add(argc_to_copy, argc_to_copy, 1); // Include receiver. + __ B(ge, &enough_arguments); + + // Fill the remaining expected arguments with undefined. + __ RecordComment("-- Fill slots with undefined --"); + Label fill; + // scratch1 still contains the size of the claimed area, + // which is RoundUp(argc_expected + 1, 2). + __ SlotAddress(copy_to, scratch1); + __ SlotAddress(copy_end, argc_to_copy); + __ LoadRoot(scratch1, RootIndex::kUndefinedValue); + // Now we can write pairs of undefineds, potentially overwriting one word + // below copy_end, but that's ok because that slot is still within claimed + // region. This loop will execute at least once because at this point we + // know that there's at least one undefined to be pushed and + // argc_to_copy >= 1. + __ Bind(&fill); + __ Stp(scratch1, scratch1, + MemOperand(copy_to, -2 * kSystemPointerSize, PreIndex)); + __ Cmp(copy_to, copy_end); + __ B(hi, &fill); + + // Enough arguments. + __ Bind(&enough_arguments); + + // Store padding if needed, when expected arguments is even. + __ RecordComment("-- Store padding --"); + Label skip_padding; + __ Tbnz(argc_expected, 0, &skip_padding); + __ SlotAddress(scratch1, argc_expected); + __ Str(padreg, MemOperand(scratch1, kSystemPointerSize)); + __ bind(&skip_padding); + + // Copy arguments. + __ RecordComment("-- Copy actual arguments --"); __ Mov(copy_to, sp); + __ Add(copy_from, fp, 2 * kSystemPointerSize); + __ CopyDoubleWords(copy_to, copy_from, argc_to_copy); + +#else // !V8_REVERSE_JSARGS + Register argc_unused_actual = x14; + Register scratch2 = x16; // Preparing the expected arguments is done in four steps, the order of // which is chosen so we can use LDP/STP and avoid conditional branches as // much as possible. + __ Mov(copy_to, sp); + // (1) If we don't have enough arguments, fill the remaining expected // arguments with undefined, otherwise skip this step. Label enough_arguments; @@ -2918,6 +3262,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { MemOperand(copy_from, argc_actual, LSL, kSystemPointerSizeLog2)); __ Str(scratch1, MemOperand(sp, argc_expected, LSL, kSystemPointerSizeLog2)); +#endif // Arguments have been adapted. Now call the entry point. __ RecordComment("-- Call entry point --"); @@ -2939,6 +3284,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ Ret(); } +#ifndef V8_REVERSE_JSARGS // ----------------------------------------- // Adapt arguments in the existing frame. // ----------------------------------------- @@ -2976,6 +3322,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ B(&dont_adapt_arguments); } } +#endif // ------------------------------------------- // Dont adapt arguments. @@ -3492,6 +3839,7 @@ void Builtins::Generate_CallApiCallback(MacroAssembler* masm) { // -- sp[(argc - 1) * 8] : first argument // -- sp[(argc + 0) * 8] : receiver // ----------------------------------- + // NOTE: The order of args in the stack are reversed if V8_REVERSE_JSARGS Register api_function_address = x1; Register argc = x2; @@ -3561,9 +3909,14 @@ void Builtins::Generate_CallApiCallback(MacroAssembler* masm) { // FunctionCallbackInfo::values_ (points at the first varargs argument passed // on the stack). +#ifdef V8_REVERSE_JSARGS + __ Add(scratch, scratch, + Operand((FCA::kArgsLength + 1) * kSystemPointerSize)); +#else __ Add(scratch, scratch, Operand((FCA::kArgsLength - 1) * kSystemPointerSize)); __ Add(scratch, scratch, Operand(argc, LSL, kSystemPointerSizeLog2)); +#endif __ Str(scratch, MemOperand(sp, 2 * kSystemPointerSize)); // FunctionCallbackInfo::length_. diff --git a/deps/v8/src/builtins/array-copywithin.tq b/deps/v8/src/builtins/array-copywithin.tq index cee0b1e1a48355..3d2a456efbb3a8 100644 --- a/deps/v8/src/builtins/array-copywithin.tq +++ b/deps/v8/src/builtins/array-copywithin.tq @@ -3,92 +3,91 @@ // found in the LICENSE file. namespace array { - macro ConvertToRelativeIndex(index: Number, length: Number): Number { - return index < 0 ? Max(index + length, 0) : Min(index, length); - } +macro ConvertToRelativeIndex(index: Number, length: Number): Number { + return index < 0 ? Max(index + length, 0) : Min(index, length); +} - // https://tc39.github.io/ecma262/#sec-array.prototype.copyWithin - transitioning javascript builtin ArrayPrototypeCopyWithin( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - // 1. Let O be ? ToObject(this value). - const object: JSReceiver = ToObject_Inline(context, receiver); +// https://tc39.github.io/ecma262/#sec-array.prototype.copyWithin +transitioning javascript builtin ArrayPrototypeCopyWithin( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + // 1. Let O be ? ToObject(this value). + const object: JSReceiver = ToObject_Inline(context, receiver); - // 2. Let len be ? ToLength(? Get(O, "length")). - const length: Number = GetLengthProperty(object); + // 2. Let len be ? ToLength(? Get(O, "length")). + const length: Number = GetLengthProperty(object); - // 3. Let relativeTarget be ? ToInteger(target). - const relativeTarget: Number = ToInteger_Inline(arguments[0]); + // 3. Let relativeTarget be ? ToInteger(target). + const relativeTarget: Number = ToInteger_Inline(arguments[0]); - // 4. If relativeTarget < 0, let to be max((len + relativeTarget), 0); - // else let to be min(relativeTarget, len). - let to: Number = ConvertToRelativeIndex(relativeTarget, length); + // 4. If relativeTarget < 0, let to be max((len + relativeTarget), 0); + // else let to be min(relativeTarget, len). + let to: Number = ConvertToRelativeIndex(relativeTarget, length); - // 5. Let relativeStart be ? ToInteger(start). - const relativeStart: Number = ToInteger_Inline(arguments[1]); + // 5. Let relativeStart be ? ToInteger(start). + const relativeStart: Number = ToInteger_Inline(arguments[1]); - // 6. If relativeStart < 0, let from be max((len + relativeStart), 0); - // else let from be min(relativeStart, len). - let from: Number = ConvertToRelativeIndex(relativeStart, length); + // 6. If relativeStart < 0, let from be max((len + relativeStart), 0); + // else let from be min(relativeStart, len). + let from: Number = ConvertToRelativeIndex(relativeStart, length); - // 7. If end is undefined, let relativeEnd be len; - // else let relativeEnd be ? ToInteger(end). - let relativeEnd: Number = length; - if (arguments[2] != Undefined) { - relativeEnd = ToInteger_Inline(arguments[2]); - } + // 7. If end is undefined, let relativeEnd be len; + // else let relativeEnd be ? ToInteger(end). + let relativeEnd: Number = length; + if (arguments[2] != Undefined) { + relativeEnd = ToInteger_Inline(arguments[2]); + } - // 8. If relativeEnd < 0, let final be max((len + relativeEnd), 0); - // else let final be min(relativeEnd, len). - const final: Number = ConvertToRelativeIndex(relativeEnd, length); + // 8. If relativeEnd < 0, let final be max((len + relativeEnd), 0); + // else let final be min(relativeEnd, len). + const final: Number = ConvertToRelativeIndex(relativeEnd, length); - // 9. Let count be min(final-from, len-to). - let count: Number = Min(final - from, length - to); + // 9. Let count be min(final-from, len-to). + let count: Number = Min(final - from, length - to); - // 10. If from 0. - while (count > 0) { - // a. Let fromKey be ! ToString(from). - // b. Let toKey be ! ToString(to). - // c. Let fromPresent be ? HasProperty(O, fromKey). - const fromPresent: Boolean = HasProperty(object, from); - - // d. If fromPresent is true, then. - if (fromPresent == True) { - // i. Let fromVal be ? Get(O, fromKey). - const fromVal: JSAny = GetProperty(object, from); - - // ii. Perform ? Set(O, toKey, fromVal, true). - SetProperty(object, to, fromVal); - } else { - // i. Perform ? DeletePropertyOrThrow(O, toKey). - DeleteProperty(object, to, LanguageMode::kStrict); - } - - // f. Let from be from + direction. - from = from + direction; - - // g. Let to be to + direction. - to = to + direction; - - // h. Let count be count - 1. - --count; + // 12. Repeat, while count > 0. + while (count > 0) { + // a. Let fromKey be ! ToString(from). + // b. Let toKey be ! ToString(to). + // c. Let fromPresent be ? HasProperty(O, fromKey). + const fromPresent: Boolean = HasProperty(object, from); + + // d. If fromPresent is true, then. + if (fromPresent == True) { + // i. Let fromVal be ? Get(O, fromKey). + const fromVal: JSAny = GetProperty(object, from); + + // ii. Perform ? Set(O, toKey, fromVal, true). + SetProperty(object, to, fromVal); + } else { + // i. Perform ? DeletePropertyOrThrow(O, toKey). + DeleteProperty(object, to, LanguageMode::kStrict); } - // 13. Return O. - return object; + // f. Let from be from + direction. + from = from + direction; + + // g. Let to be to + direction. + to = to + direction; + + // h. Let count be count - 1. + --count; } + + // 13. Return O. + return object; +} } diff --git a/deps/v8/src/builtins/array-every.tq b/deps/v8/src/builtins/array-every.tq index 4e5f99d40a5202..2514a18b747ab6 100644 --- a/deps/v8/src/builtins/array-every.tq +++ b/deps/v8/src/builtins/array-every.tq @@ -3,145 +3,142 @@ // found in the LICENSE file. namespace array { - transitioning javascript builtin - ArrayEveryLoopEagerDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny { - // All continuation points in the optimized every implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - // - // Also, this great mass of casts is necessary because the signature - // of Torque javascript builtins requires JSAny type for all parameters - // other than {context}. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const numberK = Cast(initialK) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; - - return ArrayEveryLoopContinuation( - jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK, - numberLength, Undefined); +transitioning javascript builtin +ArrayEveryLoopEagerDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny { + // All continuation points in the optimized every implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + // + // Also, this great mass of casts is necessary because the signature + // of Torque javascript builtins requires JSAny type for all parameters + // other than {context}. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const numberK = Cast(initialK) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + return ArrayEveryLoopContinuation( + jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK, + numberLength, Undefined); +} + +transitioning javascript builtin +ArrayEveryLoopLazyDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny, + result: JSAny): JSAny { + // All continuation points in the optimized every implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + let numberK = Cast(initialK) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + // This custom lazy deopt point is right after the callback. every() needs + // to pick up at the next step, which is either continuing to the next + // array element or returning false if {result} is false. + if (!ToBoolean(result)) { + return False; } - transitioning javascript builtin - ArrayEveryLoopLazyDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny, - result: JSAny): JSAny { - // All continuation points in the optimized every implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - let numberK = Cast(initialK) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; - - // This custom lazy deopt point is right after the callback. every() needs - // to pick up at the next step, which is either continuing to the next - // array element or returning false if {result} is false. - if (!ToBoolean(result)) { - return False; - } + numberK = numberK + 1; - numberK = numberK + 1; + return ArrayEveryLoopContinuation( + jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK, + numberLength, Undefined); +} - return ArrayEveryLoopContinuation( - jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK, - numberLength, Undefined); - } +transitioning builtin ArrayEveryLoopContinuation(implicit context: Context)( + _receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, _array: JSAny, + o: JSReceiver, initialK: Number, length: Number, _initialTo: JSAny): JSAny { + // 5. Let k be 0. + // 6. Repeat, while k < len + for (let k: Number = initialK; k < length; k++) { + // 6a. Let Pk be ! ToString(k). + // k is guaranteed to be a positive integer, hence ToString is + // side-effect free and HasProperty/GetProperty do the conversion inline. - transitioning builtin ArrayEveryLoopContinuation(implicit context: Context)( - _receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, - _array: JSAny, o: JSReceiver, initialK: Number, length: Number, - _initialTo: JSAny): JSAny { - // 5. Let k be 0. - // 6. Repeat, while k < len - for (let k: Number = initialK; k < length; k++) { - // 6a. Let Pk be ! ToString(k). - // k is guaranteed to be a positive integer, hence ToString is - // side-effect free and HasProperty/GetProperty do the conversion inline. - - // 6b. Let kPresent be ? HasProperty(O, Pk). - const kPresent: Boolean = HasProperty_Inline(o, k); - - // 6c. If kPresent is true, then - if (kPresent == True) { - // 6c. i. Let kValue be ? Get(O, Pk). - const kValue: JSAny = GetProperty(o, k); - - // 6c. ii. Perform ? Call(callbackfn, T, ). - const result: JSAny = Call(context, callbackfn, thisArg, kValue, k, o); - - // iii. If selected is true, then... - if (!ToBoolean(result)) { - return False; - } - } + // 6b. Let kPresent be ? HasProperty(O, Pk). + const kPresent: Boolean = HasProperty_Inline(o, k); - // 6d. Increase k by 1. (done by the loop). - } - return True; - } + // 6c. If kPresent is true, then + if (kPresent == True) { + // 6c. i. Let kValue be ? Get(O, Pk). + const kValue: JSAny = GetProperty(o, k); + + // 6c. ii. Perform ? Call(callbackfn, T, ). + const result: JSAny = Call(context, callbackfn, thisArg, kValue, k, o); - transitioning macro FastArrayEvery(implicit context: Context)( - o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): JSAny - labels Bailout(Smi) { - let k: Smi = 0; - const smiLen = Cast(len) otherwise goto Bailout(k); - const fastO: FastJSArray = Cast(o) otherwise goto Bailout(k); - let fastOW = NewFastJSArrayWitness(fastO); - - // Build a fast loop over the smi array. - for (; k < smiLen; k++) { - fastOW.Recheck() otherwise goto Bailout(k); - - // Ensure that we haven't walked beyond a possibly updated length. - if (k >= fastOW.Get().length) goto Bailout(k); - const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue; - const result: JSAny = - Call(context, callbackfn, thisArg, value, k, fastOW.Get()); + // iii. If selected is true, then... if (!ToBoolean(result)) { return False; } } - return True; - } - // https://tc39.github.io/ecma262/#sec-array.prototype.every - transitioning javascript builtin - ArrayEvery(js-implicit context: NativeContext, receiver: JSAny)(...arguments): - JSAny { - try { - RequireObjectCoercible(receiver, 'Array.prototype.every'); + // 6d. Increase k by 1. (done by the loop). + } + return True; +} - // 1. Let O be ? ToObject(this value). - const o: JSReceiver = ToObject_Inline(context, receiver); +transitioning macro FastArrayEvery(implicit context: Context)( + o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): JSAny + labels Bailout(Smi) { + let k: Smi = 0; + const smiLen = Cast(len) otherwise goto Bailout(k); + const fastO: FastJSArray = Cast(o) otherwise goto Bailout(k); + let fastOW = NewFastJSArrayWitness(fastO); + + // Build a fast loop over the smi array. + for (; k < smiLen; k++) { + fastOW.Recheck() otherwise goto Bailout(k); + + // Ensure that we haven't walked beyond a possibly updated length. + if (k >= fastOW.Get().length) goto Bailout(k); + const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue; + const result: JSAny = + Call(context, callbackfn, thisArg, value, k, fastOW.Get()); + if (!ToBoolean(result)) { + return False; + } + } + return True; +} - // 2. Let len be ? ToLength(? Get(O, "length")). - const len: Number = GetLengthProperty(o); +// https://tc39.github.io/ecma262/#sec-array.prototype.every +transitioning javascript builtin +ArrayEvery( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + try { + RequireObjectCoercible(receiver, 'Array.prototype.every'); - // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - if (arguments.length == 0) { - goto TypeError; - } - const callbackfn = Cast(arguments[0]) otherwise TypeError; + // 1. Let O be ? ToObject(this value). + const o: JSReceiver = ToObject_Inline(context, receiver); - // 4. If thisArg is present, let T be thisArg; else let T be undefined. - const thisArg: JSAny = arguments.length > 1 ? arguments[1] : Undefined; + // 2. Let len be ? ToLength(? Get(O, "length")). + const len: Number = GetLengthProperty(o); - // Special cases. - try { - return FastArrayEvery(o, len, callbackfn, thisArg) - otherwise Bailout; - } - label Bailout(kValue: Smi) deferred { - return ArrayEveryLoopContinuation( - o, callbackfn, thisArg, Undefined, o, kValue, len, Undefined); - } + // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. + if (arguments.length == 0) { + goto TypeError; } - label TypeError deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); + const callbackfn = Cast(arguments[0]) otherwise TypeError; + + // 4. If thisArg is present, let T be thisArg; else let T be undefined. + const thisArg: JSAny = arguments[1]; + + // Special cases. + try { + return FastArrayEvery(o, len, callbackfn, thisArg) + otherwise Bailout; + } label Bailout(kValue: Smi) deferred { + return ArrayEveryLoopContinuation( + o, callbackfn, thisArg, Undefined, o, kValue, len, Undefined); } + } label TypeError deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); } } +} diff --git a/deps/v8/src/builtins/array-filter.tq b/deps/v8/src/builtins/array-filter.tq index 1da1c551664675..1add88fa6a41ef 100644 --- a/deps/v8/src/builtins/array-filter.tq +++ b/deps/v8/src/builtins/array-filter.tq @@ -3,200 +3,197 @@ // found in the LICENSE file. namespace array { - transitioning javascript builtin - ArrayFilterLoopEagerDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny, - length: JSAny, initialTo: JSAny): JSAny { - // All continuation points in the optimized filter implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - // - // Also, this great mass of casts is necessary because the signature - // of Torque javascript builtins requires JSAny type for all parameters - // other than {context}. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const outputArray = Cast(array) otherwise unreachable; - const numberK = Cast(initialK) otherwise unreachable; - const numberTo = Cast(initialTo) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; +transitioning javascript builtin +ArrayFilterLoopEagerDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny, + length: JSAny, initialTo: JSAny): JSAny { + // All continuation points in the optimized filter implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + // + // Also, this great mass of casts is necessary because the signature + // of Torque javascript builtins requires JSAny type for all parameters + // other than {context}. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const outputArray = Cast(array) otherwise unreachable; + const numberK = Cast(initialK) otherwise unreachable; + const numberTo = Cast(initialTo) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + return ArrayFilterLoopContinuation( + jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK, + numberLength, numberTo); +} - return ArrayFilterLoopContinuation( - jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK, - numberLength, numberTo); +transitioning javascript builtin +ArrayFilterLoopLazyDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny, + length: JSAny, valueK: JSAny, initialTo: JSAny, result: JSAny): JSAny { + // All continuation points in the optimized filter implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const outputArray = Cast(array) otherwise unreachable; + let numberK = Cast(initialK) otherwise unreachable; + let numberTo = Cast(initialTo) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + // This custom lazy deopt point is right after the callback. filter() needs + // to pick up at the next step, which is setting the callback + // result in the output array. After incrementing k and to, we can glide + // into the loop continuation builtin. + if (ToBoolean(result)) { + FastCreateDataProperty(outputArray, numberTo, valueK); + numberTo = numberTo + 1; } - transitioning javascript builtin - ArrayFilterLoopLazyDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny, - length: JSAny, valueK: JSAny, initialTo: JSAny, result: JSAny): JSAny { - // All continuation points in the optimized filter implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const outputArray = Cast(array) otherwise unreachable; - let numberK = Cast(initialK) otherwise unreachable; - let numberTo = Cast(initialTo) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; - - // This custom lazy deopt point is right after the callback. filter() needs - // to pick up at the next step, which is setting the callback - // result in the output array. After incrementing k and to, we can glide - // into the loop continuation builtin. - if (ToBoolean(result)) { - FastCreateDataProperty(outputArray, numberTo, valueK); - numberTo = numberTo + 1; - } - - numberK = numberK + 1; + numberK = numberK + 1; - return ArrayFilterLoopContinuation( - jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK, - numberLength, numberTo); - } + return ArrayFilterLoopContinuation( + jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK, + numberLength, numberTo); +} - transitioning builtin ArrayFilterLoopContinuation(implicit context: Context)( - _receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, - array: JSReceiver, o: JSReceiver, initialK: Number, length: Number, - initialTo: Number): JSAny { - let to: Number = initialTo; - // 5. Let k be 0. - // 6. Repeat, while k < len - for (let k: Number = initialK; k < length; k++) { - // 6a. Let Pk be ! ToString(k). - // k is guaranteed to be a positive integer, hence ToString is - // side-effect free and HasProperty/GetProperty do the conversion inline. - - // 6b. Let kPresent be ? HasProperty(O, Pk). - const kPresent: Boolean = HasProperty_Inline(o, k); - - // 6c. If kPresent is true, then - if (kPresent == True) { - // 6c. i. Let kValue be ? Get(O, Pk). - const kValue: JSAny = GetProperty(o, k); - - // 6c. ii. Perform ? Call(callbackfn, T, ). - const result: JSAny = Call(context, callbackfn, thisArg, kValue, k, o); - - // iii. If selected is true, then... - if (ToBoolean(result)) { - // 1. Perform ? CreateDataPropertyOrThrow(A, ToString(to), kValue). - FastCreateDataProperty(array, to, kValue); - // 2. Increase to by 1. - to = to + 1; - } +transitioning builtin ArrayFilterLoopContinuation(implicit context: Context)( + _receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, + array: JSReceiver, o: JSReceiver, initialK: Number, length: Number, + initialTo: Number): JSAny { + let to: Number = initialTo; + // 5. Let k be 0. + // 6. Repeat, while k < len + for (let k: Number = initialK; k < length; k++) { + // 6a. Let Pk be ! ToString(k). + // k is guaranteed to be a positive integer, hence ToString is + // side-effect free and HasProperty/GetProperty do the conversion inline. + + // 6b. Let kPresent be ? HasProperty(O, Pk). + const kPresent: Boolean = HasProperty_Inline(o, k); + + // 6c. If kPresent is true, then + if (kPresent == True) { + // 6c. i. Let kValue be ? Get(O, Pk). + const kValue: JSAny = GetProperty(o, k); + + // 6c. ii. Perform ? Call(callbackfn, T, ). + const result: JSAny = Call(context, callbackfn, thisArg, kValue, k, o); + + // iii. If selected is true, then... + if (ToBoolean(result)) { + // 1. Perform ? CreateDataPropertyOrThrow(A, ToString(to), kValue). + FastCreateDataProperty(array, to, kValue); + // 2. Increase to by 1. + to = to + 1; } - - // 6d. Increase k by 1. (done by the loop). } - return array; + + // 6d. Increase k by 1. (done by the loop). } + return array; +} - transitioning macro FastArrayFilter(implicit context: Context)( - fastO: FastJSArray, len: Smi, callbackfn: Callable, thisArg: JSAny, - output: FastJSArray) labels Bailout(Number, Number) { - let k: Smi = 0; - let to: Smi = 0; - let fastOW = NewFastJSArrayWitness(fastO); - let fastOutputW = NewFastJSArrayWitness(output); - - fastOutputW.EnsureArrayPushable() otherwise goto Bailout(k, to); - - // Build a fast loop over the array. - for (; k < len; k++) { - fastOW.Recheck() otherwise goto Bailout(k, to); - - // Ensure that we haven't walked beyond a possibly updated length. - if (k >= fastOW.Get().length) goto Bailout(k, to); - const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue; - const result: JSAny = - Call(context, callbackfn, thisArg, value, k, fastOW.Get()); - if (ToBoolean(result)) { - try { - // Since the call to {callbackfn} is observable, we can't - // use the Bailout label until we've successfully stored. - // Hence the {SlowStore} label. - fastOutputW.Recheck() otherwise SlowStore; - if (fastOutputW.Get().length != to) goto SlowStore; - fastOutputW.Push(value) otherwise SlowStore; - } - label SlowStore { - FastCreateDataProperty(fastOutputW.stable, to, value); - } - to = to + 1; +transitioning macro FastArrayFilter(implicit context: Context)( + fastO: FastJSArray, len: Smi, callbackfn: Callable, thisArg: JSAny, + output: FastJSArray) labels +Bailout(Number, Number) { + let k: Smi = 0; + let to: Smi = 0; + let fastOW = NewFastJSArrayWitness(fastO); + let fastOutputW = NewFastJSArrayWitness(output); + + fastOutputW.EnsureArrayPushable() otherwise goto Bailout(k, to); + + // Build a fast loop over the array. + for (; k < len; k++) { + fastOW.Recheck() otherwise goto Bailout(k, to); + + // Ensure that we haven't walked beyond a possibly updated length. + if (k >= fastOW.Get().length) goto Bailout(k, to); + const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue; + const result: JSAny = + Call(context, callbackfn, thisArg, value, k, fastOW.Get()); + if (ToBoolean(result)) { + try { + // Since the call to {callbackfn} is observable, we can't + // use the Bailout label until we've successfully stored. + // Hence the {SlowStore} label. + fastOutputW.Recheck() otherwise SlowStore; + if (fastOutputW.Get().length != to) goto SlowStore; + fastOutputW.Push(value) otherwise SlowStore; + } label SlowStore { + FastCreateDataProperty(fastOutputW.stable, to, value); } + to = to + 1; } } +} - // This method creates a 0-length array with the ElementsKind of the - // receiver if possible, otherwise, bails out. It makes sense for the - // caller to know that the slow case needs to be invoked. - macro FastFilterSpeciesCreate(implicit context: Context)( - receiver: JSReceiver): JSReceiver labels Slow { - const len: Smi = 0; - if (IsArraySpeciesProtectorCellInvalid()) goto Slow; - const o = Cast(receiver) otherwise Slow; - const newMap: Map = - LoadJSArrayElementsMap(o.map.elements_kind, LoadNativeContext(context)); - return AllocateJSArray(ElementsKind::PACKED_SMI_ELEMENTS, newMap, len, len); - } +// This method creates a 0-length array with the ElementsKind of the +// receiver if possible, otherwise, bails out. It makes sense for the +// caller to know that the slow case needs to be invoked. +macro FastFilterSpeciesCreate(implicit context: Context)(receiver: JSReceiver): + JSReceiver labels Slow { + const len: Smi = 0; + if (IsArraySpeciesProtectorCellInvalid()) goto Slow; + const o = Cast(receiver) otherwise Slow; + const newMap: Map = + LoadJSArrayElementsMap(o.map.elements_kind, LoadNativeContext(context)); + return AllocateJSArray(ElementsKind::PACKED_SMI_ELEMENTS, newMap, len, len); +} - // https://tc39.github.io/ecma262/#sec-array.prototype.filter - transitioning javascript builtin - ArrayFilter(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): JSAny { - try { - RequireObjectCoercible(receiver, 'Array.prototype.filter'); +// https://tc39.github.io/ecma262/#sec-array.prototype.filter +transitioning javascript builtin +ArrayFilter( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + try { + RequireObjectCoercible(receiver, 'Array.prototype.filter'); - // 1. Let O be ? ToObject(this value). - const o: JSReceiver = ToObject_Inline(context, receiver); + // 1. Let O be ? ToObject(this value). + const o: JSReceiver = ToObject_Inline(context, receiver); - // 2. Let len be ? ToLength(? Get(O, "length")). - const len: Number = GetLengthProperty(o); + // 2. Let len be ? ToLength(? Get(O, "length")). + const len: Number = GetLengthProperty(o); - // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - if (arguments.length == 0) { - goto TypeError; - } - const callbackfn = Cast(arguments[0]) otherwise TypeError; + // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. + if (arguments.length == 0) { + goto TypeError; + } + const callbackfn = Cast(arguments[0]) otherwise TypeError; - // 4. If thisArg is present, let T be thisArg; else let T be undefined. - const thisArg: JSAny = arguments.length > 1 ? arguments[1] : Undefined; - let output: JSReceiver; + // 4. If thisArg is present, let T be thisArg; else let T be undefined. + const thisArg: JSAny = arguments[1]; + let output: JSReceiver; + + // Special cases. + let k: Number = 0; + let to: Number = 0; + try { + output = FastFilterSpeciesCreate(o) otherwise SlowSpeciesCreate; - // Special cases. - let k: Number = 0; - let to: Number = 0; try { - output = FastFilterSpeciesCreate(o) otherwise SlowSpeciesCreate; - - try { - const smiLen: Smi = Cast(len) otherwise goto Bailout(k, to); - const fastOutput = - Cast(output) otherwise goto Bailout(k, to); - const fastO = Cast(o) otherwise goto Bailout(k, to); - - FastArrayFilter(fastO, smiLen, callbackfn, thisArg, fastOutput) - otherwise Bailout; - return output; - } - label Bailout(kValue: Number, toValue: Number) deferred { - k = kValue; - to = toValue; - } - } - label SlowSpeciesCreate { - output = ArraySpeciesCreate(context, receiver, 0); + const smiLen: Smi = Cast(len) otherwise goto Bailout(k, to); + const fastOutput = + Cast(output) otherwise goto Bailout(k, to); + const fastO = Cast(o) otherwise goto Bailout(k, to); + + FastArrayFilter(fastO, smiLen, callbackfn, thisArg, fastOutput) + otherwise Bailout; + return output; + } label Bailout(kValue: Number, toValue: Number) deferred { + k = kValue; + to = toValue; } - - return ArrayFilterLoopContinuation( - o, callbackfn, thisArg, output, o, k, len, to); - } - label TypeError deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); + } label SlowSpeciesCreate { + output = ArraySpeciesCreate(context, receiver, 0); } + + return ArrayFilterLoopContinuation( + o, callbackfn, thisArg, output, o, k, len, to); + } label TypeError deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); } } +} diff --git a/deps/v8/src/builtins/array-find.tq b/deps/v8/src/builtins/array-find.tq index cd3ec45e98e7fe..9b53f9c7000673 100644 --- a/deps/v8/src/builtins/array-find.tq +++ b/deps/v8/src/builtins/array-find.tq @@ -3,152 +3,149 @@ // found in the LICENSE file. namespace array { - transitioning javascript builtin - ArrayFindLoopEagerDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny { - // All continuation points in the optimized find implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - // - // Also, this great mass of casts is necessary because the signature - // of Torque javascript builtins requires JSAny type for all parameters - // other than {context}. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const numberK = Cast(initialK) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; - - return ArrayFindLoopContinuation( - jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength); - } +transitioning javascript builtin +ArrayFindLoopEagerDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny { + // All continuation points in the optimized find implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + // + // Also, this great mass of casts is necessary because the signature + // of Torque javascript builtins requires JSAny type for all parameters + // other than {context}. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const numberK = Cast(initialK) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + return ArrayFindLoopContinuation( + jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength); +} + +transitioning javascript builtin +ArrayFindLoopLazyDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + _callback: JSAny, _thisArg: JSAny, _initialK: JSAny, _length: JSAny, + _result: JSAny): JSAny { + // This deopt continuation point is never actually called, it just + // exists to make stack traces correct from a ThrowTypeError if the + // callback was found to be non-callable. + unreachable; +} - transitioning javascript builtin - ArrayFindLoopLazyDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - _callback: JSAny, _thisArg: JSAny, _initialK: JSAny, _length: JSAny, - _result: JSAny): JSAny { - // This deopt continuation point is never actually called, it just - // exists to make stack traces correct from a ThrowTypeError if the - // callback was found to be non-callable. - unreachable; +// Continuation that is called after a lazy deoptimization from TF that +// happens right after the callback and it's returned value must be handled +// before iteration continues. +transitioning javascript builtin +ArrayFindLoopAfterCallbackLazyDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny, + foundValue: JSAny, isFound: JSAny): JSAny { + // All continuation points in the optimized find implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const numberK = Cast(initialK) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + // This custom lazy deopt point is right after the callback. find() needs + // to pick up at the next step, which is returning the element if the + // callback value is truthy. Otherwise, continue the search by calling the + // continuation. + + if (ToBoolean(isFound)) { + return foundValue; } - // Continuation that is called after a lazy deoptimization from TF that - // happens right after the callback and it's returned value must be handled - // before iteration continues. - transitioning javascript builtin - ArrayFindLoopAfterCallbackLazyDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny, - foundValue: JSAny, isFound: JSAny): JSAny { - // All continuation points in the optimized find implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const numberK = Cast(initialK) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; - - // This custom lazy deopt point is right after the callback. find() needs - // to pick up at the next step, which is returning the element if the - // callback value is truthy. Otherwise, continue the search by calling the - // continuation. - - if (ToBoolean(isFound)) { - return foundValue; + return ArrayFindLoopContinuation( + jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength); +} + +transitioning builtin ArrayFindLoopContinuation(implicit context: Context)( + _receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, o: JSReceiver, + initialK: Number, length: Number): JSAny { + // 5. Let k be 0. + // 6. Repeat, while k < len + for (let k: Number = initialK; k < length; k++) { + // 6a. Let Pk be ! ToString(k). + // k is guaranteed to be a positive integer, hence ToString is + // side-effect free and HasProperty/GetProperty do the conversion inline. + + // 6b. i. Let kValue be ? Get(O, Pk). + const value: JSAny = GetProperty(o, k); + + // 6c. Let testResult be ToBoolean(? Call(predicate, T, <>)). + const testResult: JSAny = Call(context, callbackfn, thisArg, value, k, o); + + // 6d. If testResult is true, return kValue. + if (ToBoolean(testResult)) { + return value; } - return ArrayFindLoopContinuation( - jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength); + // 6e. Increase k by 1. (done by the loop). } + return Undefined; +} - transitioning builtin ArrayFindLoopContinuation(implicit context: Context)( - _receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, - o: JSReceiver, initialK: Number, length: Number): JSAny { - // 5. Let k be 0. - // 6. Repeat, while k < len - for (let k: Number = initialK; k < length; k++) { - // 6a. Let Pk be ! ToString(k). - // k is guaranteed to be a positive integer, hence ToString is - // side-effect free and HasProperty/GetProperty do the conversion inline. - - // 6b. i. Let kValue be ? Get(O, Pk). - const value: JSAny = GetProperty(o, k); - - // 6c. Let testResult be ToBoolean(? Call(predicate, T, <>)). - const testResult: JSAny = Call(context, callbackfn, thisArg, value, k, o); - - // 6d. If testResult is true, return kValue. - if (ToBoolean(testResult)) { - return value; - } - - // 6e. Increase k by 1. (done by the loop). +transitioning macro FastArrayFind(implicit context: Context)( + o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): JSAny + labels Bailout(Smi) { + let k: Smi = 0; + const smiLen = Cast(len) otherwise goto Bailout(k); + const fastO = Cast(o) otherwise goto Bailout(k); + let fastOW = NewFastJSArrayWitness(fastO); + + // Build a fast loop over the smi array. + for (; k < smiLen; k++) { + fastOW.Recheck() otherwise goto Bailout(k); + + // Ensure that we haven't walked beyond a possibly updated length. + if (k >= fastOW.Get().length) goto Bailout(k); + + const value: JSAny = fastOW.LoadElementOrUndefined(k); + const testResult: JSAny = + Call(context, callbackfn, thisArg, value, k, fastOW.Get()); + if (ToBoolean(testResult)) { + return value; } - return Undefined; } + return Undefined; +} - transitioning macro FastArrayFind(implicit context: Context)( - o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): JSAny - labels Bailout(Smi) { - let k: Smi = 0; - const smiLen = Cast(len) otherwise goto Bailout(k); - const fastO = Cast(o) otherwise goto Bailout(k); - let fastOW = NewFastJSArrayWitness(fastO); - - // Build a fast loop over the smi array. - for (; k < smiLen; k++) { - fastOW.Recheck() otherwise goto Bailout(k); - - // Ensure that we haven't walked beyond a possibly updated length. - if (k >= fastOW.Get().length) goto Bailout(k); - - const value: JSAny = fastOW.LoadElementOrUndefined(k); - const testResult: JSAny = - Call(context, callbackfn, thisArg, value, k, fastOW.Get()); - if (ToBoolean(testResult)) { - return value; - } +// https://tc39.github.io/ecma262/#sec-array.prototype.find +transitioning javascript builtin +ArrayPrototypeFind( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + try { + RequireObjectCoercible(receiver, 'Array.prototype.find'); + + // 1. Let O be ? ToObject(this value). + const o: JSReceiver = ToObject_Inline(context, receiver); + + // 2. Let len be ? ToLength(? Get(O, "length")). + const len: Number = GetLengthProperty(o); + + // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. + if (arguments.length == 0) { + goto NotCallableError; } - return Undefined; - } + const callbackfn = Cast(arguments[0]) otherwise NotCallableError; - // https://tc39.github.io/ecma262/#sec-array.prototype.find - transitioning javascript builtin - ArrayPrototypeFind(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): JSAny { + // 4. If thisArg is present, let T be thisArg; else let T be undefined. + const thisArg: JSAny = arguments[1]; + + // Special cases. try { - RequireObjectCoercible(receiver, 'Array.prototype.find'); - - // 1. Let O be ? ToObject(this value). - const o: JSReceiver = ToObject_Inline(context, receiver); - - // 2. Let len be ? ToLength(? Get(O, "length")). - const len: Number = GetLengthProperty(o); - - // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - if (arguments.length == 0) { - goto NotCallableError; - } - const callbackfn = - Cast(arguments[0]) otherwise NotCallableError; - - // 4. If thisArg is present, let T be thisArg; else let T be undefined. - const thisArg: JSAny = arguments.length > 1 ? arguments[1] : Undefined; - - // Special cases. - try { - return FastArrayFind(o, len, callbackfn, thisArg) - otherwise Bailout; - } - label Bailout(k: Smi) deferred { - return ArrayFindLoopContinuation(o, callbackfn, thisArg, o, k, len); - } - } - label NotCallableError deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); + return FastArrayFind(o, len, callbackfn, thisArg) + otherwise Bailout; + } label Bailout(k: Smi) deferred { + return ArrayFindLoopContinuation(o, callbackfn, thisArg, o, k, len); } + } label NotCallableError deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); } } +} diff --git a/deps/v8/src/builtins/array-findindex.tq b/deps/v8/src/builtins/array-findindex.tq index 05a264064669b1..ed70a122594e42 100644 --- a/deps/v8/src/builtins/array-findindex.tq +++ b/deps/v8/src/builtins/array-findindex.tq @@ -3,154 +3,149 @@ // found in the LICENSE file. namespace array { - transitioning javascript builtin - ArrayFindIndexLoopEagerDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny { - // All continuation points in the optimized findIndex implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - // - // Also, this great mass of casts is necessary because the signature - // of Torque javascript builtins requires JSAny type for all parameters - // other than {context}. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const numberK = Cast(initialK) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; - - return ArrayFindIndexLoopContinuation( - jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength); - } +transitioning javascript builtin +ArrayFindIndexLoopEagerDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny { + // All continuation points in the optimized findIndex implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + // + // Also, this great mass of casts is necessary because the signature + // of Torque javascript builtins requires JSAny type for all parameters + // other than {context}. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const numberK = Cast(initialK) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + return ArrayFindIndexLoopContinuation( + jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength); +} + +transitioning javascript builtin +ArrayFindIndexLoopLazyDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + _callback: JSAny, _thisArg: JSAny, _initialK: JSAny, _length: JSAny, + _result: JSAny): JSAny { + // This deopt continuation point is never actually called, it just + // exists to make stack traces correct from a ThrowTypeError if the + // callback was found to be non-callable. + unreachable; +} - transitioning javascript builtin - ArrayFindIndexLoopLazyDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - _callback: JSAny, _thisArg: JSAny, _initialK: JSAny, _length: JSAny, - _result: JSAny): JSAny { - // This deopt continuation point is never actually called, it just - // exists to make stack traces correct from a ThrowTypeError if the - // callback was found to be non-callable. - unreachable; +// Continuation that is called after a lazy deoptimization from TF that +// happens right after the callback and it's returned value must be handled +// before iteration continues. +transitioning javascript builtin +ArrayFindIndexLoopAfterCallbackLazyDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny, + foundValue: JSAny, isFound: JSAny): JSAny { + // All continuation points in the optimized findIndex implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const numberK = Cast(initialK) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + // This custom lazy deopt point is right after the callback. find() needs + // to pick up at the next step, which is returning the element if the + // callback value is truthy. Otherwise, continue the search by calling the + // continuation. + + if (ToBoolean(isFound)) { + return foundValue; } - // Continuation that is called after a lazy deoptimization from TF that - // happens right after the callback and it's returned value must be handled - // before iteration continues. - transitioning javascript builtin - ArrayFindIndexLoopAfterCallbackLazyDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny, - foundValue: JSAny, isFound: JSAny): JSAny { - // All continuation points in the optimized findIndex implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const numberK = Cast(initialK) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; - - // This custom lazy deopt point is right after the callback. find() needs - // to pick up at the next step, which is returning the element if the - // callback value is truthy. Otherwise, continue the search by calling the - // continuation. - - if (ToBoolean(isFound)) { - return foundValue; + return ArrayFindIndexLoopContinuation( + jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength); +} + +transitioning builtin ArrayFindIndexLoopContinuation(implicit context: Context)( + _receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, o: JSReceiver, + initialK: Number, length: Number): Number { + // 5. Let k be 0. + // 6. Repeat, while k < len + for (let k: Number = initialK; k < length; k++) { + // 6a. Let Pk be ! ToString(k). + // k is guaranteed to be a positive integer, hence ToString is + // side-effect free and HasProperty/GetProperty do the conversion inline. + + // 6b. i. Let kValue be ? Get(O, Pk). + const value: JSAny = GetProperty(o, k); + + // 6c. Let testResult be ToBoolean(? Call(predicate, T, <>)). + const testResult: JSAny = Call(context, callbackfn, thisArg, value, k, o); + + // 6d. If testResult is true, return k. + if (ToBoolean(testResult)) { + return k; } - return ArrayFindIndexLoopContinuation( - jsreceiver, callbackfn, thisArg, jsreceiver, numberK, numberLength); + // 6e. Increase k by 1. (done by the loop). } + return Convert(-1); +} - transitioning builtin ArrayFindIndexLoopContinuation(implicit context: - Context)( - _receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, - o: JSReceiver, initialK: Number, length: Number): Number { - // 5. Let k be 0. - // 6. Repeat, while k < len - for (let k: Number = initialK; k < length; k++) { - // 6a. Let Pk be ! ToString(k). - // k is guaranteed to be a positive integer, hence ToString is - // side-effect free and HasProperty/GetProperty do the conversion inline. - - // 6b. i. Let kValue be ? Get(O, Pk). - const value: JSAny = GetProperty(o, k); - - // 6c. Let testResult be ToBoolean(? Call(predicate, T, <>)). - const testResult: JSAny = Call(context, callbackfn, thisArg, value, k, o); - - // 6d. If testResult is true, return k. - if (ToBoolean(testResult)) { - return k; - } - - // 6e. Increase k by 1. (done by the loop). +transitioning macro FastArrayFindIndex(implicit context: Context)( + o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): Number + labels Bailout(Smi) { + let k: Smi = 0; + const smiLen = Cast(len) otherwise goto Bailout(k); + const fastO = Cast(o) otherwise goto Bailout(k); + let fastOW = NewFastJSArrayWitness(fastO); + + // Build a fast loop over the smi array. + for (; k < smiLen; k++) { + fastOW.Recheck() otherwise goto Bailout(k); + + // Ensure that we haven't walked beyond a possibly updated length. + if (k >= fastOW.Get().length) goto Bailout(k); + + const value: JSAny = fastOW.LoadElementOrUndefined(k); + const testResult: JSAny = + Call(context, callbackfn, thisArg, value, k, fastOW.Get()); + if (ToBoolean(testResult)) { + return k; } - return Convert(-1); } + return -1; +} - transitioning macro FastArrayFindIndex(implicit context: Context)( - o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): Number - labels Bailout(Smi) { - let k: Smi = 0; - const smiLen = Cast(len) otherwise goto Bailout(k); - const fastO = Cast(o) otherwise goto Bailout(k); - let fastOW = NewFastJSArrayWitness(fastO); - - // Build a fast loop over the smi array. - for (; k < smiLen; k++) { - fastOW.Recheck() otherwise goto Bailout(k); - - // Ensure that we haven't walked beyond a possibly updated length. - if (k >= fastOW.Get().length) goto Bailout(k); - - const value: JSAny = fastOW.LoadElementOrUndefined(k); - const testResult: JSAny = - Call(context, callbackfn, thisArg, value, k, fastOW.Get()); - if (ToBoolean(testResult)) { - return k; - } +// https://tc39.github.io/ecma262/#sec-array.prototype.findIndex +transitioning javascript builtin +ArrayPrototypeFindIndex( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + try { + RequireObjectCoercible(receiver, 'Array.prototype.findIndex'); + + // 1. Let O be ? ToObject(this value). + const o: JSReceiver = ToObject_Inline(context, receiver); + + // 2. Let len be ? ToLength(? Get(O, "length")). + const len: Number = GetLengthProperty(o); + + // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. + if (arguments.length == 0) { + goto NotCallableError; } - return -1; - } + const callbackfn = Cast(arguments[0]) otherwise NotCallableError; - // https://tc39.github.io/ecma262/#sec-array.prototype.findIndex - transitioning javascript builtin - ArrayPrototypeFindIndex(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): JSAny { + // 4. If thisArg is present, let T be thisArg; else let T be undefined. + const thisArg: JSAny = arguments[1]; + + // Special cases. try { - RequireObjectCoercible(receiver, 'Array.prototype.findIndex'); - - // 1. Let O be ? ToObject(this value). - const o: JSReceiver = ToObject_Inline(context, receiver); - - // 2. Let len be ? ToLength(? Get(O, "length")). - const len: Number = GetLengthProperty(o); - - // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - if (arguments.length == 0) { - goto NotCallableError; - } - const callbackfn = - Cast(arguments[0]) otherwise NotCallableError; - - // 4. If thisArg is present, let T be thisArg; else let T be undefined. - const thisArg: JSAny = arguments.length > 1 ? arguments[1] : Undefined; - - // Special cases. - try { - return FastArrayFindIndex(o, len, callbackfn, thisArg) - otherwise Bailout; - } - label Bailout(k: Smi) deferred { - return ArrayFindIndexLoopContinuation( - o, callbackfn, thisArg, o, k, len); - } - } - label NotCallableError deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); + return FastArrayFindIndex(o, len, callbackfn, thisArg) + otherwise Bailout; + } label Bailout(k: Smi) deferred { + return ArrayFindIndexLoopContinuation(o, callbackfn, thisArg, o, k, len); } + } label NotCallableError deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); } } +} diff --git a/deps/v8/src/builtins/array-foreach.tq b/deps/v8/src/builtins/array-foreach.tq index b30c8533e69f06..938210dcdc3c61 100644 --- a/deps/v8/src/builtins/array-foreach.tq +++ b/deps/v8/src/builtins/array-foreach.tq @@ -3,129 +3,126 @@ // found in the LICENSE file. namespace array { - transitioning javascript builtin - ArrayForEachLoopEagerDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny { - // All continuation points in the optimized forEach implemntation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const numberK = Cast(initialK) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; +transitioning javascript builtin +ArrayForEachLoopEagerDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny { + // All continuation points in the optimized forEach implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const numberK = Cast(initialK) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + return ArrayForEachLoopContinuation( + jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK, + numberLength, Undefined); +} - return ArrayForEachLoopContinuation( - jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK, - numberLength, Undefined); - } +transitioning javascript builtin +ArrayForEachLoopLazyDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny, + _result: JSAny): JSAny { + // All continuation points in the optimized forEach implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const numberK = Cast(initialK) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + return ArrayForEachLoopContinuation( + jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK, + numberLength, Undefined); +} - transitioning javascript builtin - ArrayForEachLoopLazyDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny, - _result: JSAny): JSAny { - // All continuation points in the optimized forEach implemntation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const numberK = Cast(initialK) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; +transitioning builtin ArrayForEachLoopContinuation(implicit context: Context)( + _receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, _array: JSAny, + o: JSReceiver, initialK: Number, len: Number, _to: JSAny): JSAny { + // variables {array} and {to} are ignored. - return ArrayForEachLoopContinuation( - jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK, - numberLength, Undefined); - } + // 5. Let k be 0. + // 6. Repeat, while k < len + for (let k: Number = initialK; k < len; k = k + 1) { + // 6a. Let Pk be ! ToString(k). + // k is guaranteed to be a positive integer, hence ToString is + // side-effect free and HasProperty/GetProperty do the conversion inline. - transitioning builtin ArrayForEachLoopContinuation(implicit context: Context)( - _receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, - _array: JSAny, o: JSReceiver, initialK: Number, len: Number, - _to: JSAny): JSAny { - // variables {array} and {to} are ignored. + // 6b. Let kPresent be ? HasProperty(O, Pk). + const kPresent: Boolean = HasProperty_Inline(o, k); - // 5. Let k be 0. - // 6. Repeat, while k < len - for (let k: Number = initialK; k < len; k = k + 1) { - // 6a. Let Pk be ! ToString(k). - // k is guaranteed to be a positive integer, hence ToString is - // side-effect free and HasProperty/GetProperty do the conversion inline. + // 6c. If kPresent is true, then + if (kPresent == True) { + // 6c. i. Let kValue be ? Get(O, Pk). + const kValue: JSAny = GetProperty(o, k); - // 6b. Let kPresent be ? HasProperty(O, Pk). - const kPresent: Boolean = HasProperty_Inline(o, k); - - // 6c. If kPresent is true, then - if (kPresent == True) { - // 6c. i. Let kValue be ? Get(O, Pk). - const kValue: JSAny = GetProperty(o, k); + // 6c. ii. Perform ? Call(callbackfn, T, ). + Call(context, callbackfn, thisArg, kValue, k, o); + } - // 6c. ii. Perform ? Call(callbackfn, T, ). - Call(context, callbackfn, thisArg, kValue, k, o); - } + // 6d. Increase k by 1. (done by the loop). + } + return Undefined; +} - // 6d. Increase k by 1. (done by the loop). - } - return Undefined; +transitioning macro FastArrayForEach(implicit context: Context)( + o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): JSAny + labels Bailout(Smi) { + let k: Smi = 0; + const smiLen = Cast(len) otherwise goto Bailout(k); + const fastO = Cast(o) otherwise goto Bailout(k); + let fastOW = NewFastJSArrayWitness(fastO); + + // Build a fast loop over the smi array. + for (; k < smiLen; k++) { + fastOW.Recheck() otherwise goto Bailout(k); + + // Ensure that we haven't walked beyond a possibly updated length. + if (k >= fastOW.Get().length) goto Bailout(k); + const value: JSAny = fastOW.LoadElementNoHole(k) + otherwise continue; + Call(context, callbackfn, thisArg, value, k, fastOW.Get()); } + return Undefined; +} + +// https://tc39.github.io/ecma262/#sec-array.prototype.foreach +transitioning javascript builtin +ArrayForEach( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + try { + RequireObjectCoercible(receiver, 'Array.prototype.forEach'); + + // 1. Let O be ? ToObject(this value). + const o: JSReceiver = ToObject_Inline(context, receiver); - transitioning macro FastArrayForEach(implicit context: Context)( - o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): JSAny - labels Bailout(Smi) { - let k: Smi = 0; - const smiLen = Cast(len) otherwise goto Bailout(k); - const fastO = Cast(o) otherwise goto Bailout(k); - let fastOW = NewFastJSArrayWitness(fastO); - - // Build a fast loop over the smi array. - for (; k < smiLen; k++) { - fastOW.Recheck() otherwise goto Bailout(k); - - // Ensure that we haven't walked beyond a possibly updated length. - if (k >= fastOW.Get().length) goto Bailout(k); - const value: JSAny = fastOW.LoadElementNoHole(k) - otherwise continue; - Call(context, callbackfn, thisArg, value, k, fastOW.Get()); + // 2. Let len be ? ToLength(? Get(O, "length")). + const len: Number = GetLengthProperty(o); + + // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. + if (arguments.length == 0) { + goto TypeError; } - return Undefined; - } + const callbackfn = Cast(arguments[0]) otherwise TypeError; - // https://tc39.github.io/ecma262/#sec-array.prototype.foreach - transitioning javascript builtin - ArrayForEach(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): JSAny { + // 4. If thisArg is present, let T be thisArg; else let T be undefined. + const thisArg: JSAny = arguments[1]; + + // Special cases. + let k: Number = 0; try { - RequireObjectCoercible(receiver, 'Array.prototype.forEach'); - - // 1. Let O be ? ToObject(this value). - const o: JSReceiver = ToObject_Inline(context, receiver); - - // 2. Let len be ? ToLength(? Get(O, "length")). - const len: Number = GetLengthProperty(o); - - // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - if (arguments.length == 0) { - goto TypeError; - } - const callbackfn = Cast(arguments[0]) otherwise TypeError; - - // 4. If thisArg is present, let T be thisArg; else let T be undefined. - const thisArg: JSAny = arguments.length > 1 ? arguments[1] : Undefined; - - // Special cases. - let k: Number = 0; - try { - return FastArrayForEach(o, len, callbackfn, thisArg) - otherwise Bailout; - } - label Bailout(kValue: Smi) deferred { - k = kValue; - } - - return ArrayForEachLoopContinuation( - o, callbackfn, thisArg, Undefined, o, k, len, Undefined); - } - label TypeError deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); + return FastArrayForEach(o, len, callbackfn, thisArg) + otherwise Bailout; + } label Bailout(kValue: Smi) deferred { + k = kValue; } + + return ArrayForEachLoopContinuation( + o, callbackfn, thisArg, Undefined, o, k, len, Undefined); + } label TypeError deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); } } +} diff --git a/deps/v8/src/builtins/array-from.tq b/deps/v8/src/builtins/array-from.tq index 6eb3b006935733..e51d37fef2ccdb 100644 --- a/deps/v8/src/builtins/array-from.tq +++ b/deps/v8/src/builtins/array-from.tq @@ -3,182 +3,181 @@ // found in the LICENSE file. namespace array { - // Array.from( items [, mapfn [, thisArg ] ] ) - // ES #sec-array.from - transitioning javascript builtin - ArrayFrom(js-implicit context: NativeContext, receiver: JSAny)(...arguments): - JSReceiver { - // Use fast path if: - // * |items| is the only argument, and - // * the receiver is the Array function. - if (arguments.length == 1 && receiver == GetArrayFunction()) { - try { - return iterator::FastIterableToList(arguments[0]) otherwise Slow; - } - label Slow { - // fall through - } +// Array.from( items [, mapfn [, thisArg ] ] ) +// ES #sec-array.from +transitioning javascript builtin +ArrayFrom(js-implicit context: NativeContext, receiver: JSAny)(...arguments): + JSReceiver { + // Use fast path if: + // * |items| is the only argument, and + // * the receiver is the Array function. + if (arguments.length == 1 && receiver == GetArrayFunction()) { + try { + return iterator::FastIterableToList(arguments[0]) otherwise Slow; + } label Slow { + // fall through } + } - const items = arguments[0]; - const mapfn = arguments[1]; - const thisArg = arguments[2]; - - // 1. Let C be the this value. - const c = receiver; - - let mapping: bool; - // 2. If mapfn is undefined, let mapping be false. - if (mapfn == Undefined) { - mapping = false; - } else { - // a. If IsCallable(mapfn) is false, throw a TypeError exception. - if (!TaggedIsCallable(mapfn)) deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, mapfn); - } - // b. Let mapping be true. - mapping = true; + const items = arguments[0]; + const mapfn = arguments[1]; + const thisArg = arguments[2]; + + // 1. Let C be the this value. + const c = receiver; + + let mapping: bool; + // 2. If mapfn is undefined, let mapping be false. + if (mapfn == Undefined) { + mapping = false; + } else { + // a. If IsCallable(mapfn) is false, throw a TypeError exception. + if (!Is(mapfn)) deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, mapfn); + } + // b. Let mapping be true. + mapping = true; + } + + // 4. Let usingIterator be ? GetMethod(items, @@iterator). + // 5. If usingIterator is not undefined, then + try { + const usingIterator = GetMethod(items, IteratorSymbolConstant()) + otherwise IteratorIsUndefined, IteratorNotCallable; + + let a: JSReceiver; + // a. If IsConstructor(C) is true, then + typeswitch (c) { + case (c: Constructor): { + // i. Let A be ? Construct(C). + a = Construct(c); + } + case (JSAny): { + // i. Let A be ? ArrayCreate(0). + a = ArrayCreate(0); + } } - // 4. Let usingIterator be ? GetMethod(items, @@iterator). - // 5. If usingIterator is not undefined, then - try { - const usingIterator = GetMethod(items, IteratorSymbolConstant()) - otherwise IteratorIsUndefined, IteratorNotCallable; - - let a: JSReceiver; - // a. If IsConstructor(C) is true, then - typeswitch (c) { - case (c: Constructor): { - // i. Let A be ? Construct(C). - a = Construct(c); - } - case (JSAny): { - // i. Let A be ? ArrayCreate(0). - a = ArrayCreate(0); - } + // c. Let iteratorRecord be ? GetIterator(items, sync, usingIterator). + const iteratorRecord = iterator::GetIterator(items, usingIterator); + + const fastIteratorResultMap = GetIteratorResultMap(); + + // d. Let k be 0. + let k: Smi = 0; + // e. Repeat, + while (true) { + // i. If k ≥ 2^53-1, then + // 1. Let error be ThrowCompletion(a newly created TypeError object). + // 2. Return ? IteratorClose(iteratorRecord, error). + // The spec requires that we throw an exception if index reaches 2^53-1, + // but an empty loop would take >100 days to do this many iterations. To + // actually run for that long would require an iterator that never set + // done to true and a target array which somehow never ran out of + // memory, e.g. a proxy that discarded the values. Ignoring this case + // just means we would repeatedly call CreateDataProperty with index = + // 2^53 + assert(k < kMaxSafeInteger); + + // ii. Let Pk be ! ToString(k). + + // iii. Let next be ? IteratorStep(iteratorRecord). + let next: JSReceiver; + try { + next = iterator::IteratorStep(iteratorRecord, fastIteratorResultMap) + otherwise NextIsFalse; + } + // iv. If next is false, then + label NextIsFalse { + // 1. Perform ? Set(A, "length", k, true). + array::SetPropertyLength(a, k); + // 2. Return A. + return a; } - // c. Let iteratorRecord be ? GetIterator(items, sync, usingIterator). - const iteratorRecord = iterator::GetIterator(items, usingIterator); - - const fastIteratorResultMap = GetIteratorResultMap(); - - // d. Let k be 0. - let k: Smi = 0; - // e. Repeat, - while (true) { - // i. If k ≥ 2^53-1, then - // 1. Let error be ThrowCompletion(a newly created TypeError object). - // 2. Return ? IteratorClose(iteratorRecord, error). - // The spec requires that we throw an exception if index reaches 2^53-1, - // but an empty loop would take >100 days to do this many iterations. To - // actually run for that long would require an iterator that never set - // done to true and a target array which somehow never ran out of - // memory, e.g. a proxy that discarded the values. Ignoring this case - // just means we would repeatedly call CreateDataProperty with index = - // 2^53 - assert(k < kMaxSafeInteger); - - // ii. Let Pk be ! ToString(k). - - // iii. Let next be ? IteratorStep(iteratorRecord). - let next: JSReceiver; - try { - next = iterator::IteratorStep(iteratorRecord, fastIteratorResultMap) - otherwise NextIsFalse; - } - // iv. If next is false, then - label NextIsFalse { - // 1. Perform ? Set(A, "length", k, true). - array::SetPropertyLength(a, k); - // 2. Return A. - return a; - } + // v. Let nextValue be ? IteratorValue(next). + const nextValue = iterator::IteratorValue(next, fastIteratorResultMap); - // v. Let nextValue be ? IteratorValue(next). - const nextValue = iterator::IteratorValue(next, fastIteratorResultMap); - - let mappedValue: JSAny; - // vi. If mapping is true, then - if (mapping) { - // 1. Let mappedValue be Call(mapfn, thisArg, « nextValue, k »). - // 2. If mappedValue is an abrupt completion, - // return ? IteratorClose(iteratorRecord, mappedValue). - // 3. Set mappedValue to mappedValue.[[Value]]. - try { - mappedValue = Call( - context, UnsafeCast(mapfn), thisArg, nextValue, k); - } catch (e) { - iterator::IteratorCloseOnException(iteratorRecord, e); - } - } else { - mappedValue = nextValue; - } - // viii. Let defineStatus be - // CreateDataPropertyOrThrow(A, Pk, mappedValue). - // ix. If defineStatus is an abrupt completion, - // return ? IteratorClose(iteratorRecord, defineStatus). + let mappedValue: JSAny; + // vi. If mapping is true, then + if (mapping) { + // 1. Let mappedValue be Call(mapfn, thisArg, « nextValue, k »). + // 2. If mappedValue is an abrupt completion, + // return ? IteratorClose(iteratorRecord, mappedValue). + // 3. Set mappedValue to mappedValue.[[Value]]. try { - FastCreateDataProperty(a, k, mappedValue); - } catch (e) deferred { - iterator::IteratorCloseOnException(iteratorRecord, e); + mappedValue = + Call(context, UnsafeCast(mapfn), thisArg, nextValue, k); + } catch (e) { + iterator::IteratorCloseOnException(iteratorRecord); + ReThrow(context, e); } - // x. Set k to k + 1. - k += 1; + } else { + mappedValue = nextValue; } - unreachable; + // viii. Let defineStatus be + // CreateDataPropertyOrThrow(A, Pk, mappedValue). + // ix. If defineStatus is an abrupt completion, + // return ? IteratorClose(iteratorRecord, defineStatus). + try { + FastCreateDataProperty(a, k, mappedValue); + } catch (e) deferred { + iterator::IteratorCloseOnException(iteratorRecord); + ReThrow(context, e); + } + // x. Set k to k + 1. + k += 1; } - label IteratorIsUndefined { - // 6. NOTE: items is not an Iterable so assume it is an array-like object. - // 7. Let arrayLike be ! ToObject(items). - const arrayLike = ToObject_Inline(context, items); - // 8. Let len be ? LengthOfArrayLike(arrayLike). - const len = GetLengthProperty(arrayLike); - - let a: JSReceiver; - // 9. If IsConstructor(C) is true, then - typeswitch (c) { - case (c: Constructor): { - // a. Let A be ? Construct(C, « len »). - a = Construct(c, len); - } - case (JSAny): { - // a. Let A be ? ArrayCreate(len). - a = ArrayCreate(len); - } + unreachable; + } label IteratorIsUndefined { + // 6. NOTE: items is not an Iterable so assume it is an array-like object. + // 7. Let arrayLike be ! ToObject(items). + const arrayLike = ToObject_Inline(context, items); + // 8. Let len be ? LengthOfArrayLike(arrayLike). + const len = GetLengthProperty(arrayLike); + + let a: JSReceiver; + // 9. If IsConstructor(C) is true, then + typeswitch (c) { + case (c: Constructor): { + // a. Let A be ? Construct(C, « len »). + a = Construct(c, len); } - - // 11. Let k be 0. - let k: Smi = 0; - // 12. Repeat, while k < len - while (k < len) { - // a. Let Pk be ! ToString(k). - // b. Let kValue be ? Get(arrayLike, Pk). - const kValue = GetProperty(arrayLike, k); - let mappedValue: JSAny; - // c. If mapping is true, then - if (mapping) { - // i. Let mappedValue be ? Call(mapfn, thisArg, « kValue, k »). - mappedValue = - Call(context, UnsafeCast(mapfn), thisArg, kValue, k); - } else { - // d. Else, let mappedValue be kValue. - mappedValue = kValue; - } - // e. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue). - FastCreateDataProperty(a, k, mappedValue); - // f. Set k to k + 1. - k += 1; + case (JSAny): { + // a. Let A be ? ArrayCreate(len). + a = ArrayCreate(len); } - - // 13. Perform ? Set(A, "length", len, true). - array::SetPropertyLength(a, len); - // 14. Return A. - return a; } - label IteratorNotCallable(_value: JSAny) deferred { - ThrowTypeError(MessageTemplate::kIteratorSymbolNonCallable); + + // 11. Let k be 0. + let k: Smi = 0; + // 12. Repeat, while k < len + while (k < len) { + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(arrayLike, Pk). + const kValue = GetProperty(arrayLike, k); + let mappedValue: JSAny; + // c. If mapping is true, then + if (mapping) { + // i. Let mappedValue be ? Call(mapfn, thisArg, « kValue, k »). + mappedValue = + Call(context, UnsafeCast(mapfn), thisArg, kValue, k); + } else { + // d. Else, let mappedValue be kValue. + mappedValue = kValue; + } + // e. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue). + FastCreateDataProperty(a, k, mappedValue); + // f. Set k to k + 1. + k += 1; } + + // 13. Perform ? Set(A, "length", len, true). + array::SetPropertyLength(a, len); + // 14. Return A. + return a; + } label IteratorNotCallable(_value: JSAny) deferred { + ThrowTypeError(MessageTemplate::kIteratorSymbolNonCallable); } } +} diff --git a/deps/v8/src/builtins/array-isarray.tq b/deps/v8/src/builtins/array-isarray.tq index 48fca60339d777..a88c1579d1aa91 100644 --- a/deps/v8/src/builtins/array-isarray.tq +++ b/deps/v8/src/builtins/array-isarray.tq @@ -3,25 +3,25 @@ // found in the LICENSE file. namespace runtime { - extern runtime ArrayIsArray(implicit context: Context)(JSAny): JSAny; +extern runtime ArrayIsArray(implicit context: Context)(JSAny): JSAny; } // namespace runtime namespace array { - // ES #sec-array.isarray - javascript builtin ArrayIsArray(js-implicit context: - NativeContext)(arg: JSAny): JSAny { - // 1. Return ? IsArray(arg). - typeswitch (arg) { - case (JSArray): { - return True; - } - case (JSProxy): { - // TODO(verwaest): Handle proxies in-place - return runtime::ArrayIsArray(arg); - } - case (JSAny): { - return False; - } +// ES #sec-array.isarray +javascript builtin ArrayIsArray(js-implicit context: NativeContext)(arg: JSAny): + JSAny { + // 1. Return ? IsArray(arg). + typeswitch (arg) { + case (JSArray): { + return True; + } + case (JSProxy): { + // TODO(verwaest): Handle proxies in-place + return runtime::ArrayIsArray(arg); + } + case (JSAny): { + return False; } } +} } // namespace array diff --git a/deps/v8/src/builtins/array-join.tq b/deps/v8/src/builtins/array-join.tq index a06365f3354b13..08d0cbf894bf49 100644 --- a/deps/v8/src/builtins/array-join.tq +++ b/deps/v8/src/builtins/array-join.tq @@ -3,646 +3,632 @@ // found in the LICENSE file. namespace array { - type LoadJoinElementFn = builtin(Context, JSReceiver, uintptr) => JSAny; +type LoadJoinElementFn = builtin(Context, JSReceiver, uintptr) => JSAny; - // Fast C call to write a fixed array (see Buffer.fixedArray) to a single - // string. - extern macro - ArrayBuiltinsAssembler::CallJSArrayArrayJoinConcatToSequentialString( - FixedArray, intptr, String, String): String; +// Fast C call to write a fixed array (see Buffer.fixedArray) to a single +// string. +extern macro +ArrayBuiltinsAssembler::CallJSArrayArrayJoinConcatToSequentialString( + FixedArray, intptr, String, String): String; - transitioning builtin LoadJoinElement( - context: Context, receiver: JSReceiver, k: uintptr): JSAny { +transitioning builtin LoadJoinElement( + context: Context, receiver: JSReceiver, k: uintptr): JSAny { + return GetProperty(receiver, Convert(k)); +} + +transitioning LoadJoinElement( + context: Context, receiver: JSReceiver, k: uintptr): JSAny { + const array: JSArray = UnsafeCast(receiver); + const dict: NumberDictionary = UnsafeCast(array.elements); + try { + return BasicLoadNumberDictionaryElement(dict, Signed(k)) + otherwise IfNoData, IfHole; + } label IfNoData deferred { return GetProperty(receiver, Convert(k)); + } label IfHole { + return kEmptyString; } +} - transitioning LoadJoinElement( - context: Context, receiver: JSReceiver, k: uintptr): JSAny { - const array: JSArray = UnsafeCast(receiver); - const dict: NumberDictionary = UnsafeCast(array.elements); - try { - return BasicLoadNumberDictionaryElement(dict, Signed(k)) - otherwise IfNoData, IfHole; - } - label IfNoData deferred { - return GetProperty(receiver, Convert(k)); - } - label IfHole { - return kEmptyString; +LoadJoinElement( + context: Context, receiver: JSReceiver, k: uintptr): JSAny { + const array: JSArray = UnsafeCast(receiver); + const fixedArray: FixedArray = UnsafeCast(array.elements); + const element: Object = fixedArray.objects[k]; + return element == TheHole ? kEmptyString : UnsafeCast(element); +} + +LoadJoinElement( + context: Context, receiver: JSReceiver, k: uintptr): JSAny { + const array: JSArray = UnsafeCast(receiver); + const fixedDoubleArray: FixedDoubleArray = + UnsafeCast(array.elements); + const element: float64 = + fixedDoubleArray.floats[k].Value() otherwise return kEmptyString; + return AllocateHeapNumberWithValue(element); +} + +builtin LoadJoinTypedElement( + context: Context, receiver: JSReceiver, k: uintptr): JSAny { + const typedArray: JSTypedArray = UnsafeCast(receiver); + assert(!IsDetachedBuffer(typedArray.buffer)); + return typed_array::LoadFixedTypedArrayElementAsTagged( + typedArray.data_ptr, k, typed_array::KindForArrayType()); +} + +transitioning builtin ConvertToLocaleString( + context: Context, element: JSAny, locales: JSAny, options: JSAny): String { + if (IsNullOrUndefined(element)) return kEmptyString; + + const prop: JSAny = GetProperty(element, 'toLocaleString'); + try { + const callable: Callable = Cast(prop) otherwise TypeError; + let result: JSAny; + if (IsNullOrUndefined(locales)) { + result = Call(context, callable, element); + } else if (IsNullOrUndefined(options)) { + result = Call(context, callable, element, locales); + } else { + result = Call(context, callable, element, locales, options); } + return ToString_Inline(result); + } label TypeError { + ThrowTypeError(MessageTemplate::kCalledNonCallable, prop); } +} - LoadJoinElement( - context: Context, receiver: JSReceiver, k: uintptr): JSAny { - const array: JSArray = UnsafeCast(receiver); - const fixedArray: FixedArray = UnsafeCast(array.elements); - const element: Object = fixedArray.objects[k]; - return element == TheHole ? kEmptyString : UnsafeCast(element); - } +// Verifies the current element JSArray accessor can still be safely used +// (see LoadJoinElement). +macro CannotUseSameArrayAccessor(implicit context: Context)( + loadFn: LoadJoinElementFn, receiver: JSReceiver, originalMap: Map, + originalLen: Number): bool; + +CannotUseSameArrayAccessor(implicit context: Context)( + loadFn: LoadJoinElementFn, receiver: JSReceiver, originalMap: Map, + originalLen: Number): bool { + if (loadFn == LoadJoinElement) return false; + + const array: JSArray = UnsafeCast(receiver); + if (originalMap != array.map) return true; + if (originalLen != array.length) return true; + if (IsNoElementsProtectorCellInvalid()) return true; + return false; +} - LoadJoinElement( - context: Context, receiver: JSReceiver, k: uintptr): JSAny { - const array: JSArray = UnsafeCast(receiver); - const fixedDoubleArray: FixedDoubleArray = - UnsafeCast(array.elements); - const element: float64 = - fixedDoubleArray.floats[k].Value() otherwise return kEmptyString; - return AllocateHeapNumberWithValue(element); - } +CannotUseSameArrayAccessor(implicit context: Context)( + _loadFn: LoadJoinElementFn, receiver: JSReceiver, _initialMap: Map, + _initialLen: Number): bool { + const typedArray: JSTypedArray = UnsafeCast(receiver); + return IsDetachedBuffer(typedArray.buffer); +} - builtin LoadJoinTypedElement( - context: Context, receiver: JSReceiver, k: uintptr): JSAny { - const typedArray: JSTypedArray = UnsafeCast(receiver); - assert(!IsDetachedBuffer(typedArray.buffer)); - return typed_array::LoadFixedTypedArrayElementAsTagged( - typedArray.data_ptr, k, typed_array::KindForArrayType()); +// Calculates the running total length of the resulting string. If the +// calculated length exceeds the maximum string length (see +// String::kMaxLength), throws a range error. +macro AddStringLength(implicit context: Context)( + lenA: intptr, lenB: intptr): intptr { + try { + const length: intptr = TryIntPtrAdd(lenA, lenB) otherwise IfOverflow; + if (length > kStringMaxLength) goto IfOverflow; + return length; + } label IfOverflow deferred { + ThrowInvalidStringLength(context); } +} - transitioning builtin ConvertToLocaleString( - context: Context, element: JSAny, locales: JSAny, - options: JSAny): String { - if (IsNullOrUndefined(element)) return kEmptyString; - - const prop: JSAny = GetProperty(element, 'toLocaleString'); - try { - const callable: Callable = Cast(prop) otherwise TypeError; - let result: JSAny; - if (IsNullOrUndefined(locales)) { - result = Call(context, callable, element); - } else if (IsNullOrUndefined(options)) { - result = Call(context, callable, element, locales); - } else { - result = Call(context, callable, element, locales, options); - } - return ToString_Inline(result); - } - label TypeError { - ThrowTypeError(MessageTemplate::kCalledNonCallable, prop); +// Stores an element to a fixed array and return the fixed array. If the fixed +// array is not large enough, create and return a new, larger fixed array that +// contains all previously elements and the new element. +macro StoreAndGrowFixedArray( + fixedArray: FixedArray, index: intptr, element: T): FixedArray { + const length: intptr = fixedArray.length_intptr; + assert(index <= length); + if (index < length) { + fixedArray.objects[index] = element; + return fixedArray; + } else + deferred { + const newLength: intptr = CalculateNewElementsCapacity(length); + assert(index < newLength); + const newfixedArray: FixedArray = + ExtractFixedArray(fixedArray, 0, length, newLength); + newfixedArray.objects[index] = element; + return newfixedArray; } - } +} - // Verifies the current element JSArray accessor can still be safely used - // (see LoadJoinElement). - macro CannotUseSameArrayAccessor(implicit context: Context)( - loadFn: LoadJoinElementFn, receiver: JSReceiver, originalMap: Map, - originalLen: Number): bool; - - CannotUseSameArrayAccessor(implicit context: Context)( - loadFn: LoadJoinElementFn, receiver: JSReceiver, originalMap: Map, - originalLen: Number): bool { - if (loadFn == LoadJoinElement) return false; - - const array: JSArray = UnsafeCast(receiver); - if (originalMap != array.map) return true; - if (originalLen != array.length) return true; - if (IsNoElementsProtectorCellInvalid()) return true; - return false; +// Contains the information necessary to create a single, separator delimited, +// flattened one or two byte string. +// The buffer is maintained and updated by Buffer.constructor, Buffer.Add(), +// Buffer.AddSeparators(). +struct Buffer { + macro Add(implicit context: Context)( + str: String, nofSeparators: intptr, separatorLength: intptr) { + // Add separators if necessary (at the beginning or more than one) + const writeSeparators: bool = this.index == 0 | nofSeparators > 1; + this.AddSeparators(nofSeparators, separatorLength, writeSeparators); + + this.totalStringLength = + AddStringLength(this.totalStringLength, str.length_intptr); + this.fixedArray = + StoreAndGrowFixedArray(this.fixedArray, this.index++, str); + this.isOneByte = + IsOneByteStringInstanceType(str.instanceType) & this.isOneByte; } - CannotUseSameArrayAccessor(implicit context: Context)( - _loadFn: LoadJoinElementFn, receiver: JSReceiver, _initialMap: Map, - _initialLen: Number): bool { - const typedArray: JSTypedArray = UnsafeCast(receiver); - return IsDetachedBuffer(typedArray.buffer); - } + macro AddSeparators(implicit context: Context)( + nofSeparators: intptr, separatorLength: intptr, write: bool) { + if (nofSeparators == 0 || separatorLength == 0) return; - // Calculates the running total length of the resulting string. If the - // calculated length exceeds the maximum string length (see - // String::kMaxLength), throws a range error. - macro AddStringLength(implicit context: Context)(lenA: intptr, lenB: intptr): - intptr { - try { - const length: intptr = TryIntPtrAdd(lenA, lenB) otherwise IfOverflow; - if (length > kStringMaxLength) goto IfOverflow; - return length; - } - label IfOverflow deferred { - ThrowInvalidStringLength(context); - } - } + const nofSeparatorsInt: intptr = nofSeparators; + const sepsLen: intptr = separatorLength * nofSeparatorsInt; + // Detect integer overflow + // TODO(tebbi): Replace with overflow-checked multiplication. + if (sepsLen / separatorLength != nofSeparatorsInt) deferred { + ThrowInvalidStringLength(context); + } - // Stores an element to a fixed array and return the fixed array. If the fixed - // array is not large enough, create and return a new, larger fixed array that - // contains all previously elements and the new element. - macro StoreAndGrowFixedArray( - fixedArray: FixedArray, index: intptr, element: T): FixedArray { - const length: intptr = fixedArray.length_intptr; - assert(index <= length); - if (index < length) { - fixedArray.objects[index] = element; - return fixedArray; - } else - deferred { - const newLength: intptr = CalculateNewElementsCapacity(length); - assert(index < newLength); - const newfixedArray: FixedArray = - ExtractFixedArray(fixedArray, 0, length, newLength); - newfixedArray.objects[index] = element; - return newfixedArray; + this.totalStringLength = AddStringLength(this.totalStringLength, sepsLen); + if (write) deferred { + this.fixedArray = StoreAndGrowFixedArray( + this.fixedArray, this.index++, Convert(nofSeparatorsInt)); } } - // Contains the information necessary to create a single, separator delimited, - // flattened one or two byte string. - // The buffer is maintained and updated by Buffer.constructor, Buffer.Add(), - // Buffer.AddSeparators(). - struct Buffer { - macro Add(implicit context: Context)( - str: String, nofSeparators: intptr, separatorLength: intptr) { - // Add separators if necessary (at the beginning or more than one) - const writeSeparators: bool = this.index == 0 | nofSeparators > 1; - this.AddSeparators(nofSeparators, separatorLength, writeSeparators); - - this.totalStringLength = - AddStringLength(this.totalStringLength, str.length_intptr); - this.fixedArray = - StoreAndGrowFixedArray(this.fixedArray, this.index++, str); - this.isOneByte = - IsOneByteStringInstanceType(str.instanceType) & this.isOneByte; - } + // Fixed array holding elements that are either: + // 1) String result of `ToString(next)`. + // 2) Smi representing the number of consecutive separators. + // `BufferJoin()` will iterate and writes these entries to a flat string. + // + // To save space, reduce reads and writes, only separators at the beginning, + // end, or more than one are written. + // + // No hole example + // receiver: ['hello', 'world'] + // fixedArray: ['hello', 'world'] + // + // Hole example + // receiver: [, 'hello', , 'world', ] + // fixedArray: [1, 'hello', 2, 'world', 1] + fixedArray: FixedArray; + + // Index to insert a new entry into `fixedArray`. + index: intptr; + + // Running total of the resulting string length. + totalStringLength: intptr; + + // `true` if the separator and all strings in the buffer are one-byte, + // otherwise `false`. + isOneByte: bool; +} - macro AddSeparators(implicit context: Context)( - nofSeparators: intptr, separatorLength: intptr, write: bool) { - if (nofSeparators == 0 || separatorLength == 0) return; +macro NewBuffer(len: uintptr, sep: String): Buffer { + const cappedBufferSize: intptr = len > kMaxNewSpaceFixedArrayElements ? + kMaxNewSpaceFixedArrayElements : + Signed(len); + assert(cappedBufferSize > 0); + return Buffer{ + fixedArray: AllocateZeroedFixedArray(cappedBufferSize), + index: 0, + totalStringLength: 0, + isOneByte: IsOneByteStringInstanceType(sep.instanceType) + }; +} - const nofSeparatorsInt: intptr = nofSeparators; - const sepsLen: intptr = separatorLength * nofSeparatorsInt; - // Detect integer overflow - // TODO(tebbi): Replace with overflow-checked multiplication. - if (sepsLen / separatorLength != nofSeparatorsInt) deferred { - ThrowInvalidStringLength(context); - } +macro BufferJoin(implicit context: Context)( + buffer: Buffer, sep: String): String { + assert(IsValidPositiveSmi(buffer.totalStringLength)); + if (buffer.totalStringLength == 0) return kEmptyString; + + // Fast path when there's only one buffer element. + if (buffer.index == 1) { + const fixedArray: FixedArray = buffer.fixedArray; + typeswitch (fixedArray.objects[0]) { + // When the element is a string, just return it and completely avoid + // allocating another string. + case (str: String): { + return str; + } - this.totalStringLength = AddStringLength(this.totalStringLength, sepsLen); - if (write) deferred { - this.fixedArray = StoreAndGrowFixedArray( - this.fixedArray, this.index++, Convert(nofSeparatorsInt)); - } + // When the element is a smi, use StringRepeat to quickly build a memory + // efficient separator repeated string. + case (nofSeparators: Number): { + return StringRepeat(context, sep, nofSeparators); + } + case (Object): { + unreachable; + } } - - // Fixed array holding elements that are either: - // 1) String result of `ToString(next)`. - // 2) Smi representing the number of consecutive separators. - // `BufferJoin()` will iterate and writes these entries to a flat string. - // - // To save space, reduce reads and writes, only separators at the beginning, - // end, or more than one are written. - // - // No hole example - // receiver: ['hello', 'world'] - // fixedArray: ['hello', 'world'] - // - // Hole example - // receiver: [, 'hello', , 'world', ] - // fixedArray: [1, 'hello', 2, 'world', 1] - fixedArray: FixedArray; - - // Index to insert a new entry into `fixedArray`. - index: intptr; - - // Running total of the resulting string length. - totalStringLength: intptr; - - // `true` if the separator and all strings in the buffer are one-byte, - // otherwise `false`. - isOneByte: bool; } - macro NewBuffer(len: uintptr, sep: String): Buffer { - const cappedBufferSize: intptr = len > kMaxNewSpaceFixedArrayElements ? - kMaxNewSpaceFixedArrayElements : - Signed(len); - assert(cappedBufferSize > 0); - return Buffer{ - fixedArray: AllocateZeroedFixedArray(cappedBufferSize), - index: 0, - totalStringLength: 0, - isOneByte: IsOneByteStringInstanceType(sep.instanceType) - }; - } - - macro BufferJoin(implicit context: Context)(buffer: Buffer, sep: String): - String { - assert(IsValidPositiveSmi(buffer.totalStringLength)); - if (buffer.totalStringLength == 0) return kEmptyString; - - // Fast path when there's only one buffer element. - if (buffer.index == 1) { - const fixedArray: FixedArray = buffer.fixedArray; - typeswitch (fixedArray.objects[0]) { - // When the element is a string, just return it and completely avoid - // allocating another string. - case (str: String): { - return str; - } + const length: uint32 = Convert(Unsigned(buffer.totalStringLength)); + const r: String = buffer.isOneByte ? AllocateSeqOneByteString(length) : + AllocateSeqTwoByteString(length); + return CallJSArrayArrayJoinConcatToSequentialString( + buffer.fixedArray, buffer.index, sep, r); +} - // When the element is a smi, use StringRepeat to quickly build a memory - // efficient separator repeated string. - case (nofSeparators: Number): { - return StringRepeat(context, sep, nofSeparators); - } - case (Object): { - unreachable; - } +transitioning macro ArrayJoinImpl(implicit context: Context)( + receiver: JSReceiver, sep: String, lengthNumber: Number, + useToLocaleString: constexpr bool, locales: JSAny, options: JSAny, + initialLoadFn: LoadJoinElementFn): String { + const initialMap: Map = receiver.map; + const len: uintptr = Convert(lengthNumber); + const separatorLength: intptr = sep.length_intptr; + let nofSeparators: intptr = 0; + let loadFn: LoadJoinElementFn = initialLoadFn; + let buffer: Buffer = NewBuffer(len, sep); + + // 6. Let k be 0. + let k: uintptr = 0; + + // 7. Repeat, while k < len + while (k < len) { + if (CannotUseSameArrayAccessor( + loadFn, receiver, initialMap, lengthNumber)) + deferred { + loadFn = LoadJoinElement; } + + if (k > 0) { + // a. If k > 0, let R be the string-concatenation of R and sep. + nofSeparators = nofSeparators + 1; } - const length: uint32 = Convert(Unsigned(buffer.totalStringLength)); - const r: String = buffer.isOneByte ? AllocateSeqOneByteString(length) : - AllocateSeqTwoByteString(length); - return CallJSArrayArrayJoinConcatToSequentialString( - buffer.fixedArray, buffer.index, sep, r); - } + // b. Let element be ? Get(O, ! ToString(k)). + const element: JSAny = loadFn(context, receiver, k++); - transitioning macro ArrayJoinImpl(implicit context: Context)( - receiver: JSReceiver, sep: String, lengthNumber: Number, - useToLocaleString: constexpr bool, locales: JSAny, options: JSAny, - initialLoadFn: LoadJoinElementFn): String { - const initialMap: Map = receiver.map; - const len: uintptr = Convert(lengthNumber); - const separatorLength: intptr = sep.length_intptr; - let nofSeparators: intptr = 0; - let loadFn: LoadJoinElementFn = initialLoadFn; - let buffer: Buffer = NewBuffer(len, sep); - - // 6. Let k be 0. - let k: uintptr = 0; - - // 7. Repeat, while k < len - while (k < len) { - if (CannotUseSameArrayAccessor( - loadFn, receiver, initialMap, lengthNumber)) - deferred { - loadFn = LoadJoinElement; + // c. If element is undefined or null, let next be the empty String; + // otherwise, let next be ? ToString(element). + let next: String; + if constexpr (useToLocaleString) { + next = ConvertToLocaleString(context, element, locales, options); + if (next == kEmptyString) continue; + } else { + typeswitch (element) { + case (str: String): { + if (str == kEmptyString) continue; + next = str; } - - if (k > 0) { - // a. If k > 0, let R be the string-concatenation of R and sep. - nofSeparators = nofSeparators + 1; - } - - // b. Let element be ? Get(O, ! ToString(k)). - const element: JSAny = loadFn(context, receiver, k++); - - // c. If element is undefined or null, let next be the empty String; - // otherwise, let next be ? ToString(element). - let next: String; - if constexpr (useToLocaleString) { - next = ConvertToLocaleString(context, element, locales, options); - if (next == kEmptyString) continue; - } else { - typeswitch (element) { - case (str: String): { - if (str == kEmptyString) continue; - next = str; - } - case (num: Number): { - next = NumberToString(num); - } - case (obj: JSAny): { - if (IsNullOrUndefined(obj)) continue; - next = ToString(context, obj); - } + case (num: Number): { + next = NumberToString(num); + } + case (obj: JSAny): { + if (IsNullOrUndefined(obj)) continue; + next = ToString(context, obj); } } - - // d. Set R to the string-concatenation of R and next. - buffer.Add(next, nofSeparators, separatorLength); - nofSeparators = 0; } - // Add any separators at the end. - buffer.AddSeparators(nofSeparators, separatorLength, true); - - // 8. Return R. - return BufferJoin(buffer, sep); + // d. Set R to the string-concatenation of R and next. + buffer.Add(next, nofSeparators, separatorLength); + nofSeparators = 0; } - transitioning macro ArrayJoin(implicit context: Context)( - useToLocaleString: constexpr bool, receiver: JSReceiver, sep: String, - lenNumber: Number, locales: JSAny, options: JSAny): JSAny; + // Add any separators at the end. + buffer.AddSeparators(nofSeparators, separatorLength, true); - transitioning ArrayJoin(implicit context: Context)( - useToLocaleString: constexpr bool, receiver: JSReceiver, sep: String, - lenNumber: Number, locales: JSAny, options: JSAny): JSAny { - const map: Map = receiver.map; - const kind: ElementsKind = map.elements_kind; - let loadFn: LoadJoinElementFn; + // 8. Return R. + return BufferJoin(buffer, sep); +} - try { - const array: JSArray = Cast(receiver) otherwise IfSlowPath; - if (array.length != lenNumber) goto IfSlowPath; - if (!IsPrototypeInitialArrayPrototype(map)) goto IfSlowPath; - if (IsNoElementsProtectorCellInvalid()) goto IfSlowPath; - - if (IsElementsKindLessThanOrEqual(kind, ElementsKind::HOLEY_ELEMENTS)) { - loadFn = LoadJoinElement; - } else if (IsElementsKindLessThanOrEqual( - kind, ElementsKind::HOLEY_DOUBLE_ELEMENTS)) { - loadFn = LoadJoinElement; - } else if (kind == ElementsKind::DICTIONARY_ELEMENTS) - deferred { - const dict: NumberDictionary = - UnsafeCast(array.elements); - const nofElements: Smi = GetNumberDictionaryNumberOfElements(dict); - if (nofElements == 0) { - if (sep == kEmptyString) return kEmptyString; - try { - const nofSeparators: Smi = - Cast(lenNumber - 1) otherwise IfNotSmi; - return StringRepeat(context, sep, nofSeparators); - } - label IfNotSmi { - ThrowInvalidStringLength(context); - } - } else { - loadFn = LoadJoinElement; +transitioning macro ArrayJoin(implicit context: Context)( + useToLocaleString: constexpr bool, receiver: JSReceiver, sep: String, + lenNumber: Number, locales: JSAny, options: JSAny): JSAny; + +transitioning ArrayJoin(implicit context: Context)( + useToLocaleString: constexpr bool, receiver: JSReceiver, sep: String, + lenNumber: Number, locales: JSAny, options: JSAny): JSAny { + const map: Map = receiver.map; + const kind: ElementsKind = map.elements_kind; + let loadFn: LoadJoinElementFn; + + try { + const array: JSArray = Cast(receiver) otherwise IfSlowPath; + if (array.length != lenNumber) goto IfSlowPath; + if (!IsPrototypeInitialArrayPrototype(map)) goto IfSlowPath; + if (IsNoElementsProtectorCellInvalid()) goto IfSlowPath; + + if (IsElementsKindLessThanOrEqual(kind, ElementsKind::HOLEY_ELEMENTS)) { + loadFn = LoadJoinElement; + } else if (IsElementsKindLessThanOrEqual( + kind, ElementsKind::HOLEY_DOUBLE_ELEMENTS)) { + loadFn = LoadJoinElement; + } else if (kind == ElementsKind::DICTIONARY_ELEMENTS) + deferred { + const dict: NumberDictionary = + UnsafeCast(array.elements); + const nofElements: Smi = GetNumberDictionaryNumberOfElements(dict); + if (nofElements == 0) { + if (sep == kEmptyString) return kEmptyString; + try { + const nofSeparators: Smi = + Cast(lenNumber - 1) otherwise IfNotSmi; + return StringRepeat(context, sep, nofSeparators); + } label IfNotSmi { + ThrowInvalidStringLength(context); } + } else { + loadFn = LoadJoinElement; } - else { - goto IfSlowPath; } + else { + goto IfSlowPath; } - label IfSlowPath { - loadFn = LoadJoinElement; - } - return ArrayJoinImpl( - receiver, sep, lenNumber, useToLocaleString, locales, options, loadFn); + } label IfSlowPath { + loadFn = LoadJoinElement; } + return ArrayJoinImpl( + receiver, sep, lenNumber, useToLocaleString, locales, options, loadFn); +} - transitioning ArrayJoin(implicit context: Context)( - useToLocaleString: constexpr bool, receiver: JSReceiver, sep: String, - lenNumber: Number, locales: JSAny, options: JSAny): JSAny { - const map: Map = receiver.map; - const kind: ElementsKind = map.elements_kind; - let loadFn: LoadJoinElementFn; - - if (IsElementsKindGreaterThan(kind, ElementsKind::UINT32_ELEMENTS)) { - if (kind == ElementsKind::INT32_ELEMENTS) { - loadFn = LoadJoinTypedElement; - } else if (kind == ElementsKind::FLOAT32_ELEMENTS) { - loadFn = LoadJoinTypedElement; - } else if (kind == ElementsKind::FLOAT64_ELEMENTS) { - loadFn = LoadJoinTypedElement; - } else if (kind == ElementsKind::UINT8_CLAMPED_ELEMENTS) { - loadFn = LoadJoinTypedElement; - } else if (kind == ElementsKind::BIGUINT64_ELEMENTS) { - loadFn = LoadJoinTypedElement; - } else if (kind == ElementsKind::BIGINT64_ELEMENTS) { - loadFn = LoadJoinTypedElement; - } else { - unreachable; - } +transitioning ArrayJoin(implicit context: Context)( + useToLocaleString: constexpr bool, receiver: JSReceiver, sep: String, + lenNumber: Number, locales: JSAny, options: JSAny): JSAny { + const map: Map = receiver.map; + const kind: ElementsKind = map.elements_kind; + let loadFn: LoadJoinElementFn; + + if (IsElementsKindGreaterThan(kind, ElementsKind::UINT32_ELEMENTS)) { + if (kind == ElementsKind::INT32_ELEMENTS) { + loadFn = LoadJoinTypedElement; + } else if (kind == ElementsKind::FLOAT32_ELEMENTS) { + loadFn = LoadJoinTypedElement; + } else if (kind == ElementsKind::FLOAT64_ELEMENTS) { + loadFn = LoadJoinTypedElement; + } else if (kind == ElementsKind::UINT8_CLAMPED_ELEMENTS) { + loadFn = LoadJoinTypedElement; + } else if (kind == ElementsKind::BIGUINT64_ELEMENTS) { + loadFn = LoadJoinTypedElement; + } else if (kind == ElementsKind::BIGINT64_ELEMENTS) { + loadFn = LoadJoinTypedElement; } else { - if (kind == ElementsKind::UINT8_ELEMENTS) { - loadFn = LoadJoinTypedElement; - } else if (kind == ElementsKind::INT8_ELEMENTS) { - loadFn = LoadJoinTypedElement; - } else if (kind == ElementsKind::UINT16_ELEMENTS) { - loadFn = LoadJoinTypedElement; - } else if (kind == ElementsKind::INT16_ELEMENTS) { - loadFn = LoadJoinTypedElement; - } else if (kind == ElementsKind::UINT32_ELEMENTS) { - loadFn = LoadJoinTypedElement; - } else { - unreachable; - } + unreachable; + } + } else { + if (kind == ElementsKind::UINT8_ELEMENTS) { + loadFn = LoadJoinTypedElement; + } else if (kind == ElementsKind::INT8_ELEMENTS) { + loadFn = LoadJoinTypedElement; + } else if (kind == ElementsKind::UINT16_ELEMENTS) { + loadFn = LoadJoinTypedElement; + } else if (kind == ElementsKind::INT16_ELEMENTS) { + loadFn = LoadJoinTypedElement; + } else if (kind == ElementsKind::UINT32_ELEMENTS) { + loadFn = LoadJoinTypedElement; + } else { + unreachable; } - return ArrayJoinImpl( - receiver, sep, lenNumber, useToLocaleString, locales, options, loadFn); - } - - // The Join Stack detects cyclical calls to Array Join builtins - // (Array.p.join(), Array.p.toString(), Array.p.toLocaleString()). This - // FixedArray holds a stack of receivers to the current call. - // CycleProtectedArrayJoin() is responsible for calling JoinStackPush and - // JoinStackPop when visiting and leaving a receiver, respectively. - const kMinJoinStackSize: - constexpr int31 generates 'JSArray::kMinJoinStackSize'; - macro LoadJoinStack(implicit context: Context)(): FixedArray - labels IfUninitialized { - const nativeContext: NativeContext = LoadNativeContext(context); - const stack: HeapObject = UnsafeCast( - nativeContext[NativeContextSlot::ARRAY_JOIN_STACK_INDEX]); - if (stack == Undefined) goto IfUninitialized; - assert(IsFixedArray(stack)); - return UnsafeCast(stack); } + return ArrayJoinImpl( + receiver, sep, lenNumber, useToLocaleString, locales, options, loadFn); +} - macro SetJoinStack(implicit context: Context)(stack: FixedArray): void { - const nativeContext: NativeContext = LoadNativeContext(context); - nativeContext[NativeContextSlot::ARRAY_JOIN_STACK_INDEX] = stack; - } +// The Join Stack detects cyclical calls to Array Join builtins +// (Array.p.join(), Array.p.toString(), Array.p.toLocaleString()). This +// FixedArray holds a stack of receivers to the current call. +// CycleProtectedArrayJoin() is responsible for calling JoinStackPush and +// JoinStackPop when visiting and leaving a receiver, respectively. +const kMinJoinStackSize: + constexpr int31 generates 'JSArray::kMinJoinStackSize'; +macro LoadJoinStack(implicit context: Context)(): FixedArray + labels IfUninitialized { + const nativeContext: NativeContext = LoadNativeContext(context); + const stack: HeapObject = UnsafeCast( + nativeContext[NativeContextSlot::ARRAY_JOIN_STACK_INDEX]); + if (stack == Undefined) goto IfUninitialized; + assert(IsFixedArray(stack)); + return UnsafeCast(stack); +} - // Adds a receiver to the stack. The FixedArray will automatically grow to - // accommodate the receiver. If the receiver already exists on the stack, - // this indicates a cyclical call and False is returned. - builtin JoinStackPush(implicit context: Context)( - stack: FixedArray, receiver: JSReceiver): Boolean { - const capacity: intptr = stack.length_intptr; - for (let i: intptr = 0; i < capacity; i++) { - const previouslyVisited: Object = stack.objects[i]; - - // Add `receiver` to the first open slot - if (previouslyVisited == TheHole) { - stack.objects[i] = receiver; - return True; - } +macro SetJoinStack(implicit context: Context)(stack: FixedArray): void { + const nativeContext: NativeContext = LoadNativeContext(context); + nativeContext[NativeContextSlot::ARRAY_JOIN_STACK_INDEX] = stack; +} - // Detect cycles - if (receiver == previouslyVisited) return False; +// Adds a receiver to the stack. The FixedArray will automatically grow to +// accommodate the receiver. If the receiver already exists on the stack, +// this indicates a cyclical call and False is returned. +builtin JoinStackPush(implicit context: Context)( + stack: FixedArray, receiver: JSReceiver): Boolean { + const capacity: intptr = stack.length_intptr; + for (let i: intptr = 0; i < capacity; i++) { + const previouslyVisited: Object = stack.objects[i]; + + // Add `receiver` to the first open slot + if (previouslyVisited == TheHole) { + stack.objects[i] = receiver; + return True; } - // If no open slots were found, grow the stack and add receiver to the end. - const newStack: FixedArray = - StoreAndGrowFixedArray(stack, capacity, receiver); - SetJoinStack(newStack); - return True; + // Detect cycles + if (receiver == previouslyVisited) return False; } - // Fast path the common non-nested calls. If the receiver is not already on - // the stack, add it to the stack and return true. Otherwise return false. - macro JoinStackPushInline(implicit context: Context)(receiver: JSReceiver): - bool { - try { - const stack: FixedArray = LoadJoinStack() - otherwise IfUninitialized; - if (stack.objects[0] == TheHole) { - stack.objects[0] = receiver; - } else if (JoinStackPush(stack, receiver) == False) - deferred { - return false; - } - } - label IfUninitialized { - const stack: FixedArray = - AllocateFixedArrayWithHoles(kMinJoinStackSize, AllocationFlag::kNone); + // If no open slots were found, grow the stack and add receiver to the end. + const newStack: FixedArray = + StoreAndGrowFixedArray(stack, capacity, receiver); + SetJoinStack(newStack); + return True; +} + +// Fast path the common non-nested calls. If the receiver is not already on +// the stack, add it to the stack and return true. Otherwise return false. +macro JoinStackPushInline(implicit context: Context)(receiver: JSReceiver): + bool { + try { + const stack: FixedArray = LoadJoinStack() + otherwise IfUninitialized; + if (stack.objects[0] == TheHole) { stack.objects[0] = receiver; - SetJoinStack(stack); - } - return true; + } else if (JoinStackPush(stack, receiver) == False) + deferred { + return false; + } + } label IfUninitialized { + const stack: FixedArray = + AllocateFixedArrayWithHoles(kMinJoinStackSize, AllocationFlag::kNone); + stack.objects[0] = receiver; + SetJoinStack(stack); } + return true; +} - // Removes a receiver from the stack. The FixedArray will automatically shrink - // to Heap::kMinJoinStackSize once the stack becomes empty. - builtin JoinStackPop(implicit context: Context)( - stack: FixedArray, receiver: JSReceiver): JSAny { - const len: intptr = stack.length_intptr; - for (let i: intptr = 0; i < len; i++) { - if (stack.objects[i] == receiver) { - // Shrink the Join Stack if the stack will be empty and is larger than - // the minimum size. - if (i == 0 && len > kMinJoinStackSize) deferred { - const newStack: FixedArray = AllocateFixedArrayWithHoles( - kMinJoinStackSize, AllocationFlag::kNone); - SetJoinStack(newStack); - } - else { - stack.objects[i] = TheHole; +// Removes a receiver from the stack. The FixedArray will automatically shrink +// to Heap::kMinJoinStackSize once the stack becomes empty. +builtin JoinStackPop(implicit context: Context)( + stack: FixedArray, receiver: JSReceiver): JSAny { + const len: intptr = stack.length_intptr; + for (let i: intptr = 0; i < len; i++) { + if (stack.objects[i] == receiver) { + // Shrink the Join Stack if the stack will be empty and is larger than + // the minimum size. + if (i == 0 && len > kMinJoinStackSize) deferred { + const newStack: FixedArray = AllocateFixedArrayWithHoles( + kMinJoinStackSize, AllocationFlag::kNone); + SetJoinStack(newStack); } - return Undefined; + else { + stack.objects[i] = TheHole; } + return Undefined; } - unreachable; } + unreachable; +} - // Fast path the common non-nested calls. - macro JoinStackPopInline(implicit context: Context)(receiver: JSReceiver) { - const stack: FixedArray = LoadJoinStack() - otherwise unreachable; - const len: intptr = stack.length_intptr; - - // Builtin call was not nested (receiver is the first entry) and - // did not contain other nested arrays that expanded the stack. - if (stack.objects[0] == receiver && len == kMinJoinStackSize) { - StoreFixedArrayElement(stack, 0, TheHole, SKIP_WRITE_BARRIER); - } else - deferred { - JoinStackPop(stack, receiver); - } - } +// Fast path the common non-nested calls. +macro JoinStackPopInline(implicit context: Context)(receiver: JSReceiver) { + const stack: FixedArray = LoadJoinStack() + otherwise unreachable; + const len: intptr = stack.length_intptr; + + // Builtin call was not nested (receiver is the first entry) and + // did not contain other nested arrays that expanded the stack. + if (stack.objects[0] == receiver && len == kMinJoinStackSize) { + StoreFixedArrayElement(stack, 0, TheHole, SKIP_WRITE_BARRIER); + } else + deferred { + JoinStackPop(stack, receiver); + } +} - // Main entry point for all builtins using Array Join functionality. - transitioning macro CycleProtectedArrayJoin(implicit context: - Context)( - useToLocaleString: constexpr bool, o: JSReceiver, len: Number, - sepObj: JSAny, locales: JSAny, options: JSAny): JSAny { - // 3. If separator is undefined, let sep be the single-element String ",". - // 4. Else, let sep be ? ToString(separator). - const sep: String = sepObj == Undefined ? ',' : ToString_Inline(sepObj); - - // If the receiver is not empty and not already being joined, continue with - // the normal join algorithm. - if (len > 0 && JoinStackPushInline(o)) { - try { - const result: JSAny = - ArrayJoin(useToLocaleString, o, sep, len, locales, options); - JoinStackPopInline(o); - return result; - } catch (e) deferred { - JoinStackPopInline(o); - ReThrow(context, e); - } - } else { - return kEmptyString; +// Main entry point for all builtins using Array Join functionality. +transitioning macro CycleProtectedArrayJoin( + implicit context: Context)( + useToLocaleString: constexpr bool, o: JSReceiver, len: Number, + sepObj: JSAny, locales: JSAny, options: JSAny): JSAny { + // 3. If separator is undefined, let sep be the single-element String ",". + // 4. Else, let sep be ? ToString(separator). + const sep: String = sepObj == Undefined ? ',' : ToString_Inline(sepObj); + + // If the receiver is not empty and not already being joined, continue with + // the normal join algorithm. + if (len > 0 && JoinStackPushInline(o)) { + try { + const result: JSAny = + ArrayJoin(useToLocaleString, o, sep, len, locales, options); + JoinStackPopInline(o); + return result; + } catch (e) deferred { + JoinStackPopInline(o); + ReThrow(context, e); } + } else { + return kEmptyString; } +} - // https://tc39.github.io/ecma262/#sec-array.prototype.join - transitioning javascript builtin - ArrayPrototypeJoin(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): JSAny { - const separator: JSAny = arguments[0]; +// https://tc39.github.io/ecma262/#sec-array.prototype.join +transitioning javascript builtin +ArrayPrototypeJoin( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const separator: JSAny = arguments[0]; - // 1. Let O be ? ToObject(this value). - const o: JSReceiver = ToObject_Inline(context, receiver); + // 1. Let O be ? ToObject(this value). + const o: JSReceiver = ToObject_Inline(context, receiver); - // 2. Let len be ? ToLength(? Get(O, "length")). - const len: Number = GetLengthProperty(o); + // 2. Let len be ? ToLength(? Get(O, "length")). + const len: Number = GetLengthProperty(o); - // Only handle valid array lengths. Although the spec allows larger - // values, this matches historical V8 behavior. - if (len > kMaxArrayIndex + 1) - ThrowTypeError(MessageTemplate::kInvalidArrayLength); + // Only handle valid array lengths. Although the spec allows larger + // values, this matches historical V8 behavior. + if (len > kMaxArrayIndex + 1) + ThrowTypeError(MessageTemplate::kInvalidArrayLength); - return CycleProtectedArrayJoin( - false, o, len, separator, Undefined, Undefined); - } + return CycleProtectedArrayJoin( + false, o, len, separator, Undefined, Undefined); +} - // https://tc39.github.io/ecma262/#sec-array.prototype.tolocalestring - transitioning javascript builtin ArrayPrototypeToLocaleString( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const locales: JSAny = arguments[0]; - const options: JSAny = arguments[1]; +// https://tc39.github.io/ecma262/#sec-array.prototype.tolocalestring +transitioning javascript builtin ArrayPrototypeToLocaleString( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const locales: JSAny = arguments[0]; + const options: JSAny = arguments[1]; - // 1. Let O be ? ToObject(this value). - const o: JSReceiver = ToObject_Inline(context, receiver); + // 1. Let O be ? ToObject(this value). + const o: JSReceiver = ToObject_Inline(context, receiver); - // 2. Let len be ? ToLength(? Get(O, "length")). - const len: Number = GetLengthProperty(o); + // 2. Let len be ? ToLength(? Get(O, "length")). + const len: Number = GetLengthProperty(o); - // Only handle valid array lengths. Although the spec allows larger - // values, this matches historical V8 behavior. - if (len > kMaxArrayIndex + 1) - ThrowTypeError(MessageTemplate::kInvalidArrayLength); + // Only handle valid array lengths. Although the spec allows larger + // values, this matches historical V8 behavior. + if (len > kMaxArrayIndex + 1) + ThrowTypeError(MessageTemplate::kInvalidArrayLength); - return CycleProtectedArrayJoin( - true, o, len, ',', locales, options); - } + return CycleProtectedArrayJoin(true, o, len, ',', locales, options); +} - // https://tc39.github.io/ecma262/#sec-array.prototype.tostring - transitioning javascript builtin ArrayPrototypeToString( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - // 1. Let array be ? ToObject(this value). - const array: JSReceiver = ToObject_Inline(context, receiver); +// https://tc39.github.io/ecma262/#sec-array.prototype.tostring +transitioning javascript builtin ArrayPrototypeToString( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + // 1. Let array be ? ToObject(this value). + const array: JSReceiver = ToObject_Inline(context, receiver); + + // 2. Let func be ? Get(array, "join"). + const prop: JSAny = GetProperty(array, 'join'); + try { + // 3. If IsCallable(func) is false, let func be the intrinsic function + // %ObjProto_toString%. + const func: Callable = Cast(prop) otherwise NotCallable; + + // 4. Return ? Call(func, array). + return Call(context, func, array); + } label NotCallable { + return ObjectToString(context, array); + } +} - // 2. Let func be ? Get(array, "join"). - const prop: JSAny = GetProperty(array, 'join'); - try { - // 3. If IsCallable(func) is false, let func be the intrinsic function - // %ObjProto_toString%. - const func: Callable = Cast(prop) otherwise NotCallable; +// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.join +transitioning javascript builtin TypedArrayPrototypeJoin( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const separator: JSAny = arguments[0]; - // 4. Return ? Call(func, array). - return Call(context, func, array); - } - label NotCallable { - return ObjectToString(context, array); - } - } + // Spec: ValidateTypedArray is applied to the this value prior to evaluating + // the algorithm. + const typedArray: JSTypedArray = typed_array::ValidateTypedArray( + context, receiver, '%TypedArray%.prototype.join'); + const length = Convert(typedArray.length); - // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.join - transitioning javascript builtin TypedArrayPrototypeJoin( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const separator: JSAny = arguments[0]; + return CycleProtectedArrayJoin( + false, typedArray, length, separator, Undefined, Undefined); +} - // Spec: ValidateTypedArray is applied to the this value prior to evaluating - // the algorithm. - const typedArray: JSTypedArray = typed_array::ValidateTypedArray( - context, receiver, '%TypedArray%.prototype.join'); - const length = Convert(typedArray.length); +// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.tolocalestring +transitioning javascript builtin TypedArrayPrototypeToLocaleString( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const locales: JSAny = arguments[0]; + const options: JSAny = arguments[1]; - return CycleProtectedArrayJoin( - false, typedArray, length, separator, Undefined, Undefined); - } + // Spec: ValidateTypedArray is applied to the this value prior to evaluating + // the algorithm. + const typedArray: JSTypedArray = typed_array::ValidateTypedArray( + context, receiver, '%TypedArray%.prototype.toLocaleString'); + const length = Convert(typedArray.length); - // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.tolocalestring - transitioning javascript builtin TypedArrayPrototypeToLocaleString( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const locales: JSAny = arguments[0]; - const options: JSAny = arguments[1]; - - // Spec: ValidateTypedArray is applied to the this value prior to evaluating - // the algorithm. - const typedArray: JSTypedArray = typed_array::ValidateTypedArray( - context, receiver, '%TypedArray%.prototype.toLocaleString'); - const length = Convert(typedArray.length); - - return CycleProtectedArrayJoin( - true, typedArray, length, ',', locales, options); - } + return CycleProtectedArrayJoin( + true, typedArray, length, ',', locales, options); +} } diff --git a/deps/v8/src/builtins/array-lastindexof.tq b/deps/v8/src/builtins/array-lastindexof.tq index 52bcc75d195f97..fe416fa4a24289 100644 --- a/deps/v8/src/builtins/array-lastindexof.tq +++ b/deps/v8/src/builtins/array-lastindexof.tq @@ -3,154 +3,151 @@ // found in the LICENSE file. namespace array { - macro LoadWithHoleCheck( - elements: FixedArrayBase, index: Smi): JSAny - labels IfHole; - - LoadWithHoleCheck(implicit context: Context)( - elements: FixedArrayBase, index: Smi): JSAny - labels IfHole { - const elements: FixedArray = UnsafeCast(elements); - const element: Object = elements.objects[index]; - if (element == TheHole) goto IfHole; - return UnsafeCast(element); - } +macro LoadWithHoleCheck( + elements: FixedArrayBase, index: Smi): JSAny + labels IfHole; + +LoadWithHoleCheck(implicit context: Context)( + elements: FixedArrayBase, index: Smi): JSAny + labels IfHole { + const elements: FixedArray = UnsafeCast(elements); + const element: Object = elements.objects[index]; + if (element == TheHole) goto IfHole; + return UnsafeCast(element); +} - LoadWithHoleCheck(implicit context: Context)( - elements: FixedArrayBase, index: Smi): JSAny - labels IfHole { - const elements: FixedDoubleArray = UnsafeCast(elements); - const element: float64 = elements.floats[index].Value() otherwise IfHole; - return AllocateHeapNumberWithValue(element); - } +LoadWithHoleCheck(implicit context: Context)( + elements: FixedArrayBase, index: Smi): JSAny + labels IfHole { + const elements: FixedDoubleArray = UnsafeCast(elements); + const element: float64 = elements.floats[index].Value() otherwise IfHole; + return AllocateHeapNumberWithValue(element); +} - macro FastArrayLastIndexOf( - context: Context, array: JSArray, from: Smi, searchElement: JSAny): Smi { - const elements: FixedArrayBase = array.elements; - let k: Smi = from; - - // Bug(898785): Due to side-effects in the evaluation of `fromIndex` - // the {from} can be out-of-bounds here, so we need to clamp {k} to - // the {elements} length. We might be reading holes / hole NaNs still - // due to that, but those will be ignored below. - if (k >= elements.length) { - k = elements.length - 1; - } +macro FastArrayLastIndexOf( + context: Context, array: JSArray, from: Smi, searchElement: JSAny): Smi { + const elements: FixedArrayBase = array.elements; + let k: Smi = from; + + // Bug(898785): Due to side-effects in the evaluation of `fromIndex` + // the {from} can be out-of-bounds here, so we need to clamp {k} to + // the {elements} length. We might be reading holes / hole NaNs still + // due to that, but those will be ignored below. + if (k >= elements.length) { + k = elements.length - 1; + } - while (k >= 0) { - try { - const element: JSAny = LoadWithHoleCheck(elements, k) - otherwise Hole; + while (k >= 0) { + try { + const element: JSAny = LoadWithHoleCheck(elements, k) + otherwise Hole; - const same: Boolean = StrictEqual(searchElement, element); - if (same == True) { - assert(Is(array)); - return k; - } + const same: Boolean = StrictEqual(searchElement, element); + if (same == True) { + assert(Is(array)); + return k; } - label Hole {} // Do nothing for holes. - - --k; - } + } label Hole {} // Do nothing for holes. - assert(Is(array)); - return -1; + --k; } - transitioning macro - GetFromIndex(context: Context, length: Number, arguments: Arguments): Number { - // 4. If fromIndex is present, let n be ? ToInteger(fromIndex); - // else let n be len - 1. - const n: Number = - arguments.length < 2 ? length - 1 : ToInteger_Inline(arguments[1]); - - // 5. If n >= 0, then. - let k: Number = SmiConstant(0); - if (n >= 0) { - // a. If n is -0, let k be +0; else let k be min(n, len - 1). - // If n was -0 it got truncated to 0.0, so taking the minimum is fine. - k = Min(n, length - 1); - } else { - // a. Let k be len + n. - k = length + n; - } - return k; + assert(Is(array)); + return -1; +} + +transitioning macro +GetFromIndex(context: Context, length: Number, arguments: Arguments): Number { + // 4. If fromIndex is present, let n be ? ToInteger(fromIndex); + // else let n be len - 1. + const n: Number = + arguments.length < 2 ? length - 1 : ToInteger_Inline(arguments[1]); + + // 5. If n >= 0, then. + let k: Number = SmiConstant(0); + if (n >= 0) { + // a. If n is -0, let k be +0; else let k be min(n, len - 1). + // If n was -0 it got truncated to 0.0, so taking the minimum is fine. + k = Min(n, length - 1); + } else { + // a. Let k be len + n. + k = length + n; } + return k; +} - macro TryFastArrayLastIndexOf( - context: Context, receiver: JSReceiver, searchElement: JSAny, - from: Number): JSAny - labels Slow { - const array: FastJSArray = Cast(receiver) otherwise Slow; - const length: Smi = array.length; - if (length == 0) return SmiConstant(-1); - - const fromSmi: Smi = Cast(from) otherwise Slow; - const kind: ElementsKind = array.map.elements_kind; - if (IsFastSmiOrTaggedElementsKind(kind)) { - return FastArrayLastIndexOf( - context, array, fromSmi, searchElement); - } - assert(IsDoubleElementsKind(kind)); - return FastArrayLastIndexOf( +macro TryFastArrayLastIndexOf( + context: Context, receiver: JSReceiver, searchElement: JSAny, + from: Number): JSAny + labels Slow { + const array: FastJSArray = Cast(receiver) otherwise Slow; + const length: Smi = array.length; + if (length == 0) return SmiConstant(-1); + + const fromSmi: Smi = Cast(from) otherwise Slow; + const kind: ElementsKind = array.map.elements_kind; + if (IsFastSmiOrTaggedElementsKind(kind)) { + return FastArrayLastIndexOf( context, array, fromSmi, searchElement); } + assert(IsDoubleElementsKind(kind)); + return FastArrayLastIndexOf( + context, array, fromSmi, searchElement); +} - transitioning macro GenericArrayLastIndexOf( - context: Context, object: JSReceiver, searchElement: JSAny, - from: Number): JSAny { - let k: Number = from; - - // 7. Repeat, while k >= 0. - while (k >= 0) { - // a. Let kPresent be ? HasProperty(O, ! ToString(k)). - const kPresent: Boolean = HasProperty(object, k); +transitioning macro GenericArrayLastIndexOf( + context: Context, object: JSReceiver, searchElement: JSAny, + from: Number): JSAny { + let k: Number = from; - // b. If kPresent is true, then. - if (kPresent == True) { - // i. Let elementK be ? Get(O, ! ToString(k)). - const element: JSAny = GetProperty(object, k); + // 7. Repeat, while k >= 0. + while (k >= 0) { + // a. Let kPresent be ? HasProperty(O, ! ToString(k)). + const kPresent: Boolean = HasProperty(object, k); - // ii. Let same be the result of performing Strict Equality Comparison - // searchElement === elementK. - const same: Boolean = StrictEqual(searchElement, element); + // b. If kPresent is true, then. + if (kPresent == True) { + // i. Let elementK be ? Get(O, ! ToString(k)). + const element: JSAny = GetProperty(object, k); - // iii. If same is true, return k. - if (same == True) return k; - } + // ii. Let same be the result of performing Strict Equality Comparison + // searchElement === elementK. + const same: Boolean = StrictEqual(searchElement, element); - // c. Decrease k by 1. - --k; + // iii. If same is true, return k. + if (same == True) return k; } - // 8. Return -1. - return SmiConstant(-1); + // c. Decrease k by 1. + --k; } - // https://tc39.github.io/ecma262/#sec-array.prototype.lastIndexOf - transitioning javascript builtin ArrayPrototypeLastIndexOf( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - // 1. Let O be ? ToObject(this value). - const object: JSReceiver = ToObject_Inline(context, receiver); + // 8. Return -1. + return SmiConstant(-1); +} - // 2. Let len be ? ToLength(? Get(O, "length")). - const length: Number = GetLengthProperty(object); +// https://tc39.github.io/ecma262/#sec-array.prototype.lastIndexOf +transitioning javascript builtin ArrayPrototypeLastIndexOf( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + // 1. Let O be ? ToObject(this value). + const object: JSReceiver = ToObject_Inline(context, receiver); - // 3. If len is 0, return -1. - if (length == SmiConstant(0)) return SmiConstant(-1); + // 2. Let len be ? ToLength(? Get(O, "length")). + const length: Number = GetLengthProperty(object); - // Step 4 - 6. - const from: Number = GetFromIndex(context, length, arguments); + // 3. If len is 0, return -1. + if (length == SmiConstant(0)) return SmiConstant(-1); - const searchElement: JSAny = arguments[0]; + // Step 4 - 6. + const from: Number = GetFromIndex(context, length, arguments); - try { - return TryFastArrayLastIndexOf(context, object, searchElement, from) - otherwise Baseline; - } - label Baseline { - return GenericArrayLastIndexOf(context, object, searchElement, from); - } + const searchElement: JSAny = arguments[0]; + + try { + return TryFastArrayLastIndexOf(context, object, searchElement, from) + otherwise Baseline; + } label Baseline { + return GenericArrayLastIndexOf(context, object, searchElement, from); } } +} diff --git a/deps/v8/src/builtins/array-map.tq b/deps/v8/src/builtins/array-map.tq index 9b45341c0e2568..8ff3cbaccdc9ac 100644 --- a/deps/v8/src/builtins/array-map.tq +++ b/deps/v8/src/builtins/array-map.tq @@ -3,271 +3,265 @@ // found in the LICENSE file. namespace array { - transitioning javascript builtin - ArrayMapLoopEagerDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny, - length: JSAny): JSAny { - // All continuation points in the optimized filter implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - // - // Also, this great mass of casts is necessary because the signature - // of Torque javascript builtins requires JSAny type for all parameters - // other than {context}. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const outputArray = Cast(array) otherwise unreachable; - const numberK = Cast(initialK) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; - - return ArrayMapLoopContinuation( - jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK, - numberLength); - } - - transitioning javascript builtin - ArrayMapLoopLazyDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny, - length: JSAny, result: JSAny): JSAny { - // All continuation points in the optimized filter implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const outputArray = Cast(array) otherwise unreachable; - let numberK = Cast(initialK) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; - - // This custom lazy deopt point is right after the callback. map() needs - // to pick up at the next step, which is setting the callback result in - // the output array. After incrementing k, we can glide into the loop - // continuation builtin. - - // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue). - FastCreateDataProperty(outputArray, numberK, result); - - // 7d. Increase k by 1. - numberK = numberK + 1; - - return ArrayMapLoopContinuation( - jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK, - numberLength); - } +transitioning javascript builtin +ArrayMapLoopEagerDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny, + length: JSAny): JSAny { + // All continuation points in the optimized filter implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + // + // Also, this great mass of casts is necessary because the signature + // of Torque javascript builtins requires JSAny type for all parameters + // other than {context}. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const outputArray = Cast(array) otherwise unreachable; + const numberK = Cast(initialK) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + return ArrayMapLoopContinuation( + jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK, + numberLength); +} - transitioning builtin ArrayMapLoopContinuation(implicit context: Context)( - _receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, - array: JSReceiver, o: JSReceiver, initialK: Number, - length: Number): JSAny { - // 6. Let k be 0. - // 7. Repeat, while k < len - for (let k: Number = initialK; k < length; k++) { - // 7a. Let Pk be ! ToString(k). - // k is guaranteed to be a positive integer, hence ToString is - // side-effect free and HasProperty/GetProperty do the conversion inline. - - // 7b. Let kPresent be ? HasProperty(O, Pk). - const kPresent: Boolean = HasProperty_Inline(o, k); - - // 7c. If kPresent is true, then: - if (kPresent == True) { - // i. Let kValue be ? Get(O, Pk). - const kValue: JSAny = GetProperty(o, k); - - // ii. Let mapped_value be ? Call(callbackfn, T, kValue, k, O). - const mappedValue: JSAny = - Call(context, callbackfn, thisArg, kValue, k, o); - - // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value). - FastCreateDataProperty(array, k, mappedValue); - } +transitioning javascript builtin +ArrayMapLoopLazyDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, thisArg: JSAny, array: JSAny, initialK: JSAny, + length: JSAny, result: JSAny): JSAny { + // All continuation points in the optimized filter implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const outputArray = Cast(array) otherwise unreachable; + let numberK = Cast(initialK) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + // This custom lazy deopt point is right after the callback. map() needs + // to pick up at the next step, which is setting the callback result in + // the output array. After incrementing k, we can glide into the loop + // continuation builtin. + + // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue). + FastCreateDataProperty(outputArray, numberK, result); + + // 7d. Increase k by 1. + numberK = numberK + 1; + + return ArrayMapLoopContinuation( + jsreceiver, callbackfn, thisArg, outputArray, jsreceiver, numberK, + numberLength); +} - // 7d. Increase k by 1. (done by the loop). +transitioning builtin ArrayMapLoopContinuation(implicit context: Context)( + _receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, + array: JSReceiver, o: JSReceiver, initialK: Number, length: Number): JSAny { + // 6. Let k be 0. + // 7. Repeat, while k < len + for (let k: Number = initialK; k < length; k++) { + // 7a. Let Pk be ! ToString(k). + // k is guaranteed to be a positive integer, hence ToString is + // side-effect free and HasProperty/GetProperty do the conversion inline. + + // 7b. Let kPresent be ? HasProperty(O, Pk). + const kPresent: Boolean = HasProperty_Inline(o, k); + + // 7c. If kPresent is true, then: + if (kPresent == True) { + // i. Let kValue be ? Get(O, Pk). + const kValue: JSAny = GetProperty(o, k); + + // ii. Let mapped_value be ? Call(callbackfn, T, kValue, k, O). + const mappedValue: JSAny = + Call(context, callbackfn, thisArg, kValue, k, o); + + // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mapped_value). + FastCreateDataProperty(array, k, mappedValue); } - // 8. Return A. - return array; + // 7d. Increase k by 1. (done by the loop). } - struct Vector { - macro ReportSkippedElement() { - this.skippedElements = true; - } - - macro CreateJSArray(implicit context: Context)(validLength: Smi): JSArray { - const length: Smi = this.fixedArray.length; - assert(validLength <= length); - let kind: ElementsKind = ElementsKind::PACKED_SMI_ELEMENTS; - if (!this.onlySmis) { - if (this.onlyNumbers) { - kind = ElementsKind::PACKED_DOUBLE_ELEMENTS; - } else { - kind = ElementsKind::PACKED_ELEMENTS; - } - } + // 8. Return A. + return array; +} - if (this.skippedElements || validLength < length) { - // We also need to create a holey output array if we are - // bailing out of the fast path partway through the array. - // This is indicated by {validLength} < {length}. - // Who knows if the bailout condition will continue to fill in - // every element? - kind = FastHoleyElementsKind(kind); - } +struct Vector { + macro ReportSkippedElement() { + this.skippedElements = true; + } - const map: Map = LoadJSArrayElementsMap(kind, LoadNativeContext(context)); - let a: JSArray; - - if (IsDoubleElementsKind(kind)) { - // We need to allocate and copy. - // First, initialize the elements field before allocation to prevent - // heap corruption. - const elements: FixedDoubleArray = AllocateFixedDoubleArrayWithHoles( - SmiUntag(length), AllocationFlag::kAllowLargeObjectAllocation); - a = NewJSArray(map, this.fixedArray); - for (let i: Smi = 0; i < validLength; i++) { - typeswitch ( - UnsafeCast<(Number | TheHole)>(this.fixedArray.objects[i])) { - case (n: Number): { - elements.floats[i] = Convert(n); - } - case (TheHole): { - } - } - } - a.elements = elements; + macro CreateJSArray(implicit context: Context)(validLength: Smi): JSArray { + const length: Smi = this.fixedArray.length; + assert(validLength <= length); + let kind: ElementsKind = ElementsKind::PACKED_SMI_ELEMENTS; + if (!this.onlySmis) { + if (this.onlyNumbers) { + kind = ElementsKind::PACKED_DOUBLE_ELEMENTS; } else { - // Simply install the given fixedArray in {vector}. - a = NewJSArray(map, this.fixedArray); + kind = ElementsKind::PACKED_ELEMENTS; } + } - // Paranoia. the FixedArray now "belongs" to JSArray {a}. - this.fixedArray = kEmptyFixedArray; - return a; + if (this.skippedElements || validLength < length) { + // We also need to create a holey output array if we are + // bailing out of the fast path partway through the array. + // This is indicated by {validLength} < {length}. + // Who knows if the bailout condition will continue to fill in + // every element? + kind = FastHoleyElementsKind(kind); } - macro StoreResult(implicit context: Context)(index: Smi, result: JSAny) { - typeswitch (result) { - case (s: Smi): { - this.fixedArray.objects[index] = s; - } - case (s: HeapNumber): { - this.onlySmis = false; - this.fixedArray.objects[index] = s; - } - case (s: JSAnyNotNumber): { - this.onlySmis = false; - this.onlyNumbers = false; - this.fixedArray.objects[index] = s; + const map: Map = LoadJSArrayElementsMap(kind, LoadNativeContext(context)); + let a: JSArray; + + if (IsDoubleElementsKind(kind)) { + // We need to allocate and copy. + // First, initialize the elements field before allocation to prevent + // heap corruption. + const elements: FixedDoubleArray = AllocateFixedDoubleArrayWithHoles( + SmiUntag(length), AllocationFlag::kAllowLargeObjectAllocation); + a = NewJSArray(map, this.fixedArray); + for (let i: Smi = 0; i < validLength; i++) { + typeswitch ( + UnsafeCast<(Number | TheHole)>(this.fixedArray.objects[i])) { + case (n: Number): { + elements.floats[i] = Convert(n); + } + case (TheHole): { + } } } + a.elements = elements; + } else { + // Simply install the given fixedArray in {vector}. + a = NewJSArray(map, this.fixedArray); } - fixedArray: FixedArray; - onlySmis: bool; // initially true. - onlyNumbers: bool; // initially true. - skippedElements: bool; // initially false. + // Paranoia. the FixedArray now "belongs" to JSArray {a}. + this.fixedArray = kEmptyFixedArray; + return a; } - macro NewVector(implicit context: Context)(length: Smi): Vector { - const fixedArray = length > 0 ? - AllocateFixedArrayWithHoles( - SmiUntag(length), AllocationFlag::kAllowLargeObjectAllocation) : - kEmptyFixedArray; - return Vector{ - fixedArray, - onlySmis: true, - onlyNumbers: true, - skippedElements: false - }; + macro StoreResult(implicit context: Context)(index: Smi, result: JSAny) { + typeswitch (result) { + case (s: Smi): { + this.fixedArray.objects[index] = s; + } + case (s: HeapNumber): { + this.onlySmis = false; + this.fixedArray.objects[index] = s; + } + case (s: JSAnyNotNumber): { + this.onlySmis = false; + this.onlyNumbers = false; + this.fixedArray.objects[index] = s; + } + } } - transitioning macro FastArrayMap(implicit context: Context)( - fastO: FastJSArrayForRead, len: Smi, callbackfn: Callable, - thisArg: JSAny): JSArray - labels Bailout(JSArray, Smi) { - let k: Smi = 0; - let fastOW = NewFastJSArrayForReadWitness(fastO); - let vector = NewVector(len); + fixedArray: FixedArray; + onlySmis: bool; // initially true. + onlyNumbers: bool; // initially true. + skippedElements: bool; // initially false. +} - // Build a fast loop over the smi array. - // 7. Repeat, while k < len. - try { - for (; k < len; k++) { - fastOW.Recheck() otherwise goto PrepareBailout(k); - - // Ensure that we haven't walked beyond a possibly updated length. - if (k >= fastOW.Get().length) goto PrepareBailout(k); - - try { - const value: JSAny = fastOW.LoadElementNoHole(k) - otherwise FoundHole; - const result: JSAny = - Call(context, callbackfn, thisArg, value, k, fastOW.Get()); - vector.StoreResult(k, result); - } - label FoundHole { - // Our output array must necessarily be holey because of holes in - // the input array. - vector.ReportSkippedElement(); - } +macro NewVector(implicit context: Context)(length: Smi): Vector { + const fixedArray = length > 0 ? + AllocateFixedArrayWithHoles( + SmiUntag(length), AllocationFlag::kAllowLargeObjectAllocation) : + kEmptyFixedArray; + return Vector{ + fixedArray, + onlySmis: true, + onlyNumbers: true, + skippedElements: false + }; +} + +transitioning macro FastArrayMap(implicit context: Context)( + fastO: FastJSArrayForRead, len: Smi, callbackfn: Callable, + thisArg: JSAny): JSArray + labels Bailout(JSArray, Smi) { + let k: Smi = 0; + let fastOW = NewFastJSArrayForReadWitness(fastO); + let vector = NewVector(len); + + // Build a fast loop over the smi array. + // 7. Repeat, while k < len. + try { + for (; k < len; k++) { + fastOW.Recheck() otherwise goto PrepareBailout(k); + + // Ensure that we haven't walked beyond a possibly updated length. + if (k >= fastOW.Get().length) goto PrepareBailout(k); + + try { + const value: JSAny = fastOW.LoadElementNoHole(k) + otherwise FoundHole; + const result: JSAny = + Call(context, callbackfn, thisArg, value, k, fastOW.Get()); + vector.StoreResult(k, result); + } label FoundHole { + // Our output array must necessarily be holey because of holes in + // the input array. + vector.ReportSkippedElement(); } } - label PrepareBailout(k: Smi) deferred { - // Transform {vector} into a JSArray and bail out. - goto Bailout(vector.CreateJSArray(k), k); - } - - return vector.CreateJSArray(len); + } label PrepareBailout(k: Smi) deferred { + // Transform {vector} into a JSArray and bail out. + goto Bailout(vector.CreateJSArray(k), k); } - // https://tc39.github.io/ecma262/#sec-array.prototype.map - transitioning javascript builtin - ArrayMap(js-implicit context: NativeContext, receiver: JSAny)(...arguments): - JSAny { - try { - RequireObjectCoercible(receiver, 'Array.prototype.map'); + return vector.CreateJSArray(len); +} - // 1. Let O be ? ToObject(this value). - const o: JSReceiver = ToObject_Inline(context, receiver); +// https://tc39.github.io/ecma262/#sec-array.prototype.map +transitioning javascript builtin +ArrayMap( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + try { + RequireObjectCoercible(receiver, 'Array.prototype.map'); - // 2. Let len be ? ToLength(? Get(O, "length")). - const len: Number = GetLengthProperty(o); + // 1. Let O be ? ToObject(this value). + const o: JSReceiver = ToObject_Inline(context, receiver); - // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - if (arguments.length == 0) goto TypeError; + // 2. Let len be ? ToLength(? Get(O, "length")). + const len: Number = GetLengthProperty(o); - const callbackfn = Cast(arguments[0]) otherwise TypeError; + // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. + if (arguments.length == 0) goto TypeError; - // 4. If thisArg is present, let T be thisArg; else let T be undefined. - const thisArg: JSAny = arguments.length > 1 ? arguments[1] : Undefined; + const callbackfn = Cast(arguments[0]) otherwise TypeError; - let array: JSReceiver; - let k: Number = 0; - try { - // 5. Let A be ? ArraySpeciesCreate(O, len). - if (IsArraySpeciesProtectorCellInvalid()) goto SlowSpeciesCreate; - const o: FastJSArrayForRead = Cast(receiver) - otherwise SlowSpeciesCreate; - const smiLength: Smi = Cast(len) - otherwise SlowSpeciesCreate; - - return FastArrayMap(o, smiLength, callbackfn, thisArg) - otherwise Bailout; - } - label SlowSpeciesCreate { - array = ArraySpeciesCreate(context, receiver, len); - } - label Bailout(output: JSArray, kValue: Smi) deferred { - array = output; - k = kValue; - } + // 4. If thisArg is present, let T be thisArg; else let T be undefined. + const thisArg: JSAny = arguments[1]; - return ArrayMapLoopContinuation(o, callbackfn, thisArg, array, o, k, len); - } - label TypeError deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); + let array: JSReceiver; + let k: Number = 0; + try { + // 5. Let A be ? ArraySpeciesCreate(O, len). + if (IsArraySpeciesProtectorCellInvalid()) goto SlowSpeciesCreate; + const o: FastJSArrayForRead = Cast(receiver) + otherwise SlowSpeciesCreate; + const smiLength: Smi = Cast(len) + otherwise SlowSpeciesCreate; + + return FastArrayMap(o, smiLength, callbackfn, thisArg) + otherwise Bailout; + } label SlowSpeciesCreate { + array = ArraySpeciesCreate(context, receiver, len); + } label Bailout(output: JSArray, kValue: Smi) deferred { + array = output; + k = kValue; } + + return ArrayMapLoopContinuation(o, callbackfn, thisArg, array, o, k, len); + } label TypeError deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); } } +} diff --git a/deps/v8/src/builtins/array-of.tq b/deps/v8/src/builtins/array-of.tq index 2df961b9955ec2..49c67bd8231dcc 100644 --- a/deps/v8/src/builtins/array-of.tq +++ b/deps/v8/src/builtins/array-of.tq @@ -3,53 +3,53 @@ // found in the LICENSE file. namespace array { - // https://tc39.github.io/ecma262/#sec-array.of - transitioning javascript builtin - ArrayOf(js-implicit context: NativeContext, receiver: JSAny)(...arguments): - JSAny { - // 1. Let len be the actual number of arguments passed to this function. - const len: Smi = Convert(arguments.length); - - // 2. Let items be the List of arguments passed to this function. - const items: Arguments = arguments; - - // 3. Let C be the this value. - const c: JSAny = receiver; - - let a: JSReceiver; - - // 4. If IsConstructor(C) is true, then - typeswitch (c) { - case (c: Constructor): { - // a. Let A be ? Construct(C, « len »). - a = Construct(c, len); - } - case (JSAny): { - // a. Let A be ? ArrayCreate(len). - a = ArrayCreate(len); - } +// https://tc39.github.io/ecma262/#sec-array.of +transitioning javascript builtin +ArrayOf( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + // 1. Let len be the actual number of arguments passed to this function. + const len: Smi = Convert(arguments.length); + + // 2. Let items be the List of arguments passed to this function. + const items: Arguments = arguments; + + // 3. Let C be the this value. + const c: JSAny = receiver; + + let a: JSReceiver; + + // 4. If IsConstructor(C) is true, then + typeswitch (c) { + case (c: Constructor): { + // a. Let A be ? Construct(C, « len »). + a = Construct(c, len); } + case (JSAny): { + // a. Let A be ? ArrayCreate(len). + a = ArrayCreate(len); + } + } - // 6. Let k be 0. - let k: Smi = 0; + // 6. Let k be 0. + let k: Smi = 0; - // 7. Repeat, while k < len - while (k < len) { - // a. Let kValue be items[k]. - const kValue: JSAny = items[Convert(k)]; + // 7. Repeat, while k < len + while (k < len) { + // a. Let kValue be items[k]. + const kValue: JSAny = items[Convert(k)]; - // b. Let Pk be ! ToString(k). - // c. Perform ? CreateDataPropertyOrThrow(A, Pk, kValue). - FastCreateDataProperty(a, k, kValue); + // b. Let Pk be ! ToString(k). + // c. Perform ? CreateDataPropertyOrThrow(A, Pk, kValue). + FastCreateDataProperty(a, k, kValue); - // d. Increase k by 1. - k++; - } + // d. Increase k by 1. + k++; + } - // 8. Perform ? Set(A, "length", len, true). - array::SetPropertyLength(a, len); + // 8. Perform ? Set(A, "length", len, true). + array::SetPropertyLength(a, len); - // 9. Return A. - return a; - } + // 9. Return A. + return a; +} } diff --git a/deps/v8/src/builtins/array-reduce-right.tq b/deps/v8/src/builtins/array-reduce-right.tq index 45055862723af3..90e0e496f8f657 100644 --- a/deps/v8/src/builtins/array-reduce-right.tq +++ b/deps/v8/src/builtins/array-reduce-right.tq @@ -3,198 +3,195 @@ // found in the LICENSE file. namespace array { - transitioning javascript builtin - ArrayReduceRightPreLoopEagerDeoptContinuation( - js-implicit context: NativeContext, - receiver: JSAny)(callback: JSAny, length: JSAny): JSAny { - // All continuation points in the optimized every implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - // - // Also, this great mass of casts is necessary because the signature - // of Torque javascript builtins requires JSAny type for all parameters - // other than {context}. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; - const initialK = numberLength - 1; - - // Simulate starting the loop at {length - 1}, but ensuring that the - // accumulator is the hole. The continuation stub will search for the - // last non-hole element, rightly throwing an exception if not found. - return ArrayReduceRightLoopContinuation( - jsreceiver, callbackfn, TheHole, jsreceiver, initialK, numberLength); - } - - transitioning javascript builtin - ArrayReduceRightLoopEagerDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, initialK: JSAny, length: JSAny, - accumulator: JSAny): JSAny { - // All continuation points in the optimized every implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - // - // Also, this great mass of casts is necessary because the signature - // of Torque javascript builtins requires JSAny type for all parameters - // other than {context}. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const numberK = Cast(initialK) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; - - return ArrayReduceRightLoopContinuation( - jsreceiver, callbackfn, accumulator, jsreceiver, numberK, numberLength); - } - - transitioning javascript builtin - ArrayReduceRightLoopLazyDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, initialK: JSAny, length: JSAny, result: JSAny): JSAny { - // All continuation points in the optimized every implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const numberK = Cast(initialK) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; - - // The accumulator is the result from the callback call which just occured. - const r = ArrayReduceRightLoopContinuation( - jsreceiver, callbackfn, result, jsreceiver, numberK, numberLength); - return r; - } +transitioning javascript builtin +ArrayReduceRightPreLoopEagerDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, length: JSAny): JSAny { + // All continuation points in the optimized every implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + // + // Also, this great mass of casts is necessary because the signature + // of Torque javascript builtins requires JSAny type for all parameters + // other than {context}. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + const initialK = numberLength - 1; + + // Simulate starting the loop at {length - 1}, but ensuring that the + // accumulator is the hole. The continuation stub will search for the + // last non-hole element, rightly throwing an exception if not found. + return ArrayReduceRightLoopContinuation( + jsreceiver, callbackfn, TheHole, jsreceiver, initialK, numberLength); +} - transitioning builtin ArrayReduceRightLoopContinuation(implicit context: - Context)( - _receiver: JSReceiver, callbackfn: Callable, - initialAccumulator: JSAny|TheHole, o: JSReceiver, initialK: Number, - _length: Number): JSAny { - let accumulator = initialAccumulator; - - // 8b and 9. Repeat, while k >= 0 - for (let k: Number = initialK; k >= 0; k--) { - // 8b i and 9a. Let Pk be ! ToString(k). - // k is guaranteed to be a positive integer, hence ToString is - // side-effect free and HasProperty/GetProperty do the conversion inline. - - // 8b ii and 9b. Set kPresent to ? HasProperty(O, Pk). - const present: Boolean = HasProperty_Inline(o, k); - - // 8b iii and 9c. If kPresent is true, then - if (present == True) { - // 8b iii and 9c i. Let kValue be ? Get(O, Pk). - const value: JSAny = GetProperty(o, k); - - typeswitch (accumulator) { - case (TheHole): { - // 8b iii 1. - accumulator = value; - } - case (accumulatorNotHole: JSAny): { - // 9c. ii. Set accumulator to ? Call(callbackfn, undefined, - // ). - accumulator = Call( - context, callbackfn, Undefined, accumulatorNotHole, value, k, - o); - } - } - } +transitioning javascript builtin +ArrayReduceRightLoopEagerDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, initialK: JSAny, length: JSAny, + accumulator: JSAny): JSAny { + // All continuation points in the optimized every implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + // + // Also, this great mass of casts is necessary because the signature + // of Torque javascript builtins requires JSAny type for all parameters + // other than {context}. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const numberK = Cast(initialK) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + return ArrayReduceRightLoopContinuation( + jsreceiver, callbackfn, accumulator, jsreceiver, numberK, numberLength); +} - // 8b iv and 9d. Decrease k by 1. (done by the loop). - } +transitioning javascript builtin +ArrayReduceRightLoopLazyDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, initialK: JSAny, length: JSAny, result: JSAny): JSAny { + // All continuation points in the optimized every implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const numberK = Cast(initialK) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + // The accumulator is the result from the callback call which just occured. + const r = ArrayReduceRightLoopContinuation( + jsreceiver, callbackfn, result, jsreceiver, numberK, numberLength); + return r; +} - // 8c. if kPresent is false, throw a TypeError exception. - // If the accumulator is discovered with the sentinel hole value, - // this means kPresent is false. - typeswitch (accumulator) { - case (TheHole): { - ThrowTypeError( - MessageTemplate::kReduceNoInitial, 'Array.prototype.reduceRight'); - } - case (accumulator: JSAny): { - return accumulator; - } - } - } +transitioning builtin ArrayReduceRightLoopContinuation( + implicit context: Context)( + _receiver: JSReceiver, callbackfn: Callable, + initialAccumulator: JSAny|TheHole, o: JSReceiver, initialK: Number, + _length: Number): JSAny { + let accumulator = initialAccumulator; - transitioning macro FastArrayReduceRight(implicit context: Context)( - o: JSReceiver, len: Number, callbackfn: Callable, - initialAccumulator: JSAny|TheHole): JSAny - labels Bailout(Number, JSAny|TheHole) { - let accumulator = initialAccumulator; - const smiLen = Cast(len) otherwise goto Bailout(len - 1, accumulator); - const fastO = Cast(o) - otherwise goto Bailout(len - 1, accumulator); - let fastOW = NewFastJSArrayForReadWitness(fastO); + // 8b and 9. Repeat, while k >= 0 + for (let k: Number = initialK; k >= 0; k--) { + // 8b i and 9a. Let Pk be ! ToString(k). + // k is guaranteed to be a positive integer, hence ToString is + // side-effect free and HasProperty/GetProperty do the conversion inline. - // Build a fast loop over the array. - for (let k: Smi = smiLen - 1; k >= 0; k--) { - fastOW.Recheck() otherwise goto Bailout(k, accumulator); + // 8b ii and 9b. Set kPresent to ? HasProperty(O, Pk). + const present: Boolean = HasProperty_Inline(o, k); - // Ensure that we haven't walked beyond a possibly updated length. - if (k >= fastOW.Get().length) goto Bailout(k, accumulator); + // 8b iii and 9c. If kPresent is true, then + if (present == True) { + // 8b iii and 9c i. Let kValue be ? Get(O, Pk). + const value: JSAny = GetProperty(o, k); - const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue; typeswitch (accumulator) { case (TheHole): { + // 8b iii 1. accumulator = value; } case (accumulatorNotHole: JSAny): { + // 9c. ii. Set accumulator to ? Call(callbackfn, undefined, + // ). accumulator = Call( - context, callbackfn, Undefined, accumulatorNotHole, value, k, - fastOW.Get()); + context, callbackfn, Undefined, accumulatorNotHole, value, k, o); } } } + + // 8b iv and 9d. Decrease k by 1. (done by the loop). + } + + // 8c. if kPresent is false, throw a TypeError exception. + // If the accumulator is discovered with the sentinel hole value, + // this means kPresent is false. + typeswitch (accumulator) { + case (TheHole): { + ThrowTypeError( + MessageTemplate::kReduceNoInitial, 'Array.prototype.reduceRight'); + } + case (accumulator: JSAny): { + return accumulator; + } + } +} + +transitioning macro FastArrayReduceRight(implicit context: Context)( + o: JSReceiver, len: Number, callbackfn: Callable, + initialAccumulator: JSAny|TheHole): JSAny + labels Bailout(Number, JSAny | TheHole) { + let accumulator = initialAccumulator; + const smiLen = Cast(len) otherwise goto Bailout(len - 1, accumulator); + const fastO = Cast(o) + otherwise goto Bailout(len - 1, accumulator); + let fastOW = NewFastJSArrayForReadWitness(fastO); + + // Build a fast loop over the array. + for (let k: Smi = smiLen - 1; k >= 0; k--) { + fastOW.Recheck() otherwise goto Bailout(k, accumulator); + + // Ensure that we haven't walked beyond a possibly updated length. + if (k >= fastOW.Get().length) goto Bailout(k, accumulator); + + const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue; typeswitch (accumulator) { case (TheHole): { - ThrowTypeError( - MessageTemplate::kReduceNoInitial, 'Array.prototype.reduceRight'); + accumulator = value; } - case (accumulator: JSAny): { - return accumulator; + case (accumulatorNotHole: JSAny): { + accumulator = Call( + context, callbackfn, Undefined, accumulatorNotHole, value, k, + fastOW.Get()); } } } + typeswitch (accumulator) { + case (TheHole): { + ThrowTypeError( + MessageTemplate::kReduceNoInitial, 'Array.prototype.reduceRight'); + } + case (accumulator: JSAny): { + return accumulator; + } + } +} - // https://tc39.github.io/ecma262/#sec-array.prototype.reduceRight - transitioning javascript builtin - ArrayReduceRight(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): JSAny { - try { - RequireObjectCoercible(receiver, 'Array.prototype.reduceRight'); +// https://tc39.github.io/ecma262/#sec-array.prototype.reduceRight +transitioning javascript builtin +ArrayReduceRight( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + try { + RequireObjectCoercible(receiver, 'Array.prototype.reduceRight'); - // 1. Let O be ? ToObject(this value). - const o: JSReceiver = ToObject_Inline(context, receiver); + // 1. Let O be ? ToObject(this value). + const o: JSReceiver = ToObject_Inline(context, receiver); - // 2. Let len be ? ToLength(? Get(O, "length")). - const len: Number = GetLengthProperty(o); + // 2. Let len be ? ToLength(? Get(O, "length")). + const len: Number = GetLengthProperty(o); - // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - if (arguments.length == 0) { - goto NoCallableError; - } - const callbackfn = Cast(arguments[0]) otherwise NoCallableError; + // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. + if (arguments.length == 0) { + goto NoCallableError; + } + const callbackfn = Cast(arguments[0]) otherwise NoCallableError; - // 4. If len is 0 and initialValue is not present, throw a TypeError - // exception. (This case is handled at the end of - // ArrayReduceRightLoopContinuation). + // 4. If len is 0 and initialValue is not present, throw a TypeError + // exception. (This case is handled at the end of + // ArrayReduceRightLoopContinuation). - const initialValue: JSAny|TheHole = - arguments.length > 1 ? arguments[1] : TheHole; + const initialValue: JSAny|TheHole = + arguments.length > 1 ? arguments[1] : TheHole; - try { - return FastArrayReduceRight(o, len, callbackfn, initialValue) - otherwise Bailout; - } - label Bailout(value: Number, accumulator: JSAny|TheHole) { - return ArrayReduceRightLoopContinuation( - o, callbackfn, accumulator, o, value, len); - } - } - label NoCallableError deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); + try { + return FastArrayReduceRight(o, len, callbackfn, initialValue) + otherwise Bailout; + } label Bailout(value: Number, accumulator: JSAny|TheHole) { + return ArrayReduceRightLoopContinuation( + o, callbackfn, accumulator, o, value, len); } + } label NoCallableError deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); } } +} diff --git a/deps/v8/src/builtins/array-reduce.tq b/deps/v8/src/builtins/array-reduce.tq index 4a03776bd4e77a..8ab85a0cb61cfc 100644 --- a/deps/v8/src/builtins/array-reduce.tq +++ b/deps/v8/src/builtins/array-reduce.tq @@ -3,197 +3,194 @@ // found in the LICENSE file. namespace array { - transitioning javascript builtin - ArrayReducePreLoopEagerDeoptContinuation( - js-implicit context: NativeContext, - receiver: JSAny)(callback: JSAny, length: JSAny): JSAny { - // All continuation points in the optimized every implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - // - // Also, this great mass of casts is necessary because the signature - // of Torque javascript builtins requires JSAny type for all parameters - // other than {context}. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; - - // Simulate starting the loop at 0, but ensuring that the accumulator is - // the hole. The continuation stub will search for the initial non-hole - // element, rightly throwing an exception if not found. - return ArrayReduceLoopContinuation( - jsreceiver, callbackfn, TheHole, jsreceiver, 0, numberLength); - } +transitioning javascript builtin +ArrayReducePreLoopEagerDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, length: JSAny): JSAny { + // All continuation points in the optimized every implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + // + // Also, this great mass of casts is necessary because the signature + // of Torque javascript builtins requires JSAny type for all parameters + // other than {context}. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + // Simulate starting the loop at 0, but ensuring that the accumulator is + // the hole. The continuation stub will search for the initial non-hole + // element, rightly throwing an exception if not found. + return ArrayReduceLoopContinuation( + jsreceiver, callbackfn, TheHole, jsreceiver, 0, numberLength); +} - transitioning javascript builtin - ArrayReduceLoopEagerDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, initialK: JSAny, length: JSAny, - accumulator: JSAny): JSAny { - // All continuation points in the optimized every implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - // - // Also, this great mass of casts is necessary because the signature - // of Torque javascript builtins requires JSAny type for all parameters - // other than {context}. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const numberK = Cast(initialK) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; - - return ArrayReduceLoopContinuation( - jsreceiver, callbackfn, accumulator, jsreceiver, numberK, numberLength); - } +transitioning javascript builtin +ArrayReduceLoopEagerDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, initialK: JSAny, length: JSAny, + accumulator: JSAny): JSAny { + // All continuation points in the optimized every implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + // + // Also, this great mass of casts is necessary because the signature + // of Torque javascript builtins requires JSAny type for all parameters + // other than {context}. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const numberK = Cast(initialK) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + return ArrayReduceLoopContinuation( + jsreceiver, callbackfn, accumulator, jsreceiver, numberK, numberLength); +} - transitioning javascript builtin - ArrayReduceLoopLazyDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, initialK: JSAny, length: JSAny, result: JSAny): JSAny { - // All continuation points in the optimized every implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const numberK = Cast(initialK) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; - - // The accumulator is the result from the callback call which just occured. - const r = ArrayReduceLoopContinuation( - jsreceiver, callbackfn, result, jsreceiver, numberK, numberLength); - return r; - } +transitioning javascript builtin +ArrayReduceLoopLazyDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, initialK: JSAny, length: JSAny, result: JSAny): JSAny { + // All continuation points in the optimized every implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const numberK = Cast(initialK) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + // The accumulator is the result from the callback call which just occured. + const r = ArrayReduceLoopContinuation( + jsreceiver, callbackfn, result, jsreceiver, numberK, numberLength); + return r; +} - transitioning builtin ArrayReduceLoopContinuation(implicit context: Context)( - _receiver: JSReceiver, callbackfn: Callable, - initialAccumulator: JSAny|TheHole, o: JSReceiver, initialK: Number, - length: Number): JSAny { - let accumulator = initialAccumulator; - - // 8b and 9. Repeat, while k < len - for (let k: Number = initialK; k < length; k++) { - // 8b i and 9a. Let Pk be ! ToString(k). - // k is guaranteed to be a positive integer, hence ToString is - // side-effect free and HasProperty/GetProperty do the conversion inline. - - // 8b ii and 9b. Set kPresent to ? HasProperty(O, Pk). - const present: Boolean = HasProperty_Inline(o, k); - - // 6c. If kPresent is true, then - if (present == True) { - // 6c. i. Let kValue be ? Get(O, Pk). - const value: JSAny = GetProperty(o, k); - - typeswitch (accumulator) { - case (TheHole): { - // 8b. - accumulator = value; - } - case (accumulatorNotHole: JSAny): { - // 9c. ii. Set accumulator to ? Call(callbackfn, undefined, - // ). - accumulator = Call( - context, callbackfn, Undefined, accumulatorNotHole, value, k, - o); - } - } - } +transitioning builtin ArrayReduceLoopContinuation(implicit context: Context)( + _receiver: JSReceiver, callbackfn: Callable, + initialAccumulator: JSAny|TheHole, o: JSReceiver, initialK: Number, + length: Number): JSAny { + let accumulator = initialAccumulator; - // 8b iv and 9d. Increase k by 1. (done by the loop). - } + // 8b and 9. Repeat, while k < len + for (let k: Number = initialK; k < length; k++) { + // 8b i and 9a. Let Pk be ! ToString(k). + // k is guaranteed to be a positive integer, hence ToString is + // side-effect free and HasProperty/GetProperty do the conversion inline. - // 8c. if kPresent is false, throw a TypeError exception. - // If the accumulator is discovered with the sentinel hole value, - // this means kPresent is false. - typeswitch (accumulator) { - case (TheHole): { - ThrowTypeError( - MessageTemplate::kReduceNoInitial, 'Array.prototype.reduce'); - } - case (accumulator: JSAny): { - return accumulator; - } - } - } + // 8b ii and 9b. Set kPresent to ? HasProperty(O, Pk). + const present: Boolean = HasProperty_Inline(o, k); + + // 6c. If kPresent is true, then + if (present == True) { + // 6c. i. Let kValue be ? Get(O, Pk). + const value: JSAny = GetProperty(o, k); - transitioning macro FastArrayReduce(implicit context: Context)( - o: JSReceiver, len: Number, callbackfn: Callable, - initialAccumulator: JSAny|TheHole): JSAny - labels Bailout(Number, JSAny|TheHole) { - const k = 0; - let accumulator = initialAccumulator; - Cast(len) otherwise goto Bailout(k, accumulator); - const fastO = - Cast(o) otherwise goto Bailout(k, accumulator); - let fastOW = NewFastJSArrayForReadWitness(fastO); - - // Build a fast loop over the array. - for (let k: Smi = 0; k < len; k++) { - fastOW.Recheck() otherwise goto Bailout(k, accumulator); - - // Ensure that we haven't walked beyond a possibly updated length. - if (k >= fastOW.Get().length) goto Bailout(k, accumulator); - - const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue; typeswitch (accumulator) { case (TheHole): { + // 8b. accumulator = value; } case (accumulatorNotHole: JSAny): { + // 9c. ii. Set accumulator to ? Call(callbackfn, undefined, + // ). accumulator = Call( - context, callbackfn, Undefined, accumulatorNotHole, value, k, - fastOW.Get()); + context, callbackfn, Undefined, accumulatorNotHole, value, k, o); } } } + + // 8b iv and 9d. Increase k by 1. (done by the loop). + } + + // 8c. if kPresent is false, throw a TypeError exception. + // If the accumulator is discovered with the sentinel hole value, + // this means kPresent is false. + typeswitch (accumulator) { + case (TheHole): { + ThrowTypeError( + MessageTemplate::kReduceNoInitial, 'Array.prototype.reduce'); + } + case (accumulator: JSAny): { + return accumulator; + } + } +} + +transitioning macro FastArrayReduce(implicit context: Context)( + o: JSReceiver, len: Number, callbackfn: Callable, + initialAccumulator: JSAny|TheHole): JSAny + labels Bailout(Number, JSAny | TheHole) { + const k = 0; + let accumulator = initialAccumulator; + Cast(len) otherwise goto Bailout(k, accumulator); + const fastO = + Cast(o) otherwise goto Bailout(k, accumulator); + let fastOW = NewFastJSArrayForReadWitness(fastO); + + // Build a fast loop over the array. + for (let k: Smi = 0; k < len; k++) { + fastOW.Recheck() otherwise goto Bailout(k, accumulator); + + // Ensure that we haven't walked beyond a possibly updated length. + if (k >= fastOW.Get().length) goto Bailout(k, accumulator); + + const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue; typeswitch (accumulator) { case (TheHole): { - ThrowTypeError( - MessageTemplate::kReduceNoInitial, 'Array.prototype.reduce'); + accumulator = value; } - case (accumulator: JSAny): { - return accumulator; + case (accumulatorNotHole: JSAny): { + accumulator = Call( + context, callbackfn, Undefined, accumulatorNotHole, value, k, + fastOW.Get()); } } } + typeswitch (accumulator) { + case (TheHole): { + ThrowTypeError( + MessageTemplate::kReduceNoInitial, 'Array.prototype.reduce'); + } + case (accumulator: JSAny): { + return accumulator; + } + } +} - // https://tc39.github.io/ecma262/#sec-array.prototype.reduce - transitioning javascript builtin - ArrayReduce(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): JSAny { - try { - RequireObjectCoercible(receiver, 'Array.prototype.reduce'); +// https://tc39.github.io/ecma262/#sec-array.prototype.reduce +transitioning javascript builtin +ArrayReduce( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + try { + RequireObjectCoercible(receiver, 'Array.prototype.reduce'); - // 1. Let O be ? ToObject(this value). - const o: JSReceiver = ToObject_Inline(context, receiver); + // 1. Let O be ? ToObject(this value). + const o: JSReceiver = ToObject_Inline(context, receiver); - // 2. Let len be ? ToLength(? Get(O, "length")). - const len: Number = GetLengthProperty(o); + // 2. Let len be ? ToLength(? Get(O, "length")). + const len: Number = GetLengthProperty(o); - // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - if (arguments.length == 0) { - goto NoCallableError; - } - const callbackfn = Cast(arguments[0]) otherwise NoCallableError; + // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. + if (arguments.length == 0) { + goto NoCallableError; + } + const callbackfn = Cast(arguments[0]) otherwise NoCallableError; - // 4. If len is 0 and initialValue is not present, throw a TypeError - // exception. (This case is handled at the end of - // ArrayReduceLoopContinuation). + // 4. If len is 0 and initialValue is not present, throw a TypeError + // exception. (This case is handled at the end of + // ArrayReduceLoopContinuation). - const initialValue: JSAny|TheHole = - arguments.length > 1 ? arguments[1] : TheHole; + const initialValue: JSAny|TheHole = + arguments.length > 1 ? arguments[1] : TheHole; - try { - return FastArrayReduce(o, len, callbackfn, initialValue) - otherwise Bailout; - } - label Bailout(value: Number, accumulator: JSAny|TheHole) { - return ArrayReduceLoopContinuation( - o, callbackfn, accumulator, o, value, len); - } - } - label NoCallableError deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); + try { + return FastArrayReduce(o, len, callbackfn, initialValue) + otherwise Bailout; + } label Bailout(value: Number, accumulator: JSAny|TheHole) { + return ArrayReduceLoopContinuation( + o, callbackfn, accumulator, o, value, len); } + } label NoCallableError deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); } } +} diff --git a/deps/v8/src/builtins/array-reverse.tq b/deps/v8/src/builtins/array-reverse.tq index 8c7c61f2ee94f9..11c325140ee442 100644 --- a/deps/v8/src/builtins/array-reverse.tq +++ b/deps/v8/src/builtins/array-reverse.tq @@ -3,175 +3,170 @@ // found in the LICENSE file. namespace array { - macro LoadElement( - elements: FixedArrayBase, index: Smi): T; +macro LoadElement( + elements: FixedArrayBase, index: Smi): T; - LoadElement(implicit context: Context)( - elements: FixedArrayBase, index: Smi): Smi { - const elements: FixedArray = UnsafeCast(elements); - return UnsafeCast(elements.objects[index]); - } +LoadElement(implicit context: Context)( + elements: FixedArrayBase, index: Smi): Smi { + const elements: FixedArray = UnsafeCast(elements); + return UnsafeCast(elements.objects[index]); +} - LoadElement( - implicit context: Context)(elements: FixedArrayBase, index: Smi): JSAny { - const elements: FixedArray = UnsafeCast(elements); - return UnsafeCast(elements.objects[index]); - } +LoadElement(implicit context: Context)( + elements: FixedArrayBase, index: Smi): JSAny { + const elements: FixedArray = UnsafeCast(elements); + return UnsafeCast(elements.objects[index]); +} - LoadElement( - implicit context: Context)(elements: FixedArrayBase, index: Smi): - float64 { - const elements: FixedDoubleArray = UnsafeCast(elements); - // This macro is only used for PACKED_DOUBLE, loading the hole should - // be impossible. - return elements.floats[index].Value() otherwise unreachable; - } +LoadElement( + implicit context: Context)(elements: FixedArrayBase, index: Smi): float64 { + const elements: FixedDoubleArray = UnsafeCast(elements); + // This macro is only used for PACKED_DOUBLE, loading the hole should + // be impossible. + return elements.floats[index].Value() otherwise unreachable; +} - macro StoreElement( - implicit context: - Context)(elements: FixedArrayBase, index: Smi, value: T); +macro StoreElement( + implicit context: Context)(elements: FixedArrayBase, index: Smi, value: T); - StoreElement(implicit context: Context)( - elements: FixedArrayBase, index: Smi, value: Smi) { - const elems: FixedArray = UnsafeCast(elements); - StoreFixedArrayElement(elems, index, value, SKIP_WRITE_BARRIER); - } +StoreElement(implicit context: Context)( + elements: FixedArrayBase, index: Smi, value: Smi) { + const elems: FixedArray = UnsafeCast(elements); + StoreFixedArrayElement(elems, index, value, SKIP_WRITE_BARRIER); +} - StoreElement( - implicit context: - Context)(elements: FixedArrayBase, index: Smi, value: JSAny) { - const elements: FixedArray = UnsafeCast(elements); - elements.objects[index] = value; - } +StoreElement(implicit context: Context)( + elements: FixedArrayBase, index: Smi, value: JSAny) { + const elements: FixedArray = UnsafeCast(elements); + elements.objects[index] = value; +} + +StoreElement( + implicit context: Context)( + elements: FixedArrayBase, index: Smi, value: float64) { + const elems: FixedDoubleArray = UnsafeCast(elements); + StoreFixedDoubleArrayElementSmi(elems, index, value); +} - StoreElement( - implicit context: - Context)(elements: FixedArrayBase, index: Smi, value: float64) { - const elems: FixedDoubleArray = UnsafeCast(elements); - StoreFixedDoubleArrayElementSmi(elems, index, value); +// Fast-path for all PACKED_* elements kinds. These do not need to check +// whether a property is present, so we can simply swap them using fast +// FixedArray loads/stores. +macro FastPackedArrayReverse( + implicit context: Context)(elements: FixedArrayBase, length: Smi) { + let lower: Smi = 0; + let upper: Smi = length - 1; + + while (lower < upper) { + const lowerValue: T = LoadElement(elements, lower); + const upperValue: T = LoadElement(elements, upper); + StoreElement(elements, lower, upperValue); + StoreElement(elements, upper, lowerValue); + ++lower; + --upper; } +} - // Fast-path for all PACKED_* elements kinds. These do not need to check - // whether a property is present, so we can simply swap them using fast - // FixedArray loads/stores. - macro FastPackedArrayReverse( - implicit context: Context)(elements: FixedArrayBase, length: Smi) { - let lower: Smi = 0; - let upper: Smi = length - 1; - - while (lower < upper) { - const lowerValue: T = LoadElement(elements, lower); - const upperValue: T = LoadElement(elements, upper); - StoreElement(elements, lower, upperValue); - StoreElement(elements, upper, lowerValue); - ++lower; - --upper; +transitioning macro GenericArrayReverse( + context: Context, receiver: JSAny): JSAny { + // 1. Let O be ? ToObject(this value). + const object: JSReceiver = ToObject_Inline(context, receiver); + + // 2. Let len be ? ToLength(? Get(O, "length")). + const length: Number = GetLengthProperty(object); + + // 3. Let middle be floor(len / 2). + // 4. Let lower be 0. + // 5. Repeat, while lower != middle. + // a. Let upper be len - lower - 1. + + // Instead of calculating the middle value, we simply initialize upper + // with len - 1 and decrement it after each iteration. + let lower: Number = 0; + let upper: Number = length - 1; + + while (lower < upper) { + let lowerValue: JSAny = Undefined; + let upperValue: JSAny = Undefined; + + // b. Let upperP be ! ToString(upper). + // c. Let lowerP be ! ToString(lower). + // d. Let lowerExists be ? HasProperty(O, lowerP). + const lowerExists: Boolean = HasProperty(object, lower); + + // e. If lowerExists is true, then. + if (lowerExists == True) { + // i. Let lowerValue be ? Get(O, lowerP). + lowerValue = GetProperty(object, lower); } - } - transitioning macro GenericArrayReverse(context: Context, receiver: JSAny): - JSAny { - // 1. Let O be ? ToObject(this value). - const object: JSReceiver = ToObject_Inline(context, receiver); - - // 2. Let len be ? ToLength(? Get(O, "length")). - const length: Number = GetLengthProperty(object); - - // 3. Let middle be floor(len / 2). - // 4. Let lower be 0. - // 5. Repeat, while lower != middle. - // a. Let upper be len - lower - 1. - - // Instead of calculating the middle value, we simply initialize upper - // with len - 1 and decrement it after each iteration. - let lower: Number = 0; - let upper: Number = length - 1; - - while (lower < upper) { - let lowerValue: JSAny = Undefined; - let upperValue: JSAny = Undefined; - - // b. Let upperP be ! ToString(upper). - // c. Let lowerP be ! ToString(lower). - // d. Let lowerExists be ? HasProperty(O, lowerP). - const lowerExists: Boolean = HasProperty(object, lower); - - // e. If lowerExists is true, then. - if (lowerExists == True) { - // i. Let lowerValue be ? Get(O, lowerP). - lowerValue = GetProperty(object, lower); - } - - // f. Let upperExists be ? HasProperty(O, upperP). - const upperExists: Boolean = HasProperty(object, upper); - - // g. If upperExists is true, then. - if (upperExists == True) { - // i. Let upperValue be ? Get(O, upperP). - upperValue = GetProperty(object, upper); - } - - // h. If lowerExists is true and upperExists is true, then - if (lowerExists == True && upperExists == True) { - // i. Perform ? Set(O, lowerP, upperValue, true). - SetProperty(object, lower, upperValue); - - // ii. Perform ? Set(O, upperP, lowerValue, true). - SetProperty(object, upper, lowerValue); - } else if (lowerExists == False && upperExists == True) { - // i. Perform ? Set(O, lowerP, upperValue, true). - SetProperty(object, lower, upperValue); - - // ii. Perform ? DeletePropertyOrThrow(O, upperP). - DeleteProperty(object, upper, LanguageMode::kStrict); - } else if (lowerExists == True && upperExists == False) { - // i. Perform ? DeletePropertyOrThrow(O, lowerP). - DeleteProperty(object, lower, LanguageMode::kStrict); - - // ii. Perform ? Set(O, upperP, lowerValue, true). - SetProperty(object, upper, lowerValue); - } - - // l. Increase lower by 1. - ++lower; - --upper; + // f. Let upperExists be ? HasProperty(O, upperP). + const upperExists: Boolean = HasProperty(object, upper); + + // g. If upperExists is true, then. + if (upperExists == True) { + // i. Let upperValue be ? Get(O, upperP). + upperValue = GetProperty(object, upper); } - // 6. Return O. - return object; + // h. If lowerExists is true and upperExists is true, then + if (lowerExists == True && upperExists == True) { + // i. Perform ? Set(O, lowerP, upperValue, true). + SetProperty(object, lower, upperValue); + + // ii. Perform ? Set(O, upperP, lowerValue, true). + SetProperty(object, upper, lowerValue); + } else if (lowerExists == False && upperExists == True) { + // i. Perform ? Set(O, lowerP, upperValue, true). + SetProperty(object, lower, upperValue); + + // ii. Perform ? DeletePropertyOrThrow(O, upperP). + DeleteProperty(object, upper, LanguageMode::kStrict); + } else if (lowerExists == True && upperExists == False) { + // i. Perform ? DeletePropertyOrThrow(O, lowerP). + DeleteProperty(object, lower, LanguageMode::kStrict); + + // ii. Perform ? Set(O, upperP, lowerValue, true). + SetProperty(object, upper, lowerValue); + } + + // l. Increase lower by 1. + ++lower; + --upper; } - macro TryFastPackedArrayReverse(implicit context: Context)(receiver: JSAny) - labels Slow { - const array: FastJSArray = Cast(receiver) otherwise Slow; - - const kind: ElementsKind = array.map.elements_kind; - if (kind == ElementsKind::PACKED_SMI_ELEMENTS) { - array::EnsureWriteableFastElements(array); - FastPackedArrayReverse( - array.elements, array.length); - } else if (kind == ElementsKind::PACKED_ELEMENTS) { - array::EnsureWriteableFastElements(array); - FastPackedArrayReverse( - array.elements, array.length); - } else if (kind == ElementsKind::PACKED_DOUBLE_ELEMENTS) { - FastPackedArrayReverse( - array.elements, array.length); - } else { - goto Slow; - } + // 6. Return O. + return object; +} + +macro TryFastPackedArrayReverse(implicit context: Context)(receiver: JSAny) + labels Slow { + const array: FastJSArray = Cast(receiver) otherwise Slow; + + const kind: ElementsKind = array.map.elements_kind; + if (kind == ElementsKind::PACKED_SMI_ELEMENTS) { + array::EnsureWriteableFastElements(array); + FastPackedArrayReverse( + array.elements, array.length); + } else if (kind == ElementsKind::PACKED_ELEMENTS) { + array::EnsureWriteableFastElements(array); + FastPackedArrayReverse( + array.elements, array.length); + } else if (kind == ElementsKind::PACKED_DOUBLE_ELEMENTS) { + FastPackedArrayReverse( + array.elements, array.length); + } else { + goto Slow; } +} - // https://tc39.github.io/ecma262/#sec-array.prototype.reverse - transitioning javascript builtin ArrayPrototypeReverse( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - try { - TryFastPackedArrayReverse(receiver) otherwise Baseline; - return receiver; - } - label Baseline { - return GenericArrayReverse(context, receiver); - } +// https://tc39.github.io/ecma262/#sec-array.prototype.reverse +transitioning javascript builtin ArrayPrototypeReverse( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + try { + TryFastPackedArrayReverse(receiver) otherwise Baseline; + return receiver; + } label Baseline { + return GenericArrayReverse(context, receiver); } } +} diff --git a/deps/v8/src/builtins/array-shift.tq b/deps/v8/src/builtins/array-shift.tq index d32d6be32e3c72..ed1087a85afdf8 100644 --- a/deps/v8/src/builtins/array-shift.tq +++ b/deps/v8/src/builtins/array-shift.tq @@ -3,110 +3,107 @@ // found in the LICENSE file. namespace array { - extern builtin ArrayShift(Context, JSFunction, JSAny, int32): JSAny; +extern builtin ArrayShift(Context, JSFunction, JSAny, int32): JSAny; - macro TryFastArrayShift(implicit context: Context)(receiver: JSAny): JSAny - labels Slow, Runtime { - const array: FastJSArray = Cast(receiver) otherwise Slow; - let witness = NewFastJSArrayWitness(array); +macro TryFastArrayShift(implicit context: Context)(receiver: JSAny): JSAny + labels Slow, Runtime { + const array: FastJSArray = Cast(receiver) otherwise Slow; + let witness = NewFastJSArrayWitness(array); - witness.EnsureArrayPushable() otherwise Slow; + witness.EnsureArrayPushable() otherwise Slow; - if (array.length == 0) { - return Undefined; - } + if (array.length == 0) { + return Undefined; + } - const newLength = array.length - 1; + const newLength = array.length - 1; - // Check that we're not supposed to right-trim the backing store, as - // implemented in elements.cc:ElementsAccessorBase::SetLengthImpl. - if ((newLength + newLength + kMinAddedElementsCapacity) < - array.elements.length) { - goto Runtime; - } + // Check that we're not supposed to right-trim the backing store, as + // implemented in elements.cc:ElementsAccessorBase::SetLengthImpl. + if ((newLength + newLength + kMinAddedElementsCapacity) < + array.elements.length) { + goto Runtime; + } - // Check that we're not supposed to left-trim the backing store, as - // implemented in elements.cc:FastElementsAccessor::MoveElements. - if (newLength > kMaxCopyElements) goto Runtime; + // Check that we're not supposed to left-trim the backing store, as + // implemented in elements.cc:FastElementsAccessor::MoveElements. + if (newLength > kMaxCopyElements) goto Runtime; - const result = witness.LoadElementOrUndefined(0); - witness.ChangeLength(newLength); - witness.MoveElements(0, 1, Convert(newLength)); - witness.StoreHole(newLength); - return result; - } + const result = witness.LoadElementOrUndefined(0); + witness.ChangeLength(newLength); + witness.MoveElements(0, 1, Convert(newLength)); + witness.StoreHole(newLength); + return result; +} - transitioning macro GenericArrayShift(implicit context: - Context)(receiver: JSAny): JSAny { - // 1. Let O be ? ToObject(this value). - const object: JSReceiver = ToObject_Inline(context, receiver); +transitioning macro GenericArrayShift(implicit context: Context)( + receiver: JSAny): JSAny { + // 1. Let O be ? ToObject(this value). + const object: JSReceiver = ToObject_Inline(context, receiver); - // 2. Let len be ? ToLength(? Get(O, "length")). - const length: Number = GetLengthProperty(object); + // 2. Let len be ? ToLength(? Get(O, "length")). + const length: Number = GetLengthProperty(object); - // 3. If len is zero, then - if (length == 0) { - // a. Perform ? Set(O, "length", 0, true). - SetProperty(object, kLengthString, Convert(0)); - // b. Return undefined. - return Undefined; - } + // 3. If len is zero, then + if (length == 0) { + // a. Perform ? Set(O, "length", 0, true). + SetProperty(object, kLengthString, Convert(0)); + // b. Return undefined. + return Undefined; + } - // 4. Let first be ? Get(O, "0"). - const first = GetProperty(object, Convert(0)); - // 5. Let k be 1. - let k: Number = 1; - // 6. Repeat, while k < len - while (k < length) { - // a. Let from be ! ToString(k). - const from: Number = k; - - // b. Let to be ! ToString(k - 1). - const to: Number = k - 1; - - // c. Let fromPresent be ? HasProperty(O, from). - const fromPresent: Boolean = HasProperty(object, from); - - // d. If fromPresent is true, then - if (fromPresent == True) { - // i. Let fromVal be ? Get(O, from). - const fromValue: JSAny = GetProperty(object, from); - - // ii. Perform ? Set(O, to, fromValue, true). - SetProperty(object, to, fromValue); - } else { - // i. Perform ? DeletePropertyOrThrow(O, to). - DeleteProperty(object, to, LanguageMode::kStrict); - } - - // f. Increase k by 1. - k++; + // 4. Let first be ? Get(O, "0"). + const first = GetProperty(object, Convert(0)); + // 5. Let k be 1. + let k: Number = 1; + // 6. Repeat, while k < len + while (k < length) { + // a. Let from be ! ToString(k). + const from: Number = k; + + // b. Let to be ! ToString(k - 1). + const to: Number = k - 1; + + // c. Let fromPresent be ? HasProperty(O, from). + const fromPresent: Boolean = HasProperty(object, from); + + // d. If fromPresent is true, then + if (fromPresent == True) { + // i. Let fromVal be ? Get(O, from). + const fromValue: JSAny = GetProperty(object, from); + + // ii. Perform ? Set(O, to, fromValue, true). + SetProperty(object, to, fromValue); + } else { + // i. Perform ? DeletePropertyOrThrow(O, to). + DeleteProperty(object, to, LanguageMode::kStrict); } - // 7. Perform ? DeletePropertyOrThrow(O, ! ToString(len - 1)). - DeleteProperty(object, length - 1, LanguageMode::kStrict); + // f. Increase k by 1. + k++; + } - // 8. Perform ? Set(O, "length", len - 1, true). - SetProperty(object, kLengthString, length - 1); + // 7. Perform ? DeletePropertyOrThrow(O, ! ToString(len - 1)). + DeleteProperty(object, length - 1, LanguageMode::kStrict); - // 9. Return first. - return first; - } + // 8. Perform ? Set(O, "length", len - 1, true). + SetProperty(object, kLengthString, length - 1); - // https://tc39.github.io/ecma262/#sec-array.prototype.shift - transitioning javascript builtin ArrayPrototypeShift( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - try { - return TryFastArrayShift(receiver) otherwise Slow, Runtime; - } - label Slow { - return GenericArrayShift(receiver); - } - label Runtime { - tail ArrayShift( - context, LoadTargetFromFrame(), Undefined, - Convert(arguments.length)); - } + // 9. Return first. + return first; +} + +// https://tc39.github.io/ecma262/#sec-array.prototype.shift +transitioning javascript builtin ArrayPrototypeShift( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + try { + return TryFastArrayShift(receiver) otherwise Slow, Runtime; + } label Slow { + return GenericArrayShift(receiver); + } label Runtime { + tail ArrayShift( + context, LoadTargetFromFrame(), Undefined, + Convert(arguments.length)); } } +} diff --git a/deps/v8/src/builtins/array-slice.tq b/deps/v8/src/builtins/array-slice.tq index b11b07e48be65d..147dae6f72f46b 100644 --- a/deps/v8/src/builtins/array-slice.tq +++ b/deps/v8/src/builtins/array-slice.tq @@ -3,225 +3,222 @@ // found in the LICENSE file. namespace array { - macro HandleSimpleArgumentsSlice( - context: NativeContext, args: JSArgumentsObjectWithLength, start: Smi, - count: Smi): JSArray - labels Bailout { - // If the resulting array doesn't fit in new space, use the slow path. - if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout; - - const end: Smi = start + count; - const sourceElements: FixedArray = - Cast(args.elements) otherwise Bailout; - if (SmiAbove(end, sourceElements.length)) goto Bailout; - - const arrayMap: Map = - LoadJSArrayElementsMap(ElementsKind::HOLEY_ELEMENTS, context); - const result: JSArray = - AllocateJSArray(ElementsKind::HOLEY_ELEMENTS, arrayMap, count, count); - const newElements: FixedArray = - Cast(result.elements) otherwise Bailout; - CopyElements( - ElementsKind::PACKED_ELEMENTS, newElements, 0, sourceElements, - Convert(start), Convert(count)); - return result; - } - - macro HandleFastAliasedSloppyArgumentsSlice( - context: NativeContext, args: JSArgumentsObjectWithLength, start: Smi, - count: Smi): JSArray - labels Bailout { - // If the resulting array doesn't fit in new space, use the slow path. - if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout; - - const sloppyElements: SloppyArgumentsElements = - Cast(args.elements) otherwise Bailout; - const sloppyElementsLength: Smi = sloppyElements.length; - const parameterMapLength: Smi = - sloppyElementsLength - kSloppyArgumentsParameterMapStart; - - // Check to make sure that the extraction will not access outside the - // defined arguments - const end: Smi = start + count; - const unmappedElements: FixedArray = - Cast(sloppyElements.objects[kSloppyArgumentsArgumentsIndex]) - otherwise Bailout; - const unmappedElementsLength: Smi = unmappedElements.length; - if (SmiAbove(end, unmappedElementsLength)) goto Bailout; - - const argumentsContext: Context = UnsafeCast( - sloppyElements.objects[kSloppyArgumentsContextIndex]); - - const arrayMap: Map = - LoadJSArrayElementsMap(ElementsKind::HOLEY_ELEMENTS, context); - const result: JSArray = - AllocateJSArray(ElementsKind::HOLEY_ELEMENTS, arrayMap, count, count); - - let indexOut: Smi = 0; - const resultElements: FixedArray = UnsafeCast(result.elements); - const to: Smi = SmiMin(parameterMapLength, end); - - // Fill in the part of the result that map to context-mapped parameters. - for (let current: Smi = start; current < to; ++current) { - const e: Object = - sloppyElements.objects[current + kSloppyArgumentsParameterMapStart]; - const newElement = UnsafeCast<(JSAny | TheHole)>( - e != TheHole ? argumentsContext[UnsafeCast(e)] : - unmappedElements.objects[current]); - // It is safe to skip the write barrier here because resultElements was - // allocated together with result in a folded allocation. - // TODO(tebbi): The verification of this fails at the moment due to - // missing load elimination. - StoreFixedArrayElement( - resultElements, indexOut++, newElement, UNSAFE_SKIP_WRITE_BARRIER); - } +macro HandleSimpleArgumentsSlice( + context: NativeContext, args: JSArgumentsObjectWithLength, start: Smi, + count: Smi): JSArray + labels Bailout { + // If the resulting array doesn't fit in new space, use the slow path. + if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout; + + const end: Smi = start + count; + const sourceElements: FixedArray = + Cast(args.elements) otherwise Bailout; + if (SmiAbove(end, sourceElements.length)) goto Bailout; + + const arrayMap: Map = + LoadJSArrayElementsMap(ElementsKind::HOLEY_ELEMENTS, context); + const result: JSArray = + AllocateJSArray(ElementsKind::HOLEY_ELEMENTS, arrayMap, count, count); + const newElements: FixedArray = + Cast(result.elements) otherwise Bailout; + CopyElements( + ElementsKind::PACKED_ELEMENTS, newElements, 0, sourceElements, + Convert(start), Convert(count)); + return result; +} - // Fill in the rest of the result that contains the unmapped parameters - // above the formal parameters. - const unmappedFrom: Smi = SmiMin(SmiMax(parameterMapLength, start), end); - const restCount: Smi = end - unmappedFrom; - CopyElements( - ElementsKind::PACKED_ELEMENTS, resultElements, - Convert(indexOut), unmappedElements, - Convert(unmappedFrom), Convert(restCount)); - return result; +macro HandleFastAliasedSloppyArgumentsSlice( + context: NativeContext, args: JSArgumentsObjectWithLength, start: Smi, + count: Smi): JSArray + labels Bailout { + // If the resulting array doesn't fit in new space, use the slow path. + if (count >= kMaxNewSpaceFixedArrayElements) goto Bailout; + + const sloppyElements: SloppyArgumentsElements = + Cast(args.elements) otherwise Bailout; + const sloppyElementsLength: Smi = sloppyElements.length; + const parameterMapLength: Smi = + sloppyElementsLength - kSloppyArgumentsParameterMapStart; + + // Check to make sure that the extraction will not access outside the + // defined arguments + const end: Smi = start + count; + const unmappedElements: FixedArray = + Cast(sloppyElements.objects[kSloppyArgumentsArgumentsIndex]) + otherwise Bailout; + const unmappedElementsLength: Smi = unmappedElements.length; + if (SmiAbove(end, unmappedElementsLength)) goto Bailout; + + const argumentsContext: Context = + UnsafeCast(sloppyElements.objects[kSloppyArgumentsContextIndex]); + + const arrayMap: Map = + LoadJSArrayElementsMap(ElementsKind::HOLEY_ELEMENTS, context); + const result: JSArray = + AllocateJSArray(ElementsKind::HOLEY_ELEMENTS, arrayMap, count, count); + + let indexOut: Smi = 0; + const resultElements: FixedArray = UnsafeCast(result.elements); + const to: Smi = SmiMin(parameterMapLength, end); + + // Fill in the part of the result that map to context-mapped parameters. + for (let current: Smi = start; current < to; ++current) { + const e: Object = + sloppyElements.objects[current + kSloppyArgumentsParameterMapStart]; + const newElement = UnsafeCast<(JSAny | TheHole)>( + e != TheHole ? argumentsContext[UnsafeCast(e)] : + unmappedElements.objects[current]); + // It is safe to skip the write barrier here because resultElements was + // allocated together with result in a folded allocation. + // TODO(tebbi): The verification of this fails at the moment due to + // missing load elimination. + StoreFixedArrayElement( + resultElements, indexOut++, newElement, UNSAFE_SKIP_WRITE_BARRIER); } - macro HandleFastSlice( - context: NativeContext, o: JSAny, startNumber: Number, - countNumber: Number): JSArray - labels Bailout { - const start: Smi = Cast(startNumber) otherwise Bailout; - const count: Smi = Cast(countNumber) otherwise Bailout; - assert(start >= 0); - - try { - typeswitch (o) { - case (a: FastJSArrayForCopy): { - // It's possible to modify the array length from a valueOf - // callback between the original array length read and this - // point. That can change the length of the array backing store, - // in the worst case, making it smaller than the region that needs - // to be copied out. Therefore, re-check the length before calling - // the appropriate fast path. See regress-785804.js - if (SmiAbove(start + count, a.length)) goto Bailout; - return ExtractFastJSArray(context, a, start, count); - } - case (a: JSStrictArgumentsObject): { + // Fill in the rest of the result that contains the unmapped parameters + // above the formal parameters. + const unmappedFrom: Smi = SmiMin(SmiMax(parameterMapLength, start), end); + const restCount: Smi = end - unmappedFrom; + CopyElements( + ElementsKind::PACKED_ELEMENTS, resultElements, Convert(indexOut), + unmappedElements, Convert(unmappedFrom), + Convert(restCount)); + return result; +} + +macro HandleFastSlice( + context: NativeContext, o: JSAny, startNumber: Number, + countNumber: Number): JSArray + labels Bailout { + const start: Smi = Cast(startNumber) otherwise Bailout; + const count: Smi = Cast(countNumber) otherwise Bailout; + assert(start >= 0); + + try { + typeswitch (o) { + case (a: FastJSArrayForCopy): { + // It's possible to modify the array length from a valueOf + // callback between the original array length read and this + // point. That can change the length of the array backing store, + // in the worst case, making it smaller than the region that needs + // to be copied out. Therefore, re-check the length before calling + // the appropriate fast path. See regress-785804.js + if (SmiAbove(start + count, a.length)) goto Bailout; + return ExtractFastJSArray(context, a, start, count); + } + case (a: JSStrictArgumentsObject): { + goto HandleSimpleArgumentsSlice(a); + } + case (a: JSSloppyArgumentsObject): { + const map: Map = a.map; + if (IsFastAliasedArgumentsMap(map)) { + return HandleFastAliasedSloppyArgumentsSlice(context, a, start, count) + otherwise Bailout; + } else if (IsSloppyArgumentsMap(map)) { goto HandleSimpleArgumentsSlice(a); } - case (a: JSSloppyArgumentsObject): { - const map: Map = a.map; - if (IsFastAliasedArgumentsMap(map)) { - return HandleFastAliasedSloppyArgumentsSlice( - context, a, start, count) - otherwise Bailout; - } else if (IsSloppyArgumentsMap(map)) { - goto HandleSimpleArgumentsSlice(a); - } - goto Bailout; - } - case (JSAny): { - goto Bailout; - } + goto Bailout; + } + case (JSAny): { + goto Bailout; } } - label HandleSimpleArgumentsSlice(a: JSArgumentsObjectWithLength) { - return HandleSimpleArgumentsSlice(context, a, start, count) - otherwise Bailout; - } + } label HandleSimpleArgumentsSlice(a: JSArgumentsObjectWithLength) { + return HandleSimpleArgumentsSlice(context, a, start, count) + otherwise Bailout; } +} - // https://tc39.github.io/ecma262/#sec-array.prototype.slice - transitioning javascript builtin - ArrayPrototypeSlice(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): JSAny { - // Handle array cloning case if the receiver is a fast array. - if (arguments.length == 0) { - typeswitch (receiver) { - case (a: FastJSArrayForCopy): { - return CloneFastJSArray(context, a); - } - case (JSAny): { - } +// https://tc39.github.io/ecma262/#sec-array.prototype.slice +transitioning javascript builtin +ArrayPrototypeSlice( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + // Handle array cloning case if the receiver is a fast array. + if (arguments.length == 0) { + typeswitch (receiver) { + case (a: FastJSArrayForCopy): { + return CloneFastJSArray(context, a); + } + case (JSAny): { } } + } - // 1. Let O be ? ToObject(this value). - const o: JSReceiver = ToObject_Inline(context, receiver); - - // 2. Let len be ? ToLength(? Get(O, "length")). - const len: Number = GetLengthProperty(o); - - // 3. Let relativeStart be ? ToInteger(start). - const start: JSAny = arguments[0]; - const relativeStart: Number = ToInteger_Inline(start); + // 1. Let O be ? ToObject(this value). + const o: JSReceiver = ToObject_Inline(context, receiver); - // 4. If relativeStart < 0, let k be max((len + relativeStart), 0); - // else let k be min(relativeStart, len). - let k: Number = relativeStart < 0 ? Max((len + relativeStart), 0) : - Min(relativeStart, len); + // 2. Let len be ? ToLength(? Get(O, "length")). + const len: Number = GetLengthProperty(o); - // 5. If end is undefined, let relativeEnd be len; - // else let relativeEnd be ? ToInteger(end). - const end: JSAny = arguments[1]; - const relativeEnd: Number = end == Undefined ? len : ToInteger_Inline(end); + // 3. Let relativeStart be ? ToInteger(start). + const start: JSAny = arguments[0]; + const relativeStart: Number = ToInteger_Inline(start); - // 6. If relativeEnd < 0, let final be max((len + relativeEnd), 0); - // else let final be min(relativeEnd, len). - const final: Number = - relativeEnd < 0 ? Max((len + relativeEnd), 0) : Min(relativeEnd, len); + // 4. If relativeStart < 0, let k be max((len + relativeStart), 0); + // else let k be min(relativeStart, len). + let k: Number = relativeStart < 0 ? Max((len + relativeStart), 0) : + Min(relativeStart, len); - // 7. Let count be max(final - k, 0). - const count: Number = Max(final - k, 0); + // 5. If end is undefined, let relativeEnd be len; + // else let relativeEnd be ? ToInteger(end). + const end: JSAny = arguments[1]; + const relativeEnd: Number = end == Undefined ? len : ToInteger_Inline(end); - assert(0 <= k); - assert(k <= len); - assert(0 <= final); - assert(final <= len); - assert(0 <= count); - assert(count <= len); + // 6. If relativeEnd < 0, let final be max((len + relativeEnd), 0); + // else let final be min(relativeEnd, len). + const final: Number = + relativeEnd < 0 ? Max((len + relativeEnd), 0) : Min(relativeEnd, len); - try { - return HandleFastSlice(context, o, k, count) - otherwise Slow; - } - label Slow {} + // 7. Let count be max(final - k, 0). + const count: Number = Max(final - k, 0); - // 8. Let A be ? ArraySpeciesCreate(O, count). - const a: JSReceiver = ArraySpeciesCreate(context, o, count); + assert(0 <= k); + assert(k <= len); + assert(0 <= final); + assert(final <= len); + assert(0 <= count); + assert(count <= len); - // 9. Let n be 0. - let n: Number = 0; + try { + return HandleFastSlice(context, o, k, count) + otherwise Slow; + } label Slow {} - // 10. Repeat, while k < final - while (k < final) { - // a. Let Pk be ! ToString(k). - const pK: Number = k; + // 8. Let A be ? ArraySpeciesCreate(O, count). + const a: JSReceiver = ArraySpeciesCreate(context, o, count); - // b. Let kPresent be ? HasProperty(O, Pk). - const fromPresent: Boolean = HasProperty(o, pK); + // 9. Let n be 0. + let n: Number = 0; - // c. If kPresent is true, then - if (fromPresent == True) { - // i. Let kValue be ? Get(O, Pk). - const kValue: JSAny = GetProperty(o, pK); + // 10. Repeat, while k < final + while (k < final) { + // a. Let Pk be ! ToString(k). + const pK: Number = k; - // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(n), kValue). - FastCreateDataProperty(a, n, kValue); - } + // b. Let kPresent be ? HasProperty(O, Pk). + const fromPresent: Boolean = HasProperty(o, pK); - // d. Increase k by 1. - k++; + // c. If kPresent is true, then + if (fromPresent == True) { + // i. Let kValue be ? Get(O, Pk). + const kValue: JSAny = GetProperty(o, pK); - // e. Increase n by 1. - n++; + // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(n), kValue). + FastCreateDataProperty(a, n, kValue); } - // 11. Perform ? Set(A, "length", n, true). - SetProperty(a, kLengthString, n); + // d. Increase k by 1. + k++; - // 12. Return A. - return a; + // e. Increase n by 1. + n++; } + + // 11. Perform ? Set(A, "length", n, true). + SetProperty(a, kLengthString, n); + + // 12. Return A. + return a; +} } diff --git a/deps/v8/src/builtins/array-some.tq b/deps/v8/src/builtins/array-some.tq index 59b8294f74d17b..69467bba276a12 100644 --- a/deps/v8/src/builtins/array-some.tq +++ b/deps/v8/src/builtins/array-some.tq @@ -3,145 +3,142 @@ // found in the LICENSE file. namespace array { - transitioning javascript builtin - ArraySomeLoopEagerDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny { - // All continuation points in the optimized some implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - // - // Also, this great mass of casts is necessary because the signature - // of Torque javascript builtins requires JSAny type for all parameters - // other than {context}. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - const numberK = Cast(initialK) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; - - return ArraySomeLoopContinuation( - jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK, - numberLength, Undefined); +transitioning javascript builtin +ArraySomeLoopEagerDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny): JSAny { + // All continuation points in the optimized some implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + // + // Also, this great mass of casts is necessary because the signature + // of Torque javascript builtins requires JSAny type for all parameters + // other than {context}. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + const numberK = Cast(initialK) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + return ArraySomeLoopContinuation( + jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK, + numberLength, Undefined); +} + +transitioning javascript builtin +ArraySomeLoopLazyDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny, + result: JSAny): JSAny { + // All continuation points in the optimized some implementation are + // after the ToObject(O) call that ensures we are dealing with a + // JSReceiver. + const jsreceiver = Cast(receiver) otherwise unreachable; + const callbackfn = Cast(callback) otherwise unreachable; + let numberK = Cast(initialK) otherwise unreachable; + const numberLength = Cast(length) otherwise unreachable; + + // This custom lazy deopt point is right after the callback. some() needs + // to pick up at the next step: if the result is true, then return, + // otherwise, keep going through the array starting from k + 1. + if (ToBoolean(result)) { + return True; } - transitioning javascript builtin - ArraySomeLoopLazyDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - callback: JSAny, thisArg: JSAny, initialK: JSAny, length: JSAny, - result: JSAny): JSAny { - // All continuation points in the optimized some implementation are - // after the ToObject(O) call that ensures we are dealing with a - // JSReceiver. - const jsreceiver = Cast(receiver) otherwise unreachable; - const callbackfn = Cast(callback) otherwise unreachable; - let numberK = Cast(initialK) otherwise unreachable; - const numberLength = Cast(length) otherwise unreachable; - - // This custom lazy deopt point is right after the callback. some() needs - // to pick up at the next step: if the result is true, then return, - // otherwise, keep going through the array starting from k + 1. - if (ToBoolean(result)) { - return True; - } + numberK = numberK + 1; - numberK = numberK + 1; + return ArraySomeLoopContinuation( + jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK, + numberLength, Undefined); +} - return ArraySomeLoopContinuation( - jsreceiver, callbackfn, thisArg, Undefined, jsreceiver, numberK, - numberLength, Undefined); - } +transitioning builtin ArraySomeLoopContinuation(implicit context: Context)( + _receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, _array: JSAny, + o: JSReceiver, initialK: Number, length: Number, _initialTo: JSAny): JSAny { + // 5. Let k be 0. + // 6. Repeat, while k < len + for (let k: Number = initialK; k < length; k++) { + // 6a. Let Pk be ! ToString(k). + // k is guaranteed to be a positive integer, hence ToString is + // side-effect free and HasProperty/GetProperty do the conversion inline. - transitioning builtin ArraySomeLoopContinuation(implicit context: Context)( - _receiver: JSReceiver, callbackfn: Callable, thisArg: JSAny, - _array: JSAny, o: JSReceiver, initialK: Number, length: Number, - _initialTo: JSAny): JSAny { - // 5. Let k be 0. - // 6. Repeat, while k < len - for (let k: Number = initialK; k < length; k++) { - // 6a. Let Pk be ! ToString(k). - // k is guaranteed to be a positive integer, hence ToString is - // side-effect free and HasProperty/GetProperty do the conversion inline. - - // 6b. Let kPresent be ? HasProperty(O, Pk). - const kPresent: Boolean = HasProperty_Inline(o, k); - - // 6c. If kPresent is true, then - if (kPresent == True) { - // 6c. i. Let kValue be ? Get(O, Pk). - const kValue: JSAny = GetProperty(o, k); - - // 6c. ii. Perform ? Call(callbackfn, T, ). - const result: JSAny = Call(context, callbackfn, thisArg, kValue, k, o); - - // iii. If selected is true, then... - if (ToBoolean(result)) { - return True; - } - } + // 6b. Let kPresent be ? HasProperty(O, Pk). + const kPresent: Boolean = HasProperty_Inline(o, k); - // 6d. Increase k by 1. (done by the loop). - } - return False; - } + // 6c. If kPresent is true, then + if (kPresent == True) { + // 6c. i. Let kValue be ? Get(O, Pk). + const kValue: JSAny = GetProperty(o, k); + + // 6c. ii. Perform ? Call(callbackfn, T, ). + const result: JSAny = Call(context, callbackfn, thisArg, kValue, k, o); - transitioning macro FastArraySome(implicit context: Context)( - o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): JSAny - labels Bailout(Smi) { - let k: Smi = 0; - const smiLen = Cast(len) otherwise goto Bailout(k); - const fastO = Cast(o) otherwise goto Bailout(k); - let fastOW = NewFastJSArrayWitness(fastO); - - // Build a fast loop over the smi array. - for (; k < smiLen; k++) { - fastOW.Recheck() otherwise goto Bailout(k); - - // Ensure that we haven't walked beyond a possibly updated length. - if (k >= fastOW.Get().length) goto Bailout(k); - const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue; - const result: JSAny = - Call(context, callbackfn, thisArg, value, k, fastOW.Get()); + // iii. If selected is true, then... if (ToBoolean(result)) { return True; } } - return False; - } - // https://tc39.github.io/ecma262/#sec-array.prototype.some - transitioning javascript builtin - ArraySome(js-implicit context: NativeContext, receiver: JSAny)(...arguments): - JSAny { - try { - RequireObjectCoercible(receiver, 'Array.prototype.some'); + // 6d. Increase k by 1. (done by the loop). + } + return False; +} - // 1. Let O be ? ToObject(this value). - const o: JSReceiver = ToObject_Inline(context, receiver); +transitioning macro FastArraySome(implicit context: Context)( + o: JSReceiver, len: Number, callbackfn: Callable, thisArg: JSAny): JSAny + labels Bailout(Smi) { + let k: Smi = 0; + const smiLen = Cast(len) otherwise goto Bailout(k); + const fastO = Cast(o) otherwise goto Bailout(k); + let fastOW = NewFastJSArrayWitness(fastO); + + // Build a fast loop over the smi array. + for (; k < smiLen; k++) { + fastOW.Recheck() otherwise goto Bailout(k); + + // Ensure that we haven't walked beyond a possibly updated length. + if (k >= fastOW.Get().length) goto Bailout(k); + const value: JSAny = fastOW.LoadElementNoHole(k) otherwise continue; + const result: JSAny = + Call(context, callbackfn, thisArg, value, k, fastOW.Get()); + if (ToBoolean(result)) { + return True; + } + } + return False; +} - // 2. Let len be ? ToLength(? Get(O, "length")). - const len: Number = GetLengthProperty(o); +// https://tc39.github.io/ecma262/#sec-array.prototype.some +transitioning javascript builtin +ArraySome( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + try { + RequireObjectCoercible(receiver, 'Array.prototype.some'); - // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. - if (arguments.length == 0) { - goto TypeError; - } - const callbackfn = Cast(arguments[0]) otherwise TypeError; + // 1. Let O be ? ToObject(this value). + const o: JSReceiver = ToObject_Inline(context, receiver); - // 4. If thisArg is present, let T be thisArg; else let T be undefined. - const thisArg: JSAny = arguments.length > 1 ? arguments[1] : Undefined; + // 2. Let len be ? ToLength(? Get(O, "length")). + const len: Number = GetLengthProperty(o); - // Special cases. - try { - return FastArraySome(o, len, callbackfn, thisArg) - otherwise Bailout; - } - label Bailout(kValue: Smi) deferred { - return ArraySomeLoopContinuation( - o, callbackfn, thisArg, Undefined, o, kValue, len, Undefined); - } + // 3. If IsCallable(callbackfn) is false, throw a TypeError exception. + if (arguments.length == 0) { + goto TypeError; } - label TypeError deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); + const callbackfn = Cast(arguments[0]) otherwise TypeError; + + // 4. If thisArg is present, let T be thisArg; else let T be undefined. + const thisArg: JSAny = arguments[1]; + + // Special cases. + try { + return FastArraySome(o, len, callbackfn, thisArg) + otherwise Bailout; + } label Bailout(kValue: Smi) deferred { + return ArraySomeLoopContinuation( + o, callbackfn, thisArg, Undefined, o, kValue, len, Undefined); } + } label TypeError deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); } } +} diff --git a/deps/v8/src/builtins/array-splice.tq b/deps/v8/src/builtins/array-splice.tq index 9d7a223d97411c..92eace071cbe20 100644 --- a/deps/v8/src/builtins/array-splice.tq +++ b/deps/v8/src/builtins/array-splice.tq @@ -3,416 +3,418 @@ // found in the LICENSE file. namespace array { - // Given {elements}, we want to create a non-zero length array of type - // FixedArrayType. Most of this behavior is outsourced to ExtractFixedArray(), - // but the special case of wanting to have a FixedDoubleArray when given a - // zero-length input FixedArray is handled here. - macro Extract(implicit context: Context)( - elements: FixedArray, first: Smi, count: Smi, capacity: Smi): FixedArray { - return ExtractFixedArray( - elements, Convert(first), Convert(count), - Convert(capacity)); - } +// Given {source}, we want to create a non-zero length array of type +// FixedArrayType with the specified {result_capacity}. Starting from +// {startIndex}, {count} number of elements are copied to the newly +// created result array. Most of this behavior is outsourced to +// ExtractFixedArray(). We handle the case where the {source} is +// EmptyFixedArray but result is expected to be a FixedDoubleArray. +macro Extract(implicit context: Context)( + source: FixedArray, startIndex: Smi, count: Smi, + resultCapacity: Smi): FixedArray { + return ExtractFixedArray( + source, Convert(startIndex), Convert(count), + Convert(resultCapacity)); +} - macro Extract(implicit context: Context)( - elements: FixedDoubleArray|EmptyFixedArray, first: Smi, count: Smi, - capacity: Smi): FixedDoubleArray|EmptyFixedArray { - typeswitch (elements) { - case (EmptyFixedArray): { - return AllocateZeroedFixedDoubleArray(Convert(capacity)); - } - case (elements: FixedDoubleArray): { - return ExtractFixedDoubleArray( - elements, Convert(first), Convert(count), - Convert(capacity)); - } +macro Extract(implicit context: Context)( + source: FixedDoubleArray|EmptyFixedArray, startIndex: Smi, count: Smi, + resultCapacity: Smi): FixedDoubleArray|EmptyFixedArray { + typeswitch (source) { + case (EmptyFixedArray): { + // ExtractFixedDoubleArray expects {source} to be a FixedDoubleArray. + // Handle the case where {source} is empty here. + return AllocateFixedDoubleArrayWithHoles( + Convert(resultCapacity), + AllocationFlag::kAllowLargeObjectAllocation); + } + case (source: FixedDoubleArray): { + return ExtractFixedDoubleArray( + source, Convert(startIndex), Convert(count), + Convert(resultCapacity)); } } +} - macro DoMoveElements( - elements: FixedArrayType, dstIndex: Smi, srcIndex: Smi, - count: Smi): void { - TorqueMoveElements( - elements, Convert(dstIndex), Convert(srcIndex), - Convert(count)); - } +macro DoMoveElements( + elements: FixedArrayType, dstIndex: Smi, srcIndex: Smi, count: Smi): void { + TorqueMoveElements( + elements, Convert(dstIndex), Convert(srcIndex), + Convert(count)); +} - macro StoreHoles( - elements: FixedArrayType, holeStartIndex: Smi, holeEndIndex: Smi): void { - for (let i: Smi = holeStartIndex; i < holeEndIndex; i++) { - array::StoreArrayHole(elements, i); - } +macro StoreHoles( + elements: FixedArrayType, holeStartIndex: Smi, holeEndIndex: Smi): void { + for (let i: Smi = holeStartIndex; i < holeEndIndex; i++) { + array::StoreArrayHole(elements, i); } +} - macro DoCopyElements( - dstElements: FixedArrayType, dstIndex: Smi, srcElements: FixedArrayType, - srcIndex: Smi, count: Smi): void { - TorqueCopyElements( - dstElements, Convert(dstIndex), srcElements, - Convert(srcIndex), Convert(count)); - } +macro DoCopyElements( + dstElements: FixedArrayType, dstIndex: Smi, srcElements: FixedArrayType, + srcIndex: Smi, count: Smi): void { + TorqueCopyElements( + dstElements, Convert(dstIndex), srcElements, + Convert(srcIndex), Convert(count)); +} - macro - FastSplice( - implicit context: Context)( - args: Arguments, a: JSArray, length: Smi, newLength: Smi, - actualStart: Smi, insertCount: Smi, actualDeleteCount: Smi): void { - // Make sure elements are writable. - array::EnsureWriteableFastElements(a); - - if (insertCount != actualDeleteCount) { - const elements = - UnsafeCast<(FixedArrayType | EmptyFixedArray)>(a.elements); - const dstIndex: Smi = actualStart + insertCount; - const srcIndex: Smi = actualStart + actualDeleteCount; - const count: Smi = length - actualDeleteCount - actualStart; - if (insertCount < actualDeleteCount) { - // Shrink. +macro +FastSplice( + implicit context: Context)( + args: Arguments, a: JSArray, length: Smi, newLength: Smi, actualStart: Smi, + insertCount: Smi, actualDeleteCount: Smi): void { + // Make sure elements are writable. + array::EnsureWriteableFastElements(a); + + if (insertCount != actualDeleteCount) { + const elements = UnsafeCast<(FixedArrayType | EmptyFixedArray)>(a.elements); + const dstIndex: Smi = actualStart + insertCount; + const srcIndex: Smi = actualStart + actualDeleteCount; + const count: Smi = length - actualDeleteCount - actualStart; + if (insertCount < actualDeleteCount) { + // Shrink. + DoMoveElements( + UnsafeCast(elements), dstIndex, srcIndex, count); + StoreHoles(UnsafeCast(elements), newLength, length); + } else if (insertCount > actualDeleteCount) { + // If the backing store is big enough, then moving elements is enough. + if (newLength <= elements.length) { DoMoveElements( UnsafeCast(elements), dstIndex, srcIndex, count); - StoreHoles(UnsafeCast(elements), newLength, length); - } else if (insertCount > actualDeleteCount) { - // If the backing store is big enough, then moving elements is enough. - if (newLength <= elements.length) { - DoMoveElements( - UnsafeCast(elements), dstIndex, srcIndex, count); - } else { - // Grow. - const capacity: Smi = CalculateNewElementsCapacity(newLength); - const newElements: FixedArrayType = UnsafeCast( - Extract(elements, 0, actualStart, capacity)); - a.elements = newElements; - if (elements.length > 0) { - DoCopyElements( - newElements, dstIndex, UnsafeCast(elements), - srcIndex, count); - } + } else { + // Grow. + const capacity: Smi = CalculateNewElementsCapacity(newLength); + const newElements: FixedArrayType = UnsafeCast( + Extract(elements, 0, actualStart, capacity)); + a.elements = newElements; + if (elements.length > 0) { + DoCopyElements( + newElements, dstIndex, UnsafeCast(elements), + srcIndex, count); } } } - - // Copy arguments. - let k: Smi = actualStart; - if (insertCount > 0) { - const typedNewElements: FixedArrayType = - UnsafeCast(a.elements); - for (let i: intptr = 2; i < args.length; ++i) { - const e: JSAny = args[i]; - // The argument elements were already validated to be an appropriate - // {ElementType} to store in {FixedArrayType}. - typedNewElements[k++] = UnsafeCast(e); - } - } - - // Update the array's length after all the FixedArray shuffling is done. - a.length = newLength; } - transitioning macro FastArraySplice( - context: Context, args: Arguments, o: JSReceiver, - originalLengthNumber: Number, actualStartNumber: Number, insertCount: Smi, - actualDeleteCountNumber: Number): JSAny - labels Bailout { - const originalLength: Smi = - Cast(originalLengthNumber) otherwise Bailout; - const actualStart: Smi = Cast(actualStartNumber) otherwise Bailout; - const actualDeleteCount: Smi = - Cast(actualDeleteCountNumber) otherwise Bailout; - const lengthDelta: Smi = insertCount - actualDeleteCount; - const newLength: Smi = originalLength + lengthDelta; - - const a: JSArray = Cast(o) otherwise Bailout; - - const map: Map = a.map; - if (!IsPrototypeInitialArrayPrototype(map)) goto Bailout; - if (IsNoElementsProtectorCellInvalid()) goto Bailout; - if (IsArraySpeciesProtectorCellInvalid()) goto Bailout; - - // Fast path only works on fast elements kind and with writable length. - let elementsKind: ElementsKind = EnsureArrayPushable(map) otherwise Bailout; - if (!IsFastElementsKind(elementsKind)) goto Bailout; - - const oldElementsKind: ElementsKind = elementsKind; + // Copy arguments. + let k: Smi = actualStart; + if (insertCount > 0) { + const typedNewElements: FixedArrayType = + UnsafeCast(a.elements); for (let i: intptr = 2; i < args.length; ++i) { const e: JSAny = args[i]; - if (IsFastSmiElementsKind(elementsKind)) { - if (TaggedIsNotSmi(e)) { - const heapObject: HeapObject = UnsafeCast(e); - elementsKind = IsHeapNumber(heapObject) ? - AllowDoubleElements(elementsKind) : - AllowNonNumberElements(elementsKind); - } - } else if (IsDoubleElementsKind(elementsKind)) { - if (!IsNumber(e)) { - elementsKind = AllowNonNumberElements(elementsKind); - } - } + // The argument elements were already validated to be an appropriate + // {ElementType} to store in {FixedArrayType}. + typedNewElements[k++] = UnsafeCast(e); } + } - if (elementsKind != oldElementsKind) { - const smiElementsKind: Smi = Convert(Convert(elementsKind)); - TransitionElementsKindWithKind(context, a, smiElementsKind); - } + // Update the array's length after all the FixedArray shuffling is done. + a.length = newLength; +} - // Make sure that the length hasn't been changed by side-effect. - const length: Smi = Cast(a.length) otherwise Bailout; - if (originalLength != length) goto Bailout; +transitioning macro FastArraySplice( + context: Context, args: Arguments, o: JSReceiver, + originalLengthNumber: Number, actualStartNumber: Number, insertCount: Smi, + actualDeleteCountNumber: Number): JSAny + labels Bailout { + const originalLength: Smi = Cast(originalLengthNumber) otherwise Bailout; + const actualStart: Smi = Cast(actualStartNumber) otherwise Bailout; + const actualDeleteCount: Smi = + Cast(actualDeleteCountNumber) otherwise Bailout; + const lengthDelta: Smi = insertCount - actualDeleteCount; + const newLength: Smi = originalLength + lengthDelta; + + const a: JSArray = Cast(o) otherwise Bailout; + + const map: Map = a.map; + if (!IsPrototypeInitialArrayPrototype(map)) goto Bailout; + if (IsNoElementsProtectorCellInvalid()) goto Bailout; + if (IsArraySpeciesProtectorCellInvalid()) goto Bailout; + + // Fast path only works on fast elements kind and with writable length. + let elementsKind: ElementsKind = EnsureArrayPushable(map) otherwise Bailout; + if (!IsFastElementsKind(elementsKind)) goto Bailout; + + const oldElementsKind: ElementsKind = elementsKind; + for (let i: intptr = 2; i < args.length; ++i) { + const e: JSAny = args[i]; + if (IsFastSmiElementsKind(elementsKind)) { + if (TaggedIsNotSmi(e)) { + const heapObject: HeapObject = UnsafeCast(e); + elementsKind = IsHeapNumber(heapObject) ? + AllowDoubleElements(elementsKind) : + AllowNonNumberElements(elementsKind); + } + } else if (IsDoubleElementsKind(elementsKind)) { + if (!IsNumber(e)) { + elementsKind = AllowNonNumberElements(elementsKind); + } + } + } - const deletedResult: JSArray = - ExtractFastJSArray(context, a, actualStart, actualDeleteCount); + if (elementsKind != oldElementsKind) { + const smiElementsKind: Smi = Convert(Convert(elementsKind)); + TransitionElementsKindWithKind(context, a, smiElementsKind); + } - if (newLength == 0) { - a.elements = kEmptyFixedArray; - a.length = 0; - return deletedResult; - } + // Make sure that the length hasn't been changed by side-effect. + const length: Smi = Cast(a.length) otherwise Bailout; + if (originalLength != length) goto Bailout; - if (IsFastSmiOrTaggedElementsKind(elementsKind)) { - FastSplice( - args, a, length, newLength, actualStart, insertCount, - actualDeleteCount); - } else { - FastSplice( - args, a, length, newLength, actualStart, insertCount, - actualDeleteCount); - } + const deletedResult: JSArray = + ExtractFastJSArray(context, a, actualStart, actualDeleteCount); + if (newLength == 0) { + a.elements = kEmptyFixedArray; + a.length = 0; return deletedResult; } - transitioning macro FillDeletedElementsArray( - context: Context, o: JSReceiver, actualStart: Number, - actualDeleteCount: Number, a: JSReceiver): JSAny { - // 10. Let k be 0. - let k: Number = 0; + if (IsFastSmiOrTaggedElementsKind(elementsKind)) { + FastSplice( + args, a, length, newLength, actualStart, insertCount, + actualDeleteCount); + } else { + FastSplice( + args, a, length, newLength, actualStart, insertCount, + actualDeleteCount); + } + + return deletedResult; +} - // 11. Repeat, while k < actualDeleteCount - while (k < actualDeleteCount) { - // a. Let from be ! ToString(actualStart + k). - const from: Number = actualStart + k; +transitioning macro FillDeletedElementsArray( + context: Context, o: JSReceiver, actualStart: Number, + actualDeleteCount: Number, a: JSReceiver): JSAny { + // 10. Let k be 0. + let k: Number = 0; - // b. Let fromPresent be ? HasProperty(O, from). - const fromPresent: Boolean = HasProperty(o, from); + // 11. Repeat, while k < actualDeleteCount + while (k < actualDeleteCount) { + // a. Let from be ! ToString(actualStart + k). + const from: Number = actualStart + k; - // c. If fromPresent is true, then - if (fromPresent == True) { - // i. Let fromValue be ? Get(O, from). - const fromValue: JSAny = GetProperty(o, from); + // b. Let fromPresent be ? HasProperty(O, from). + const fromPresent: Boolean = HasProperty(o, from); - // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(k), fromValue). - FastCreateDataProperty(a, k, fromValue); - } + // c. If fromPresent is true, then + if (fromPresent == True) { + // i. Let fromValue be ? Get(O, from). + const fromValue: JSAny = GetProperty(o, from); - // d. Increment k by 1. - k++; + // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(k), fromValue). + FastCreateDataProperty(a, k, fromValue); } - // 12. Perform ? Set(A, "length", actualDeleteCount, true). - SetProperty(a, kLengthString, actualDeleteCount); - return a; + + // d. Increment k by 1. + k++; } + // 12. Perform ? Set(A, "length", actualDeleteCount, true). + SetProperty(a, kLengthString, actualDeleteCount); + return a; +} - // HandleForwardCase implements step 15. "If itemCount < actualDeleteCount, - // then..."" - transitioning macro HandleForwardCase( - context: Context, o: JSReceiver, len: Number, itemCount: Number, - actualStart: Number, actualDeleteCount: Number): void { - // 15. If itemCount < actualDeleteCount, then - // a. Let k be actualStart. - let k: Number = actualStart; - - // b. Repeat, while k < (len - actualDeleteCount) - while (k < (len - actualDeleteCount)) { - // i. Let from be ! ToString(k + actualDeleteCount). - const from: Number = k + actualDeleteCount; - // ii. Let to be ! ToString(k + itemCount). - const to: Number = k + itemCount; - - // iii. Let fromPresent be ? HasProperty(O, from). - const fromPresent: Boolean = HasProperty(o, from); - - // iv. If fromPresent is true, then - if (fromPresent == True) { - // 1. Let fromValue be ? Get(O, from). - const fromValue: JSAny = GetProperty(o, from); - - // 2. Perform ? Set(O, to, fromValue, true). - SetProperty(o, to, fromValue); - - // v. Else fromPresent is false, - } else { - // 1. Perform ? DeletePropertyOrThrow(O, to). - DeleteProperty(o, to, LanguageMode::kStrict); - } - // vi. Increase k by 1. - k++; +// HandleForwardCase implements step 15. "If itemCount < actualDeleteCount, +// then..."" +transitioning macro HandleForwardCase( + context: Context, o: JSReceiver, len: Number, itemCount: Number, + actualStart: Number, actualDeleteCount: Number): void { + // 15. If itemCount < actualDeleteCount, then + // a. Let k be actualStart. + let k: Number = actualStart; + + // b. Repeat, while k < (len - actualDeleteCount) + while (k < (len - actualDeleteCount)) { + // i. Let from be ! ToString(k + actualDeleteCount). + const from: Number = k + actualDeleteCount; + // ii. Let to be ! ToString(k + itemCount). + const to: Number = k + itemCount; + + // iii. Let fromPresent be ? HasProperty(O, from). + const fromPresent: Boolean = HasProperty(o, from); + + // iv. If fromPresent is true, then + if (fromPresent == True) { + // 1. Let fromValue be ? Get(O, from). + const fromValue: JSAny = GetProperty(o, from); + + // 2. Perform ? Set(O, to, fromValue, true). + SetProperty(o, to, fromValue); + + // v. Else fromPresent is false, + } else { + // 1. Perform ? DeletePropertyOrThrow(O, to). + DeleteProperty(o, to, LanguageMode::kStrict); } + // vi. Increase k by 1. + k++; + } - // c. Let k be len. - k = len; + // c. Let k be len. + k = len; - // d. Repeat, while k > (len - actualDeleteCount + itemCount) - while (k > (len - actualDeleteCount + itemCount)) { - // i. Perform ? DeletePropertyOrThrow(O, ! ToString(k - 1)). - DeleteProperty(o, k - 1, LanguageMode::kStrict); - // ii. Decrease k by 1. - k--; - } + // d. Repeat, while k > (len - actualDeleteCount + itemCount) + while (k > (len - actualDeleteCount + itemCount)) { + // i. Perform ? DeletePropertyOrThrow(O, ! ToString(k - 1)). + DeleteProperty(o, k - 1, LanguageMode::kStrict); + // ii. Decrease k by 1. + k--; } +} - // HandleBackwardCase implements step 16. "Else if itemCount > - // actualDeleteCount, then..." - transitioning macro HandleBackwardCase( - context: Context, o: JSReceiver, len: Number, itemCount: Number, - actualStart: Number, actualDeleteCount: Number): void { - // 16. Else if itemCount > actualDeleteCount, then - // a. Let k be (len - actualDeleteCount). - let k: Number = len - actualDeleteCount; - - // b. Repeat, while k > actualStart - while (k > actualStart) { - // i. Let from be ! ToString(k + actualDeleteCount - 1). - const from: Number = k + actualDeleteCount - 1; +// HandleBackwardCase implements step 16. "Else if itemCount > +// actualDeleteCount, then..." +transitioning macro HandleBackwardCase( + context: Context, o: JSReceiver, len: Number, itemCount: Number, + actualStart: Number, actualDeleteCount: Number): void { + // 16. Else if itemCount > actualDeleteCount, then + // a. Let k be (len - actualDeleteCount). + let k: Number = len - actualDeleteCount; - // ii. Let to be ! ToString(k + itemCount - 1). - const to: Number = k + itemCount - 1; + // b. Repeat, while k > actualStart + while (k > actualStart) { + // i. Let from be ! ToString(k + actualDeleteCount - 1). + const from: Number = k + actualDeleteCount - 1; - // iii. Let fromPresent be ? HasProperty(O, from). - const fromPresent: Boolean = HasProperty(o, from); + // ii. Let to be ! ToString(k + itemCount - 1). + const to: Number = k + itemCount - 1; - // iv. If fromPresent is true, then - if (fromPresent == True) { - // 1. Let fromValue be ? Get(O, from). - const fromValue: JSAny = GetProperty(o, from); + // iii. Let fromPresent be ? HasProperty(O, from). + const fromPresent: Boolean = HasProperty(o, from); - // 2. Perform ? Set(O, to, fromValue, true). - SetProperty(o, to, fromValue); + // iv. If fromPresent is true, then + if (fromPresent == True) { + // 1. Let fromValue be ? Get(O, from). + const fromValue: JSAny = GetProperty(o, from); - // v. Else fromPresent is false, - } else { - // 1. Perform ? DeletePropertyOrThrow(O, to). - DeleteProperty(o, to, LanguageMode::kStrict); - } + // 2. Perform ? Set(O, to, fromValue, true). + SetProperty(o, to, fromValue); - // vi. Decrease k by 1. - k--; + // v. Else fromPresent is false, + } else { + // 1. Perform ? DeletePropertyOrThrow(O, to). + DeleteProperty(o, to, LanguageMode::kStrict); } + + // vi. Decrease k by 1. + k--; } +} - transitioning macro SlowSplice( - context: Context, arguments: Arguments, o: JSReceiver, len: Number, - actualStart: Number, insertCount: Smi, actualDeleteCount: Number): JSAny { - // 9. Let A be ? ArraySpeciesCreate(O, actualDeleteCount). - const a: JSReceiver = ArraySpeciesCreate(context, o, actualDeleteCount); - const itemCount: Number = insertCount; - - // Steps 9 through 12: creating the array of deleted elements. - FillDeletedElementsArray(context, o, actualStart, actualDeleteCount, a); - - // 13. Let items be a List whose elements are, in left-to-right order, - // the portion of the actual argument list starting with the third - // argument. The list is empty if fewer than three arguments were - // passed. - // 14. Let itemCount be the Number of elements in items. - // (done above). - - // 15. If itemCount < actualDeleteCount, then - if (itemCount < actualDeleteCount) { - HandleForwardCase( - context, o, len, itemCount, actualStart, actualDeleteCount); - // 16. Else if itemCount > actualDeleteCount, then - } else if (itemCount > actualDeleteCount) { - HandleBackwardCase( - context, o, len, itemCount, actualStart, actualDeleteCount); - } +transitioning macro SlowSplice( + context: Context, arguments: Arguments, o: JSReceiver, len: Number, + actualStart: Number, insertCount: Smi, actualDeleteCount: Number): JSAny { + // 9. Let A be ? ArraySpeciesCreate(O, actualDeleteCount). + const a: JSReceiver = ArraySpeciesCreate(context, o, actualDeleteCount); + const itemCount: Number = insertCount; + + // Steps 9 through 12: creating the array of deleted elements. + FillDeletedElementsArray(context, o, actualStart, actualDeleteCount, a); + + // 13. Let items be a List whose elements are, in left-to-right order, + // the portion of the actual argument list starting with the third + // argument. The list is empty if fewer than three arguments were + // passed. + // 14. Let itemCount be the Number of elements in items. + // (done above). + + // 15. If itemCount < actualDeleteCount, then + if (itemCount < actualDeleteCount) { + HandleForwardCase( + context, o, len, itemCount, actualStart, actualDeleteCount); + // 16. Else if itemCount > actualDeleteCount, then + } else if (itemCount > actualDeleteCount) { + HandleBackwardCase( + context, o, len, itemCount, actualStart, actualDeleteCount); + } - // 17. Let k be actualStart. - let k: Number = actualStart; + // 17. Let k be actualStart. + let k: Number = actualStart; - // 18. Repeat, while items is not empty - // a. Remove the first element from items and let E be the value of that - // element. - if (arguments.length > 2) { - for (let i: intptr = 2; i < arguments.length; ++i) { - const e: JSAny = arguments[i]; - // b. Perform ? Set(O, ! ToString(k), E, true). - SetProperty(o, k, e); + // 18. Repeat, while items is not empty + // a. Remove the first element from items and let E be the value of that + // element. + if (arguments.length > 2) { + for (let i: intptr = 2; i < arguments.length; ++i) { + const e: JSAny = arguments[i]; + // b. Perform ? Set(O, ! ToString(k), E, true). + SetProperty(o, k, e); - // c. Increase k by 1. - k = k + 1; - } + // c. Increase k by 1. + k = k + 1; } - - // 19. Perform ? Set(O, "length", len - actualDeleteCount + itemCount, - // true). - SetProperty(o, kLengthString, len - actualDeleteCount + itemCount); - - return a; } - // https://tc39.github.io/ecma262/#sec-array.prototype.splice - transitioning javascript builtin - ArrayPrototypeSplice(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): JSAny { - // 1. Let O be ? ToObject(this value). - const o: JSReceiver = ToObject(context, receiver); - - // 2. Let len be ? ToLength(? Get(O, "length")). - const len: Number = GetLengthProperty(o); - - // 3. Let relativeStart be ? ToInteger(start). - const start: JSAny = arguments[0]; - const relativeStart: Number = ToInteger_Inline(start); - - // 4. If relativeStart < 0, let actualStart be max((len + relativeStart), - // 0); - // else let actualStart be min(relativeStart, len). - const actualStart: Number = relativeStart < 0 ? - Max((len + relativeStart), 0) : - Min(relativeStart, len); - - let insertCount: Smi; - let actualDeleteCount: Number; - // 5. If the Number of actual arguments is 0, then - if (arguments.length == 0) { - // a. Let insertCount be 0. - insertCount = 0; - // b. Let actualDeleteCount be 0. - actualDeleteCount = 0; - // 6. Else if the Number of actual arguments is 1, then - } else if (arguments.length == 1) { - // a. Let insertCount be 0. - insertCount = 0; - // b. Let actualDeleteCount be len - actualStart. - actualDeleteCount = len - actualStart; - // 7. Else, - } else { - // a. Let insertCount be the Number of actual arguments minus 2. - insertCount = Convert(arguments.length) - 2; - // b. Let dc be ? ToInteger(deleteCount). - const deleteCount: JSAny = arguments[1]; - const dc: Number = ToInteger_Inline(deleteCount); - // c. Let actualDeleteCount be min(max(dc, 0), len - actualStart). - actualDeleteCount = Min(Max(dc, 0), len - actualStart); - } + // 19. Perform ? Set(O, "length", len - actualDeleteCount + itemCount, + // true). + SetProperty(o, kLengthString, len - actualDeleteCount + itemCount); - // 8. If len + insertCount - actualDeleteCount > 2^53-1, throw a - // Bailout exception. - const newLength: Number = len + insertCount - actualDeleteCount; - if (newLength > kMaxSafeInteger) { - ThrowTypeError(MessageTemplate::kInvalidArrayLength, start); - } + return a; +} - try { - return FastArraySplice( - context, arguments, o, len, actualStart, insertCount, - actualDeleteCount) otherwise Bailout; - } - label Bailout {} +// https://tc39.github.io/ecma262/#sec-array.prototype.splice +transitioning javascript builtin +ArrayPrototypeSplice( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + // 1. Let O be ? ToObject(this value). + const o: JSReceiver = ToObject(context, receiver); + + // 2. Let len be ? ToLength(? Get(O, "length")). + const len: Number = GetLengthProperty(o); + + // 3. Let relativeStart be ? ToInteger(start). + const start: JSAny = arguments[0]; + const relativeStart: Number = ToInteger_Inline(start); + + // 4. If relativeStart < 0, let actualStart be max((len + relativeStart), + // 0); + // else let actualStart be min(relativeStart, len). + const actualStart: Number = relativeStart < 0 ? + Max((len + relativeStart), 0) : + Min(relativeStart, len); + + let insertCount: Smi; + let actualDeleteCount: Number; + // 5. If the Number of actual arguments is 0, then + if (arguments.length == 0) { + // a. Let insertCount be 0. + insertCount = 0; + // b. Let actualDeleteCount be 0. + actualDeleteCount = 0; + // 6. Else if the Number of actual arguments is 1, then + } else if (arguments.length == 1) { + // a. Let insertCount be 0. + insertCount = 0; + // b. Let actualDeleteCount be len - actualStart. + actualDeleteCount = len - actualStart; + // 7. Else, + } else { + // a. Let insertCount be the Number of actual arguments minus 2. + insertCount = Convert(arguments.length) - 2; + // b. Let dc be ? ToInteger(deleteCount). + const deleteCount: JSAny = arguments[1]; + const dc: Number = ToInteger_Inline(deleteCount); + // c. Let actualDeleteCount be min(max(dc, 0), len - actualStart). + actualDeleteCount = Min(Max(dc, 0), len - actualStart); + } - // If the fast case fails, just continue with the slow, correct, - // spec-compliant case. - return SlowSplice( - context, arguments, o, len, actualStart, insertCount, - actualDeleteCount); + // 8. If len + insertCount - actualDeleteCount > 2^53-1, throw a + // Bailout exception. + const newLength: Number = len + insertCount - actualDeleteCount; + if (newLength > kMaxSafeInteger) { + ThrowTypeError(MessageTemplate::kInvalidArrayLength, start); } + + try { + return FastArraySplice( + context, arguments, o, len, actualStart, insertCount, actualDeleteCount) + otherwise Bailout; + } label Bailout {} + + // If the fast case fails, just continue with the slow, correct, + // spec-compliant case. + return SlowSplice( + context, arguments, o, len, actualStart, insertCount, actualDeleteCount); +} } diff --git a/deps/v8/src/builtins/array-unshift.tq b/deps/v8/src/builtins/array-unshift.tq index 3b66015d3b32fd..7afeeb06271643 100644 --- a/deps/v8/src/builtins/array-unshift.tq +++ b/deps/v8/src/builtins/array-unshift.tq @@ -3,97 +3,95 @@ // found in the LICENSE file. namespace array { - extern builtin ArrayUnshift(Context, JSFunction, JSAny, int32): JSAny; +extern builtin ArrayUnshift(Context, JSFunction, JSAny, int32): JSAny; - transitioning macro GenericArrayUnshift( - context: Context, receiver: JSAny, arguments: Arguments): Number { - // 1. Let O be ? ToObject(this value). - const object: JSReceiver = ToObject_Inline(context, receiver); +transitioning macro GenericArrayUnshift( + context: Context, receiver: JSAny, arguments: Arguments): Number { + // 1. Let O be ? ToObject(this value). + const object: JSReceiver = ToObject_Inline(context, receiver); - // 2. Let len be ? ToLength(? Get(O, "length")). - const length: Number = GetLengthProperty(object); + // 2. Let len be ? ToLength(? Get(O, "length")). + const length: Number = GetLengthProperty(object); - // 3. Let argCount be the number of actual arguments. - const argCount: Smi = Convert(arguments.length); + // 3. Let argCount be the number of actual arguments. + const argCount: Smi = Convert(arguments.length); - // 4. If argCount > 0, then. - if (argCount > 0) { - // a. If len + argCount > 2**53 - 1, throw a TypeError exception. - if (length + argCount > kMaxSafeInteger) { - ThrowTypeError(MessageTemplate::kInvalidArrayLength); - } - - // b. Let k be len. - let k: Number = length; + // 4. If argCount > 0, then. + if (argCount > 0) { + // a. If len + argCount > 2**53 - 1, throw a TypeError exception. + if (length + argCount > kMaxSafeInteger) { + ThrowTypeError(MessageTemplate::kInvalidArrayLength); + } - // c. Repeat, while k > 0. - while (k > 0) { - // i. Let from be ! ToString(k - 1). - const from: Number = k - 1; + // b. Let k be len. + let k: Number = length; - // ii. Let to be ! ToString(k + argCount - 1). - const to: Number = k + argCount - 1; + // c. Repeat, while k > 0. + while (k > 0) { + // i. Let from be ! ToString(k - 1). + const from: Number = k - 1; - // iii. Let fromPresent be ? HasProperty(O, from). - const fromPresent: Boolean = HasProperty(object, from); + // ii. Let to be ! ToString(k + argCount - 1). + const to: Number = k + argCount - 1; - // iv. If fromPresent is true, then - if (fromPresent == True) { - // 1. Let fromValue be ? Get(O, from). - const fromValue: JSAny = GetProperty(object, from); + // iii. Let fromPresent be ? HasProperty(O, from). + const fromPresent: Boolean = HasProperty(object, from); - // 2. Perform ? Set(O, to, fromValue, true). - SetProperty(object, to, fromValue); - } else { - // 1. Perform ? DeletePropertyOrThrow(O, to). - DeleteProperty(object, to, LanguageMode::kStrict); - } + // iv. If fromPresent is true, then + if (fromPresent == True) { + // 1. Let fromValue be ? Get(O, from). + const fromValue: JSAny = GetProperty(object, from); - // vi. Decrease k by 1. - --k; + // 2. Perform ? Set(O, to, fromValue, true). + SetProperty(object, to, fromValue); + } else { + // 1. Perform ? DeletePropertyOrThrow(O, to). + DeleteProperty(object, to, LanguageMode::kStrict); } - // d. Let j be 0. - let j: Smi = 0; + // vi. Decrease k by 1. + --k; + } - // e. Let items be a List whose elements are, in left to right order, - // the arguments that were passed to this function invocation. - // f. Repeat, while items is not empty - while (j < argCount) { - // ii .Perform ? Set(O, ! ToString(j), E, true). - SetProperty(object, j, arguments[Convert(j)]); + // d. Let j be 0. + let j: Smi = 0; - // iii. Increase j by 1. - ++j; - } + // e. Let items be a List whose elements are, in left to right order, + // the arguments that were passed to this function invocation. + // f. Repeat, while items is not empty + while (j < argCount) { + // ii .Perform ? Set(O, ! ToString(j), E, true). + SetProperty(object, j, arguments[Convert(j)]); + + // iii. Increase j by 1. + ++j; } + } - // 5. Perform ? Set(O, "length", len + argCount, true). - const newLength: Number = length + argCount; - SetProperty(object, kLengthString, newLength); + // 5. Perform ? Set(O, "length", len + argCount, true). + const newLength: Number = length + argCount; + SetProperty(object, kLengthString, newLength); - // 6. Return length + argCount. - return newLength; - } + // 6. Return length + argCount. + return newLength; +} - // https://tc39.github.io/ecma262/#sec-array.prototype.unshift - transitioning javascript builtin ArrayPrototypeUnshift( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - try { - const array: FastJSArray = Cast(receiver) otherwise Slow; - array::EnsureWriteableFastElements(array); - - const map: Map = array.map; - if (!IsExtensibleMap(map)) goto Slow; - EnsureArrayLengthWritable(map) otherwise Slow; - - tail ArrayUnshift( - context, LoadTargetFromFrame(), Undefined, - Convert(arguments.length)); - } - label Slow { - return GenericArrayUnshift(context, receiver, arguments); - } +// https://tc39.github.io/ecma262/#sec-array.prototype.unshift +transitioning javascript builtin ArrayPrototypeUnshift( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + try { + const array: FastJSArray = Cast(receiver) otherwise Slow; + array::EnsureWriteableFastElements(array); + + const map: Map = array.map; + if (!IsExtensibleMap(map)) goto Slow; + EnsureArrayLengthWritable(map) otherwise Slow; + + tail ArrayUnshift( + context, LoadTargetFromFrame(), Undefined, + Convert(arguments.length)); + } label Slow { + return GenericArrayUnshift(context, receiver, arguments); } } +} diff --git a/deps/v8/src/builtins/array.tq b/deps/v8/src/builtins/array.tq index a5ffcfe40e90a9..92b2c520e28cce 100644 --- a/deps/v8/src/builtins/array.tq +++ b/deps/v8/src/builtins/array.tq @@ -5,78 +5,85 @@ #include 'src/builtins/builtins-array-gen.h' namespace array { - // Naming convention from elements.cc. We have a similar intent but implement - // fastpaths using generics instead of using a class hierarchy for elements - // kinds specific implementations. - type GenericElementsAccessor extends ElementsKind; - type FastPackedSmiElements extends ElementsKind; - type FastPackedObjectElements extends ElementsKind; - type FastPackedDoubleElements extends ElementsKind; - type FastSmiOrObjectElements extends ElementsKind; - type FastDoubleElements extends ElementsKind; - type DictionaryElements extends ElementsKind; +// Naming convention from elements.cc. We have a similar intent but implement +// fastpaths using generics instead of using a class hierarchy for elements +// kinds specific implementations. +type GenericElementsAccessor extends ElementsKind; +type FastPackedSmiElements extends ElementsKind; +type FastPackedObjectElements extends ElementsKind; +type FastPackedDoubleElements extends ElementsKind; +type FastSmiOrObjectElements extends ElementsKind; +type FastDoubleElements extends ElementsKind; +type DictionaryElements extends ElementsKind; - macro EnsureWriteableFastElements(implicit context: Context)(array: JSArray) { - assert(IsFastElementsKind(array.map.elements_kind)); +macro EnsureWriteableFastElements(implicit context: Context)(array: JSArray) { + assert(IsFastElementsKind(array.map.elements_kind)); - const elements: FixedArrayBase = array.elements; - if (elements.map != kCOWMap) return; + const elements: FixedArrayBase = array.elements; + if (elements.map != kCOWMap) return; - // There are no COW *_DOUBLE_ELEMENTS arrays, so we are allowed to always - // extract FixedArrays and don't have to worry about FixedDoubleArrays. - assert(IsFastSmiOrTaggedElementsKind(array.map.elements_kind)); + // There are no COW *_DOUBLE_ELEMENTS arrays, so we are allowed to always + // extract FixedArrays and don't have to worry about FixedDoubleArrays. + assert(IsFastSmiOrTaggedElementsKind(array.map.elements_kind)); - const length = - Convert(Cast(array.length) otherwise unreachable); - array.elements = - ExtractFixedArray(UnsafeCast(elements), 0, length, length); - assert(array.elements.map != kCOWMap); - } + const length = Convert(Cast(array.length) otherwise unreachable); + array.elements = + ExtractFixedArray(UnsafeCast(elements), 0, length, length); + assert(array.elements.map != kCOWMap); +} - macro LoadElementOrUndefined(implicit context: - Context)(a: FixedArray, i: Smi): JSAny { - const e = UnsafeCast<(JSAny | TheHole)>(a.objects[i]); - return ReplaceTheHoleWithUndefined(e); - } +macro LoadElementOrUndefined(implicit context: Context)( + a: FixedArray, i: Smi): JSAny { + const e = UnsafeCast<(JSAny | TheHole)>(a.objects[i]); + return ReplaceTheHoleWithUndefined(e); +} - macro LoadElementOrUndefined(a: FixedDoubleArray, i: Smi): NumberOrUndefined { - const f: float64 = a.floats[i].Value() otherwise return Undefined; - return AllocateHeapNumberWithValue(f); - } +macro LoadElementOrUndefined(a: FixedDoubleArray, i: Smi): NumberOrUndefined { + const f: float64 = a.floats[i].Value() otherwise return Undefined; + return AllocateHeapNumberWithValue(f); +} - macro StoreArrayHole(elements: FixedDoubleArray, k: Smi): void { - elements.floats[k] = kDoubleHole; - } +macro StoreArrayHole(elements: FixedDoubleArray, k: Smi): void { + elements.floats[k] = kDoubleHole; +} - macro StoreArrayHole(elements: FixedArray, k: Smi): void { - elements.objects[k] = TheHole; - } +macro StoreArrayHole(elements: FixedArray, k: Smi): void { + elements.objects[k] = TheHole; +} - extern macro SetPropertyLength(implicit context: Context)(JSAny, Number); +extern macro SetPropertyLength(implicit context: Context)(JSAny, Number); - const kLengthDescriptorIndex: - constexpr int31 generates 'JSArray::kLengthDescriptorIndex'; - const kAttributesReadOnlyMask: constexpr int31 - generates 'PropertyDetails::kAttributesReadOnlyMask'; +const kLengthDescriptorIndex: + constexpr int31 generates 'JSArray::kLengthDescriptorIndex'; +const kAttributesReadOnlyMask: constexpr int31 + generates 'PropertyDetails::kAttributesReadOnlyMask'; - @export - macro EnsureArrayLengthWritable(implicit context: Context)(map: Map): - void labels Bailout { - // Don't support arrays in dictionary named property mode. - if (IsDictionaryMap(map)) { - goto Bailout; - } +@export +macro EnsureArrayLengthWritable(implicit context: Context)(map: Map): + void labels Bailout { + // Don't support arrays in dictionary named property mode. + if (IsDictionaryMap(map)) { + goto Bailout; + } - // Check whether the length property is writable. The length property is the - // only default named property on arrays. It's nonconfigurable, hence is - // guaranteed to stay the first property. - const descriptors: DescriptorArray = map.instance_descriptors; - const descriptor:&DescriptorEntry = - & descriptors.descriptors[kLengthDescriptorIndex]; - assert(TaggedEqual(descriptor->key, LengthStringConstant())); - const details: Smi = UnsafeCast(descriptor->details); - if ((details & kAttributesReadOnlyMask) != 0) { - goto Bailout; - } + // Check whether the length property is writable. The length property is the + // only default named property on arrays. It's nonconfigurable, hence is + // guaranteed to stay the first property. + const descriptors: DescriptorArray = map.instance_descriptors; + const descriptor:&DescriptorEntry = + & descriptors.descriptors[kLengthDescriptorIndex]; + assert(TaggedEqual(descriptor->key, LengthStringConstant())); + const details: Smi = UnsafeCast(descriptor->details); + if ((details & kAttributesReadOnlyMask) != 0) { + goto Bailout; } } + +macro CreateJSArrayWithElements(implicit context: Context)(array: FixedArray): + JSArray { + const nativeContext: NativeContext = LoadNativeContext(context); + const map: Map = + LoadJSArrayElementsMap(ElementsKind::PACKED_ELEMENTS, nativeContext); + return AllocateJSArray(map, array, array.length); +} +} diff --git a/deps/v8/src/builtins/base.tq b/deps/v8/src/builtins/base.tq index 7d87a55e884db2..1d2c4546461cd8 100644 --- a/deps/v8/src/builtins/base.tq +++ b/deps/v8/src/builtins/base.tq @@ -52,6 +52,7 @@ extern macro MakeWeak(HeapObject): WeakHeapObject; extern macro GetHeapObjectAssumeWeak(WeakHeapObject): HeapObject labels ClearedWeakPointer; extern macro IsWeakOrCleared(MaybeObject): bool; +extern macro IsWeakReferenceToObject(MaybeObject, Object): bool; macro StrongToWeak(x: T): Weak { return %RawDownCast>(MakeWeak(x)); @@ -147,6 +148,8 @@ type ObjectHashTable extends HashTable extern class NumberDictionary extends HashTable; type RawPtr generates 'TNode' constexpr 'void*'; +type ExternalPointer + generates 'TNode' constexpr 'ExternalPointer_t'; extern class Code extends HeapObject; type BuiltinPtr extends Smi generates 'TNode'; @@ -253,10 +256,12 @@ constexpr 'CodeStubAssembler::ExtractFixedArrayFlag' { const kBigIntMaxLength: constexpr intptr generates 'BigInt::kMaxLength'; extern enum MessageTemplate { + kAllPromisesRejected, kInvalidArrayBufferLength, kInvalidArrayLength, kInvalidIndex, kNotConstructor, + kNotGeneric, kCalledNonCallable, kCalledOnNullOrUndefined, kProtoObjectOrNull, @@ -287,12 +292,11 @@ extern enum MessageTemplate { kPromiseNonCallable, kNotAPromise, kResolverNotAFunction, - kTooManyElementsInPromiseAll, + kTooManyElementsInPromiseCombinator, kToRadixFormatRange, kCalledOnNonObject, kRegExpGlobalInvokedOnNonGlobal, kProxyNonObject, - kProxyHandlerOrTargetRevoked, kProxyRevoked, kProxyTrapReturnedFalsishFor, kProxyPrivate, @@ -303,6 +307,24 @@ extern enum MessageTemplate { kProxyGetPrototypeOfNonExtensible, kProxySetPrototypeOfNonExtensible, kProxyDeletePropertyNonExtensible, + kWeakRefsCleanupMustBeCallable, + kWasmTrapUnreachable, + kWasmTrapMemOutOfBounds, + kWasmTrapUnalignedAccess, + kWasmTrapDivByZero, + kWasmTrapDivUnrepresentable, + kWasmTrapRemByZero, + kWasmTrapFloatUnrepresentable, + kWasmTrapFuncInvalid, + kWasmTrapFuncSigMismatch, + kWasmTrapDataSegmentDropped, + kWasmTrapElemSegmentDropped, + kWasmTrapTableOutOfBounds, + kWasmTrapBrOnExnNullRef, + kWasmTrapRethrowNullRef, + kWasmTrapNullDereference, + kWasmTrapIllegalCast, + kWasmTrapArrayOutOfBounds, ... } @@ -329,6 +351,8 @@ const kStringMaxLengthUintptr: constexpr uintptr generates 'String::kMaxLength'; const kFixedArrayMaxLength: constexpr int31 generates 'FixedArray::kMaxLength'; +const kFixedDoubleArrayMaxLength: + constexpr int31 generates 'FixedDoubleArray::kMaxLength'; const kObjectAlignmentMask: constexpr intptr generates 'kObjectAlignmentMask'; const kMinAddedElementsCapacity: @@ -363,19 +387,20 @@ type Boolean = True|False; type NumberOrUndefined = Number|Undefined; -extern macro TheHoleConstant(): TheHole; -extern macro NullConstant(): Null; -extern macro UndefinedConstant(): Undefined; -extern macro TrueConstant(): True; +extern macro EmptyStringConstant(): EmptyString; extern macro FalseConstant(): False; -extern macro Int32TrueConstant(): bool; extern macro Int32FalseConstant(): bool; -extern macro EmptyStringConstant(): EmptyString; -extern macro LengthStringConstant(): String; -extern macro NanConstant(): NaN; +extern macro Int32TrueConstant(): bool; extern macro IteratorSymbolConstant(): PublicSymbol; +extern macro LengthStringConstant(): String; extern macro MatchSymbolConstant(): Symbol; +extern macro MessageStringConstant(): String; +extern macro NanConstant(): NaN; +extern macro NullConstant(): Null; extern macro ReturnStringConstant(): String; +extern macro TheHoleConstant(): TheHole; +extern macro TrueConstant(): True; +extern macro UndefinedConstant(): Undefined; const TheHole: TheHole = TheHoleConstant(); const Null: Null = NullConstant(); @@ -384,6 +409,7 @@ const True: True = TrueConstant(); const False: False = FalseConstant(); const kEmptyString: EmptyString = EmptyStringConstant(); const kLengthString: String = LengthStringConstant(); +const kMessageString: String = MessageStringConstant(); const kReturnString: String = ReturnStringConstant(); const kNaN: NaN = NanConstant(); @@ -491,9 +517,9 @@ extern transitioning macro ToThisValue(implicit context: Context)( extern transitioning macro GetProperty(implicit context: Context)( JSAny, JSAny): JSAny; extern transitioning builtin SetProperty(implicit context: Context)( - JSAny, JSAny, JSAny); + JSAny, JSAny, JSAny): JSAny; extern transitioning builtin SetPropertyInLiteral(implicit context: Context)( - JSAny, JSAny, JSAny); + JSAny, JSAny, JSAny): JSAny; extern transitioning builtin DeleteProperty(implicit context: Context)( JSAny, JSAny | PrivateSymbol, LanguageModeSmi): Boolean; extern transitioning builtin HasProperty(implicit context: Context)( @@ -502,8 +528,6 @@ extern transitioning macro HasProperty_Inline(implicit context: Context)( JSReceiver, JSAny): Boolean; extern builtin LoadIC( Context, JSAny, JSAny, TaggedIndex, FeedbackVector): JSAny; -extern macro CollectCallFeedback( - JSAny, Context, Undefined | FeedbackVector, uintptr); extern macro ThrowRangeError(implicit context: Context)( constexpr MessageTemplate): never; @@ -640,6 +664,7 @@ extern macro IsFastAliasedArgumentsMap(implicit context: Context)(Map): bool; extern macro IsSlowAliasedArgumentsMap(implicit context: Context)(Map): bool; extern macro IsSloppyArgumentsMap(implicit context: Context)(Map): bool; extern macro IsStrictArgumentsMap(implicit context: Context)(Map): bool; +extern macro IsTuple2Map(Map): bool; extern macro SmiAbove(Smi, Smi): bool; @@ -701,7 +726,8 @@ macro Float64IsNaN(n: float64): bool { } // The type of all tagged values that can safely be compared with TaggedEqual. -type TaggedWithIdentity = JSReceiver|FixedArrayBase|Oddball|Map|EmptyString; +type TaggedWithIdentity = + JSReceiver|FixedArrayBase|Oddball|Map|WeakCell|Context|EmptyString; extern operator '==' macro TaggedEqual(TaggedWithIdentity, Object): bool; extern operator '==' macro TaggedEqual(Object, TaggedWithIdentity): bool; @@ -877,10 +903,14 @@ extern macro TruncateIntPtrToInt32(intptr): int32; extern macro SmiTag(intptr): Smi; extern macro SmiFromInt32(int32): Smi; extern macro SmiFromUint32(uint32): Smi; +extern macro SmiFromIntPtr(intptr): Smi; extern macro SmiUntag(Smi): intptr; macro SmiUntag(value: SmiTagged): T { return %RawDownCast(Unsigned(SmiToInt32(Convert(value)))); } +macro SmiTag(value: T): SmiTagged { + return %RawDownCast>(SmiFromUint32(value)); +} extern macro SmiToInt32(Smi): int32; extern macro TaggedIndexToIntPtr(TaggedIndex): intptr; extern macro IntPtrToTaggedIndex(intptr): TaggedIndex; @@ -889,9 +919,13 @@ extern macro SmiToTaggedIndex(Smi): TaggedIndex; extern macro RoundIntPtrToFloat64(intptr): float64; extern macro ChangeFloat32ToFloat64(float32): float64; extern macro ChangeNumberToFloat64(Number): float64; +extern macro ChangeTaggedNonSmiToInt32(implicit context: Context)(JSAnyNotSmi): + int32; +extern macro ChangeTaggedToFloat64(implicit context: Context)(JSAny): float64; extern macro ChangeFloat64ToTagged(float64): Number; extern macro ChangeFloat64ToUintPtr(float64): uintptr; extern macro ChangeFloat64ToIntPtr(float64): intptr; +extern macro ChangeInt32ToFloat64(int32): float64; extern macro ChangeInt32ToIntPtr(int32): intptr; // Sign-extends. extern macro ChangeUint32ToWord(uint32): uintptr; // Doesn't sign-extend. extern macro LoadNativeContext(Context): NativeContext; @@ -1241,8 +1275,7 @@ macro ChangeUintPtrNumberToUintPtr(value: Number): uintptr { try { return TryNumberToUintPtr(value, kModeValueIsSafeIntegerUintPtr) otherwise InvalidValue, InvalidValue, InvalidValue; - } - label InvalidValue { + } label InvalidValue { unreachable; } } @@ -1253,8 +1286,7 @@ macro ChangeSafeIntegerNumberToUintPtr(value: Number): try { return TryNumberToUintPtr(value, kModeValueIsSafeInteger) otherwise InvalidValue, IfUIntPtrOverflow, InvalidValue; - } - label InvalidValue { + } label InvalidValue { unreachable; } } @@ -1300,8 +1332,7 @@ transitioning macro GetLengthProperty(implicit context: Context)(o: JSAny): goto ToLength(GetProperty(o, kLengthString)); } } - } - label ToLength(length: JSAny) deferred { + } label ToLength(length: JSAny) deferred { return ToLength_Inline(length); } } @@ -1321,8 +1352,7 @@ transitioning macro GetMethod(implicit context: Context)( o: JSAny, name: String): Callable labels IfNullOrUndefined { try { return GetMethod(o, name) otherwise IfNullOrUndefined, IfMethodNotCallable; - } - label IfMethodNotCallable(value: JSAny) deferred { + } label IfMethodNotCallable(value: JSAny) deferred { ThrowTypeError(MessageTemplate::kPropertyNotFunction, value, name, o); } } @@ -1428,7 +1458,7 @@ macro ClampToIndexRange(indexNumber: Number, limit: uintptr): uintptr { } } -extern builtin ObjectToString(Context, JSAny): JSAny; +extern builtin ObjectToString(Context, JSAny): String; extern builtin StringRepeat(Context, String, Number): String; @export @@ -1485,8 +1515,8 @@ extern transitioning runtime CreateDataProperty(implicit context: Context)(JSReceiver, JSAny, JSAny); namespace runtime { - extern runtime - GetDerivedMap(Context, JSFunction, JSReceiver): Map; +extern runtime +GetDerivedMap(Context, JSFunction, JSReceiver): Map; } transitioning builtin FastCreateDataProperty(implicit context: Context)( @@ -1537,8 +1567,7 @@ transitioning builtin FastCreateDataProperty(implicit context: Context)( elements[index] = value; } } - } - label Slow { + } label Slow { CreateDataProperty(receiver, key, value); } return Undefined; diff --git a/deps/v8/src/builtins/bigint.tq b/deps/v8/src/builtins/bigint.tq index 02dca7543efabd..d52de7f84eab50 100644 --- a/deps/v8/src/builtins/bigint.tq +++ b/deps/v8/src/builtins/bigint.tq @@ -23,225 +23,218 @@ Convert(i: MutableBigInt): BigInt { namespace bigint { - const kPositiveSign: uint32 = 0; - const kNegativeSign: uint32 = 1; +const kPositiveSign: uint32 = 0; +const kNegativeSign: uint32 = 1; + +extern macro BigIntBuiltinsAssembler::CppAbsoluteAddAndCanonicalize( + MutableBigInt, BigIntBase, BigIntBase): void; +extern macro BigIntBuiltinsAssembler::CppAbsoluteSubAndCanonicalize( + MutableBigInt, BigIntBase, BigIntBase): void; +extern macro BigIntBuiltinsAssembler::CppAbsoluteCompare( + BigIntBase, BigIntBase): int32; + +extern macro BigIntBuiltinsAssembler::ReadBigIntSign(BigIntBase): uint32; +extern macro BigIntBuiltinsAssembler::ReadBigIntLength(BigIntBase): intptr; +extern macro BigIntBuiltinsAssembler::WriteBigIntSignAndLength( + MutableBigInt, uint32, intptr): void; + +extern macro CodeStubAssembler::AllocateBigInt(intptr): MutableBigInt; +extern macro CodeStubAssembler::StoreBigIntDigit( + MutableBigInt, intptr, uintptr): void; +extern macro CodeStubAssembler::LoadBigIntDigit(BigIntBase, intptr): uintptr; + +macro IsCanonicalized(bigint: BigIntBase): bool { + const length = ReadBigIntLength(bigint); + + if (length == 0) { + return ReadBigIntSign(bigint) == kPositiveSign; + } + + return LoadBigIntDigit(bigint, length - 1) != 0; +} + +macro InvertSign(sign: uint32): uint32 { + return sign == kPositiveSign ? kNegativeSign : kPositiveSign; +} + +macro AllocateEmptyBigIntNoThrow(implicit context: Context)( + sign: uint32, length: intptr): MutableBigInt labels BigIntTooBig { + if (length > kBigIntMaxLength) { + goto BigIntTooBig; + } + const result: MutableBigInt = AllocateBigInt(length); + + WriteBigIntSignAndLength(result, sign, length); + return result; +} + +macro AllocateEmptyBigInt(implicit context: Context)( + sign: uint32, length: intptr): MutableBigInt { + try { + return AllocateEmptyBigIntNoThrow(sign, length) otherwise BigIntTooBig; + } label BigIntTooBig { + ThrowRangeError(MessageTemplate::kBigIntTooBig); + } +} + +macro MutableBigIntAbsoluteCompare(x: BigIntBase, y: BigIntBase): int32 { + return CppAbsoluteCompare(x, y); +} + +macro MutableBigIntAbsoluteSub(implicit context: Context)( + x: BigInt, y: BigInt, resultSign: uint32): BigInt { + const xlength = ReadBigIntLength(x); + const ylength = ReadBigIntLength(y); + const xsign = ReadBigIntSign(x); + + assert(MutableBigIntAbsoluteCompare(x, y) >= 0); + if (xlength == 0) { + assert(ylength == 0); + return x; + } + + if (ylength == 0) { + return resultSign == xsign ? x : BigIntUnaryMinus(x); + } + + const result = AllocateEmptyBigInt(resultSign, xlength); + CppAbsoluteSubAndCanonicalize(result, x, y); + return Convert(result); +} + +macro MutableBigIntAbsoluteAdd(implicit context: Context)( + xBigint: BigInt, yBigint: BigInt, + resultSign: uint32): BigInt labels BigIntTooBig { + let xlength = ReadBigIntLength(xBigint); + let ylength = ReadBigIntLength(yBigint); + + let x = xBigint; + let y = yBigint; + if (xlength < ylength) { + // Swap x and y so that x is longer. + x = yBigint; + y = xBigint; + const tempLength = xlength; + xlength = ylength; + ylength = tempLength; + } + + // case: 0n + 0n + if (xlength == 0) { + assert(ylength == 0); + return x; + } + + // case: x + 0n + if (ylength == 0) { + return resultSign == ReadBigIntSign(x) ? x : BigIntUnaryMinus(x); + } - extern macro BigIntBuiltinsAssembler::CppAbsoluteAddAndCanonicalize( - MutableBigInt, BigIntBase, BigIntBase): void; - extern macro BigIntBuiltinsAssembler::CppAbsoluteSubAndCanonicalize( - MutableBigInt, BigIntBase, BigIntBase): void; - extern macro BigIntBuiltinsAssembler::CppAbsoluteCompare( - BigIntBase, BigIntBase): int32; - - extern macro BigIntBuiltinsAssembler::ReadBigIntSign(BigIntBase): uint32; - extern macro BigIntBuiltinsAssembler::ReadBigIntLength(BigIntBase): intptr; - extern macro BigIntBuiltinsAssembler::WriteBigIntSignAndLength( - MutableBigInt, uint32, intptr): void; + // case: x + y + const result = AllocateEmptyBigIntNoThrow(resultSign, xlength + 1) + otherwise BigIntTooBig; + CppAbsoluteAddAndCanonicalize(result, x, y); + return Convert(result); +} + +macro BigIntAddImpl(implicit context: Context)(x: BigInt, y: BigInt): BigInt + labels BigIntTooBig { + const xsign = ReadBigIntSign(x); + const ysign = ReadBigIntSign(y); + if (xsign == ysign) { + // x + y == x + y + // -x + -y == -(x + y) + return MutableBigIntAbsoluteAdd(x, y, xsign) otherwise BigIntTooBig; + } + + // x + -y == x - y == -(y - x) + // -x + y == y - x == -(x - y) + if (MutableBigIntAbsoluteCompare(x, y) >= 0) { + return MutableBigIntAbsoluteSub(x, y, xsign); + } + return MutableBigIntAbsoluteSub(y, x, InvertSign(xsign)); +} + +builtin BigIntAddNoThrow(implicit context: Context)( + x: BigInt, y: BigInt): Numeric { + try { + return BigIntAddImpl(x, y) otherwise BigIntTooBig; + } label BigIntTooBig { + // Smi sentinal is used to signal BigIntTooBig exception. + return Convert(0); + } +} + +builtin BigIntAdd(implicit context: Context)( + xNum: Numeric, yNum: Numeric): BigInt { + try { + const x = Cast(xNum) otherwise MixedTypes; + const y = Cast(yNum) otherwise MixedTypes; + + return BigIntAddImpl(x, y) otherwise BigIntTooBig; + } label MixedTypes { + ThrowTypeError(MessageTemplate::kBigIntMixedTypes); + } label BigIntTooBig { + ThrowRangeError(MessageTemplate::kBigIntTooBig); + } +} + +macro BigIntSubtractImpl(implicit context: Context)( + x: BigInt, y: BigInt): BigInt labels BigIntTooBig { + const xsign = ReadBigIntSign(x); + const ysign = ReadBigIntSign(y); + if (xsign != ysign) { + // x - (-y) == x + y + // (-x) - y == -(x + y) + return MutableBigIntAbsoluteAdd(x, y, xsign) otherwise BigIntTooBig; + } - extern macro CodeStubAssembler::AllocateBigInt(intptr): MutableBigInt; - extern macro CodeStubAssembler::StoreBigIntDigit( - MutableBigInt, intptr, uintptr): void; - extern macro CodeStubAssembler::LoadBigIntDigit(BigIntBase, intptr): uintptr; - - macro IsCanonicalized(bigint: BigIntBase): bool { - const length = ReadBigIntLength(bigint); - - if (length == 0) { - return ReadBigIntSign(bigint) == kPositiveSign; - } - - return LoadBigIntDigit(bigint, length - 1) != 0; - } - - macro InvertSign(sign: uint32): uint32 { - return sign == kPositiveSign ? kNegativeSign : kPositiveSign; - } - - macro AllocateEmptyBigIntNoThrow(implicit context: Context)( - sign: uint32, length: intptr): MutableBigInt labels BigIntTooBig { - if (length > kBigIntMaxLength) { - goto BigIntTooBig; - } - const result: MutableBigInt = AllocateBigInt(length); - - WriteBigIntSignAndLength(result, sign, length); - return result; - } - - macro AllocateEmptyBigInt(implicit context: Context)( - sign: uint32, length: intptr): MutableBigInt { - try { - return AllocateEmptyBigIntNoThrow(sign, length) otherwise BigIntTooBig; - } - label BigIntTooBig { - ThrowRangeError(MessageTemplate::kBigIntTooBig); - } - } - - macro MutableBigIntAbsoluteCompare(x: BigIntBase, y: BigIntBase): int32 { - return CppAbsoluteCompare(x, y); - } - - macro MutableBigIntAbsoluteSub(implicit context: Context)( - x: BigInt, y: BigInt, resultSign: uint32): BigInt { - const xlength = ReadBigIntLength(x); - const ylength = ReadBigIntLength(y); - const xsign = ReadBigIntSign(x); - - assert(MutableBigIntAbsoluteCompare(x, y) >= 0); - if (xlength == 0) { - assert(ylength == 0); - return x; - } - - if (ylength == 0) { - return resultSign == xsign ? x : BigIntUnaryMinus(x); - } - - const result = AllocateEmptyBigInt(resultSign, xlength); - CppAbsoluteSubAndCanonicalize(result, x, y); - return Convert(result); - } - - macro MutableBigIntAbsoluteAdd(implicit context: Context)( - xBigint: BigInt, yBigint: BigInt, - resultSign: uint32): BigInt labels BigIntTooBig { - let xlength = ReadBigIntLength(xBigint); - let ylength = ReadBigIntLength(yBigint); - - let x = xBigint; - let y = yBigint; - if (xlength < ylength) { - // Swap x and y so that x is longer. - x = yBigint; - y = xBigint; - const tempLength = xlength; - xlength = ylength; - ylength = tempLength; - } - - // case: 0n + 0n - if (xlength == 0) { - assert(ylength == 0); - return x; - } - - // case: x + 0n - if (ylength == 0) { - return resultSign == ReadBigIntSign(x) ? x : BigIntUnaryMinus(x); - } - - // case: x + y - const result = AllocateEmptyBigIntNoThrow(resultSign, xlength + 1) - otherwise BigIntTooBig; - CppAbsoluteAddAndCanonicalize(result, x, y); - return Convert(result); - } - - macro BigIntAddImpl(implicit context: Context)(x: BigInt, y: BigInt): BigInt - labels BigIntTooBig { - const xsign = ReadBigIntSign(x); - const ysign = ReadBigIntSign(y); - if (xsign == ysign) { - // x + y == x + y - // -x + -y == -(x + y) - return MutableBigIntAbsoluteAdd(x, y, xsign) otherwise BigIntTooBig; - } - - // x + -y == x - y == -(y - x) - // -x + y == y - x == -(x - y) - if (MutableBigIntAbsoluteCompare(x, y) >= 0) { - return MutableBigIntAbsoluteSub(x, y, xsign); - } - return MutableBigIntAbsoluteSub(y, x, InvertSign(xsign)); - } - - builtin BigIntAddNoThrow(implicit context: Context)(x: BigInt, y: BigInt): - Numeric { - try { - return BigIntAddImpl(x, y) otherwise BigIntTooBig; - } - label BigIntTooBig { - // Smi sentinal is used to signal BigIntTooBig exception. - return Convert(0); - } - } - - builtin BigIntAdd(implicit context: Context)(xNum: Numeric, yNum: Numeric): - BigInt { - try { - const x = Cast(xNum) otherwise MixedTypes; - const y = Cast(yNum) otherwise MixedTypes; - - return BigIntAddImpl(x, y) otherwise BigIntTooBig; - } - label MixedTypes { - ThrowTypeError(MessageTemplate::kBigIntMixedTypes); - } - label BigIntTooBig { - ThrowRangeError(MessageTemplate::kBigIntTooBig); - } - } - - macro BigIntSubtractImpl(implicit context: Context)(x: BigInt, y: BigInt): - BigInt labels BigIntTooBig { - const xsign = ReadBigIntSign(x); - const ysign = ReadBigIntSign(y); - if (xsign != ysign) { - // x - (-y) == x + y - // (-x) - y == -(x + y) - return MutableBigIntAbsoluteAdd(x, y, xsign) otherwise BigIntTooBig; - } - - // x - y == -(y - x) - // (-x) - (-y) == y - x == -(x - y) - if (MutableBigIntAbsoluteCompare(x, y) >= 0) { - return MutableBigIntAbsoluteSub(x, y, xsign); - } - return MutableBigIntAbsoluteSub(y, x, InvertSign(xsign)); - } - - builtin BigIntSubtractNoThrow(implicit context: - Context)(x: BigInt, y: BigInt): Numeric { - try { - return BigIntSubtractImpl(x, y) otherwise BigIntTooBig; - } - label BigIntTooBig { - // Smi sentinal is used to signal BigIntTooBig exception. - return Convert(0); - } - } - - builtin BigIntSubtract(implicit context: - Context)(xNum: Numeric, yNum: Numeric): BigInt { - try { - const x = Cast(xNum) otherwise MixedTypes; - const y = Cast(yNum) otherwise MixedTypes; - - return BigIntSubtractImpl(x, y) otherwise BigIntTooBig; - } - label MixedTypes { - ThrowTypeError(MessageTemplate::kBigIntMixedTypes); - } - label BigIntTooBig { - ThrowRangeError(MessageTemplate::kBigIntTooBig); - } - } - - builtin BigIntUnaryMinus(implicit context: Context)(bigint: BigInt): BigInt { - const length = ReadBigIntLength(bigint); - - // There is no -0n. - if (length == 0) { - return bigint; - } - - const result = - AllocateEmptyBigInt(InvertSign(ReadBigIntSign(bigint)), length); - for (let i: intptr = 0; i < length; ++i) { - StoreBigIntDigit(result, i, LoadBigIntDigit(bigint, i)); - } - return Convert(result); + // x - y == -(y - x) + // (-x) - (-y) == y - x == -(x - y) + if (MutableBigIntAbsoluteCompare(x, y) >= 0) { + return MutableBigIntAbsoluteSub(x, y, xsign); } + return MutableBigIntAbsoluteSub(y, x, InvertSign(xsign)); +} + +builtin BigIntSubtractNoThrow(implicit context: Context)( + x: BigInt, y: BigInt): Numeric { + try { + return BigIntSubtractImpl(x, y) otherwise BigIntTooBig; + } label BigIntTooBig { + // Smi sentinal is used to signal BigIntTooBig exception. + return Convert(0); + } +} + +builtin BigIntSubtract(implicit context: Context)( + xNum: Numeric, yNum: Numeric): BigInt { + try { + const x = Cast(xNum) otherwise MixedTypes; + const y = Cast(yNum) otherwise MixedTypes; + + return BigIntSubtractImpl(x, y) otherwise BigIntTooBig; + } label MixedTypes { + ThrowTypeError(MessageTemplate::kBigIntMixedTypes); + } label BigIntTooBig { + ThrowRangeError(MessageTemplate::kBigIntTooBig); + } +} + +builtin BigIntUnaryMinus(implicit context: Context)(bigint: BigInt): BigInt { + const length = ReadBigIntLength(bigint); + + // There is no -0n. + if (length == 0) { + return bigint; + } + + const result = + AllocateEmptyBigInt(InvertSign(ReadBigIntSign(bigint)), length); + for (let i: intptr = 0; i < length; ++i) { + StoreBigIntDigit(result, i, LoadBigIntDigit(bigint, i)); + } + return Convert(result); +} } // namespace bigint diff --git a/deps/v8/src/builtins/boolean.tq b/deps/v8/src/builtins/boolean.tq index c8f0d8134ac7b3..40a011d4e09e59 100644 --- a/deps/v8/src/builtins/boolean.tq +++ b/deps/v8/src/builtins/boolean.tq @@ -3,43 +3,43 @@ // found in the LICENSE file. namespace boolean { - transitioning macro ThisBooleanValue(implicit context: Context)( - receiver: JSAny, method: constexpr string): Boolean { - return UnsafeCast( - ToThisValue(receiver, PrimitiveType::kBoolean, method)); - } +transitioning macro ThisBooleanValue(implicit context: Context)( + receiver: JSAny, method: constexpr string): Boolean { + return UnsafeCast( + ToThisValue(receiver, PrimitiveType::kBoolean, method)); +} - javascript builtin - BooleanConstructor( - js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny, - target: JSFunction)(...arguments): JSAny { - const value = SelectBooleanConstant(ToBoolean(arguments[0])); +javascript builtin +BooleanConstructor( + js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny, + target: JSFunction)(...arguments): JSAny { + const value = SelectBooleanConstant(ToBoolean(arguments[0])); - if (newTarget == Undefined) { - return value; - } + if (newTarget == Undefined) { + return value; + } - const map = GetDerivedMap(target, UnsafeCast(newTarget)); + const map = GetDerivedMap(target, UnsafeCast(newTarget)); - const obj = - UnsafeCast(AllocateFastOrSlowJSObjectFromMap(map)); - obj.value = value; - return obj; - } + const obj = + UnsafeCast(AllocateFastOrSlowJSObjectFromMap(map)); + obj.value = value; + return obj; +} - // ES #sec-boolean.prototype.tostring - transitioning javascript builtin BooleanPrototypeToString( - js-implicit context: NativeContext, receiver: JSAny)(): JSAny { - // 1. Let b be ? thisBooleanValue(this value). - const b = ThisBooleanValue(receiver, 'Boolean.prototype.toString'); - // 2. If b is true, return "true"; else return "false". - return b.to_string; - } +// ES #sec-boolean.prototype.tostring +transitioning javascript builtin BooleanPrototypeToString( + js-implicit context: NativeContext, receiver: JSAny)(): JSAny { + // 1. Let b be ? thisBooleanValue(this value). + const b = ThisBooleanValue(receiver, 'Boolean.prototype.toString'); + // 2. If b is true, return "true"; else return "false". + return b.to_string; +} - // ES #sec-boolean.prototype.valueof - transitioning javascript builtin BooleanPrototypeValueOf( - js-implicit context: NativeContext, receiver: JSAny)(): JSAny { - // 1. Return ? thisBooleanValue(this value). - return ThisBooleanValue(receiver, 'Boolean.prototype.valueOf'); - } +// ES #sec-boolean.prototype.valueof +transitioning javascript builtin BooleanPrototypeValueOf( + js-implicit context: NativeContext, receiver: JSAny)(): JSAny { + // 1. Return ? thisBooleanValue(this value). + return ThisBooleanValue(receiver, 'Boolean.prototype.valueOf'); +} } diff --git a/deps/v8/src/builtins/builtins-array.cc b/deps/v8/src/builtins/builtins-array.cc index 40accae57a796a..3c2fe33c5b4b33 100644 --- a/deps/v8/src/builtins/builtins-array.cc +++ b/deps/v8/src/builtins/builtins-array.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "src/base/logging.h" #include "src/builtins/builtins-utils-inl.h" #include "src/builtins/builtins.h" #include "src/codegen/code-factory.h" @@ -471,6 +472,15 @@ BUILTIN(ArrayPop) { uint32_t new_length = len - 1; ASSIGN_RETURN_FAILURE_ON_EXCEPTION( isolate, result, JSReceiver::GetElement(isolate, array, new_length)); + + // The length could have become read-only during the last GetElement() call, + // so check again. + if (JSArray::HasReadOnlyLength(array)) { + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError(MessageTemplate::kStrictReadOnlyProperty, + isolate->factory()->length_string(), + Object::TypeOf(isolate, array), array)); + } JSArray::SetLength(array, new_length); } diff --git a/deps/v8/src/builtins/builtins-async-function-gen.cc b/deps/v8/src/builtins/builtins-async-function-gen.cc index aec64b9ccd3a93..e84442295cfc90 100644 --- a/deps/v8/src/builtins/builtins-async-function-gen.cc +++ b/deps/v8/src/builtins/builtins-async-function-gen.cc @@ -271,10 +271,12 @@ void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait( Goto(&after_debug_hook); BIND(&after_debug_hook); - Await(context, async_function_object, value, outer_promise, - Context::ASYNC_FUNCTION_AWAIT_RESOLVE_SHARED_FUN, - Context::ASYNC_FUNCTION_AWAIT_REJECT_SHARED_FUN, - is_predicted_as_caught); + TNode on_resolve_sfi = + AsyncFunctionAwaitResolveSharedFunConstant(); + TNode on_reject_sfi = + AsyncFunctionAwaitRejectSharedFunConstant(); + Await(context, async_function_object, value, outer_promise, on_resolve_sfi, + on_reject_sfi, is_predicted_as_caught); // Return outer promise to avoid adding an load of the outer promise before // suspending in BytecodeGenerator. diff --git a/deps/v8/src/builtins/builtins-async-gen.cc b/deps/v8/src/builtins/builtins-async-gen.cc index 785408339e8ed0..383289fd0f3ea4 100644 --- a/deps/v8/src/builtins/builtins-async-gen.cc +++ b/deps/v8/src/builtins/builtins-async-gen.cc @@ -27,8 +27,8 @@ class ValueUnwrapContext { TNode AsyncBuiltinsAssembler::AwaitOld( TNode context, TNode generator, TNode value, TNode outer_promise, - TNode on_resolve_context_index, - TNode on_reject_context_index, + TNode on_resolve_sfi, + TNode on_reject_sfi, TNode is_predicted_as_caught) { const TNode native_context = LoadNativeContext(context); @@ -90,12 +90,12 @@ TNode AsyncBuiltinsAssembler::AwaitOld( // Initialize resolve handler TNode on_resolve = InnerAllocate(base, kResolveClosureOffset); InitializeNativeClosure(closure_context, native_context, on_resolve, - on_resolve_context_index); + on_resolve_sfi); // Initialize reject handler TNode on_reject = InnerAllocate(base, kRejectClosureOffset); InitializeNativeClosure(closure_context, native_context, on_reject, - on_reject_context_index); + on_reject_sfi); TVARIABLE(HeapObject, var_throwaway, UndefinedConstant()); @@ -122,8 +122,8 @@ TNode AsyncBuiltinsAssembler::AwaitOld( TNode AsyncBuiltinsAssembler::AwaitOptimized( TNode context, TNode generator, TNode promise, TNode outer_promise, - TNode on_resolve_context_index, - TNode on_reject_context_index, + TNode on_resolve_sfi, + TNode on_reject_sfi, TNode is_predicted_as_caught) { const TNode native_context = LoadNativeContext(context); @@ -161,12 +161,12 @@ TNode AsyncBuiltinsAssembler::AwaitOptimized( // Initialize resolve handler TNode on_resolve = InnerAllocate(base, kResolveClosureOffset); InitializeNativeClosure(closure_context, native_context, on_resolve, - on_resolve_context_index); + on_resolve_sfi); // Initialize reject handler TNode on_reject = InnerAllocate(base, kRejectClosureOffset); InitializeNativeClosure(closure_context, native_context, on_reject, - on_reject_context_index); + on_reject_sfi); TVARIABLE(HeapObject, var_throwaway, UndefinedConstant()); @@ -190,8 +190,8 @@ TNode AsyncBuiltinsAssembler::AwaitOptimized( TNode AsyncBuiltinsAssembler::Await( TNode context, TNode generator, TNode value, TNode outer_promise, - TNode on_resolve_context_index, - TNode on_reject_context_index, + TNode on_resolve_sfi, + TNode on_reject_sfi, TNode is_predicted_as_caught) { TVARIABLE(Object, result); Label if_old(this), if_new(this), done(this), @@ -230,15 +230,14 @@ TNode AsyncBuiltinsAssembler::Await( } BIND(&if_old); - result = AwaitOld(context, generator, value, outer_promise, - on_resolve_context_index, on_reject_context_index, - is_predicted_as_caught); + result = AwaitOld(context, generator, value, outer_promise, on_resolve_sfi, + on_reject_sfi, is_predicted_as_caught); Goto(&done); BIND(&if_new); - result = AwaitOptimized(context, generator, CAST(value), outer_promise, - on_resolve_context_index, on_reject_context_index, - is_predicted_as_caught); + result = + AwaitOptimized(context, generator, CAST(value), outer_promise, + on_resolve_sfi, on_reject_sfi, is_predicted_as_caught); Goto(&done); BIND(&done); @@ -247,7 +246,7 @@ TNode AsyncBuiltinsAssembler::Await( void AsyncBuiltinsAssembler::InitializeNativeClosure( TNode context, TNode native_context, - TNode function, TNode context_index) { + TNode function, TNode shared_info) { TNode function_map = CAST(LoadContextElement( native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX)); // Ensure that we don't have to initialize prototype_or_initial_map field of @@ -265,8 +264,6 @@ void AsyncBuiltinsAssembler::InitializeNativeClosure( StoreObjectFieldRoot(function, JSFunction::kFeedbackCellOffset, RootIndex::kManyClosuresCell); - TNode shared_info = - CAST(LoadContextElement(native_context, context_index)); StoreObjectFieldNoWriteBarrier( function, JSFunction::kSharedFunctionInfoOffset, shared_info); StoreObjectFieldNoWriteBarrier(function, JSFunction::kContextOffset, context); @@ -286,8 +283,8 @@ TNode AsyncBuiltinsAssembler::CreateUnwrapClosure( TNode native_context, TNode done) { const TNode map = CAST(LoadContextElement( native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX)); - const TNode on_fulfilled_shared = CAST(LoadContextElement( - native_context, Context::ASYNC_ITERATOR_VALUE_UNWRAP_SHARED_FUN)); + const TNode on_fulfilled_shared = + AsyncIteratorValueUnwrapSharedFunConstant(); const TNode closure_context = AllocateAsyncIteratorValueUnwrapContext(native_context, done); return AllocateFunctionWithMapAndContext(map, on_fulfilled_shared, diff --git a/deps/v8/src/builtins/builtins-async-gen.h b/deps/v8/src/builtins/builtins-async-gen.h index 7b9c944f4acaea..833e78d45d5be0 100644 --- a/deps/v8/src/builtins/builtins-async-gen.h +++ b/deps/v8/src/builtins/builtins-async-gen.h @@ -17,34 +17,23 @@ class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler { protected: // Perform steps to resume generator after `value` is resolved. - // `on_reject_context_index` is an index into the Native Context, which should - // point to a SharedFunctioninfo instance used to create the closure. The - // value following the reject index should be a similar value for the resolve - // closure. Returns the Promise-wrapped `value`. + // `on_reject` is the SharedFunctioninfo instance used to create the reject + // closure. `on_resolve` is the SharedFunctioninfo instance used to create the + // resolve closure. Returns the Promise-wrapped `value`. TNode Await(TNode context, TNode generator, TNode value, TNode outer_promise, - TNode on_resolve_context_index, - TNode on_reject_context_index, + TNode on_resolve_sfi, + TNode on_reject_sfi, TNode is_predicted_as_caught); TNode Await(TNode context, TNode generator, TNode value, TNode outer_promise, - int on_resolve_context_index, int on_reject_context_index, - TNode is_predicted_as_caught) { - return Await(context, generator, value, outer_promise, - IntPtrConstant(on_resolve_context_index), - IntPtrConstant(on_reject_context_index), - is_predicted_as_caught); - } - TNode Await(TNode context, - TNode generator, TNode value, - TNode outer_promise, - int on_resolve_context_index, int on_reject_context_index, + TNode on_resolve_sfi, + TNode on_reject_sfi, bool is_predicted_as_caught) { - return Await(context, generator, value, outer_promise, - on_resolve_context_index, on_reject_context_index, - BooleanConstant(is_predicted_as_caught)); + return Await(context, generator, value, outer_promise, on_resolve_sfi, + on_reject_sfi, BooleanConstant(is_predicted_as_caught)); } // Return a new built-in function object as defined in @@ -56,22 +45,22 @@ class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler { void InitializeNativeClosure(TNode context, TNode native_context, TNode function, - TNode context_index); + TNode shared_info); TNode AllocateAsyncIteratorValueUnwrapContext( TNode native_context, TNode done); TNode AwaitOld(TNode context, TNode generator, TNode value, TNode outer_promise, - TNode on_resolve_context_index, - TNode on_reject_context_index, + TNode on_resolve_sfi, + TNode on_reject_sfi, TNode is_predicted_as_caught); TNode AwaitOptimized(TNode context, TNode generator, TNode promise, TNode outer_promise, - TNode on_resolve_context_index, - TNode on_reject_context_index, + TNode on_resolve_sfi, + TNode on_reject_sfi, TNode is_predicted_as_caught); }; diff --git a/deps/v8/src/builtins/builtins-async-generator-gen.cc b/deps/v8/src/builtins/builtins-async-generator-gen.cc index 592400415b9f4b..2b6d72088083b8 100644 --- a/deps/v8/src/builtins/builtins-async-generator-gen.cc +++ b/deps/v8/src/builtins/builtins-async-generator-gen.cc @@ -242,12 +242,10 @@ void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwait(bool is_catchable) { TNode outer_promise = LoadObjectField( request, AsyncGeneratorRequest::kPromiseOffset); - const int resolve_index = Context::ASYNC_GENERATOR_AWAIT_RESOLVE_SHARED_FUN; - const int reject_index = Context::ASYNC_GENERATOR_AWAIT_REJECT_SHARED_FUN; - SetGeneratorAwaiting(async_generator_object); - Await(context, async_generator_object, value, outer_promise, resolve_index, - reject_index, is_catchable); + Await(context, async_generator_object, value, outer_promise, + AsyncGeneratorAwaitResolveSharedFunConstant(), + AsyncGeneratorAwaitRejectSharedFunConstant(), is_catchable); Return(UndefinedConstant()); } @@ -573,12 +571,10 @@ TF_BUILTIN(AsyncGeneratorYield, AsyncGeneratorBuiltinsAssembler) { const TNode outer_promise = LoadPromiseFromAsyncGeneratorRequest(request); - const int on_resolve = Context::ASYNC_GENERATOR_YIELD_RESOLVE_SHARED_FUN; - const int on_reject = Context::ASYNC_GENERATOR_AWAIT_REJECT_SHARED_FUN; - SetGeneratorAwaiting(generator); - Await(context, generator, value, outer_promise, on_resolve, on_reject, - is_caught); + Await(context, generator, value, outer_promise, + AsyncGeneratorYieldResolveSharedFunConstant(), + AsyncGeneratorAwaitRejectSharedFunConstant(), is_caught); Return(UndefinedConstant()); } @@ -623,19 +619,17 @@ TF_BUILTIN(AsyncGeneratorReturn, AsyncGeneratorBuiltinsAssembler) { CAST(LoadFirstAsyncGeneratorRequestFromQueue(generator)); Label perform_await(this); - TVARIABLE(IntPtrT, var_on_resolve, - IntPtrConstant( - Context::ASYNC_GENERATOR_RETURN_CLOSED_RESOLVE_SHARED_FUN)); - TVARIABLE( - IntPtrT, var_on_reject, - IntPtrConstant(Context::ASYNC_GENERATOR_RETURN_CLOSED_REJECT_SHARED_FUN)); + TVARIABLE(SharedFunctionInfo, var_on_resolve, + AsyncGeneratorReturnClosedResolveSharedFunConstant()); + + TVARIABLE(SharedFunctionInfo, var_on_reject, + AsyncGeneratorReturnClosedRejectSharedFunConstant()); const TNode state = LoadGeneratorState(generator); GotoIf(IsGeneratorStateClosed(state), &perform_await); - var_on_resolve = - IntPtrConstant(Context::ASYNC_GENERATOR_RETURN_RESOLVE_SHARED_FUN); - var_on_reject = - IntPtrConstant(Context::ASYNC_GENERATOR_AWAIT_REJECT_SHARED_FUN); + var_on_resolve = AsyncGeneratorReturnResolveSharedFunConstant(); + var_on_reject = AsyncGeneratorAwaitRejectSharedFunConstant(); + Goto(&perform_await); BIND(&perform_await); diff --git a/deps/v8/src/builtins/builtins-call-gen.cc b/deps/v8/src/builtins/builtins-call-gen.cc index 1b53e9ca8e8d07..d457e0331490f8 100644 --- a/deps/v8/src/builtins/builtins-call-gen.cc +++ b/deps/v8/src/builtins/builtins-call-gen.cc @@ -317,7 +317,9 @@ void CallOrConstructBuiltinsAssembler::CallOrConstructWithSpread( BIND(&if_generic); { Label if_iterator_fn_not_callable(this, Label::kDeferred), - if_iterator_is_null_or_undefined(this, Label::kDeferred); + if_iterator_is_null_or_undefined(this, Label::kDeferred), + throw_spread_error(this, Label::kDeferred); + TVARIABLE(Smi, message_id); GotoIf(IsNullOrUndefined(spread), &if_iterator_is_null_or_undefined); @@ -336,10 +338,18 @@ void CallOrConstructBuiltinsAssembler::CallOrConstructWithSpread( &if_smiorobject, &if_double); BIND(&if_iterator_fn_not_callable); - ThrowTypeError(context, MessageTemplate::kIteratorSymbolNonCallable); + message_id = SmiConstant( + static_cast(MessageTemplate::kIteratorSymbolNonCallable)), + Goto(&throw_spread_error); BIND(&if_iterator_is_null_or_undefined); - CallRuntime(Runtime::kThrowSpreadArgIsNullOrUndefined, context, spread); + message_id = SmiConstant( + static_cast(MessageTemplate::kNotIterableNoSymbolLoad)); + Goto(&throw_spread_error); + + BIND(&throw_spread_error); + CallRuntime(Runtime::kThrowSpreadArgError, context, message_id.value(), + spread); Unreachable(); } @@ -565,7 +575,7 @@ void CallOrConstructBuiltinsAssembler::CallFunctionTemplate( TNode foreign = LoadObjectField( call_handler_info, CallHandlerInfo::kJsCallbackOffset); TNode callback = - LoadObjectField(foreign, Foreign::kForeignAddressOffset); + DecodeExternalPointer(LoadForeignForeignAddress(foreign)); TNode call_data = LoadObjectField(call_handler_info, CallHandlerInfo::kDataOffset); TailCallStub(CodeFactory::CallApiCallback(isolate()), context, callback, argc, diff --git a/deps/v8/src/builtins/builtins-callsite.cc b/deps/v8/src/builtins/builtins-callsite.cc index d1082291ef1414..5b7807ed4a9e50 100644 --- a/deps/v8/src/builtins/builtins-callsite.cc +++ b/deps/v8/src/builtins/builtins-callsite.cc @@ -76,7 +76,11 @@ BUILTIN(CallSitePrototypeGetFunction) { GetFrameIndex(isolate, recv)); StackFrameBase* frame = it.Frame(); - if (frame->IsStrict()) return ReadOnlyRoots(isolate).undefined_value(); + if (frame->IsStrict() || + (frame->GetFunction()->IsJSFunction() && + JSFunction::cast(*frame->GetFunction()).shared().is_toplevel())) { + return ReadOnlyRoots(isolate).undefined_value(); + } isolate->CountUsage(v8::Isolate::kCallSiteAPIGetFunctionSloppyCall); diff --git a/deps/v8/src/builtins/builtins-collections-gen.cc b/deps/v8/src/builtins/builtins-collections-gen.cc index df0ebce993fc4f..2f0e5a756026b4 100644 --- a/deps/v8/src/builtins/builtins-collections-gen.cc +++ b/deps/v8/src/builtins/builtins-collections-gen.cc @@ -334,8 +334,9 @@ void BaseCollectionsAssembler::AddConstructorEntriesFromIterable( } BIND(&if_exception); { - iterator_assembler.IteratorCloseOnException(context, iterator, - var_exception.value()); + IteratorCloseOnException(context, iterator); + CallRuntime(Runtime::kReThrow, context, var_exception.value()); + Unreachable(); } BIND(&exit); } diff --git a/deps/v8/src/builtins/builtins-dataview.cc b/deps/v8/src/builtins/builtins-dataview.cc index 01f43c7fd9a8fa..1718ea97ad184a 100644 --- a/deps/v8/src/builtins/builtins-dataview.cc +++ b/deps/v8/src/builtins/builtins-dataview.cc @@ -102,6 +102,7 @@ BUILTIN(DataViewConstructor) { // 13. Set O's [[ByteOffset]] internal slot to offset. Handle::cast(result)->set_byte_offset(view_byte_offset); Handle::cast(result)->set_data_pointer( + isolate, static_cast(array_buffer->backing_store()) + view_byte_offset); // 14. Return O. diff --git a/deps/v8/src/builtins/builtins-date-gen.cc b/deps/v8/src/builtins/builtins-date-gen.cc index 98c1343d2c8f8e..a32003303508cf 100644 --- a/deps/v8/src/builtins/builtins-date-gen.cc +++ b/deps/v8/src/builtins/builtins-date-gen.cc @@ -50,11 +50,14 @@ void DateBuiltinsAssembler::Generate_DatePrototype_GetField( BIND(&stamp_mismatch); } + TNode isolate_ptr = + ExternalConstant(ExternalReference::isolate_address(isolate())); TNode field_index_smi = SmiConstant(field_index); TNode function = ExternalConstant(ExternalReference::get_date_field_function()); TNode result = CAST(CallCFunction( function, MachineType::AnyTagged(), + std::make_pair(MachineType::Pointer(), isolate_ptr), std::make_pair(MachineType::AnyTagged(), date_receiver), std::make_pair(MachineType::AnyTagged(), field_index_smi))); Return(result); diff --git a/deps/v8/src/builtins/builtins-definitions.h b/deps/v8/src/builtins/builtins-definitions.h index 7ed38062c810a5..84ddf55f6f47d3 100644 --- a/deps/v8/src/builtins/builtins-definitions.h +++ b/deps/v8/src/builtins/builtins-definitions.h @@ -498,11 +498,6 @@ namespace internal { CPP(ErrorConstructor) \ CPP(ErrorCaptureStackTrace) \ CPP(ErrorPrototypeToString) \ - CPP(MakeError) \ - CPP(MakeRangeError) \ - CPP(MakeSyntaxError) \ - CPP(MakeTypeError) \ - CPP(MakeURIError) \ \ /* Function */ \ CPP(FunctionConstructor) \ @@ -579,7 +574,9 @@ namespace internal { /* IterableToList */ \ /* ES #sec-iterabletolist */ \ TFS(IterableToList, kIterable, kIteratorFn) \ + TFS(IterableToFixedArray, kIterable, kIteratorFn) \ TFS(IterableToListWithSymbolLookup, kIterable) \ + TFS(IterableToFixedArrayWithSymbolLookupSlow, kIterable) \ TFS(IterableToListMayPreserveHoles, kIterable, kIteratorFn) \ TFS(IterableToFixedArrayForWasm, kIterable, kExpectedLength) \ \ @@ -678,18 +675,12 @@ namespace internal { TFJ(ObjectKeys, 1, kReceiver, kObject) \ CPP(ObjectLookupGetter) \ CPP(ObjectLookupSetter) \ - /* ES6 #sec-object.prototype.tostring */ \ - TFJ(ObjectPrototypeToString, 0, kReceiver) \ - /* ES6 #sec-object.prototype.valueof */ \ - TFJ(ObjectPrototypeValueOf, 0, kReceiver) \ /* ES6 #sec-object.prototype.hasownproperty */ \ TFJ(ObjectPrototypeHasOwnProperty, 1, kReceiver, kKey) \ TFJ(ObjectPrototypeIsPrototypeOf, 1, kReceiver, kValue) \ CPP(ObjectPrototypePropertyIsEnumerable) \ CPP(ObjectPrototypeGetProto) \ CPP(ObjectPrototypeSetProto) \ - /* ES #sec-object.prototype.tolocalestring */ \ - TFJ(ObjectPrototypeToLocaleString, 0, kReceiver) \ CPP(ObjectSeal) \ TFS(ObjectToString, kReceiver) \ TFJ(ObjectValues, 1, kReceiver, kObject) \ @@ -702,9 +693,6 @@ namespace internal { TFS(ForInEnumerate, kReceiver) \ TFS(ForInFilter, kKey, kObject) \ \ - /* Promise */ \ - CPP(IsPromise) \ - \ /* Reflect */ \ ASM(ReflectApply, JSTrampoline) \ ASM(ReflectConstruct, JSTrampoline) \ @@ -851,35 +839,17 @@ namespace internal { /* Wasm */ \ ASM(WasmCompileLazy, Dummy) \ ASM(WasmDebugBreak, Dummy) \ + TFC(WasmFloat32ToNumber, WasmFloat32ToNumber) \ + TFC(WasmFloat64ToNumber, WasmFloat64ToNumber) \ + TFS(WasmAllocateArray, kMapIndex, kLength, kElementSize) \ + TFS(WasmAllocateStruct, kMapIndex) \ TFC(WasmAtomicNotify, WasmAtomicNotify) \ TFC(WasmI32AtomicWait32, WasmI32AtomicWait32) \ TFC(WasmI32AtomicWait64, WasmI32AtomicWait64) \ TFC(WasmI64AtomicWait32, WasmI64AtomicWait32) \ TFC(WasmI64AtomicWait64, WasmI64AtomicWait64) \ - TFC(WasmMemoryGrow, WasmMemoryGrow) \ TFC(WasmTableInit, WasmTableInit) \ TFC(WasmTableCopy, WasmTableCopy) \ - TFC(WasmTableGet, WasmTableGet) \ - TFC(WasmTableSet, WasmTableSet) \ - TFC(WasmStackGuard, NoContext) \ - TFC(WasmStackOverflow, NoContext) \ - TFC(WasmThrow, WasmThrow) \ - TFC(WasmRethrow, WasmThrow) \ - TFS(WasmTraceMemory, kMemoryTracingInfo) \ - TFS(ThrowWasmTrapUnreachable) \ - TFS(ThrowWasmTrapMemOutOfBounds) \ - TFS(ThrowWasmTrapUnalignedAccess) \ - TFS(ThrowWasmTrapDivByZero) \ - TFS(ThrowWasmTrapDivUnrepresentable) \ - TFS(ThrowWasmTrapRemByZero) \ - TFS(ThrowWasmTrapFloatUnrepresentable) \ - TFS(ThrowWasmTrapFuncInvalid) \ - TFS(ThrowWasmTrapFuncSigMismatch) \ - TFS(ThrowWasmTrapDataSegmentDropped) \ - TFS(ThrowWasmTrapElemSegmentDropped) \ - TFS(ThrowWasmTrapTableOutOfBounds) \ - TFS(ThrowWasmTrapBrOnExnNullRef) \ - TFS(ThrowWasmTrapRethrowNullRef) \ \ /* WeakMap */ \ TFJ(WeakMapConstructor, kDontAdaptArgumentsSentinel) \ @@ -976,8 +946,6 @@ namespace internal { CPP(Trace) \ \ /* Weak refs */ \ - CPP(FinalizationRegistryCleanupIteratorNext) \ - CPP(FinalizationRegistryCleanupSome) \ CPP(FinalizationRegistryConstructor) \ CPP(FinalizationRegistryRegister) \ CPP(FinalizationRegistryUnregister) \ @@ -1164,6 +1132,7 @@ namespace internal { V(AsyncGeneratorAwaitCaught) \ V(AsyncGeneratorAwaitUncaught) \ V(PromiseAll) \ + V(PromiseAny) \ V(PromiseConstructor) \ V(PromiseConstructorLazyDeoptContinuation) \ V(PromiseFulfillReactionJob) \ diff --git a/deps/v8/src/builtins/builtins-error.cc b/deps/v8/src/builtins/builtins-error.cc index 3634362205c6f0..840298eacbf532 100644 --- a/deps/v8/src/builtins/builtins-error.cc +++ b/deps/v8/src/builtins/builtins-error.cc @@ -18,24 +18,9 @@ namespace internal { // ES6 section 19.5.1.1 Error ( message ) BUILTIN(ErrorConstructor) { HandleScope scope(isolate); - - FrameSkipMode mode = SKIP_FIRST; - Handle caller; - - // When we're passed a JSFunction as new target, we can skip frames until that - // specific function is seen instead of unconditionally skipping the first - // frame. - if (args.new_target()->IsJSFunction()) { - mode = SKIP_UNTIL_SEEN; - caller = args.new_target(); - } - RETURN_RESULT_OR_FAILURE( - isolate, - ErrorUtils::Construct(isolate, args.target(), - Handle::cast(args.new_target()), - args.atOrUndefined(isolate, 1), mode, caller, - ErrorUtils::StackTraceCollection::kDetailed)); + isolate, ErrorUtils::Construct(isolate, args.target(), args.new_target(), + args.atOrUndefined(isolate, 1))); } // static @@ -85,53 +70,5 @@ BUILTIN(ErrorPrototypeToString) { ErrorUtils::ToString(isolate, args.receiver())); } -namespace { - -Object MakeGenericError(Isolate* isolate, BuiltinArguments args, - Handle constructor) { - Handle template_index = args.atOrUndefined(isolate, 1); - Handle arg0 = args.atOrUndefined(isolate, 2); - Handle arg1 = args.atOrUndefined(isolate, 3); - Handle arg2 = args.atOrUndefined(isolate, 4); - - DCHECK(template_index->IsSmi()); - - return *ErrorUtils::MakeGenericError( - isolate, constructor, MessageTemplateFromInt(Smi::ToInt(*template_index)), - arg0, arg1, arg2, SKIP_NONE); -} - -} // namespace - -BUILTIN(MakeError) { - HandleScope scope(isolate); - return MakeGenericError(isolate, args, isolate->error_function()); -} - -BUILTIN(MakeRangeError) { - HandleScope scope(isolate); - return MakeGenericError(isolate, args, isolate->range_error_function()); -} - -BUILTIN(MakeSyntaxError) { - HandleScope scope(isolate); - return MakeGenericError(isolate, args, isolate->syntax_error_function()); -} - -BUILTIN(MakeTypeError) { - HandleScope scope(isolate); - return MakeGenericError(isolate, args, isolate->type_error_function()); -} - -BUILTIN(MakeURIError) { - HandleScope scope(isolate); - Handle constructor = isolate->uri_error_function(); - Handle undefined = isolate->factory()->undefined_value(); - MessageTemplate template_index = MessageTemplate::kURIMalformed; - return *ErrorUtils::MakeGenericError(isolate, constructor, template_index, - undefined, undefined, undefined, - SKIP_NONE); -} - } // namespace internal } // namespace v8 diff --git a/deps/v8/src/builtins/builtins-handler-gen.cc b/deps/v8/src/builtins/builtins-handler-gen.cc index 48a137abc620d2..0325ddab7c9745 100644 --- a/deps/v8/src/builtins/builtins-handler-gen.cc +++ b/deps/v8/src/builtins/builtins-handler-gen.cc @@ -158,31 +158,26 @@ TNode HandlerBuiltinsAssembler::EmitKeyedSloppyArguments( TNode backing_store_length = LoadAndUntagFixedArrayBaseLength(backing_store); - if (access_mode == ArgumentsAccessMode::kHas) { - Label out_of_bounds(this); - GotoIf(UintPtrGreaterThanOrEqual(key, backing_store_length), - &out_of_bounds); - TNode result = LoadFixedArrayElement(backing_store, key); - var_result = - SelectBooleanConstant(TaggedNotEqual(result, TheHoleConstant())); - Goto(&end); - - BIND(&out_of_bounds); - var_result = FalseConstant(); - Goto(&end); + + // Out-of-bounds access may involve prototype chain walk and is handled + // in runtime. + GotoIf(UintPtrGreaterThanOrEqual(key, backing_store_length), bailout); + + // The key falls into unmapped range. + if (access_mode == ArgumentsAccessMode::kStore) { + StoreFixedArrayElement(backing_store, key, *value); } else { - GotoIf(UintPtrGreaterThanOrEqual(key, backing_store_length), bailout); + TNode value = LoadFixedArrayElement(backing_store, key); + GotoIf(TaggedEqual(value, TheHoleConstant()), bailout); - // The key falls into unmapped range. - if (access_mode == ArgumentsAccessMode::kLoad) { - TNode result = LoadFixedArrayElement(backing_store, key); - GotoIf(TaggedEqual(result, TheHoleConstant()), bailout); - var_result = result; + if (access_mode == ArgumentsAccessMode::kHas) { + var_result = TrueConstant(); } else { - StoreFixedArrayElement(backing_store, key, *value); + DCHECK_EQ(access_mode, ArgumentsAccessMode::kLoad); + var_result = value; } - Goto(&end); } + Goto(&end); } BIND(&end); diff --git a/deps/v8/src/builtins/builtins-internal-gen.cc b/deps/v8/src/builtins/builtins-internal-gen.cc index 6f4f54656d6664..61f03b3f993380 100644 --- a/deps/v8/src/builtins/builtins-internal-gen.cc +++ b/deps/v8/src/builtins/builtins-internal-gen.cc @@ -7,7 +7,8 @@ #include "src/builtins/builtins.h" #include "src/codegen/code-stub-assembler.h" #include "src/codegen/macro-assembler.h" -#include "src/heap/heap-inl.h" // crbug.com/v8/8499 +#include "src/execution/frame-constants.h" +#include "src/heap/memory-chunk.h" #include "src/ic/accessor-assembler.h" #include "src/ic/keyed-store-generic.h" #include "src/logging/counters.h" diff --git a/deps/v8/src/builtins/builtins-intl.cc b/deps/v8/src/builtins/builtins-intl.cc index 3b624af91b1989..7c8cde70dd89bd 100644 --- a/deps/v8/src/builtins/builtins-intl.cc +++ b/deps/v8/src/builtins/builtins-intl.cc @@ -604,23 +604,35 @@ BUILTIN(ListFormatSupportedLocalesOf) { JSListFormat::GetAvailableLocales(), locales, options)); } -namespace { +// Intl.Locale implementation +BUILTIN(LocaleConstructor) { + HandleScope scope(isolate); + + isolate->CountUsage(v8::Isolate::UseCounterFeature::kLocale); + + if (args.new_target()->IsUndefined(isolate)) { // [[Call]] + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError(MessageTemplate::kConstructorNotFunction, + isolate->factory()->NewStringFromAsciiChecked( + "Intl.Locale"))); + } + // [[Construct]] + Handle target = args.target(); + Handle new_target = Handle::cast(args.new_target()); + + Handle tag = args.atOrUndefined(isolate, 1); + Handle options = args.atOrUndefined(isolate, 2); -MaybeHandle CreateLocale(Isolate* isolate, - Handle constructor, - Handle new_target, - Handle tag, Handle options) { Handle map; // 6. Let locale be ? OrdinaryCreateFromConstructor(NewTarget, // %LocalePrototype%, internalSlotsList). - ASSIGN_RETURN_ON_EXCEPTION( - isolate, map, JSFunction::GetDerivedMap(isolate, constructor, new_target), - JSLocale); + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, map, JSFunction::GetDerivedMap(isolate, target, new_target)); // 7. If Type(tag) is not String or Object, throw a TypeError exception. if (!tag->IsString() && !tag->IsJSReceiver()) { - THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kLocaleNotEmpty), - JSLocale); + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError(MessageTemplate::kLocaleNotEmpty)); } Handle locale_string; @@ -631,8 +643,8 @@ MaybeHandle CreateLocale(Isolate* isolate, locale_string = JSLocale::ToString(isolate, Handle::cast(tag)); } else { // 9. Else, // a. Let tag be ? ToString(tag). - ASSIGN_RETURN_ON_EXCEPTION(isolate, locale_string, - Object::ToString(isolate, tag), JSLocale); + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, locale_string, + Object::ToString(isolate, tag)); } Handle options_object; @@ -642,60 +654,24 @@ MaybeHandle CreateLocale(Isolate* isolate, options_object = isolate->factory()->NewJSObjectWithNullProto(); } else { // 11. Else // a. Let options be ? ToObject(options). - ASSIGN_RETURN_ON_EXCEPTION(isolate, options_object, - Object::ToObject(isolate, options), JSLocale); + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, options_object, + Object::ToObject(isolate, options)); } - return JSLocale::New(isolate, map, locale_string, options_object); -} - -} // namespace - -// Intl.Locale implementation -BUILTIN(LocaleConstructor) { - HandleScope scope(isolate); - - isolate->CountUsage(v8::Isolate::UseCounterFeature::kLocale); - - if (args.new_target()->IsUndefined(isolate)) { // [[Call]] - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewTypeError(MessageTemplate::kConstructorNotFunction, - isolate->factory()->NewStringFromAsciiChecked( - "Intl.Locale"))); - } - // [[Construct]] - Handle target = args.target(); - Handle new_target = Handle::cast(args.new_target()); - - Handle tag = args.atOrUndefined(isolate, 1); - Handle options = args.atOrUndefined(isolate, 2); - RETURN_RESULT_OR_FAILURE( - isolate, CreateLocale(isolate, target, new_target, tag, options)); + isolate, JSLocale::New(isolate, map, locale_string, options_object)); } BUILTIN(LocalePrototypeMaximize) { HandleScope scope(isolate); CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.maximize"); - Handle constructor( - isolate->native_context()->intl_locale_function(), isolate); - Handle locale_str = JSLocale::ToString(isolate, locale); - RETURN_RESULT_OR_FAILURE( - isolate, CreateLocale(isolate, constructor, constructor, - JSLocale::Maximize(isolate, *locale_str), - isolate->factory()->NewJSObjectWithNullProto())); + RETURN_RESULT_OR_FAILURE(isolate, JSLocale::Maximize(isolate, locale)); } BUILTIN(LocalePrototypeMinimize) { HandleScope scope(isolate); CHECK_RECEIVER(JSLocale, locale, "Intl.Locale.prototype.minimize"); - Handle constructor( - isolate->native_context()->intl_locale_function(), isolate); - Handle locale_str = JSLocale::ToString(isolate, locale); - RETURN_RESULT_OR_FAILURE( - isolate, CreateLocale(isolate, constructor, constructor, - JSLocale::Minimize(isolate, *locale_str), - isolate->factory()->NewJSObjectWithNullProto())); + RETURN_RESULT_OR_FAILURE(isolate, JSLocale::Minimize(isolate, locale)); } BUILTIN(RelativeTimeFormatSupportedLocalesOf) { diff --git a/deps/v8/src/builtins/builtins-iterator-gen.cc b/deps/v8/src/builtins/builtins-iterator-gen.cc index 94a79d2a32c29d..9f3ec5c32346f9 100644 --- a/deps/v8/src/builtins/builtins-iterator-gen.cc +++ b/deps/v8/src/builtins/builtins-iterator-gen.cc @@ -133,59 +133,34 @@ TNode IteratorBuiltinsAssembler::IteratorValue( return var_value.value(); } -void IteratorBuiltinsAssembler::IteratorCloseOnException( - TNode context, const IteratorRecord& iterator, Label* if_exception, - TVariable* exception) { - // Perform ES #sec-iteratorclose when an exception occurs. This simpler - // algorithm does not include redundant steps which are never reachable from - // the spec IteratorClose algorithm. - DCHECK((if_exception != nullptr && exception != nullptr)); - CSA_ASSERT(this, IsNotTheHole(exception->value())); - CSA_ASSERT(this, IsJSReceiver(iterator.object)); - - // Let return be ? GetMethod(iterator, "return"). - TNode method; - { - compiler::ScopedExceptionHandler handler(this, if_exception, exception); - method = GetProperty(context, iterator.object, factory()->return_string()); - } - - // If return is undefined, return Completion(completion). - GotoIf(Word32Or(IsUndefined(method), IsNull(method)), if_exception); - - { - // Let innerResult be Call(return, iterator, « »). - // If an exception occurs, the original exception remains bound. - compiler::ScopedExceptionHandler handler(this, if_exception, nullptr); - Call(context, method, iterator.object); - } - - // (If completion.[[Type]] is throw) return Completion(completion). - Goto(if_exception); +TNode IteratorBuiltinsAssembler::IterableToList( + TNode context, TNode iterable, TNode iterator_fn) { + GrowableFixedArray values(state()); + FillFixedArrayFromIterable(context, iterable, iterator_fn, &values); + return values.ToJSArray(context); } -void IteratorBuiltinsAssembler::IteratorCloseOnException( - TNode context, const IteratorRecord& iterator, - TNode exception) { - Label rethrow(this, Label::kDeferred); - TVARIABLE(Object, exception_variable, exception); - IteratorCloseOnException(context, iterator, &rethrow, &exception_variable); - - BIND(&rethrow); - CallRuntime(Runtime::kReThrow, context, exception_variable.value()); - Unreachable(); +TNode IteratorBuiltinsAssembler::IterableToFixedArray( + TNode context, TNode iterable, TNode iterator_fn) { + GrowableFixedArray values(state()); + FillFixedArrayFromIterable(context, iterable, iterator_fn, &values); + TNode new_array = values.ToFixedArray(); + return new_array; } -TNode IteratorBuiltinsAssembler::IterableToList( - TNode context, TNode iterable, TNode iterator_fn) { +void IteratorBuiltinsAssembler::FillFixedArrayFromIterable( + TNode context, TNode iterable, TNode iterator_fn, + GrowableFixedArray* values) { // 1. Let iteratorRecord be ? GetIterator(items, method). IteratorRecord iterator_record = GetIterator(context, iterable, iterator_fn); // 2. Let values be a new empty List. - GrowableFixedArray values(state()); - Label loop_start( - this, {values.var_array(), values.var_length(), values.var_capacity()}), + // The GrowableFixedArray has already been created. It's ok if we do this step + // out of order, since creating an empty List is not observable. + + Label loop_start(this, {values->var_array(), values->var_length(), + values->var_capacity()}), done(this); Goto(&loop_start); // 3. Let next be true. @@ -198,12 +173,11 @@ TNode IteratorBuiltinsAssembler::IterableToList( // i. Let nextValue be ? IteratorValue(next). TNode next_value = IteratorValue(context, next); // ii. Append nextValue to the end of the List values. - values.Push(next_value); + values->Push(next_value); Goto(&loop_start); } BIND(&done); - return values.ToJSArray(context); } TF_BUILTIN(IterableToList, IteratorBuiltinsAssembler) { @@ -214,31 +188,26 @@ TF_BUILTIN(IterableToList, IteratorBuiltinsAssembler) { Return(IterableToList(context, iterable, iterator_fn)); } +TF_BUILTIN(IterableToFixedArray, IteratorBuiltinsAssembler) { + TNode context = CAST(Parameter(Descriptor::kContext)); + TNode iterable = CAST(Parameter(Descriptor::kIterable)); + TNode iterator_fn = CAST(Parameter(Descriptor::kIteratorFn)); + + Return(IterableToFixedArray(context, iterable, iterator_fn)); +} + TF_BUILTIN(IterableToFixedArrayForWasm, IteratorBuiltinsAssembler) { TNode context = CAST(Parameter(Descriptor::kContext)); TNode iterable = CAST(Parameter(Descriptor::kIterable)); TNode expected_length = CAST(Parameter(Descriptor::kExpectedLength)); TNode iterator_fn = GetIteratorMethod(context, iterable); - - IteratorRecord iterator_record = GetIterator(context, iterable, iterator_fn); - GrowableFixedArray values(state()); - Label loop_start( - this, {values.var_array(), values.var_length(), values.var_capacity()}), - compare_length(this), done(this); - Goto(&loop_start); - BIND(&loop_start); - { - TNode next = - IteratorStep(context, iterator_record, &compare_length); - TNode next_value = IteratorValue(context, next); - values.Push(next_value); - Goto(&loop_start); - } + Label done(this); + + FillFixedArrayFromIterable(context, iterable, iterator_fn, &values); - BIND(&compare_length); GotoIf(WordEqual(SmiUntag(expected_length), values.var_length()->value()), &done); Return(CallRuntime( @@ -299,7 +268,9 @@ TNode IteratorBuiltinsAssembler::StringListFromIterable( // 2. Return ? IteratorClose(iteratorRecord, error). BIND(&if_exception); - IteratorCloseOnException(context, iterator_record, var_exception.value()); + IteratorCloseOnException(context, iterator_record); + CallRuntime(Runtime::kReThrow, context, var_exception.value()); + Unreachable(); } } @@ -452,5 +423,17 @@ TF_BUILTIN(GetIteratorWithFeedbackLazyDeoptContinuation, Return(result); } +// This builtin creates a FixedArray based on an Iterable and doesn't have a +// fast path for anything. +TF_BUILTIN(IterableToFixedArrayWithSymbolLookupSlow, + IteratorBuiltinsAssembler) { + TNode context = CAST(Parameter(Descriptor::kContext)); + TNode iterable = CAST(Parameter(Descriptor::kIterable)); + + TNode iterator_fn = GetIteratorMethod(context, iterable); + TailCallBuiltin(Builtins::kIterableToFixedArray, context, iterable, + iterator_fn); +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/builtins/builtins-iterator-gen.h b/deps/v8/src/builtins/builtins-iterator-gen.h index 4d496fa3841bbf..6cea2c77ff4b37 100644 --- a/deps/v8/src/builtins/builtins-iterator-gen.h +++ b/deps/v8/src/builtins/builtins-iterator-gen.h @@ -12,6 +12,8 @@ namespace internal { using compiler::Node; +class GrowableFixedArray; + class IteratorBuiltinsAssembler : public CodeStubAssembler { public: explicit IteratorBuiltinsAssembler(compiler::CodeAssemblerState* state) @@ -50,21 +52,21 @@ class IteratorBuiltinsAssembler : public CodeStubAssembler { TNode context, TNode result, base::Optional> fast_iterator_result_map = base::nullopt); - // https://tc39.github.io/ecma262/#sec-iteratorclose - void IteratorCloseOnException(TNode context, - const IteratorRecord& iterator, - Label* if_exception, - TVariable* exception); - void IteratorCloseOnException(TNode context, - const IteratorRecord& iterator, - TNode exception); - // #sec-iterabletolist // Build a JSArray by iterating over {iterable} using {iterator_fn}, // following the ECMAscript operation with the same name. TNode IterableToList(TNode context, TNode iterable, TNode iterator_fn); + TNode IterableToFixedArray(TNode context, + TNode iterable, + TNode iterator_fn); + + void FillFixedArrayFromIterable(TNode context, + TNode iterable, + TNode iterator_fn, + GrowableFixedArray* values); + // Currently at https://tc39.github.io/proposal-intl-list-format/ // #sec-createstringlistfromiterable TNode StringListFromIterable(TNode context, diff --git a/deps/v8/src/builtins/builtins-microtask-queue-gen.cc b/deps/v8/src/builtins/builtins-microtask-queue-gen.cc index e6787b2da8c34b..e613ae9c08c2f4 100644 --- a/deps/v8/src/builtins/builtins-microtask-queue-gen.cc +++ b/deps/v8/src/builtins/builtins-microtask-queue-gen.cc @@ -53,8 +53,8 @@ class MicrotaskQueueBuiltinsAssembler : public CodeStubAssembler { TNode MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueue( TNode native_context) { CSA_ASSERT(this, IsNativeContext(native_context)); - return LoadObjectField(native_context, - NativeContext::kMicrotaskQueueOffset); + return DecodeExternalPointer(LoadObjectField( + native_context, NativeContext::kMicrotaskQueueOffset)); } TNode MicrotaskQueueBuiltinsAssembler::GetMicrotaskRingBuffer( @@ -198,18 +198,11 @@ void MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask( const TNode thenable = LoadObjectField( microtask, PromiseResolveThenableJobTask::kThenableOffset); - RunPromiseHook(Runtime::kPromiseHookBefore, microtask_context, - CAST(promise_to_resolve)); - { ScopedExceptionHandler handler(this, &if_exception, &var_exception); CallBuiltin(Builtins::kPromiseResolveThenableJob, native_context, promise_to_resolve, thenable, then); } - - RunPromiseHook(Runtime::kPromiseHookAfter, microtask_context, - CAST(promise_to_resolve)); - RewindEnteredContext(saved_entered_context_count); SetCurrentContext(current_context); Goto(&done); diff --git a/deps/v8/src/builtins/builtins-object-gen.cc b/deps/v8/src/builtins/builtins-object-gen.cc index 9af4affa681585..060454955817bb 100644 --- a/deps/v8/src/builtins/builtins-object-gen.cc +++ b/deps/v8/src/builtins/builtins-object-gen.cc @@ -350,22 +350,6 @@ ObjectEntriesValuesBuiltinsAssembler::FinalizeValuesOrEntriesJSArray( return TNode::UncheckedCast(array); } -TF_BUILTIN(ObjectPrototypeToLocaleString, CodeStubAssembler) { - TNode context = CAST(Parameter(Descriptor::kContext)); - TNode receiver = CAST(Parameter(Descriptor::kReceiver)); - - Label if_null_or_undefined(this, Label::kDeferred); - GotoIf(IsNullOrUndefined(receiver), &if_null_or_undefined); - - TNode method = - GetProperty(context, receiver, factory()->toString_string()); - Return(Call(context, method, receiver)); - - BIND(&if_null_or_undefined); - ThrowTypeError(context, MessageTemplate::kCalledOnNullOrUndefined, - "Object.prototype.toLocaleString"); -} - TF_BUILTIN(ObjectPrototypeHasOwnProperty, ObjectBuiltinsAssembler) { TNode object = CAST(Parameter(Descriptor::kReceiver)); TNode key = CAST(Parameter(Descriptor::kKey)); @@ -724,20 +708,13 @@ TF_BUILTIN(ObjectPrototypeIsPrototypeOf, ObjectBuiltinsAssembler) { Return(FalseConstant()); } -// ES #sec-object.prototype.tostring -TF_BUILTIN(ObjectPrototypeToString, CodeStubAssembler) { - TNode receiver = CAST(Parameter(Descriptor::kReceiver)); - TNode context = CAST(Parameter(Descriptor::kContext)); - Return(CallBuiltin(Builtins::kObjectToString, context, receiver)); -} - TF_BUILTIN(ObjectToString, ObjectBuiltinsAssembler) { - Label checkstringtag(this), if_apiobject(this, Label::kDeferred), - if_arguments(this), if_array(this), if_boolean(this), if_date(this), - if_error(this), if_function(this), if_number(this, Label::kDeferred), - if_object(this), if_primitive(this), if_proxy(this, Label::kDeferred), - if_regexp(this), if_string(this), if_symbol(this, Label::kDeferred), - if_value(this), if_bigint(this, Label::kDeferred); + Label checkstringtag(this), if_arguments(this), if_array(this), + if_boolean(this), if_date(this), if_error(this), if_function(this), + if_number(this, Label::kDeferred), if_object(this), if_primitive(this), + if_proxy(this, Label::kDeferred), if_regexp(this), if_string(this), + if_symbol(this, Label::kDeferred), if_value(this), + if_bigint(this, Label::kDeferred); TNode receiver = CAST(Parameter(Descriptor::kReceiver)); TNode context = CAST(Parameter(Descriptor::kContext)); @@ -763,8 +740,8 @@ TF_BUILTIN(ObjectToString, ObjectBuiltinsAssembler) { {JS_ARGUMENTS_OBJECT_TYPE, &if_arguments}, {JS_DATE_TYPE, &if_date}, {JS_BOUND_FUNCTION_TYPE, &if_function}, - {JS_API_OBJECT_TYPE, &if_apiobject}, - {JS_SPECIAL_API_OBJECT_TYPE, &if_apiobject}, + {JS_API_OBJECT_TYPE, &if_object}, + {JS_SPECIAL_API_OBJECT_TYPE, &if_object}, {JS_PROXY_TYPE, &if_proxy}, {JS_ERROR_TYPE, &if_error}, {JS_PRIMITIVE_WRAPPER_TYPE, &if_value}}; @@ -778,25 +755,6 @@ TF_BUILTIN(ObjectToString, ObjectBuiltinsAssembler) { Switch(receiver_instance_type, &if_object, case_values, case_labels, arraysize(case_values)); - BIND(&if_apiobject); - { - // Lookup the @@toStringTag property on the {receiver}. - TVARIABLE(Object, var_tag, - GetProperty(context, receiver, - isolate()->factory()->to_string_tag_symbol())); - Label if_tagisnotstring(this), if_tagisstring(this); - GotoIf(TaggedIsSmi(var_tag.value()), &if_tagisnotstring); - Branch(IsString(CAST(var_tag.value())), &if_tagisstring, - &if_tagisnotstring); - BIND(&if_tagisnotstring); - { - var_tag = CallRuntime(Runtime::kClassOf, context, receiver); - Goto(&if_tagisstring); - } - BIND(&if_tagisstring); - ReturnToStringFormat(context, CAST(var_tag.value())); - } - BIND(&if_arguments); { var_default = ArgumentsToStringConstant(); @@ -1053,14 +1011,6 @@ TF_BUILTIN(ObjectToString, ObjectBuiltinsAssembler) { } } -// ES6 #sec-object.prototype.valueof -TF_BUILTIN(ObjectPrototypeValueOf, CodeStubAssembler) { - TNode receiver = CAST(Parameter(Descriptor::kReceiver)); - TNode context = CAST(Parameter(Descriptor::kContext)); - - Return(ToObject_Inline(context, receiver)); -} - // ES #sec-object.create TF_BUILTIN(ObjectCreate, ObjectBuiltinsAssembler) { int const kPrototypeArg = 0; diff --git a/deps/v8/src/builtins/builtins-promise.cc b/deps/v8/src/builtins/builtins-promise.cc deleted file mode 100644 index 5eca1eb9c09896..00000000000000 --- a/deps/v8/src/builtins/builtins-promise.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2017 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-utils-inl.h" -#include "src/builtins/builtins.h" -#include "src/heap/heap-inl.h" // For ToBoolean. TODO(jkummerow): Drop. -#include "src/logging/counters.h" -#include "src/objects/objects-inl.h" - -namespace v8 { -namespace internal { - -BUILTIN(IsPromise) { - SealHandleScope scope(isolate); - - Handle object = args.atOrUndefined(isolate, 1); - return isolate->heap()->ToBoolean(object->IsJSPromise()); -} - -} // namespace internal -} // namespace v8 diff --git a/deps/v8/src/builtins/builtins-promise.h b/deps/v8/src/builtins/builtins-promise.h index a97ab7ad1dc466..fd938ff8418d94 100644 --- a/deps/v8/src/builtins/builtins-promise.h +++ b/deps/v8/src/builtins/builtins-promise.h @@ -40,6 +40,18 @@ class PromiseBuiltins { kPromiseAllResolveElementLength }; + enum PromiseAnyRejectElementContextSlots { + // Remaining elements count + kPromiseAnyRejectElementRemainingSlot = Context::MIN_CONTEXT_SLOTS, + + // Promise capability from Promise.any + kPromiseAnyRejectElementCapabilitySlot, + + // errors array from Promise.any + kPromiseAnyRejectElementErrorsArraySlot, + kPromiseAnyRejectElementLength + }; + enum FunctionContextSlot { kCapabilitySlot = Context::MIN_CONTEXT_SLOTS, diff --git a/deps/v8/src/builtins/builtins-proxy-gen.cc b/deps/v8/src/builtins/builtins-proxy-gen.cc index caafcf6506f646..f398a6c28254d0 100644 --- a/deps/v8/src/builtins/builtins-proxy-gen.cc +++ b/deps/v8/src/builtins/builtins-proxy-gen.cc @@ -76,8 +76,7 @@ TNode ProxiesCodeStubAssembler::AllocateProxyRevokeFunction( CreateProxyRevokeFunctionContext(proxy, native_context); const TNode revoke_map = CAST(LoadContextElement( native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX)); - const TNode revoke_info = CAST( - LoadContextElement(native_context, Context::PROXY_REVOKE_SHARED_FUN)); + const TNode revoke_info = ProxyRevokeSharedFunConstant(); return AllocateFunctionWithMapAndContext(revoke_map, revoke_info, proxy_context); diff --git a/deps/v8/src/builtins/builtins-regexp-gen.cc b/deps/v8/src/builtins/builtins-regexp-gen.cc index d06ced76d2e9e2..b9c1b8980ea8db 100644 --- a/deps/v8/src/builtins/builtins-regexp-gen.cc +++ b/deps/v8/src/builtins/builtins-regexp-gen.cc @@ -528,7 +528,7 @@ TNode RegExpBuiltinsAssembler::RegExpExecInternal( data, JSRegExp::kIrregexpCaptureCountIndex)); // capture_count is the number of captures without the match itself. // Required registers = (capture_count + 1) * 2. - STATIC_ASSERT(Internals::IsValidSmi((JSRegExp::kMaxCaptures + 1) << 1)); + STATIC_ASSERT(Internals::IsValidSmi((JSRegExp::kMaxCaptures + 1) * 2)); TNode register_count = SmiShl(SmiAdd(capture_count, SmiConstant(1)), 1); @@ -729,13 +729,9 @@ void RegExpBuiltinsAssembler::BranchIfFastRegExp( // This should only be needed for String.p.(split||matchAll), but we are // conservative here. - // Note: we are using the current native context here, which may or may not - // match the object's native context. That's fine: in case of a mismatch, we - // will bail in the next step when comparing the object's map against the - // current native context's initial regexp map. - TNode native_context = LoadNativeContext(context); - GotoIf(IsRegExpSpeciesProtectorCellInvalid(native_context), if_ismodified); + GotoIf(IsRegExpSpeciesProtectorCellInvalid(), if_ismodified); + TNode native_context = LoadNativeContext(context); TNode regexp_fun = CAST(LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX)); TNode initial_map = CAST( @@ -1336,10 +1332,10 @@ TNode RegExpMatchAllAssembler::CreateRegExpStringIterator( // 9. Set iterator.[[Done]] to false. TNode global_flag = Word32Shl(ReinterpretCast(global), - Int32Constant(JSRegExpStringIterator::kGlobalBit)); + Int32Constant(JSRegExpStringIterator::GlobalBit::kShift)); TNode unicode_flag = Word32Shl(ReinterpretCast(full_unicode), - Int32Constant(JSRegExpStringIterator::kUnicodeBit)); + Int32Constant(JSRegExpStringIterator::UnicodeBit::kShift)); TNode iterator_flags = Word32Or(global_flag, unicode_flag); StoreObjectFieldNoWriteBarrier(iterator, JSRegExpStringIterator::kFlagsOffset, SmiFromInt32(iterator_flags)); diff --git a/deps/v8/src/builtins/builtins-sharedarraybuffer-gen.cc b/deps/v8/src/builtins/builtins-sharedarraybuffer-gen.cc index 3049b01d2f5b4c..010bf965cc6a6e 100644 --- a/deps/v8/src/builtins/builtins-sharedarraybuffer-gen.cc +++ b/deps/v8/src/builtins/builtins-sharedarraybuffer-gen.cc @@ -89,7 +89,7 @@ void SharedArrayBufferBuiltinsAssembler::ValidateSharedTypedArray( BIND(¬_float_or_clamped); *out_elements_kind = elements_kind; - TNode backing_store = LoadJSArrayBufferBackingStore(array_buffer); + TNode backing_store = LoadJSArrayBufferBackingStorePtr(array_buffer); TNode byte_offset = LoadJSArrayBufferViewByteOffset(array); *out_backing_store = RawPtrAdd(backing_store, Signed(byte_offset)); } diff --git a/deps/v8/src/builtins/builtins-string-gen.cc b/deps/v8/src/builtins/builtins-string-gen.cc index e2d1635274913c..7ccb99792eda55 100644 --- a/deps/v8/src/builtins/builtins-string-gen.cc +++ b/deps/v8/src/builtins/builtins-string-gen.cc @@ -20,10 +20,10 @@ namespace internal { using Node = compiler::Node; -TNode StringBuiltinsAssembler::DirectStringData( +TNode StringBuiltinsAssembler::DirectStringData( TNode string, TNode string_instance_type) { // Compute the effective offset of the first character. - TVARIABLE(IntPtrT, var_data); + TVARIABLE(RawPtrT, var_data); Label if_sequential(this), if_external(this), if_join(this); Branch(Word32Equal(Word32And(string_instance_type, Int32Constant(kStringRepresentationMask)), @@ -32,9 +32,9 @@ TNode StringBuiltinsAssembler::DirectStringData( BIND(&if_sequential); { - var_data = IntPtrAdd( - IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag), - BitcastTaggedToWord(string)); + var_data = RawPtrAdd( + ReinterpretCast(BitcastTaggedToWord(string)), + IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); Goto(&if_join); } @@ -47,7 +47,7 @@ TNode StringBuiltinsAssembler::DirectStringData( Int32Constant(kUncachedExternalStringMask)), Int32Constant(kUncachedExternalStringTag))); var_data = - LoadObjectField(string, ExternalString::kResourceDataOffset); + DecodeExternalPointer(LoadExternalStringResourceData(CAST(string))); Goto(&if_join); } @@ -254,8 +254,8 @@ void StringBuiltinsAssembler::StringEqual_Loop( CSA_ASSERT(this, WordEqual(LoadStringLengthAsWord(rhs), length)); // Compute the effective offset of the first character. - TNode lhs_data = DirectStringData(lhs, lhs_instance_type); - TNode rhs_data = DirectStringData(rhs, rhs_instance_type); + TNode lhs_data = DirectStringData(lhs, lhs_instance_type); + TNode rhs_data = DirectStringData(rhs, rhs_instance_type); // Loop over the {lhs} and {rhs} strings to see if they are equal. TVARIABLE(IntPtrT, var_offset, IntPtrConstant(0)); @@ -1635,6 +1635,12 @@ TNode StringBuiltinsAssembler::StringToArray( ToDirectStringAssembler to_direct(state(), subject_string); to_direct.TryToDirect(&call_runtime); + + // The extracted direct string may be two-byte even though the wrapping + // string is one-byte. + GotoIfNot(IsOneByteStringInstanceType(to_direct.instance_type()), + &call_runtime); + TNode elements = CAST(AllocateFixedArray( PACKED_ELEMENTS, length, AllocationFlag::kAllowLargeObjectAllocation)); // Don't allocate anything while {string_data} is live! diff --git a/deps/v8/src/builtins/builtins-string-gen.h b/deps/v8/src/builtins/builtins-string-gen.h index 93b2086dd7f61c..2b4dadbbb03773 100644 --- a/deps/v8/src/builtins/builtins-string-gen.h +++ b/deps/v8/src/builtins/builtins-string-gen.h @@ -67,7 +67,7 @@ class StringBuiltinsAssembler : public CodeStubAssembler { TNode rhs_instance_type, MachineType rhs_type, TNode length, Label* if_equal, Label* if_not_equal); - TNode DirectStringData(TNode string, + TNode DirectStringData(TNode string, TNode string_instance_type); void DispatchOnStringEncodings(const TNode lhs_instance_type, diff --git a/deps/v8/src/builtins/builtins-string.tq b/deps/v8/src/builtins/builtins-string.tq index 61cd984e7f4a13..a4edc94418cb1a 100644 --- a/deps/v8/src/builtins/builtins-string.tq +++ b/deps/v8/src/builtins/builtins-string.tq @@ -5,222 +5,215 @@ #include 'src/builtins/builtins-string-gen.h' namespace string { - extern macro StringBuiltinsAssembler::SubString(String, uintptr, uintptr): - String; - - // ES6 #sec-string.prototype.tostring - transitioning javascript builtin - StringPrototypeToString( - js-implicit context: NativeContext, receiver: JSAny)(): JSAny { - return ToThisValue( - receiver, PrimitiveType::kString, 'String.prototype.toString'); - } - - // ES6 #sec-string.prototype.valueof - transitioning javascript builtin - StringPrototypeValueOf(js-implicit context: NativeContext, receiver: JSAny)(): - JSAny { - return ToThisValue( - receiver, PrimitiveType::kString, 'String.prototype.valueOf'); - } +extern macro StringBuiltinsAssembler::SubString( + String, uintptr, uintptr): String; + +// ES6 #sec-string.prototype.tostring +transitioning javascript builtin +StringPrototypeToString( + js-implicit context: NativeContext, receiver: JSAny)(): JSAny { + return ToThisValue( + receiver, PrimitiveType::kString, 'String.prototype.toString'); +} - extern macro StringBuiltinsAssembler::LoadSurrogatePairAt( - String, intptr, intptr, constexpr UnicodeEncoding): int32; - extern macro StringBuiltinsAssembler::StringFromSingleUTF16EncodedCodePoint( - int32): String; - - // This function assumes StringPrimitiveWithNoCustomIteration is true. - transitioning builtin StringToList(implicit context: Context)(string: String): - JSArray { - const kind = ElementsKind::PACKED_ELEMENTS; - const stringLength: intptr = string.length_intptr; - - const nativeContext = LoadNativeContext(context); - const map: Map = LoadJSArrayElementsMap(kind, nativeContext); - const array: JSArray = AllocateJSArray( - kind, map, stringLength, SmiTag(stringLength), - AllocationFlag::kAllowLargeObjectAllocation); - const elements = UnsafeCast(array.elements); - const encoding = UnicodeEncoding::UTF16; - let arrayLength: Smi = 0; - let i: intptr = 0; - while (i < stringLength) { - const ch: int32 = LoadSurrogatePairAt(string, stringLength, i, encoding); - const value: String = StringFromSingleUTF16EncodedCodePoint(ch); - elements[arrayLength] = value; - // Increment and continue the loop. - i = i + value.length_intptr; - arrayLength++; - } - assert(arrayLength >= 0); - assert(SmiTag(stringLength) >= arrayLength); - array.length = arrayLength; +// ES6 #sec-string.prototype.valueof +transitioning javascript builtin +StringPrototypeValueOf( + js-implicit context: NativeContext, receiver: JSAny)(): JSAny { + return ToThisValue( + receiver, PrimitiveType::kString, 'String.prototype.valueOf'); +} - return array; +extern macro StringBuiltinsAssembler::LoadSurrogatePairAt( + String, intptr, intptr, constexpr UnicodeEncoding): int32; +extern macro StringBuiltinsAssembler::StringFromSingleUTF16EncodedCodePoint( + int32): String; + +// This function assumes StringPrimitiveWithNoCustomIteration is true. +transitioning builtin StringToList(implicit context: Context)(string: String): + JSArray { + const kind = ElementsKind::PACKED_ELEMENTS; + const stringLength: intptr = string.length_intptr; + + const nativeContext = LoadNativeContext(context); + const map: Map = LoadJSArrayElementsMap(kind, nativeContext); + const array: JSArray = AllocateJSArray( + kind, map, stringLength, SmiTag(stringLength), + AllocationFlag::kAllowLargeObjectAllocation); + const elements = UnsafeCast(array.elements); + const encoding = UnicodeEncoding::UTF16; + let arrayLength: Smi = 0; + let i: intptr = 0; + while (i < stringLength) { + const ch: int32 = LoadSurrogatePairAt(string, stringLength, i, encoding); + const value: String = StringFromSingleUTF16EncodedCodePoint(ch); + elements[arrayLength] = value; + // Increment and continue the loop. + i = i + value.length_intptr; + arrayLength++; } + assert(arrayLength >= 0); + assert(SmiTag(stringLength) >= arrayLength); + array.length = arrayLength; - transitioning macro GenerateStringAt(implicit context: Context)( - receiver: JSAny, position: JSAny, - methodName: constexpr string): never labels - IfInBounds(String, uintptr, uintptr), IfOutOfBounds { - // 1. Let O be ? RequireObjectCoercible(this value). - // 2. Let S be ? ToString(O). - const string: String = ToThisString(receiver, methodName); - - // 3. Let position be ? ToInteger(pos). - const indexNumber: Number = ToInteger_Inline(position); - - // Convert the {position} to a uintptr and check that it's in bounds of - // the {string}. - typeswitch (indexNumber) { - case (indexSmi: Smi): { - const length: uintptr = string.length_uintptr; - const index: uintptr = Unsigned(Convert(indexSmi)); - // Max string length fits Smi range, so we can do an unsigned bounds - // check. - const kMaxStringLengthFitsSmi: constexpr bool = - kStringMaxLengthUintptr < kSmiMaxValue; - StaticAssert(kMaxStringLengthFitsSmi); - if (index >= length) goto IfOutOfBounds; - goto IfInBounds(string, index, length); - } - case (indexHeapNumber: HeapNumber): { - assert(IsNumberNormalized(indexHeapNumber)); - // Valid string indices fit into Smi range, so HeapNumber index is - // definitely an out of bounds case. - goto IfOutOfBounds; - } - } - } + return array; +} - // ES6 #sec-string.prototype.charat - transitioning javascript builtin StringPrototypeCharAt( - js-implicit context: NativeContext, - receiver: JSAny)(position: JSAny): JSAny { - try { - GenerateStringAt(receiver, position, 'String.prototype.charAt') - otherwise IfInBounds, IfOutOfBounds; - } - label IfInBounds(string: String, index: uintptr, _length: uintptr) { - const code: int32 = StringCharCodeAt(string, index); - return StringFromSingleCharCode(code); +transitioning macro GenerateStringAt(implicit context: Context)( + receiver: JSAny, position: JSAny, + methodName: constexpr string): never labels +IfInBounds(String, uintptr, uintptr), IfOutOfBounds { + // 1. Let O be ? RequireObjectCoercible(this value). + // 2. Let S be ? ToString(O). + const string: String = ToThisString(receiver, methodName); + + // 3. Let position be ? ToInteger(pos). + const indexNumber: Number = ToInteger_Inline(position); + + // Convert the {position} to a uintptr and check that it's in bounds of + // the {string}. + typeswitch (indexNumber) { + case (indexSmi: Smi): { + const length: uintptr = string.length_uintptr; + const index: uintptr = Unsigned(Convert(indexSmi)); + // Max string length fits Smi range, so we can do an unsigned bounds + // check. + const kMaxStringLengthFitsSmi: constexpr bool = + kStringMaxLengthUintptr < kSmiMaxValue; + StaticAssert(kMaxStringLengthFitsSmi); + if (index >= length) goto IfOutOfBounds; + goto IfInBounds(string, index, length); } - label IfOutOfBounds { - return kEmptyString; + case (indexHeapNumber: HeapNumber): { + assert(IsNumberNormalized(indexHeapNumber)); + // Valid string indices fit into Smi range, so HeapNumber index is + // definitely an out of bounds case. + goto IfOutOfBounds; } } +} - // ES6 #sec-string.prototype.charcodeat - transitioning javascript builtin StringPrototypeCharCodeAt( - js-implicit context: NativeContext, - receiver: JSAny)(position: JSAny): JSAny { - try { - GenerateStringAt(receiver, position, 'String.prototype.charCodeAt') - otherwise IfInBounds, IfOutOfBounds; - } - label IfInBounds(string: String, index: uintptr, _length: uintptr) { - const code: int32 = StringCharCodeAt(string, index); - return Convert(code); - } - label IfOutOfBounds { - return kNaN; - } +// ES6 #sec-string.prototype.charat +transitioning javascript builtin StringPrototypeCharAt( + js-implicit context: NativeContext, + receiver: JSAny)(position: JSAny): JSAny { + try { + GenerateStringAt(receiver, position, 'String.prototype.charAt') + otherwise IfInBounds, IfOutOfBounds; + } label IfInBounds(string: String, index: uintptr, _length: uintptr) { + const code: int32 = StringCharCodeAt(string, index); + return StringFromSingleCharCode(code); + } label IfOutOfBounds { + return kEmptyString; } +} - // ES6 #sec-string.prototype.codepointat - transitioning javascript builtin StringPrototypeCodePointAt( - js-implicit context: NativeContext, - receiver: JSAny)(position: JSAny): JSAny { - try { - GenerateStringAt(receiver, position, 'String.prototype.codePointAt') - otherwise IfInBounds, IfOutOfBounds; - } - label IfInBounds(string: String, index: uintptr, length: uintptr) { - // This is always a call to a builtin from Javascript, so we need to - // produce UTF32. - const code: int32 = LoadSurrogatePairAt( - string, Signed(length), Signed(index), UnicodeEncoding::UTF32); - return Convert(code); - } - label IfOutOfBounds { - return Undefined; - } +// ES6 #sec-string.prototype.charcodeat +transitioning javascript builtin StringPrototypeCharCodeAt( + js-implicit context: NativeContext, + receiver: JSAny)(position: JSAny): JSAny { + try { + GenerateStringAt(receiver, position, 'String.prototype.charCodeAt') + otherwise IfInBounds, IfOutOfBounds; + } label IfInBounds(string: String, index: uintptr, _length: uintptr) { + const code: int32 = StringCharCodeAt(string, index); + return Convert(code); + } label IfOutOfBounds { + return kNaN; } +} - // ES6 String.prototype.concat(...args) - // ES6 #sec-string.prototype.concat - transitioning javascript builtin StringPrototypeConcat( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - // Check that {receiver} is coercible to Object and convert it to a String. - let string: String = ToThisString(receiver, 'String.prototype.concat'); - - // Concatenate all the arguments passed to this builtin. - const length: intptr = Convert(arguments.length); - for (let i: intptr = 0; i < length; i++) { - const temp: String = ToString_Inline(arguments[i]); - string = string + temp; - } - return string; +// ES6 #sec-string.prototype.codepointat +transitioning javascript builtin StringPrototypeCodePointAt( + js-implicit context: NativeContext, + receiver: JSAny)(position: JSAny): JSAny { + try { + GenerateStringAt(receiver, position, 'String.prototype.codePointAt') + otherwise IfInBounds, IfOutOfBounds; + } label IfInBounds(string: String, index: uintptr, length: uintptr) { + // This is always a call to a builtin from Javascript, so we need to + // produce UTF32. + const code: int32 = LoadSurrogatePairAt( + string, Signed(length), Signed(index), UnicodeEncoding::UTF32); + return Convert(code); + } label IfOutOfBounds { + return Undefined; + } +} + +// ES6 String.prototype.concat(...args) +// ES6 #sec-string.prototype.concat +transitioning javascript builtin StringPrototypeConcat( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + // Check that {receiver} is coercible to Object and convert it to a String. + let string: String = ToThisString(receiver, 'String.prototype.concat'); + + // Concatenate all the arguments passed to this builtin. + const length: intptr = Convert(arguments.length); + for (let i: intptr = 0; i < length; i++) { + const temp: String = ToString_Inline(arguments[i]); + string = string + temp; } + return string; +} - extern transitioning runtime - SymbolDescriptiveString(implicit context: Context)(Symbol): String; - - // ES #sec-string-constructor - // https://tc39.github.io/ecma262/#sec-string-constructor - transitioning javascript builtin StringConstructor( - js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny, - target: JSFunction)(...arguments): JSAny { - const length: intptr = Convert(arguments.length); - let s: String; - // 1. If no arguments were passed to this function invocation, let s be "". - if (length == 0) { - s = EmptyStringConstant(); - } else { - // 2. Else, - // 2. a. If NewTarget is undefined and Type(value) is Symbol, return - // SymbolDescriptiveString(value). - if (newTarget == Undefined) { - typeswitch (arguments[0]) { - case (value: Symbol): { - return SymbolDescriptiveString(value); - } - case (JSAny): { - } +extern transitioning runtime +SymbolDescriptiveString(implicit context: Context)(Symbol): String; + +// ES #sec-string-constructor +// https://tc39.github.io/ecma262/#sec-string-constructor +transitioning javascript builtin StringConstructor( + js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny, + target: JSFunction)(...arguments): JSAny { + const length: intptr = Convert(arguments.length); + let s: String; + // 1. If no arguments were passed to this function invocation, let s be "". + if (length == 0) { + s = EmptyStringConstant(); + } else { + // 2. Else, + // 2. a. If NewTarget is undefined and Type(value) is Symbol, return + // SymbolDescriptiveString(value). + if (newTarget == Undefined) { + typeswitch (arguments[0]) { + case (value: Symbol): { + return SymbolDescriptiveString(value); + } + case (JSAny): { } } - // 2. b. Let s be ? ToString(value). - s = ToString_Inline(arguments[0]); - } - // 3. If NewTarget is undefined, return s. - if (newTarget == Undefined) { - return s; } - // 4. Return ! StringCreate(s, ? GetPrototypeFromConstructor(NewTarget, - // "%String.prototype%")). - const map = GetDerivedMap(target, UnsafeCast(newTarget)); - const obj = - UnsafeCast(AllocateFastOrSlowJSObjectFromMap(map)); - obj.value = s; - return obj; + // 2. b. Let s be ? ToString(value). + s = ToString_Inline(arguments[0]); } - - transitioning builtin StringAddConvertLeft(implicit context: Context)( - left: JSAny, right: String): String { - return ToStringImpl(context, ToPrimitiveDefault(left)) + right; + // 3. If NewTarget is undefined, return s. + if (newTarget == Undefined) { + return s; } + // 4. Return ! StringCreate(s, ? GetPrototypeFromConstructor(NewTarget, + // "%String.prototype%")). + const map = GetDerivedMap(target, UnsafeCast(newTarget)); + const obj = + UnsafeCast(AllocateFastOrSlowJSObjectFromMap(map)); + obj.value = s; + return obj; +} - transitioning builtin StringAddConvertRight(implicit context: Context)( - left: String, right: JSAny): String { - return left + ToStringImpl(context, ToPrimitiveDefault(right)); - } +transitioning builtin StringAddConvertLeft(implicit context: Context)( + left: JSAny, right: String): String { + return ToStringImpl(context, ToPrimitiveDefault(left)) + right; +} - builtin StringCharAt(implicit context: Context)( - receiver: String, position: uintptr): String { - // Load the character code at the {position} from the {receiver}. - const code: int32 = StringCharCodeAt(receiver, position); - // And return the single character string with only that {code} - return StringFromSingleCharCode(code); - } +transitioning builtin StringAddConvertRight(implicit context: Context)( + left: String, right: JSAny): String { + return left + ToStringImpl(context, ToPrimitiveDefault(right)); +} + +builtin StringCharAt(implicit context: Context)( + receiver: String, position: uintptr): String { + // Load the character code at the {position} from the {receiver}. + const code: int32 = StringCharCodeAt(receiver, position); + // And return the single character string with only that {code} + return StringFromSingleCharCode(code); +} } diff --git a/deps/v8/src/builtins/builtins-trace.cc b/deps/v8/src/builtins/builtins-trace.cc index b067bb0249f5d1..e98b38d1a5d157 100644 --- a/deps/v8/src/builtins/builtins-trace.cc +++ b/deps/v8/src/builtins/builtins-trace.cc @@ -10,6 +10,10 @@ #include "src/logging/counters.h" #include "src/objects/objects-inl.h" +#if defined(V8_USE_PERFETTO) +#include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h" +#endif + namespace v8 { namespace internal { @@ -69,6 +73,7 @@ class MaybeUtf8 { std::unique_ptr allocated_; }; +#if !defined(V8_USE_PERFETTO) class JsonTraceValue : public ConvertableToTraceFormat { public: explicit JsonTraceValue(Isolate* isolate, Handle object) { @@ -91,6 +96,7 @@ const uint8_t* GetCategoryGroupEnabled(Isolate* isolate, MaybeUtf8 category(isolate, string); return TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(*category); } +#endif // !defined(V8_USE_PERFETTO) #undef MAX_STACK_LENGTH @@ -104,8 +110,15 @@ BUILTIN(IsTraceCategoryEnabled) { THROW_NEW_ERROR_RETURN_FAILURE( isolate, NewTypeError(MessageTemplate::kTraceEventCategoryError)); } - return isolate->heap()->ToBoolean( - *GetCategoryGroupEnabled(isolate, Handle::cast(category))); + bool enabled; +#if defined(V8_USE_PERFETTO) + MaybeUtf8 category_str(isolate, Handle::cast(category)); + perfetto::DynamicCategory dynamic_category{*category_str}; + enabled = TRACE_EVENT_CATEGORY_ENABLED(dynamic_category); +#else + enabled = *GetCategoryGroupEnabled(isolate, Handle::cast(category)); +#endif + return isolate->heap()->ToBoolean(enabled); } // Builtins::kTrace(phase, category, name, id, data) : bool @@ -118,18 +131,23 @@ BUILTIN(Trace) { Handle id_arg = args.atOrUndefined(isolate, 4); Handle data_arg = args.atOrUndefined(isolate, 5); - const uint8_t* category_group_enabled = - GetCategoryGroupEnabled(isolate, Handle::cast(category)); - // Exit early if the category group is not enabled. - if (!*category_group_enabled) { +#if defined(V8_USE_PERFETTO) + MaybeUtf8 category_str(isolate, Handle::cast(category)); + perfetto::DynamicCategory dynamic_category{*category_str}; + if (!TRACE_EVENT_CATEGORY_ENABLED(dynamic_category)) return ReadOnlyRoots(isolate).false_value(); - } +#else + const uint8_t* category_group_enabled = + GetCategoryGroupEnabled(isolate, Handle::cast(category)); + if (!*category_group_enabled) return ReadOnlyRoots(isolate).false_value(); +#endif if (!phase_arg->IsNumber()) { THROW_NEW_ERROR_RETURN_FAILURE( isolate, NewTypeError(MessageTemplate::kTraceEventPhaseError)); } + char phase = static_cast(DoubleToInt32(phase_arg->Number())); if (!category->IsString()) { THROW_NEW_ERROR_RETURN_FAILURE( isolate, NewTypeError(MessageTemplate::kTraceEventCategoryError)); @@ -160,32 +178,67 @@ BUILTIN(Trace) { // We support passing one additional trace event argument with the // name "data". Any JSON serializable value may be passed. static const char* arg_name = "data"; + Handle arg_json; int32_t num_args = 0; - uint8_t arg_type; - uint64_t arg_value; - if (!data_arg->IsUndefined(isolate)) { // Serializes the data argument as a JSON string, which is then // copied into an object. This eliminates duplicated code but // could have perf costs. It is also subject to all the same // limitations as JSON.stringify() as it relates to circular // references and value limitations (e.g. BigInt is not supported). - Handle result; ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, result, + isolate, arg_json, JsonStringify(isolate, data_arg, isolate->factory()->undefined_value(), isolate->factory()->undefined_value())); - std::unique_ptr traced_value; - traced_value.reset( - new JsonTraceValue(isolate, Handle::cast(result))); - tracing::SetTraceValue(std::move(traced_value), &arg_type, &arg_value); num_args++; } +#if defined(V8_USE_PERFETTO) + auto trace_args = [&](perfetto::EventContext ctx) { + // TODO(skyostil): Use interned names to reduce trace size. + if (phase != TRACE_EVENT_PHASE_END) { + ctx.event()->set_name(*name); + } + if (num_args) { + MaybeUtf8 arg_contents(isolate, Handle::cast(arg_json)); + auto annotation = ctx.event()->add_debug_annotations(); + annotation->set_name(arg_name); + annotation->set_legacy_json_value(*arg_contents); + } + if (flags & TRACE_EVENT_FLAG_HAS_ID) { + auto legacy_event = ctx.event()->set_legacy_event(); + legacy_event->set_global_id(id); + } + }; + + switch (phase) { + case TRACE_EVENT_PHASE_BEGIN: + TRACE_EVENT_BEGIN(dynamic_category, nullptr, trace_args); + break; + case TRACE_EVENT_PHASE_END: + TRACE_EVENT_END(dynamic_category, trace_args); + break; + case TRACE_EVENT_PHASE_INSTANT: + TRACE_EVENT_INSTANT(dynamic_category, nullptr, trace_args); + break; + default: + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError(MessageTemplate::kTraceEventPhaseError)); + } + +#else // !defined(V8_USE_PERFETTO) + uint8_t arg_type; + uint64_t arg_value; + if (num_args) { + std::unique_ptr traced_value( + new JsonTraceValue(isolate, Handle::cast(arg_json))); + tracing::SetTraceValue(std::move(traced_value), &arg_type, &arg_value); + } + TRACE_EVENT_API_ADD_TRACE_EVENT( - static_cast(DoubleToInt32(phase_arg->Number())), - category_group_enabled, *name, tracing::kGlobalScope, id, tracing::kNoId, - num_args, &arg_name, &arg_type, &arg_value, flags); + phase, category_group_enabled, *name, tracing::kGlobalScope, id, + tracing::kNoId, num_args, &arg_name, &arg_type, &arg_value, flags); +#endif // !defined(V8_USE_PERFETTO) return ReadOnlyRoots(isolate).true_value(); } diff --git a/deps/v8/src/builtins/builtins-typed-array-gen.cc b/deps/v8/src/builtins/builtins-typed-array-gen.cc index 021a0e9240ceef..a6d3887ad31fa0 100644 --- a/deps/v8/src/builtins/builtins-typed-array-gen.cc +++ b/deps/v8/src/builtins/builtins-typed-array-gen.cc @@ -65,8 +65,9 @@ TNode TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer( StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset, byte_length); - StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBackingStoreOffset, - IntPtrConstant(0)); + StoreJSArrayBufferBackingStore( + buffer, + EncodeExternalPointer(ReinterpretCast(IntPtrConstant(0)))); if (V8_ARRAY_BUFFER_EXTENSION_BOOL) { StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kExtensionOffset, IntPtrConstant(0)); @@ -239,7 +240,7 @@ TNode TypedArrayBuiltinsAssembler::GetBuffer( TNode buffer = LoadJSArrayBufferViewBuffer(array); GotoIf(IsDetachedBuffer(buffer), &call_runtime); - TNode backing_store = LoadJSArrayBufferBackingStore(buffer); + TNode backing_store = LoadJSArrayBufferBackingStorePtr(buffer); GotoIf(WordEqual(backing_store, IntPtrConstant(0)), &call_runtime); var_result = buffer; Goto(&done); @@ -397,8 +398,8 @@ void TypedArrayBuiltinsAssembler::SetJSTypedArrayOnHeapDataPtr( } StoreObjectField(holder, JSTypedArray::kBasePointerOffset, base); - StoreObjectFieldNoWriteBarrier( - holder, JSTypedArray::kExternalPointerOffset, offset); + StoreJSTypedArrayExternalPointer( + holder, EncodeExternalPointer(ReinterpretCast(offset))); } void TypedArrayBuiltinsAssembler::SetJSTypedArrayOffHeapDataPtr( @@ -407,8 +408,7 @@ void TypedArrayBuiltinsAssembler::SetJSTypedArrayOffHeapDataPtr( SmiConstant(0)); base = RawPtrAdd(base, Signed(offset)); - StoreObjectFieldNoWriteBarrier( - holder, JSTypedArray::kExternalPointerOffset, base); + StoreJSTypedArrayExternalPointer(holder, EncodeExternalPointer(base)); } void TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromNumeric( diff --git a/deps/v8/src/builtins/builtins-wasm-gen.cc b/deps/v8/src/builtins/builtins-wasm-gen.cc index 770f5da97b3d07..28efa39c67dc10 100644 --- a/deps/v8/src/builtins/builtins-wasm-gen.cc +++ b/deps/v8/src/builtins/builtins-wasm-gen.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "src/builtins/builtins-wasm-gen.h" + #include "src/builtins/builtins-utils-gen.h" #include "src/codegen/code-stub-assembler.h" #include "src/codegen/interface-descriptors.h" @@ -12,71 +14,45 @@ namespace v8 { namespace internal { -class WasmBuiltinsAssembler : public CodeStubAssembler { - public: - explicit WasmBuiltinsAssembler(compiler::CodeAssemblerState* state) - : CodeStubAssembler(state) {} - - protected: - TNode LoadInstanceFromFrame() { - return CAST( - LoadFromParentFrame(WasmCompiledFrameConstants::kWasmInstanceOffset)); - } - - TNode LoadContextFromInstance(TNode instance) { - return CAST(Load(MachineType::AnyTagged(), instance, - IntPtrConstant(WasmInstanceObject::kNativeContextOffset - - kHeapObjectTag))); - } - - TNode SmiFromUint32WithSaturation(TNode value, uint32_t max) { - DCHECK_LE(max, static_cast(Smi::kMaxValue)); - TNode capped_value = SelectConstant( - Uint32LessThan(value, Uint32Constant(max)), value, Uint32Constant(max)); - return SmiFromUint32(capped_value); - } -}; - -TF_BUILTIN(WasmStackGuard, WasmBuiltinsAssembler) { - TNode instance = LoadInstanceFromFrame(); - TNode context = LoadContextFromInstance(instance); - TailCallRuntime(Runtime::kWasmStackGuard, context); +TNode WasmBuiltinsAssembler::LoadInstanceFromFrame() { + return CAST(LoadFromParentFrame(WasmFrameConstants::kWasmInstanceOffset)); } -TF_BUILTIN(WasmStackOverflow, WasmBuiltinsAssembler) { - TNode instance = LoadInstanceFromFrame(); - TNode context = LoadContextFromInstance(instance); - TailCallRuntime(Runtime::kThrowWasmStackOverflow, context); +TNode WasmBuiltinsAssembler::LoadContextFromInstance( + TNode instance) { + return CAST(Load(MachineType::AnyTagged(), instance, + IntPtrConstant(WasmInstanceObject::kNativeContextOffset - + kHeapObjectTag))); } -TF_BUILTIN(WasmThrow, WasmBuiltinsAssembler) { - TNode exception = CAST(Parameter(Descriptor::kException)); - TNode instance = LoadInstanceFromFrame(); - TNode context = LoadContextFromInstance(instance); - TailCallRuntime(Runtime::kThrow, context, exception); +TNode WasmBuiltinsAssembler::LoadTablesFromInstance( + TNode instance) { + return LoadObjectField(instance, + WasmInstanceObject::kTablesOffset); } -TF_BUILTIN(WasmRethrow, WasmBuiltinsAssembler) { - TNode exception = CAST(Parameter(Descriptor::kException)); - TNode instance = LoadInstanceFromFrame(); - TNode context = LoadContextFromInstance(instance); - - Label nullref(this, Label::kDeferred); - GotoIf(TaggedEqual(NullConstant(), exception), &nullref); +TNode WasmBuiltinsAssembler::LoadExternalFunctionsFromInstance( + TNode instance) { + return LoadObjectField( + instance, WasmInstanceObject::kWasmExternalFunctionsOffset); +} - TailCallRuntime(Runtime::kReThrow, context, exception); +TNode WasmBuiltinsAssembler::SmiFromUint32WithSaturation( + TNode value, uint32_t max) { + DCHECK_LE(max, static_cast(Smi::kMaxValue)); + TNode capped_value = SelectConstant( + Uint32LessThan(value, Uint32Constant(max)), value, Uint32Constant(max)); + return SmiFromUint32(capped_value); +} - BIND(&nullref); - MessageTemplate message_id = MessageTemplate::kWasmTrapRethrowNullRef; - TailCallRuntime(Runtime::kThrowWasmError, context, - SmiConstant(static_cast(message_id))); +TF_BUILTIN(WasmFloat32ToNumber, WasmBuiltinsAssembler) { + TNode val = UncheckedCast(Parameter(Descriptor::kValue)); + Return(ChangeFloat32ToTagged(val)); } -TF_BUILTIN(WasmTraceMemory, WasmBuiltinsAssembler) { - TNode info = CAST(Parameter(Descriptor::kMemoryTracingInfo)); - TNode instance = LoadInstanceFromFrame(); - TNode context = LoadContextFromInstance(instance); - TailCallRuntime(Runtime::kWasmTraceMemory, context, info); +TF_BUILTIN(WasmFloat64ToNumber, WasmBuiltinsAssembler) { + TNode val = UncheckedCast(Parameter(Descriptor::kValue)); + Return(ChangeFloat64ToTagged(val)); } TF_BUILTIN(WasmAtomicNotify, WasmBuiltinsAssembler) { @@ -210,26 +186,6 @@ TF_BUILTIN(WasmI64AtomicWait64, WasmBuiltinsAssembler) { Return(Unsigned(SmiToInt32(result_smi))); } -TF_BUILTIN(WasmMemoryGrow, WasmBuiltinsAssembler) { - TNode num_pages = - UncheckedCast(Parameter(Descriptor::kNumPages)); - Label num_pages_out_of_range(this, Label::kDeferred); - - TNode num_pages_fits_in_smi = - IsValidPositiveSmi(ChangeInt32ToIntPtr(num_pages)); - GotoIfNot(num_pages_fits_in_smi, &num_pages_out_of_range); - - TNode num_pages_smi = SmiFromInt32(num_pages); - TNode instance = LoadInstanceFromFrame(); - TNode context = LoadContextFromInstance(instance); - TNode ret_smi = CAST( - CallRuntime(Runtime::kWasmMemoryGrow, context, instance, num_pages_smi)); - Return(SmiToInt32(ret_smi)); - - BIND(&num_pages_out_of_range); - Return(Int32Constant(-1)); -} - TF_BUILTIN(WasmTableInit, WasmBuiltinsAssembler) { TNode dst_raw = UncheckedCast(Parameter(Descriptor::kDestination)); @@ -290,65 +246,42 @@ TF_BUILTIN(WasmTableCopy, WasmBuiltinsAssembler) { src_table, dst, src, size); } -TF_BUILTIN(WasmTableGet, WasmBuiltinsAssembler) { - TNode entry_index = - UncheckedCast(Parameter(Descriptor::kEntryIndex)); +TF_BUILTIN(WasmAllocateArray, WasmBuiltinsAssembler) { TNode instance = LoadInstanceFromFrame(); - TNode context = LoadContextFromInstance(instance); - Label entry_index_out_of_range(this, Label::kDeferred); - - TNode entry_index_fits_in_smi = - IsValidPositiveSmi(ChangeInt32ToIntPtr(entry_index)); - GotoIfNot(entry_index_fits_in_smi, &entry_index_out_of_range); - - TNode entry_index_smi = SmiFromInt32(entry_index); - TNode table_index_smi = CAST(Parameter(Descriptor::kTableIndex)); - - TailCallRuntime(Runtime::kWasmFunctionTableGet, context, instance, - table_index_smi, entry_index_smi); - - BIND(&entry_index_out_of_range); - MessageTemplate message_id = - wasm::WasmOpcodes::TrapReasonToMessageId(wasm::kTrapTableOutOfBounds); - TailCallRuntime(Runtime::kThrowWasmError, context, - SmiConstant(static_cast(message_id))); + TNode map_index = CAST(Parameter(Descriptor::kMapIndex)); + TNode length = CAST(Parameter(Descriptor::kLength)); + TNode element_size = CAST(Parameter(Descriptor::kElementSize)); + TNode maps_list = LoadObjectField( + instance, WasmInstanceObject::kManagedObjectMapsOffset); + TNode map = CAST(LoadFixedArrayElement(maps_list, map_index)); + TNode untagged_length = SmiUntag(length); + // instance_size = WasmArray::kHeaderSize + // + RoundUp(element_size * length, kObjectAlignment) + TNode raw_size = IntPtrMul(SmiUntag(element_size), untagged_length); + TNode rounded_size = + WordAnd(IntPtrAdd(raw_size, IntPtrConstant(kObjectAlignmentMask)), + IntPtrConstant(~kObjectAlignmentMask)); + TNode instance_size = + IntPtrAdd(IntPtrConstant(WasmArray::kHeaderSize), rounded_size); + TNode result = UncheckedCast(Allocate(instance_size)); + StoreMap(result, map); + StoreObjectFieldNoWriteBarrier(result, WasmArray::kLengthOffset, + TruncateIntPtrToInt32(untagged_length)); + Return(result); } -TF_BUILTIN(WasmTableSet, WasmBuiltinsAssembler) { - TNode entry_index = - UncheckedCast(Parameter(Descriptor::kEntryIndex)); +TF_BUILTIN(WasmAllocateStruct, WasmBuiltinsAssembler) { TNode instance = LoadInstanceFromFrame(); - TNode context = LoadContextFromInstance(instance); - Label entry_index_out_of_range(this, Label::kDeferred); - - TNode entry_index_fits_in_smi = - IsValidPositiveSmi(ChangeInt32ToIntPtr(entry_index)); - GotoIfNot(entry_index_fits_in_smi, &entry_index_out_of_range); - - TNode entry_index_smi = SmiFromInt32(entry_index); - TNode table_index_smi = CAST(Parameter(Descriptor::kTableIndex)); - TNode value = CAST(Parameter(Descriptor::kValue)); - TailCallRuntime(Runtime::kWasmFunctionTableSet, context, instance, - table_index_smi, entry_index_smi, value); - - BIND(&entry_index_out_of_range); - MessageTemplate message_id = - wasm::WasmOpcodes::TrapReasonToMessageId(wasm::kTrapTableOutOfBounds); - TailCallRuntime(Runtime::kThrowWasmError, context, - SmiConstant(static_cast(message_id))); + TNode map_index = CAST(Parameter(Descriptor::kMapIndex)); + TNode maps_list = LoadObjectField( + instance, WasmInstanceObject::kManagedObjectMapsOffset); + TNode map = CAST(LoadFixedArrayElement(maps_list, map_index)); + TNode instance_size = + TimesTaggedSize(LoadMapInstanceSizeInWords(map)); + TNode result = UncheckedCast(Allocate(instance_size)); + StoreMap(result, map); + Return(result); } -#define DECLARE_THROW_RUNTIME_FN(name) \ - TF_BUILTIN(ThrowWasm##name, WasmBuiltinsAssembler) { \ - TNode instance = LoadInstanceFromFrame(); \ - TNode context = LoadContextFromInstance(instance); \ - MessageTemplate message_id = \ - wasm::WasmOpcodes::TrapReasonToMessageId(wasm::k##name); \ - TailCallRuntime(Runtime::kThrowWasmError, context, \ - SmiConstant(static_cast(message_id))); \ - } -FOREACH_WASM_TRAPREASON(DECLARE_THROW_RUNTIME_FN) -#undef DECLARE_THROW_RUNTIME_FN - } // namespace internal } // namespace v8 diff --git a/deps/v8/src/builtins/builtins-wasm-gen.h b/deps/v8/src/builtins/builtins-wasm-gen.h new file mode 100644 index 00000000000000..3740560666d5ae --- /dev/null +++ b/deps/v8/src/builtins/builtins-wasm-gen.h @@ -0,0 +1,35 @@ +// Copyright 2020 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. + +#ifndef V8_BUILTINS_BUILTINS_WASM_GEN_H_ +#define V8_BUILTINS_BUILTINS_WASM_GEN_H_ + +#include "src/codegen/code-stub-assembler.h" + +namespace v8 { +namespace internal { + +class WasmBuiltinsAssembler : public CodeStubAssembler { + public: + explicit WasmBuiltinsAssembler(compiler::CodeAssemblerState* state) + : CodeStubAssembler(state) {} + + TNode LoadInstanceFromFrame(); + + TNode LoadContextFromInstance( + TNode instance); + + TNode LoadTablesFromInstance(TNode instance); + + TNode LoadExternalFunctionsFromInstance( + TNode instance); + + protected: + TNode SmiFromUint32WithSaturation(TNode value, uint32_t max); +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_BUILTINS_BUILTINS_WASM_GEN_H_ diff --git a/deps/v8/src/builtins/builtins-weak-refs.cc b/deps/v8/src/builtins/builtins-weak-refs.cc index e75c7fae9d1d30..d5cceda4541f89 100644 --- a/deps/v8/src/builtins/builtins-weak-refs.cc +++ b/deps/v8/src/builtins/builtins-weak-refs.cc @@ -36,7 +36,7 @@ BUILTIN(FinalizationRegistryConstructor) { finalization_registry->set_native_context(*isolate->native_context()); finalization_registry->set_cleanup(*cleanup); finalization_registry->set_flags( - JSFinalizationRegistry::ScheduledForCleanupField::encode(false)); + JSFinalizationRegistry::ScheduledForCleanupBit::encode(false)); DCHECK(finalization_registry->active_cells().IsUndefined(isolate)); DCHECK(finalization_registry->cleared_cells().IsUndefined(isolate)); @@ -122,61 +122,6 @@ BUILTIN(FinalizationRegistryUnregister) { return *isolate->factory()->ToBoolean(success); } -BUILTIN(FinalizationRegistryCleanupSome) { - HandleScope scope(isolate); - const char* method_name = "FinalizationRegistry.prototype.cleanupSome"; - - // 1. Let finalizationGroup be the this value. - // - // 2. If Type(finalizationGroup) is not Object, throw a TypeError - // exception. - // - // 3. If finalizationGroup does not have a [[Cells]] internal slot, - // throw a TypeError exception. - CHECK_RECEIVER(JSFinalizationRegistry, finalization_registry, method_name); - - Handle callback(finalization_registry->cleanup(), isolate); - Handle callback_obj = args.atOrUndefined(isolate, 1); - - // 4. If callback is not undefined and IsCallable(callback) is - // false, throw a TypeError exception. - if (!callback_obj->IsUndefined(isolate)) { - if (!callback_obj->IsCallable()) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, - NewTypeError(MessageTemplate::kWeakRefsCleanupMustBeCallable)); - } - callback = callback_obj; - } - - // Don't do set_scheduled_for_cleanup(false); we still have the task - // scheduled. - if (JSFinalizationRegistry::Cleanup(isolate, finalization_registry, callback) - .IsNothing()) { - DCHECK(isolate->has_pending_exception()); - return ReadOnlyRoots(isolate).exception(); - } - return ReadOnlyRoots(isolate).undefined_value(); -} - -BUILTIN(FinalizationRegistryCleanupIteratorNext) { - HandleScope scope(isolate); - CHECK_RECEIVER(JSFinalizationRegistryCleanupIterator, iterator, "next"); - - Handle finalization_registry( - iterator->finalization_registry(), isolate); - if (!finalization_registry->NeedsCleanup()) { - return *isolate->factory()->NewJSIteratorResult( - handle(ReadOnlyRoots(isolate).undefined_value(), isolate), true); - } - Handle holdings = - handle(JSFinalizationRegistry::PopClearedCellHoldings( - finalization_registry, isolate), - isolate); - - return *isolate->factory()->NewJSIteratorResult(holdings, false); -} - BUILTIN(WeakRefConstructor) { HandleScope scope(isolate); Handle target = args.target(); diff --git a/deps/v8/src/builtins/cast.tq b/deps/v8/src/builtins/cast.tq index cb7ff412dea2eb..dfac2035784ee3 100644 --- a/deps/v8/src/builtins/cast.tq +++ b/deps/v8/src/builtins/cast.tq @@ -2,44 +2,47 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -extern macro IsCallable(HeapObject): bool; +extern macro IsAllocationSite(HeapObject): bool; +extern macro IsBigInt(HeapObject): bool; extern macro IsConstructor(HeapObject): bool; +extern macro IsContext(HeapObject): bool; +extern macro IsCustomElementsReceiverInstanceType(int32): bool; +extern macro IsExtensibleMap(Map): bool; +extern macro IsFeedbackCell(HeapObject): bool; extern macro IsFeedbackVector(HeapObject): bool; +extern macro IsFixedArray(HeapObject): bool; +extern macro IsHeapNumber(HeapObject): bool; +extern macro IsJSAggregateError(HeapObject): bool; extern macro IsJSArray(HeapObject): bool; -extern macro IsJSProxy(HeapObject): bool; -extern macro IsJSRegExp(HeapObject): bool; -extern macro IsJSRegExpStringIterator(HeapObject): bool; -extern macro IsMap(HeapObject): bool; -extern macro IsJSFunction(HeapObject): bool; +extern macro IsJSArrayMap(Map): bool; extern macro IsJSBoundFunction(HeapObject): bool; +extern macro IsJSFinalizationRegistry(HeapObject): bool; +extern macro IsJSFunction(HeapObject): bool; extern macro IsJSObject(HeapObject): bool; +extern macro IsJSPrimitiveWrapper(HeapObject): bool; extern macro IsJSPromise(HeapObject): bool; -extern macro IsJSTypedArray(HeapObject): bool; -extern macro IsNumberDictionary(HeapObject): bool; -extern macro IsContext(HeapObject): bool; -extern macro IsNativeContext(HeapObject): bool; +extern macro IsJSProxy(HeapObject): bool; extern macro IsJSReceiver(HeapObject): bool; -extern macro TaggedIsCallable(Object): bool; -extern macro IsHeapNumber(HeapObject): bool; -extern macro IsBigInt(HeapObject): bool; -extern macro IsFixedArray(HeapObject): bool; +extern macro IsJSRegExp(HeapObject): bool; +extern macro IsJSRegExpStringIterator(HeapObject): bool; +extern macro IsJSTypedArray(HeapObject): bool; +extern macro IsMap(HeapObject): bool; extern macro IsName(HeapObject): bool; -extern macro IsPrivateSymbol(HeapObject): bool; -extern macro IsNumber(Object): bool; +extern macro IsNativeContext(HeapObject): bool; +extern macro IsNumberDictionary(HeapObject): bool; extern macro IsNumberNormalized(Number): bool; -extern macro IsSafeInteger(Object): bool; +extern macro IsNumber(Object): bool; extern macro IsOddball(HeapObject): bool; -extern macro IsSymbol(HeapObject): bool; -extern macro IsJSArrayMap(Map): bool; -extern macro IsExtensibleMap(Map): bool; -extern macro IsJSPrimitiveWrapper(HeapObject): bool; +extern macro IsPrivateSymbol(HeapObject): bool; extern macro IsPromiseCapability(HeapObject): bool; +extern macro IsPromiseFulfillReactionJobTask(HeapObject): bool; extern macro IsPromiseReaction(HeapObject): bool; extern macro IsPromiseReactionJobTask(HeapObject): bool; extern macro IsPromiseRejectReactionJobTask(HeapObject): bool; -extern macro IsPromiseFulfillReactionJobTask(HeapObject): bool; +extern macro IsSafeInteger(Object): bool; extern macro IsSharedFunctionInfo(HeapObject): bool; -extern macro IsCustomElementsReceiverInstanceType(int32): bool; +extern macro IsSymbol(HeapObject): bool; +extern macro IsTuple2(HeapObject): bool; extern macro HeapObjectToJSDataView(HeapObject): JSDataView labels CastError; @@ -57,6 +60,8 @@ extern macro TaggedToPositiveSmi(Object): PositiveSmi labels CastError; extern macro TaggedToDirectString(Object): DirectString labels CastError; +extern macro HeapObjectToJSAggregateError(HeapObject): JSAggregateError + labels CastError; extern macro HeapObjectToJSArray(HeapObject): JSArray labels CastError; extern macro HeapObjectToCallable(HeapObject): Callable @@ -278,6 +283,12 @@ Cast(o: HeapObject): Undefined return Cast(o) otherwise CastError; } +Cast(o: HeapObject): AllocationSite + labels CastError { + if (IsAllocationSite(o)) return %RawDownCast(o); + goto CastError; +} + Cast(o: HeapObject): FixedArray labels CastError { return HeapObjectToFixedArray(o) otherwise CastError; @@ -365,6 +376,11 @@ Cast(o: HeapObject): Undefined|Callable return HeapObjectToCallable(o) otherwise CastError; } +Cast(o: HeapObject): JSAggregateError + labels CastError { + return HeapObjectToJSAggregateError(o) otherwise CastError; +} + Cast(o: HeapObject): JSArray labels CastError { return HeapObjectToJSArray(o) otherwise CastError; @@ -481,6 +497,12 @@ Cast(implicit context: Context)(o: HeapObject): Map goto CastError; } +Cast(implicit context: Context)(o: HeapObject): FeedbackCell + labels CastError { + if (IsFeedbackCell(o)) return %RawDownCast(o); + goto CastError; +} + Cast(implicit context: Context)(o: HeapObject): FeedbackVector labels CastError { if (IsFeedbackVector(o)) return %RawDownCast(o); @@ -735,8 +757,21 @@ Cast(o: HeapObject): JSPromise labels CastError { goto CastError; } +Cast(o: HeapObject): + JSFinalizationRegistry labels CastError { + if (IsJSFinalizationRegistry(o)) { + return %RawDownCast(o); + } + goto CastError; +} + UnsafeCast(implicit context: Context)(o: Object): RegExpMatchInfo { assert(Is(o)); return %RawDownCast(o); } + +macro CastOrDefault( + implicit context: Context)(x: Arg, default: Default): T|Default { + return Cast(x) otherwise return default; +} diff --git a/deps/v8/src/builtins/collections.tq b/deps/v8/src/builtins/collections.tq index 60136af63365ec..c0d311a825f159 100644 --- a/deps/v8/src/builtins/collections.tq +++ b/deps/v8/src/builtins/collections.tq @@ -5,54 +5,53 @@ #include 'src/builtins/builtins-collections-gen.h' namespace collections { - @export - macro LoadKeyValuePairNoSideEffects(implicit context: Context)(o: JSAny): - KeyValuePair labels MayHaveSideEffects { - typeswitch (o) { - case (a: FastJSArray): { - const length: Smi = a.length; - typeswitch (a.elements) { - case (elements: FixedArray): { - return KeyValuePair{ - key: length > 0 ? array::LoadElementOrUndefined(elements, 0) : - Undefined, - value: length > 1 ? array::LoadElementOrUndefined(elements, 1) : - Undefined - }; - } - case (elements: FixedDoubleArray): { - return KeyValuePair{ - key: length > 0 ? array::LoadElementOrUndefined(elements, 0) : - Undefined, - value: length > 1 ? array::LoadElementOrUndefined(elements, 1) : - Undefined - }; - } - case (FixedArrayBase): deferred { - unreachable; - } +@export +macro LoadKeyValuePairNoSideEffects(implicit context: Context)(o: JSAny): + KeyValuePair labels MayHaveSideEffects { + typeswitch (o) { + case (a: FastJSArray): { + const length: Smi = a.length; + typeswitch (a.elements) { + case (elements: FixedArray): { + return KeyValuePair{ + key: length > 0 ? array::LoadElementOrUndefined(elements, 0) : + Undefined, + value: length > 1 ? array::LoadElementOrUndefined(elements, 1) : + Undefined + }; + } + case (elements: FixedDoubleArray): { + return KeyValuePair{ + key: length > 0 ? array::LoadElementOrUndefined(elements, 0) : + Undefined, + value: length > 1 ? array::LoadElementOrUndefined(elements, 1) : + Undefined + }; + } + case (FixedArrayBase): deferred { + unreachable; } - } - case (JSReceiver): { - goto MayHaveSideEffects; - } - case (o: JSAny): deferred { - ThrowTypeError(MessageTemplate::kIteratorValueNotAnObject, o); } } - } - - @export - transitioning macro LoadKeyValuePair(implicit context: Context)(o: JSAny): - KeyValuePair { - try { - return LoadKeyValuePairNoSideEffects(o) otherwise Generic; + case (JSReceiver): { + goto MayHaveSideEffects; } - label Generic { - return KeyValuePair{ - key: GetProperty(o, Convert(0)), - value: GetProperty(o, Convert(1)) - }; + case (o: JSAny): deferred { + ThrowTypeError(MessageTemplate::kIteratorValueNotAnObject, o); } } } + +@export +transitioning macro LoadKeyValuePair(implicit context: Context)(o: JSAny): + KeyValuePair { + try { + return LoadKeyValuePairNoSideEffects(o) otherwise Generic; + } label Generic { + return KeyValuePair{ + key: GetProperty(o, Convert(0)), + value: GetProperty(o, Convert(1)) + }; + } +} +} diff --git a/deps/v8/src/builtins/console.tq b/deps/v8/src/builtins/console.tq index 48d5d08abc4f87..c0daa19b6dfb7c 100644 --- a/deps/v8/src/builtins/console.tq +++ b/deps/v8/src/builtins/console.tq @@ -3,16 +3,16 @@ // found in the LICENSE file. namespace console { - extern builtin ConsoleAssert(implicit context: - Context)(JSFunction, JSAny, int32): JSAny; +extern builtin ConsoleAssert(implicit context: Context)( + JSFunction, JSAny, int32): JSAny; - javascript builtin FastConsoleAssert( - js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny, - target: JSFunction)(...arguments): JSAny { - if (ToBoolean(arguments[0])) { - return Undefined; - } else { - tail ConsoleAssert(target, newTarget, Convert(arguments.length)); - } +javascript builtin FastConsoleAssert( + js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny, + target: JSFunction)(...arguments): JSAny { + if (ToBoolean(arguments[0])) { + return Undefined; + } else { + tail ConsoleAssert(target, newTarget, Convert(arguments.length)); } } +} diff --git a/deps/v8/src/builtins/convert.tq b/deps/v8/src/builtins/convert.tq index ee9be1d411a434..e2c11120381aec 100644 --- a/deps/v8/src/builtins/convert.tq +++ b/deps/v8/src/builtins/convert.tq @@ -90,6 +90,10 @@ FromConstexpr( c: constexpr LanguageMode): LanguageModeSmi { return %RawDownCast(SmiConstant(c)); } +FromConstexpr(c: constexpr PromiseState): + PromiseState { + return %RawDownCast(Int32Constant(c)); +} macro Convert(i: From): To { return i; @@ -209,6 +213,9 @@ Convert(n: Number): float64 { Convert(n: Number): uintptr { return ChangeUintPtrNumberToUintPtr(n); } +Convert(f: int32): float64 { + return ChangeInt32ToFloat64(f); +} Convert(f: float32): float64 { return ChangeFloat32ToFloat64(f); } diff --git a/deps/v8/src/builtins/data-view.tq b/deps/v8/src/builtins/data-view.tq index c5808dfd9de727..5f61a194728210 100644 --- a/deps/v8/src/builtins/data-view.tq +++ b/deps/v8/src/builtins/data-view.tq @@ -6,877 +6,830 @@ namespace data_view { - macro MakeDataViewGetterNameString(kind: constexpr ElementsKind): String { - if constexpr (kind == ElementsKind::UINT8_ELEMENTS) { - return 'DataView.prototype.getUint8'; - } else if constexpr (kind == ElementsKind::INT8_ELEMENTS) { - return 'DataView.prototype.getInt8'; - } else if constexpr (kind == ElementsKind::UINT16_ELEMENTS) { - return 'DataView.prototype.getUint16'; - } else if constexpr (kind == ElementsKind::INT16_ELEMENTS) { - return 'DataView.prototype.getInt16'; - } else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) { - return 'DataView.prototype.getUint32'; - } else if constexpr (kind == ElementsKind::INT32_ELEMENTS) { - return 'DataView.prototype.getInt32'; - } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) { - return 'DataView.prototype.getFloat32'; - } else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) { - return 'DataView.prototype.getFloat64'; - } else if constexpr (kind == ElementsKind::BIGINT64_ELEMENTS) { - return 'DataView.prototype.getBigInt64'; - } else if constexpr (kind == ElementsKind::BIGUINT64_ELEMENTS) { - return 'DataView.prototype.getBigUint64'; - } else { - unreachable; - } +macro MakeDataViewGetterNameString(kind: constexpr ElementsKind): String { + if constexpr (kind == ElementsKind::UINT8_ELEMENTS) { + return 'DataView.prototype.getUint8'; + } else if constexpr (kind == ElementsKind::INT8_ELEMENTS) { + return 'DataView.prototype.getInt8'; + } else if constexpr (kind == ElementsKind::UINT16_ELEMENTS) { + return 'DataView.prototype.getUint16'; + } else if constexpr (kind == ElementsKind::INT16_ELEMENTS) { + return 'DataView.prototype.getInt16'; + } else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) { + return 'DataView.prototype.getUint32'; + } else if constexpr (kind == ElementsKind::INT32_ELEMENTS) { + return 'DataView.prototype.getInt32'; + } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) { + return 'DataView.prototype.getFloat32'; + } else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) { + return 'DataView.prototype.getFloat64'; + } else if constexpr (kind == ElementsKind::BIGINT64_ELEMENTS) { + return 'DataView.prototype.getBigInt64'; + } else if constexpr (kind == ElementsKind::BIGUINT64_ELEMENTS) { + return 'DataView.prototype.getBigUint64'; + } else { + unreachable; } +} - macro MakeDataViewSetterNameString(kind: constexpr ElementsKind): String { - if constexpr (kind == ElementsKind::UINT8_ELEMENTS) { - return 'DataView.prototype.setUint8'; - } else if constexpr (kind == ElementsKind::INT8_ELEMENTS) { - return 'DataView.prototype.setInt8'; - } else if constexpr (kind == ElementsKind::UINT16_ELEMENTS) { - return 'DataView.prototype.setUint16'; - } else if constexpr (kind == ElementsKind::INT16_ELEMENTS) { - return 'DataView.prototype.setInt16'; - } else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) { - return 'DataView.prototype.setUint32'; - } else if constexpr (kind == ElementsKind::INT32_ELEMENTS) { - return 'DataView.prototype.setInt32'; - } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) { - return 'DataView.prototype.setFloat32'; - } else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) { - return 'DataView.prototype.setFloat64'; - } else if constexpr (kind == ElementsKind::BIGINT64_ELEMENTS) { - return 'DataView.prototype.setBigInt64'; - } else if constexpr (kind == ElementsKind::BIGUINT64_ELEMENTS) { - return 'DataView.prototype.setBigUint64'; - } else { - unreachable; - } +macro MakeDataViewSetterNameString(kind: constexpr ElementsKind): String { + if constexpr (kind == ElementsKind::UINT8_ELEMENTS) { + return 'DataView.prototype.setUint8'; + } else if constexpr (kind == ElementsKind::INT8_ELEMENTS) { + return 'DataView.prototype.setInt8'; + } else if constexpr (kind == ElementsKind::UINT16_ELEMENTS) { + return 'DataView.prototype.setUint16'; + } else if constexpr (kind == ElementsKind::INT16_ELEMENTS) { + return 'DataView.prototype.setInt16'; + } else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) { + return 'DataView.prototype.setUint32'; + } else if constexpr (kind == ElementsKind::INT32_ELEMENTS) { + return 'DataView.prototype.setInt32'; + } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) { + return 'DataView.prototype.setFloat32'; + } else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) { + return 'DataView.prototype.setFloat64'; + } else if constexpr (kind == ElementsKind::BIGINT64_ELEMENTS) { + return 'DataView.prototype.setBigInt64'; + } else if constexpr (kind == ElementsKind::BIGUINT64_ELEMENTS) { + return 'DataView.prototype.setBigUint64'; + } else { + unreachable; } +} - macro WasDetached(view: JSArrayBufferView): bool { - return IsDetachedBuffer(view.buffer); - } +macro WasDetached(view: JSArrayBufferView): bool { + return IsDetachedBuffer(view.buffer); +} - macro ValidateDataView(context: Context, o: JSAny, method: String): - JSDataView { - try { - return Cast(o) otherwise CastError; - } - label CastError { - ThrowTypeError(MessageTemplate::kIncompatibleMethodReceiver, method); - } +macro ValidateDataView(context: Context, o: JSAny, method: String): JSDataView { + try { + return Cast(o) otherwise CastError; + } label CastError { + ThrowTypeError(MessageTemplate::kIncompatibleMethodReceiver, method); } +} - // ES6 section 24.2.4.1 get DataView.prototype.buffer - javascript builtin DataViewPrototypeGetBuffer( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSArrayBuffer { - const dataView: JSDataView = - ValidateDataView(context, receiver, 'get DataView.prototype.buffer'); - return dataView.buffer; - } +// ES6 section 24.2.4.1 get DataView.prototype.buffer +javascript builtin DataViewPrototypeGetBuffer( + js-implicit context: NativeContext, + receiver: JSAny)(...arguments): JSArrayBuffer { + const dataView: JSDataView = + ValidateDataView(context, receiver, 'get DataView.prototype.buffer'); + return dataView.buffer; +} - // ES6 section 24.2.4.2 get DataView.prototype.byteLength - javascript builtin DataViewPrototypeGetByteLength( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): Number { - const dataView: JSDataView = ValidateDataView( - context, receiver, 'get DataView.prototype.byte_length'); - if (WasDetached(dataView)) { - // TODO(bmeurer): According to the ES6 spec, we should throw a TypeError - // here if the JSArrayBuffer of the {dataView} was detached. - return 0; - } - return Convert(dataView.byte_length); - } +// ES6 section 24.2.4.2 get DataView.prototype.byteLength +javascript builtin DataViewPrototypeGetByteLength( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): Number { + const dataView: JSDataView = + ValidateDataView(context, receiver, 'get DataView.prototype.byte_length'); + if (WasDetached(dataView)) { + // TODO(bmeurer): According to the ES6 spec, we should throw a TypeError + // here if the JSArrayBuffer of the {dataView} was detached. + return 0; + } + return Convert(dataView.byte_length); +} - // ES6 section 24.2.4.3 get DataView.prototype.byteOffset - javascript builtin DataViewPrototypeGetByteOffset( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): Number { - const dataView: JSDataView = ValidateDataView( - context, receiver, 'get DataView.prototype.byte_offset'); - if (WasDetached(dataView)) { - // TODO(bmeurer): According to the ES6 spec, we should throw a TypeError - // here if the JSArrayBuffer of the {dataView} was detached. - return 0; - } - return Convert(dataView.byte_offset); - } +// ES6 section 24.2.4.3 get DataView.prototype.byteOffset +javascript builtin DataViewPrototypeGetByteOffset( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): Number { + const dataView: JSDataView = + ValidateDataView(context, receiver, 'get DataView.prototype.byte_offset'); + if (WasDetached(dataView)) { + // TODO(bmeurer): According to the ES6 spec, we should throw a TypeError + // here if the JSArrayBuffer of the {dataView} was detached. + return 0; + } + return Convert(dataView.byte_offset); +} - extern macro BitcastInt32ToFloat32(uint32): float32; - extern macro BitcastFloat32ToInt32(float32): uint32; - extern macro Float64ExtractLowWord32(float64): uint32; - extern macro Float64ExtractHighWord32(float64): uint32; - extern macro Float64InsertLowWord32(float64, uint32): float64; - extern macro Float64InsertHighWord32(float64, uint32): float64; +extern macro BitcastInt32ToFloat32(uint32): float32; +extern macro BitcastFloat32ToInt32(float32): uint32; +extern macro Float64ExtractLowWord32(float64): uint32; +extern macro Float64ExtractHighWord32(float64): uint32; +extern macro Float64InsertLowWord32(float64, uint32): float64; +extern macro Float64InsertHighWord32(float64, uint32): float64; - extern macro DataViewBuiltinsAssembler::LoadUint8(RawPtr, uintptr): uint32; - extern macro DataViewBuiltinsAssembler::LoadInt8(RawPtr, uintptr): int32; +extern macro DataViewBuiltinsAssembler::LoadUint8(RawPtr, uintptr): uint32; +extern macro DataViewBuiltinsAssembler::LoadInt8(RawPtr, uintptr): int32; - macro LoadDataView8( - buffer: JSArrayBuffer, offset: uintptr, signed: constexpr bool): Smi { - if constexpr (signed) { - return Convert(LoadInt8(buffer.backing_store, offset)); - } else { - return Convert(LoadUint8(buffer.backing_store, offset)); - } +macro LoadDataView8( + buffer: JSArrayBuffer, offset: uintptr, signed: constexpr bool): Smi { + if constexpr (signed) { + return Convert(LoadInt8(buffer.backing_store_ptr, offset)); + } else { + return Convert(LoadUint8(buffer.backing_store_ptr, offset)); } +} - macro LoadDataView16( - buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool, - signed: constexpr bool): Number { - const dataPointer: RawPtr = buffer.backing_store; +macro LoadDataView16( + buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool, + signed: constexpr bool): Number { + const dataPointer: RawPtr = buffer.backing_store_ptr; + + let b0: int32; + let b1: int32; + let result: int32; + + // Sign-extend the most significant byte by loading it as an Int8. + if (requestedLittleEndian) { + b0 = Signed(LoadUint8(dataPointer, offset)); + b1 = LoadInt8(dataPointer, offset + 1); + result = (b1 << 8) + b0; + } else { + b0 = LoadInt8(dataPointer, offset); + b1 = Signed(LoadUint8(dataPointer, offset + 1)); + result = (b0 << 8) + b1; + } + if constexpr (signed) { + return Convert(result); + } else { + // Bit-mask the higher bits to prevent sign extension if we're unsigned. + return Convert(result & 0xFFFF); + } +} - let b0: int32; - let b1: int32; - let result: int32; +macro LoadDataView32( + buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool, + kind: constexpr ElementsKind): Number { + const dataPointer: RawPtr = buffer.backing_store_ptr; - // Sign-extend the most significant byte by loading it as an Int8. - if (requestedLittleEndian) { - b0 = Signed(LoadUint8(dataPointer, offset)); - b1 = LoadInt8(dataPointer, offset + 1); - result = (b1 << 8) + b0; - } else { - b0 = LoadInt8(dataPointer, offset); - b1 = Signed(LoadUint8(dataPointer, offset + 1)); - result = (b0 << 8) + b1; - } - if constexpr (signed) { - return Convert(result); - } else { - // Bit-mask the higher bits to prevent sign extension if we're unsigned. - return Convert(result & 0xFFFF); - } + const b0: uint32 = LoadUint8(dataPointer, offset); + const b1: uint32 = LoadUint8(dataPointer, offset + 1); + const b2: uint32 = LoadUint8(dataPointer, offset + 2); + const b3: uint32 = LoadUint8(dataPointer, offset + 3); + let result: uint32; + + if (requestedLittleEndian) { + result = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; + } else { + result = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; } - macro LoadDataView32( - buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool, - kind: constexpr ElementsKind): Number { - const dataPointer: RawPtr = buffer.backing_store; + if constexpr (kind == ElementsKind::INT32_ELEMENTS) { + return Convert(Signed(result)); + } else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) { + return Convert(result); + } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) { + const floatRes: float64 = Convert(BitcastInt32ToFloat32(result)); + return Convert(floatRes); + } else { + unreachable; + } +} - const b0: uint32 = LoadUint8(dataPointer, offset); - const b1: uint32 = LoadUint8(dataPointer, offset + 1); - const b2: uint32 = LoadUint8(dataPointer, offset + 2); - const b3: uint32 = LoadUint8(dataPointer, offset + 3); - let result: uint32; +macro LoadDataViewFloat64( + buffer: JSArrayBuffer, offset: uintptr, + requestedLittleEndian: bool): Number { + const dataPointer: RawPtr = buffer.backing_store_ptr; + + const b0: uint32 = LoadUint8(dataPointer, offset); + const b1: uint32 = LoadUint8(dataPointer, offset + 1); + const b2: uint32 = LoadUint8(dataPointer, offset + 2); + const b3: uint32 = LoadUint8(dataPointer, offset + 3); + const b4: uint32 = LoadUint8(dataPointer, offset + 4); + const b5: uint32 = LoadUint8(dataPointer, offset + 5); + const b6: uint32 = LoadUint8(dataPointer, offset + 6); + const b7: uint32 = LoadUint8(dataPointer, offset + 7); + let lowWord: uint32; + let highWord: uint32; + + if (requestedLittleEndian) { + lowWord = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; + highWord = (b7 << 24) | (b6 << 16) | (b5 << 8) | b4; + } else { + highWord = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + lowWord = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7; + } + + let result: float64 = 0; + result = Float64InsertLowWord32(result, lowWord); + result = Float64InsertHighWord32(result, highWord); + + return Convert(result); +} - if (requestedLittleEndian) { - result = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; - } else { - result = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; - } +const kZeroDigitBigInt: constexpr int31 = 0; +const kOneDigitBigInt: constexpr int31 = 1; +const kTwoDigitBigInt: constexpr int31 = 2; - if constexpr (kind == ElementsKind::INT32_ELEMENTS) { - return Convert(Signed(result)); - } else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) { - return Convert(result); - } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) { - const floatRes: float64 = Convert(BitcastInt32ToFloat32(result)); - return Convert(floatRes); - } else { - unreachable; - } +// Create a BigInt on a 64-bit architecture from two 32-bit values. +macro MakeBigIntOn64Bit(implicit context: Context)( + lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt { + // 0n is represented by a zero-length BigInt. + if (lowWord == 0 && highWord == 0) { + return Convert(bigint::AllocateBigInt(kZeroDigitBigInt)); } - macro LoadDataViewFloat64( - buffer: JSArrayBuffer, offset: uintptr, - requestedLittleEndian: bool): Number { - const dataPointer: RawPtr = buffer.backing_store; - - const b0: uint32 = LoadUint8(dataPointer, offset); - const b1: uint32 = LoadUint8(dataPointer, offset + 1); - const b2: uint32 = LoadUint8(dataPointer, offset + 2); - const b3: uint32 = LoadUint8(dataPointer, offset + 3); - const b4: uint32 = LoadUint8(dataPointer, offset + 4); - const b5: uint32 = LoadUint8(dataPointer, offset + 5); - const b6: uint32 = LoadUint8(dataPointer, offset + 6); - const b7: uint32 = LoadUint8(dataPointer, offset + 7); - let lowWord: uint32; - let highWord: uint32; - - if (requestedLittleEndian) { - lowWord = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; - highWord = (b7 << 24) | (b6 << 16) | (b5 << 8) | b4; - } else { - highWord = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; - lowWord = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7; + let sign: uint32 = bigint::kPositiveSign; + const highPart: intptr = Signed(Convert(highWord)); + const lowPart: intptr = Signed(Convert(lowWord)); + let rawValue: intptr = (highPart << 32) + lowPart; + + if constexpr (signed) { + if (rawValue < 0) { + sign = bigint::kNegativeSign; + // We have to store the absolute value of rawValue in the digit. + rawValue = 0 - rawValue; } + } - let result: float64 = 0; - result = Float64InsertLowWord32(result, lowWord); - result = Float64InsertHighWord32(result, highWord); + // Allocate the BigInt and store the absolute value. + const result: MutableBigInt = + bigint::AllocateEmptyBigInt(sign, kOneDigitBigInt); + bigint::StoreBigIntDigit(result, 0, Unsigned(rawValue)); + return Convert(result); +} - return Convert(result); +// Create a BigInt on a 32-bit architecture from two 32-bit values. +macro MakeBigIntOn32Bit(implicit context: Context)( + lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt { + // 0n is represented by a zero-length BigInt. + if (lowWord == 0 && highWord == 0) { + return Convert(bigint::AllocateBigInt(kZeroDigitBigInt)); } - const kZeroDigitBigInt: constexpr int31 = 0; - const kOneDigitBigInt: constexpr int31 = 1; - const kTwoDigitBigInt: constexpr int31 = 2; - - // Create a BigInt on a 64-bit architecture from two 32-bit values. - macro MakeBigIntOn64Bit(implicit context: Context)( - lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt { - // 0n is represented by a zero-length BigInt. - if (lowWord == 0 && highWord == 0) { - return Convert(bigint::AllocateBigInt(kZeroDigitBigInt)); - } + // On a 32-bit platform, we might need 1 or 2 digits to store the number. + let needTwoDigits: bool = false; + let sign: uint32 = bigint::kPositiveSign; - let sign: uint32 = bigint::kPositiveSign; - const highPart: intptr = Signed(Convert(highWord)); - const lowPart: intptr = Signed(Convert(lowWord)); - let rawValue: intptr = (highPart << 32) + lowPart; + // We need to do some math on lowWord and highWord, + // so Convert them to int32. + let lowPart: int32 = Signed(lowWord); + let highPart: int32 = Signed(highWord); + // If highWord == 0, the number is positive, and we only need 1 digit, + // so we don't have anything to do. + // Otherwise, all cases are possible. + if (highWord != 0) { if constexpr (signed) { - if (rawValue < 0) { + // If highPart < 0, the number is always negative. + if (highPart < 0) { sign = bigint::kNegativeSign; - // We have to store the absolute value of rawValue in the digit. - rawValue = 0 - rawValue; - } - } - // Allocate the BigInt and store the absolute value. - const result: MutableBigInt = - bigint::AllocateEmptyBigInt(sign, kOneDigitBigInt); - bigint::StoreBigIntDigit(result, 0, Unsigned(rawValue)); - return Convert(result); - } - - // Create a BigInt on a 32-bit architecture from two 32-bit values. - macro MakeBigIntOn32Bit(implicit context: Context)( - lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt { - // 0n is represented by a zero-length BigInt. - if (lowWord == 0 && highWord == 0) { - return Convert(bigint::AllocateBigInt(kZeroDigitBigInt)); - } + // We have to compute the absolute value by hand. + // There will be a negative carry from the low word + // to the high word iff low != 0. + highPart = 0 - highPart; + if (lowPart != 0) { + highPart = highPart - 1; + } + lowPart = 0 - lowPart; - // On a 32-bit platform, we might need 1 or 2 digits to store the number. - let needTwoDigits: bool = false; - let sign: uint32 = bigint::kPositiveSign; - - // We need to do some math on lowWord and highWord, - // so Convert them to int32. - let lowPart: int32 = Signed(lowWord); - let highPart: int32 = Signed(highWord); - - // If highWord == 0, the number is positive, and we only need 1 digit, - // so we don't have anything to do. - // Otherwise, all cases are possible. - if (highWord != 0) { - if constexpr (signed) { - // If highPart < 0, the number is always negative. - if (highPart < 0) { - sign = bigint::kNegativeSign; - - // We have to compute the absolute value by hand. - // There will be a negative carry from the low word - // to the high word iff low != 0. - highPart = 0 - highPart; - if (lowPart != 0) { - highPart = highPart - 1; - } - lowPart = 0 - lowPart; - - // Here, highPart could be 0 again so we might have 1 or 2 digits. - if (highPart != 0) { - needTwoDigits = true; - } - - } else { - // In this case, the number is positive, and we need 2 digits. + // Here, highPart could be 0 again so we might have 1 or 2 digits. + if (highPart != 0) { needTwoDigits = true; } } else { - // In this case, the number is positive (unsigned), - // and we need 2 digits. + // In this case, the number is positive, and we need 2 digits. needTwoDigits = true; } - } - // Allocate the BigInt with the right sign and length. - let result: MutableBigInt; - if (needTwoDigits) { - result = bigint::AllocateEmptyBigInt(sign, kTwoDigitBigInt); } else { - result = bigint::AllocateEmptyBigInt(sign, kOneDigitBigInt); - } - - // Finally, write the digit(s) to the BigInt. - bigint::StoreBigIntDigit(result, 0, Unsigned(Convert(lowPart))); - if (needTwoDigits) { - bigint::StoreBigIntDigit(result, 1, Unsigned(Convert(highPart))); + // In this case, the number is positive (unsigned), + // and we need 2 digits. + needTwoDigits = true; } - return Convert(result); } - macro MakeBigInt(implicit context: Context)( - lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt { - // A BigInt digit has the platform word size, so we only need one digit - // on 64-bit platforms but may need two on 32-bit. - if constexpr (Is64()) { - return MakeBigIntOn64Bit(lowWord, highWord, signed); - } else { - return MakeBigIntOn32Bit(lowWord, highWord, signed); - } + // Allocate the BigInt with the right sign and length. + let result: MutableBigInt; + if (needTwoDigits) { + result = bigint::AllocateEmptyBigInt(sign, kTwoDigitBigInt); + } else { + result = bigint::AllocateEmptyBigInt(sign, kOneDigitBigInt); } - macro LoadDataViewBigInt(implicit context: Context)( - buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool, - signed: constexpr bool): BigInt { - const dataPointer: RawPtr = buffer.backing_store; - - const b0: uint32 = LoadUint8(dataPointer, offset); - const b1: uint32 = LoadUint8(dataPointer, offset + 1); - const b2: uint32 = LoadUint8(dataPointer, offset + 2); - const b3: uint32 = LoadUint8(dataPointer, offset + 3); - const b4: uint32 = LoadUint8(dataPointer, offset + 4); - const b5: uint32 = LoadUint8(dataPointer, offset + 5); - const b6: uint32 = LoadUint8(dataPointer, offset + 6); - const b7: uint32 = LoadUint8(dataPointer, offset + 7); - let lowWord: uint32; - let highWord: uint32; - - if (requestedLittleEndian) { - lowWord = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; - highWord = (b7 << 24) | (b6 << 16) | (b5 << 8) | b4; - } else { - highWord = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; - lowWord = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7; - } + // Finally, write the digit(s) to the BigInt. + bigint::StoreBigIntDigit(result, 0, Unsigned(Convert(lowPart))); + if (needTwoDigits) { + bigint::StoreBigIntDigit(result, 1, Unsigned(Convert(highPart))); + } + return Convert(result); +} - return MakeBigInt(lowWord, highWord, signed); +macro MakeBigInt(implicit context: Context)( + lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt { + // A BigInt digit has the platform word size, so we only need one digit + // on 64-bit platforms but may need two on 32-bit. + if constexpr (Is64()) { + return MakeBigIntOn64Bit(lowWord, highWord, signed); + } else { + return MakeBigIntOn32Bit(lowWord, highWord, signed); } +} - extern macro DataViewBuiltinsAssembler::DataViewElementSize( - constexpr ElementsKind): constexpr int31; - - // GetViewValue ( view, requestIndex, isLittleEndian, type ) - // https://tc39.es/ecma262/#sec-getviewvalue - transitioning macro DataViewGet( - context: Context, receiver: JSAny, requestIndex: JSAny, - requestedLittleEndian: JSAny, kind: constexpr ElementsKind): Numeric { - // 1. Perform ? RequireInternalSlot(view, [[DataView]]). - // 2. Assert: view has a [[ViewedArrayBuffer]] internal slot. - const dataView: JSDataView = - ValidateDataView(context, receiver, MakeDataViewGetterNameString(kind)); - - try { - // 3. Let getIndex be ? ToIndex(requestIndex). - const getIndex: uintptr = ToIndex(requestIndex) otherwise RangeError; - - // 4. Set isLittleEndian to ! ToBoolean(isLittleEndian). - const littleEndian: bool = ToBoolean(requestedLittleEndian); - - // 5. Let buffer be view.[[ViewedArrayBuffer]]. - const buffer: JSArrayBuffer = dataView.buffer; - - // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. - if (IsDetachedBuffer(buffer)) { - ThrowTypeError( - MessageTemplate::kDetachedOperation, - MakeDataViewGetterNameString(kind)); - } +macro LoadDataViewBigInt(implicit context: Context)( + buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool, + signed: constexpr bool): BigInt { + const dataPointer: RawPtr = buffer.backing_store_ptr; + + const b0: uint32 = LoadUint8(dataPointer, offset); + const b1: uint32 = LoadUint8(dataPointer, offset + 1); + const b2: uint32 = LoadUint8(dataPointer, offset + 2); + const b3: uint32 = LoadUint8(dataPointer, offset + 3); + const b4: uint32 = LoadUint8(dataPointer, offset + 4); + const b5: uint32 = LoadUint8(dataPointer, offset + 5); + const b6: uint32 = LoadUint8(dataPointer, offset + 6); + const b7: uint32 = LoadUint8(dataPointer, offset + 7); + let lowWord: uint32; + let highWord: uint32; + + if (requestedLittleEndian) { + lowWord = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; + highWord = (b7 << 24) | (b6 << 16) | (b5 << 8) | b4; + } else { + highWord = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + lowWord = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7; + } + + return MakeBigInt(lowWord, highWord, signed); +} - // 7. Let viewOffset be view.[[ByteOffset]]. - const viewOffset: uintptr = dataView.byte_offset; - - // 8. Let viewSize be view.[[ByteLength]]. - const viewSize: uintptr = dataView.byte_length; - - // 9. Let elementSize be the Element Size value specified in Table 62 - // for Element Type type. - const elementSize: uintptr = DataViewElementSize(kind); - - // 10. If getIndex + elementSize > viewSize, throw a RangeError exception. - CheckIntegerIndexAdditionOverflow(getIndex, elementSize, viewSize) - otherwise RangeError; - - // 11. Let bufferIndex be getIndex + viewOffset. - const bufferIndex: uintptr = getIndex + viewOffset; - - if constexpr (kind == ElementsKind::UINT8_ELEMENTS) { - return LoadDataView8(buffer, bufferIndex, false); - } else if constexpr (kind == ElementsKind::INT8_ELEMENTS) { - return LoadDataView8(buffer, bufferIndex, true); - } else if constexpr (kind == ElementsKind::UINT16_ELEMENTS) { - return LoadDataView16(buffer, bufferIndex, littleEndian, false); - } else if constexpr (kind == ElementsKind::INT16_ELEMENTS) { - return LoadDataView16(buffer, bufferIndex, littleEndian, true); - } else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) { - return LoadDataView32(buffer, bufferIndex, littleEndian, kind); - } else if constexpr (kind == ElementsKind::INT32_ELEMENTS) { - return LoadDataView32(buffer, bufferIndex, littleEndian, kind); - } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) { - return LoadDataView32(buffer, bufferIndex, littleEndian, kind); - } else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) { - return LoadDataViewFloat64(buffer, bufferIndex, littleEndian); - } else if constexpr (kind == ElementsKind::BIGUINT64_ELEMENTS) { - return LoadDataViewBigInt(buffer, bufferIndex, littleEndian, false); - } else if constexpr (kind == ElementsKind::BIGINT64_ELEMENTS) { - return LoadDataViewBigInt(buffer, bufferIndex, littleEndian, true); - } else { - unreachable; - } +extern macro DataViewBuiltinsAssembler::DataViewElementSize( + constexpr ElementsKind): constexpr int31; + +// GetViewValue ( view, requestIndex, isLittleEndian, type ) +// https://tc39.es/ecma262/#sec-getviewvalue +transitioning macro DataViewGet( + context: Context, receiver: JSAny, requestIndex: JSAny, + requestedLittleEndian: JSAny, kind: constexpr ElementsKind): Numeric { + // 1. Perform ? RequireInternalSlot(view, [[DataView]]). + // 2. Assert: view has a [[ViewedArrayBuffer]] internal slot. + const dataView: JSDataView = + ValidateDataView(context, receiver, MakeDataViewGetterNameString(kind)); + + try { + // 3. Let getIndex be ? ToIndex(requestIndex). + const getIndex: uintptr = ToIndex(requestIndex) otherwise RangeError; + + // 4. Set isLittleEndian to ! ToBoolean(isLittleEndian). + const littleEndian: bool = ToBoolean(requestedLittleEndian); + + // 5. Let buffer be view.[[ViewedArrayBuffer]]. + const buffer: JSArrayBuffer = dataView.buffer; + + // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(buffer)) { + ThrowTypeError( + MessageTemplate::kDetachedOperation, + MakeDataViewGetterNameString(kind)); } - label RangeError { - ThrowRangeError(MessageTemplate::kInvalidDataViewAccessorOffset); + + // 7. Let viewOffset be view.[[ByteOffset]]. + const viewOffset: uintptr = dataView.byte_offset; + + // 8. Let viewSize be view.[[ByteLength]]. + const viewSize: uintptr = dataView.byte_length; + + // 9. Let elementSize be the Element Size value specified in Table 62 + // for Element Type type. + const elementSize: uintptr = DataViewElementSize(kind); + + // 10. If getIndex + elementSize > viewSize, throw a RangeError exception. + CheckIntegerIndexAdditionOverflow(getIndex, elementSize, viewSize) + otherwise RangeError; + + // 11. Let bufferIndex be getIndex + viewOffset. + const bufferIndex: uintptr = getIndex + viewOffset; + + if constexpr (kind == ElementsKind::UINT8_ELEMENTS) { + return LoadDataView8(buffer, bufferIndex, false); + } else if constexpr (kind == ElementsKind::INT8_ELEMENTS) { + return LoadDataView8(buffer, bufferIndex, true); + } else if constexpr (kind == ElementsKind::UINT16_ELEMENTS) { + return LoadDataView16(buffer, bufferIndex, littleEndian, false); + } else if constexpr (kind == ElementsKind::INT16_ELEMENTS) { + return LoadDataView16(buffer, bufferIndex, littleEndian, true); + } else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) { + return LoadDataView32(buffer, bufferIndex, littleEndian, kind); + } else if constexpr (kind == ElementsKind::INT32_ELEMENTS) { + return LoadDataView32(buffer, bufferIndex, littleEndian, kind); + } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) { + return LoadDataView32(buffer, bufferIndex, littleEndian, kind); + } else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) { + return LoadDataViewFloat64(buffer, bufferIndex, littleEndian); + } else if constexpr (kind == ElementsKind::BIGUINT64_ELEMENTS) { + return LoadDataViewBigInt(buffer, bufferIndex, littleEndian, false); + } else if constexpr (kind == ElementsKind::BIGINT64_ELEMENTS) { + return LoadDataViewBigInt(buffer, bufferIndex, littleEndian, true); + } else { + unreachable; } + } label RangeError { + ThrowRangeError(MessageTemplate::kInvalidDataViewAccessorOffset); } +} - transitioning javascript builtin DataViewPrototypeGetUint8( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - return DataViewGet( - context, receiver, offset, Undefined, ElementsKind::UINT8_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeGetUint8( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + return DataViewGet( + context, receiver, offset, Undefined, ElementsKind::UINT8_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeGetInt8( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - return DataViewGet( - context, receiver, offset, Undefined, ElementsKind::INT8_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeGetInt8( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + return DataViewGet( + context, receiver, offset, Undefined, ElementsKind::INT8_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeGetUint16( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const isLittleEndian: JSAny = - arguments.length > 1 ? arguments[1] : Undefined; - return DataViewGet( - context, receiver, offset, isLittleEndian, - ElementsKind::UINT16_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeGetUint16( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const isLittleEndian: JSAny = arguments[1]; + return DataViewGet( + context, receiver, offset, isLittleEndian, ElementsKind::UINT16_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeGetInt16( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const isLittleEndian: JSAny = - arguments.length > 1 ? arguments[1] : Undefined; - return DataViewGet( - context, receiver, offset, isLittleEndian, - ElementsKind::INT16_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeGetInt16( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const isLittleEndian: JSAny = arguments[1]; + return DataViewGet( + context, receiver, offset, isLittleEndian, ElementsKind::INT16_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeGetUint32( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const isLittleEndian: JSAny = - arguments.length > 1 ? arguments[1] : Undefined; - return DataViewGet( - context, receiver, offset, isLittleEndian, - ElementsKind::UINT32_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeGetUint32( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const isLittleEndian: JSAny = arguments[1]; + return DataViewGet( + context, receiver, offset, isLittleEndian, ElementsKind::UINT32_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeGetInt32( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const isLittleEndian: JSAny = - arguments.length > 1 ? arguments[1] : Undefined; - return DataViewGet( - context, receiver, offset, isLittleEndian, - ElementsKind::INT32_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeGetInt32( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const isLittleEndian: JSAny = arguments[1]; + return DataViewGet( + context, receiver, offset, isLittleEndian, ElementsKind::INT32_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeGetFloat32( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const isLittleEndian: JSAny = - arguments.length > 1 ? arguments[1] : Undefined; - return DataViewGet( - context, receiver, offset, isLittleEndian, - ElementsKind::FLOAT32_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeGetFloat32( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const isLittleEndian: JSAny = arguments[1]; + return DataViewGet( + context, receiver, offset, isLittleEndian, + ElementsKind::FLOAT32_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeGetFloat64( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const isLittleEndian: JSAny = - arguments.length > 1 ? arguments[1] : Undefined; - return DataViewGet( - context, receiver, offset, isLittleEndian, - ElementsKind::FLOAT64_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeGetFloat64( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const isLittleEndian: JSAny = arguments[1]; + return DataViewGet( + context, receiver, offset, isLittleEndian, + ElementsKind::FLOAT64_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeGetBigUint64( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const isLittleEndian: JSAny = - arguments.length > 1 ? arguments[1] : Undefined; - return DataViewGet( - context, receiver, offset, isLittleEndian, - ElementsKind::BIGUINT64_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeGetBigUint64( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const isLittleEndian: JSAny = arguments[1]; + return DataViewGet( + context, receiver, offset, isLittleEndian, + ElementsKind::BIGUINT64_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeGetBigInt64( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const isLittleEndian: JSAny = - arguments.length > 1 ? arguments[1] : Undefined; - return DataViewGet( - context, receiver, offset, isLittleEndian, - ElementsKind::BIGINT64_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeGetBigInt64( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const isLittleEndian: JSAny = arguments[1]; + return DataViewGet( + context, receiver, offset, isLittleEndian, + ElementsKind::BIGINT64_ELEMENTS); +} - extern macro ToNumber(Context, JSAny): Number; - extern macro ToBigInt(Context, JSAny): BigInt; - extern macro TruncateFloat64ToWord32(float64): uint32; +extern macro ToNumber(Context, JSAny): Number; +extern macro ToBigInt(Context, JSAny): BigInt; +extern macro TruncateFloat64ToWord32(float64): uint32; - extern macro DataViewBuiltinsAssembler::StoreWord8(RawPtr, uintptr, uint32): - void; +extern macro DataViewBuiltinsAssembler::StoreWord8( + RawPtr, uintptr, uint32): void; - macro StoreDataView8(buffer: JSArrayBuffer, offset: uintptr, value: uint32) { - StoreWord8(buffer.backing_store, offset, value & 0xFF); - } +macro StoreDataView8(buffer: JSArrayBuffer, offset: uintptr, value: uint32) { + StoreWord8(buffer.backing_store_ptr, offset, value & 0xFF); +} - macro StoreDataView16( - buffer: JSArrayBuffer, offset: uintptr, value: uint32, - requestedLittleEndian: bool) { - const dataPointer: RawPtr = buffer.backing_store; +macro StoreDataView16( + buffer: JSArrayBuffer, offset: uintptr, value: uint32, + requestedLittleEndian: bool) { + const dataPointer: RawPtr = buffer.backing_store_ptr; - const b0: uint32 = value & 0xFF; - const b1: uint32 = (value >>> 8) & 0xFF; + const b0: uint32 = value & 0xFF; + const b1: uint32 = (value >>> 8) & 0xFF; - if (requestedLittleEndian) { - StoreWord8(dataPointer, offset, b0); - StoreWord8(dataPointer, offset + 1, b1); - } else { - StoreWord8(dataPointer, offset, b1); - StoreWord8(dataPointer, offset + 1, b0); - } + if (requestedLittleEndian) { + StoreWord8(dataPointer, offset, b0); + StoreWord8(dataPointer, offset + 1, b1); + } else { + StoreWord8(dataPointer, offset, b1); + StoreWord8(dataPointer, offset + 1, b0); } +} - macro StoreDataView32( - buffer: JSArrayBuffer, offset: uintptr, value: uint32, - requestedLittleEndian: bool) { - const dataPointer: RawPtr = buffer.backing_store; - - const b0: uint32 = value & 0xFF; - const b1: uint32 = (value >>> 8) & 0xFF; - const b2: uint32 = (value >>> 16) & 0xFF; - const b3: uint32 = value >>> 24; // We don't need to mask here. - - if (requestedLittleEndian) { - StoreWord8(dataPointer, offset, b0); - StoreWord8(dataPointer, offset + 1, b1); - StoreWord8(dataPointer, offset + 2, b2); - StoreWord8(dataPointer, offset + 3, b3); - } else { - StoreWord8(dataPointer, offset, b3); - StoreWord8(dataPointer, offset + 1, b2); - StoreWord8(dataPointer, offset + 2, b1); - StoreWord8(dataPointer, offset + 3, b0); - } +macro StoreDataView32( + buffer: JSArrayBuffer, offset: uintptr, value: uint32, + requestedLittleEndian: bool) { + const dataPointer: RawPtr = buffer.backing_store_ptr; + + const b0: uint32 = value & 0xFF; + const b1: uint32 = (value >>> 8) & 0xFF; + const b2: uint32 = (value >>> 16) & 0xFF; + const b3: uint32 = value >>> 24; // We don't need to mask here. + + if (requestedLittleEndian) { + StoreWord8(dataPointer, offset, b0); + StoreWord8(dataPointer, offset + 1, b1); + StoreWord8(dataPointer, offset + 2, b2); + StoreWord8(dataPointer, offset + 3, b3); + } else { + StoreWord8(dataPointer, offset, b3); + StoreWord8(dataPointer, offset + 1, b2); + StoreWord8(dataPointer, offset + 2, b1); + StoreWord8(dataPointer, offset + 3, b0); } +} - macro StoreDataView64( - buffer: JSArrayBuffer, offset: uintptr, lowWord: uint32, highWord: uint32, - requestedLittleEndian: bool) { - const dataPointer: RawPtr = buffer.backing_store; - - const b0: uint32 = lowWord & 0xFF; - const b1: uint32 = (lowWord >>> 8) & 0xFF; - const b2: uint32 = (lowWord >>> 16) & 0xFF; - const b3: uint32 = lowWord >>> 24; - - const b4: uint32 = highWord & 0xFF; - const b5: uint32 = (highWord >>> 8) & 0xFF; - const b6: uint32 = (highWord >>> 16) & 0xFF; - const b7: uint32 = highWord >>> 24; - - if (requestedLittleEndian) { - StoreWord8(dataPointer, offset, b0); - StoreWord8(dataPointer, offset + 1, b1); - StoreWord8(dataPointer, offset + 2, b2); - StoreWord8(dataPointer, offset + 3, b3); - StoreWord8(dataPointer, offset + 4, b4); - StoreWord8(dataPointer, offset + 5, b5); - StoreWord8(dataPointer, offset + 6, b6); - StoreWord8(dataPointer, offset + 7, b7); - } else { - StoreWord8(dataPointer, offset, b7); - StoreWord8(dataPointer, offset + 1, b6); - StoreWord8(dataPointer, offset + 2, b5); - StoreWord8(dataPointer, offset + 3, b4); - StoreWord8(dataPointer, offset + 4, b3); - StoreWord8(dataPointer, offset + 5, b2); - StoreWord8(dataPointer, offset + 6, b1); - StoreWord8(dataPointer, offset + 7, b0); - } +macro StoreDataView64( + buffer: JSArrayBuffer, offset: uintptr, lowWord: uint32, highWord: uint32, + requestedLittleEndian: bool) { + const dataPointer: RawPtr = buffer.backing_store_ptr; + + const b0: uint32 = lowWord & 0xFF; + const b1: uint32 = (lowWord >>> 8) & 0xFF; + const b2: uint32 = (lowWord >>> 16) & 0xFF; + const b3: uint32 = lowWord >>> 24; + + const b4: uint32 = highWord & 0xFF; + const b5: uint32 = (highWord >>> 8) & 0xFF; + const b6: uint32 = (highWord >>> 16) & 0xFF; + const b7: uint32 = highWord >>> 24; + + if (requestedLittleEndian) { + StoreWord8(dataPointer, offset, b0); + StoreWord8(dataPointer, offset + 1, b1); + StoreWord8(dataPointer, offset + 2, b2); + StoreWord8(dataPointer, offset + 3, b3); + StoreWord8(dataPointer, offset + 4, b4); + StoreWord8(dataPointer, offset + 5, b5); + StoreWord8(dataPointer, offset + 6, b6); + StoreWord8(dataPointer, offset + 7, b7); + } else { + StoreWord8(dataPointer, offset, b7); + StoreWord8(dataPointer, offset + 1, b6); + StoreWord8(dataPointer, offset + 2, b5); + StoreWord8(dataPointer, offset + 3, b4); + StoreWord8(dataPointer, offset + 4, b3); + StoreWord8(dataPointer, offset + 5, b2); + StoreWord8(dataPointer, offset + 6, b1); + StoreWord8(dataPointer, offset + 7, b0); } +} - extern macro DataViewBuiltinsAssembler::DataViewDecodeBigIntLength( - BigIntBase): uint32; - extern macro DataViewBuiltinsAssembler::DataViewDecodeBigIntSign(BigIntBase): - uint32; - - // We might get here a BigInt that is bigger than 64 bits, but we're only - // interested in the 64 lowest ones. This means the lowest BigInt digit - // on 64-bit platforms, and the 2 lowest BigInt digits on 32-bit ones. - macro StoreDataViewBigInt( - buffer: JSArrayBuffer, offset: uintptr, bigIntValue: BigInt, - requestedLittleEndian: bool) { - const length: uint32 = DataViewDecodeBigIntLength(bigIntValue); - const sign: uint32 = DataViewDecodeBigIntSign(bigIntValue); - - // The 32-bit words that will hold the BigInt's value in - // two's complement representation. - let lowWord: uint32 = 0; - let highWord: uint32 = 0; - - // The length is nonzero if and only if the BigInt's value is nonzero. - if (length != 0) { - if constexpr (Is64()) { - // There is always exactly 1 BigInt digit to load in this case. - const value: uintptr = bigint::LoadBigIntDigit(bigIntValue, 0); - lowWord = Convert(value); // Truncates value to 32 bits. - highWord = Convert(value >>> 32); - } else { // There might be either 1 or 2 BigInt digits we need to load. - lowWord = Convert(bigint::LoadBigIntDigit(bigIntValue, 0)); - if (length >= 2) { // Only load the second digit if there is one. - highWord = Convert(bigint::LoadBigIntDigit(bigIntValue, 1)); - } +extern macro DataViewBuiltinsAssembler::DataViewDecodeBigIntLength(BigIntBase): + uint32; +extern macro DataViewBuiltinsAssembler::DataViewDecodeBigIntSign(BigIntBase): + uint32; + +// We might get here a BigInt that is bigger than 64 bits, but we're only +// interested in the 64 lowest ones. This means the lowest BigInt digit +// on 64-bit platforms, and the 2 lowest BigInt digits on 32-bit ones. +macro StoreDataViewBigInt( + buffer: JSArrayBuffer, offset: uintptr, bigIntValue: BigInt, + requestedLittleEndian: bool) { + const length: uint32 = DataViewDecodeBigIntLength(bigIntValue); + const sign: uint32 = DataViewDecodeBigIntSign(bigIntValue); + + // The 32-bit words that will hold the BigInt's value in + // two's complement representation. + let lowWord: uint32 = 0; + let highWord: uint32 = 0; + + // The length is nonzero if and only if the BigInt's value is nonzero. + if (length != 0) { + if constexpr (Is64()) { + // There is always exactly 1 BigInt digit to load in this case. + const value: uintptr = bigint::LoadBigIntDigit(bigIntValue, 0); + lowWord = Convert(value); // Truncates value to 32 bits. + highWord = Convert(value >>> 32); + } else { // There might be either 1 or 2 BigInt digits we need to load. + lowWord = Convert(bigint::LoadBigIntDigit(bigIntValue, 0)); + if (length >= 2) { // Only load the second digit if there is one. + highWord = Convert(bigint::LoadBigIntDigit(bigIntValue, 1)); } } + } - if (sign != 0) { // The number is negative, Convert it. - highWord = Unsigned(0 - Signed(highWord)); - if (lowWord != 0) { - highWord = Unsigned(Signed(highWord) - 1); - } - lowWord = Unsigned(0 - Signed(lowWord)); + if (sign != 0) { // The number is negative, Convert it. + highWord = Unsigned(0 - Signed(highWord)); + if (lowWord != 0) { + highWord = Unsigned(Signed(highWord) - 1); } - - StoreDataView64(buffer, offset, lowWord, highWord, requestedLittleEndian); + lowWord = Unsigned(0 - Signed(lowWord)); } - // SetViewValue ( view, requestIndex, isLittleEndian, type, value ) - // https://tc39.es/ecma262/#sec-setviewvalue - transitioning macro DataViewSet( - context: Context, receiver: JSAny, requestIndex: JSAny, value: JSAny, - requestedLittleEndian: JSAny, kind: constexpr ElementsKind): JSAny { - // 1. Perform ? RequireInternalSlot(view, [[DataView]]). - // 2. Assert: view has a [[ViewedArrayBuffer]] internal slot. - const dataView: JSDataView = - ValidateDataView(context, receiver, MakeDataViewSetterNameString(kind)); - - try { - // 3. Let getIndex be ? ToIndex(requestIndex). - const getIndex: uintptr = ToIndex(requestIndex) otherwise RangeError; + StoreDataView64(buffer, offset, lowWord, highWord, requestedLittleEndian); +} - const littleEndian: bool = ToBoolean(requestedLittleEndian); - const buffer: JSArrayBuffer = dataView.buffer; +// SetViewValue ( view, requestIndex, isLittleEndian, type, value ) +// https://tc39.es/ecma262/#sec-setviewvalue +transitioning macro DataViewSet( + context: Context, receiver: JSAny, requestIndex: JSAny, value: JSAny, + requestedLittleEndian: JSAny, kind: constexpr ElementsKind): JSAny { + // 1. Perform ? RequireInternalSlot(view, [[DataView]]). + // 2. Assert: view has a [[ViewedArrayBuffer]] internal slot. + const dataView: JSDataView = + ValidateDataView(context, receiver, MakeDataViewSetterNameString(kind)); + + try { + // 3. Let getIndex be ? ToIndex(requestIndex). + const getIndex: uintptr = ToIndex(requestIndex) otherwise RangeError; + + const littleEndian: bool = ToBoolean(requestedLittleEndian); + const buffer: JSArrayBuffer = dataView.buffer; + + let numberValue: Numeric; + if constexpr ( + kind == ElementsKind::BIGUINT64_ELEMENTS || + kind == ElementsKind::BIGINT64_ELEMENTS) { + // 4. If ! IsBigIntElementType(type) is true, let numberValue be + // ? ToBigInt(value). + numberValue = ToBigInt(context, value); + } else { + // 5. Otherwise, let numberValue be ? ToNumber(value). + numberValue = ToNumber(context, value); + } - let numberValue: Numeric; - if constexpr ( - kind == ElementsKind::BIGUINT64_ELEMENTS || - kind == ElementsKind::BIGINT64_ELEMENTS) { - // 4. If ! IsBigIntElementType(type) is true, let numberValue be - // ? ToBigInt(value). - numberValue = ToBigInt(context, value); - } else { - // 5. Otherwise, let numberValue be ? ToNumber(value). - numberValue = ToNumber(context, value); - } + // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(buffer)) { + ThrowTypeError( + MessageTemplate::kDetachedOperation, + MakeDataViewSetterNameString(kind)); + } - // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. - if (IsDetachedBuffer(buffer)) { - ThrowTypeError( - MessageTemplate::kDetachedOperation, - MakeDataViewSetterNameString(kind)); - } + // 9. Let viewOffset be view.[[ByteOffset]]. + const viewOffset: uintptr = dataView.byte_offset; - // 9. Let viewOffset be view.[[ByteOffset]]. - const viewOffset: uintptr = dataView.byte_offset; + // 10. Let viewSize be view.[[ByteLength]]. + const viewSize: uintptr = dataView.byte_length; - // 10. Let viewSize be view.[[ByteLength]]. - const viewSize: uintptr = dataView.byte_length; + // 11. Let elementSize be the Element Size value specified in Table 62 + // for Element Type type. + const elementSize: uintptr = DataViewElementSize(kind); - // 11. Let elementSize be the Element Size value specified in Table 62 - // for Element Type type. - const elementSize: uintptr = DataViewElementSize(kind); + // 12. If getIndex + elementSize > viewSize, throw a RangeError exception. + CheckIntegerIndexAdditionOverflow(getIndex, elementSize, viewSize) + otherwise RangeError; - // 12. If getIndex + elementSize > viewSize, throw a RangeError exception. - CheckIntegerIndexAdditionOverflow(getIndex, elementSize, viewSize) - otherwise RangeError; + // 13. Let bufferIndex be getIndex + viewOffset. + const bufferIndex: uintptr = getIndex + viewOffset; - // 13. Let bufferIndex be getIndex + viewOffset. - const bufferIndex: uintptr = getIndex + viewOffset; + if constexpr ( + kind == ElementsKind::BIGUINT64_ELEMENTS || + kind == ElementsKind::BIGINT64_ELEMENTS) { + // For these elements kinds numberValue is BigInt. + const bigIntValue: BigInt = %RawDownCast(numberValue); + StoreDataViewBigInt(buffer, bufferIndex, bigIntValue, littleEndian); + } else { + // For these elements kinds numberValue is Number. + const numValue: Number = %RawDownCast(numberValue); + const doubleValue: float64 = ChangeNumberToFloat64(numValue); if constexpr ( - kind == ElementsKind::BIGUINT64_ELEMENTS || - kind == ElementsKind::BIGINT64_ELEMENTS) { - // For these elements kinds numberValue is BigInt. - const bigIntValue: BigInt = %RawDownCast(numberValue); - StoreDataViewBigInt(buffer, bufferIndex, bigIntValue, littleEndian); - } else { - // For these elements kinds numberValue is Number. - const numValue: Number = %RawDownCast(numberValue); - const doubleValue: float64 = ChangeNumberToFloat64(numValue); - - if constexpr ( - kind == ElementsKind::UINT8_ELEMENTS || - kind == ElementsKind::INT8_ELEMENTS) { - StoreDataView8( - buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue)); - } else if constexpr ( - kind == ElementsKind::UINT16_ELEMENTS || - kind == ElementsKind::INT16_ELEMENTS) { - StoreDataView16( - buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue), - littleEndian); - } else if constexpr ( - kind == ElementsKind::UINT32_ELEMENTS || - kind == ElementsKind::INT32_ELEMENTS) { - StoreDataView32( - buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue), - littleEndian); - } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) { - const floatValue: float32 = TruncateFloat64ToFloat32(doubleValue); - StoreDataView32( - buffer, bufferIndex, BitcastFloat32ToInt32(floatValue), - littleEndian); - } else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) { - const lowWord: uint32 = Float64ExtractLowWord32(doubleValue); - const highWord: uint32 = Float64ExtractHighWord32(doubleValue); - StoreDataView64(buffer, bufferIndex, lowWord, highWord, littleEndian); - } + kind == ElementsKind::UINT8_ELEMENTS || + kind == ElementsKind::INT8_ELEMENTS) { + StoreDataView8( + buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue)); + } else if constexpr ( + kind == ElementsKind::UINT16_ELEMENTS || + kind == ElementsKind::INT16_ELEMENTS) { + StoreDataView16( + buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue), + littleEndian); + } else if constexpr ( + kind == ElementsKind::UINT32_ELEMENTS || + kind == ElementsKind::INT32_ELEMENTS) { + StoreDataView32( + buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue), + littleEndian); + } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) { + const floatValue: float32 = TruncateFloat64ToFloat32(doubleValue); + StoreDataView32( + buffer, bufferIndex, BitcastFloat32ToInt32(floatValue), + littleEndian); + } else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) { + const lowWord: uint32 = Float64ExtractLowWord32(doubleValue); + const highWord: uint32 = Float64ExtractHighWord32(doubleValue); + StoreDataView64(buffer, bufferIndex, lowWord, highWord, littleEndian); } - return Undefined; - } - label RangeError { - ThrowRangeError(MessageTemplate::kInvalidDataViewAccessorOffset); } + return Undefined; + } label RangeError { + ThrowRangeError(MessageTemplate::kInvalidDataViewAccessorOffset); } +} - transitioning javascript builtin DataViewPrototypeSetUint8( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; - return DataViewSet( - context, receiver, offset, value, Undefined, - ElementsKind::UINT8_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeSetUint8( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const value: JSAny = arguments[1]; + return DataViewSet( + context, receiver, offset, value, Undefined, + ElementsKind::UINT8_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeSetInt8( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; - return DataViewSet( - context, receiver, offset, value, Undefined, - ElementsKind::INT8_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeSetInt8( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const value: JSAny = arguments[1]; + return DataViewSet( + context, receiver, offset, value, Undefined, ElementsKind::INT8_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeSetUint16( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; - const isLittleEndian: JSAny = - arguments.length > 2 ? arguments[2] : Undefined; - return DataViewSet( - context, receiver, offset, value, isLittleEndian, - ElementsKind::UINT16_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeSetUint16( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const value: JSAny = arguments[1]; + const isLittleEndian: JSAny = arguments[2]; + return DataViewSet( + context, receiver, offset, value, isLittleEndian, + ElementsKind::UINT16_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeSetInt16( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; - const isLittleEndian: JSAny = - arguments.length > 2 ? arguments[2] : Undefined; - return DataViewSet( - context, receiver, offset, value, isLittleEndian, - ElementsKind::INT16_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeSetInt16( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const value: JSAny = arguments[1]; + const isLittleEndian: JSAny = arguments[2]; + return DataViewSet( + context, receiver, offset, value, isLittleEndian, + ElementsKind::INT16_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeSetUint32( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; - const isLittleEndian: JSAny = - arguments.length > 2 ? arguments[2] : Undefined; - return DataViewSet( - context, receiver, offset, value, isLittleEndian, - ElementsKind::UINT32_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeSetUint32( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const value: JSAny = arguments[1]; + const isLittleEndian: JSAny = arguments[2]; + return DataViewSet( + context, receiver, offset, value, isLittleEndian, + ElementsKind::UINT32_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeSetInt32( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; - const isLittleEndian: JSAny = - arguments.length > 2 ? arguments[2] : Undefined; - return DataViewSet( - context, receiver, offset, value, isLittleEndian, - ElementsKind::INT32_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeSetInt32( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const value: JSAny = arguments[1]; + const isLittleEndian: JSAny = arguments[2]; + return DataViewSet( + context, receiver, offset, value, isLittleEndian, + ElementsKind::INT32_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeSetFloat32( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; - const isLittleEndian: JSAny = - arguments.length > 2 ? arguments[2] : Undefined; - return DataViewSet( - context, receiver, offset, value, isLittleEndian, - ElementsKind::FLOAT32_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeSetFloat32( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const value: JSAny = arguments[1]; + const isLittleEndian: JSAny = arguments[2]; + return DataViewSet( + context, receiver, offset, value, isLittleEndian, + ElementsKind::FLOAT32_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeSetFloat64( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; - const isLittleEndian: JSAny = - arguments.length > 2 ? arguments[2] : Undefined; - return DataViewSet( - context, receiver, offset, value, isLittleEndian, - ElementsKind::FLOAT64_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeSetFloat64( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const value: JSAny = arguments[1]; + const isLittleEndian: JSAny = arguments[2]; + return DataViewSet( + context, receiver, offset, value, isLittleEndian, + ElementsKind::FLOAT64_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeSetBigUint64( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; - const isLittleEndian: JSAny = - arguments.length > 2 ? arguments[2] : Undefined; - return DataViewSet( - context, receiver, offset, value, isLittleEndian, - ElementsKind::BIGUINT64_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeSetBigUint64( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const value: JSAny = arguments[1]; + const isLittleEndian: JSAny = arguments[2]; + return DataViewSet( + context, receiver, offset, value, isLittleEndian, + ElementsKind::BIGUINT64_ELEMENTS); +} - transitioning javascript builtin DataViewPrototypeSetBigInt64( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; - const isLittleEndian: JSAny = - arguments.length > 2 ? arguments[2] : Undefined; - return DataViewSet( - context, receiver, offset, value, isLittleEndian, - ElementsKind::BIGINT64_ELEMENTS); - } +transitioning javascript builtin DataViewPrototypeSetBigInt64( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const offset: JSAny = arguments[0]; + const value: JSAny = arguments[1]; + const isLittleEndian: JSAny = arguments[2]; + return DataViewSet( + context, receiver, offset, value, isLittleEndian, + ElementsKind::BIGINT64_ELEMENTS); +} } diff --git a/deps/v8/src/builtins/finalization-registry.tq b/deps/v8/src/builtins/finalization-registry.tq new file mode 100644 index 00000000000000..143486c73768f1 --- /dev/null +++ b/deps/v8/src/builtins/finalization-registry.tq @@ -0,0 +1,105 @@ +// Copyright 2020 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. + +namespace runtime { +extern runtime +ShrinkFinalizationRegistryUnregisterTokenMap( + Context, JSFinalizationRegistry): void; +} + +namespace weakref { +extern transitioning macro +RemoveFinalizationRegistryCellFromUnregisterTokenMap( + JSFinalizationRegistry, WeakCell): void; + +macro SplitOffTail(weakCell: WeakCell): WeakCell|Undefined { + const weakCellTail = weakCell.next; + weakCell.next = Undefined; + typeswitch (weakCellTail) { + case (Undefined): { + } + case (tailIsNowAHead: WeakCell): { + assert(tailIsNowAHead.prev == weakCell); + tailIsNowAHead.prev = Undefined; + } + } + return weakCellTail; +} + +transitioning macro +PopClearedCell(finalizationRegistry: JSFinalizationRegistry): WeakCell| + Undefined { + typeswitch (finalizationRegistry.cleared_cells) { + case (Undefined): { + return Undefined; + } + case (weakCell: WeakCell): { + assert(weakCell.prev == Undefined); + finalizationRegistry.cleared_cells = SplitOffTail(weakCell); + + // If the WeakCell has an unregister token, remove the cell from the + // unregister token linked lists and and the unregister token from + // key_map. This doesn't shrink key_map, which is done manually after + // the cleanup loop to avoid a runtime call. + if (weakCell.unregister_token != Undefined) { + RemoveFinalizationRegistryCellFromUnregisterTokenMap( + finalizationRegistry, weakCell); + } + + return weakCell; + } + } +} + +transitioning macro +FinalizationRegistryCleanupLoop(implicit context: Context)( + finalizationRegistry: JSFinalizationRegistry, callback: Callable) { + while (true) { + const weakCellHead = PopClearedCell(finalizationRegistry); + typeswitch (weakCellHead) { + case (Undefined): { + break; + } + case (weakCell: WeakCell): { + try { + Call(context, callback, Undefined, weakCell.holdings); + } catch (e) { + runtime::ShrinkFinalizationRegistryUnregisterTokenMap( + context, finalizationRegistry); + ReThrow(context, e); + } + } + } + } + + runtime::ShrinkFinalizationRegistryUnregisterTokenMap( + context, finalizationRegistry); +} + +transitioning javascript builtin +FinalizationRegistryPrototypeCleanupSome( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + // 1. Let finalizationRegistry be the this value. + // + // 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]). + const methodName: constexpr string = + 'FinalizationRegistry.prototype.cleanupSome'; + const finalizationRegistry = + Cast(receiver) otherwise ThrowTypeError( + MessageTemplate::kIncompatibleMethodReceiver, methodName, receiver); + + let callback: Callable; + if (arguments[0] != Undefined) { + // 4. If callback is not undefined and IsCallable(callback) is + // false, throw a TypeError exception. + callback = Cast(arguments[0]) otherwise ThrowTypeError( + MessageTemplate::kWeakRefsCleanupMustBeCallable, arguments[0]); + } else { + callback = finalizationRegistry.cleanup; + } + + FinalizationRegistryCleanupLoop(finalizationRegistry, callback); + return Undefined; +} +} diff --git a/deps/v8/src/builtins/growable-fixed-array-gen.cc b/deps/v8/src/builtins/growable-fixed-array-gen.cc index 487d01c060a0ec..042207bff6a6c6 100644 --- a/deps/v8/src/builtins/growable-fixed-array-gen.cc +++ b/deps/v8/src/builtins/growable-fixed-array-gen.cc @@ -33,6 +33,10 @@ void GrowableFixedArray::Push(const TNode value) { } } +TNode GrowableFixedArray::ToFixedArray() { + return ResizeFixedArray(length(), length()); +} + TNode GrowableFixedArray::ToJSArray(const TNode context) { const ElementsKind kind = PACKED_ELEMENTS; diff --git a/deps/v8/src/builtins/growable-fixed-array-gen.h b/deps/v8/src/builtins/growable-fixed-array-gen.h index 6e5d2ac7683b76..e61fce37e8d49c 100644 --- a/deps/v8/src/builtins/growable-fixed-array-gen.h +++ b/deps/v8/src/builtins/growable-fixed-array-gen.h @@ -32,6 +32,7 @@ class GrowableFixedArray : public CodeStubAssembler { void Push(const TNode value); + TNode ToFixedArray(); TNode ToJSArray(const TNode context); private: diff --git a/deps/v8/src/builtins/growable-fixed-array.tq b/deps/v8/src/builtins/growable-fixed-array.tq index 0666c39fd75e53..094e051a65acd2 100644 --- a/deps/v8/src/builtins/growable-fixed-array.tq +++ b/deps/v8/src/builtins/growable-fixed-array.tq @@ -3,44 +3,48 @@ // found in the LICENSE file. namespace growable_fixed_array { - // TODO(pwong): Support FixedTypedArrays. - struct GrowableFixedArray { - macro Push(obj: Object) { - this.EnsureCapacity(); - this.array.objects[this.length++] = obj; - } - macro ResizeFixedArray(newCapacity: intptr): FixedArray { - assert(this.length >= 0); - assert(newCapacity >= 0); - assert(newCapacity >= this.length); - const first: intptr = 0; - return ExtractFixedArray(this.array, first, this.length, newCapacity); - } - macro EnsureCapacity() { - assert(this.length <= this.capacity); - if (this.capacity == this.length) { - // Growth rate is analog to JSObject::NewElementsCapacity: - // new_capacity = (current_capacity + (current_capacity >> 1)) + 16. - this.capacity = this.capacity + (this.capacity >> 1) + 16; - this.array = this.ResizeFixedArray(this.capacity); - } - } - macro ToJSArray(implicit context: Context)(): JSArray { - const nativeContext: NativeContext = LoadNativeContext(context); - const map: Map = - LoadJSArrayElementsMap(ElementsKind::PACKED_ELEMENTS, nativeContext); - const fixedArray: FixedArray = this.ResizeFixedArray(this.length); - const lengthSmi = Convert(this.length); - return AllocateJSArray(map, fixedArray, lengthSmi); +// TODO(pwong): Support FixedTypedArrays. +struct GrowableFixedArray { + macro Push(obj: Object) { + this.EnsureCapacity(); + this.array.objects[this.length++] = obj; + } + macro ResizeFixedArray(newCapacity: intptr): FixedArray { + assert(this.length >= 0); + assert(newCapacity >= 0); + assert(newCapacity >= this.length); + const first: intptr = 0; + return ExtractFixedArray(this.array, first, this.length, newCapacity); + } + macro EnsureCapacity() { + assert(this.length <= this.capacity); + if (this.capacity == this.length) { + // Growth rate is analog to JSObject::NewElementsCapacity: + // new_capacity = (current_capacity + (current_capacity >> 1)) + 16. + this.capacity = this.capacity + (this.capacity >> 1) + 16; + this.array = this.ResizeFixedArray(this.capacity); } - - array: FixedArray; - // TODO(v8:4153): make capacity and length uintptr - capacity: intptr; - length: intptr; + } + macro ToFixedArray(): FixedArray { + return this.ResizeFixedArray(this.length); } - macro NewGrowableFixedArray(): GrowableFixedArray { - return GrowableFixedArray{array: kEmptyFixedArray, capacity: 0, length: 0}; + macro ToJSArray(implicit context: Context)(): JSArray { + const nativeContext: NativeContext = LoadNativeContext(context); + const map: Map = + LoadJSArrayElementsMap(ElementsKind::PACKED_ELEMENTS, nativeContext); + const fixedArray: FixedArray = this.ResizeFixedArray(this.length); + const lengthSmi = Convert(this.length); + return AllocateJSArray(map, fixedArray, lengthSmi); } + + array: FixedArray; + // TODO(v8:4153): make capacity and length uintptr + capacity: intptr; + length: intptr; +} + +macro NewGrowableFixedArray(): GrowableFixedArray { + return GrowableFixedArray{array: kEmptyFixedArray, capacity: 0, length: 0}; +} } diff --git a/deps/v8/src/builtins/ia32/builtins-ia32.cc b/deps/v8/src/builtins/ia32/builtins-ia32.cc index 5bea93214cc1f5..04a1fa9e0db4d9 100644 --- a/deps/v8/src/builtins/ia32/builtins-ia32.cc +++ b/deps/v8/src/builtins/ia32/builtins-ia32.cc @@ -130,31 +130,22 @@ void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) { __ push(eax); __ SmiUntag(eax); +#ifdef V8_REVERSE_JSARGS + // Set up pointer to first argument (skip receiver). + __ lea(esi, Operand(ebp, StandardFrameConstants::kCallerSPOffset + + kSystemPointerSize)); + // Copy arguments to the expression stack. + __ PushArray(esi, eax, ecx); + // The receiver for the builtin/api call. + __ PushRoot(RootIndex::kTheHoleValue); +#else // The receiver for the builtin/api call. __ PushRoot(RootIndex::kTheHoleValue); - // Set up pointer to last argument. We are using esi as scratch register. __ lea(esi, Operand(ebp, StandardFrameConstants::kCallerSPOffset)); - - // Copy arguments and receiver to the expression stack. - Label loop, entry; - __ mov(ecx, eax); - // ----------- S t a t e ------------- - // -- eax: number of arguments (untagged) - // -- edi: constructor function - // -- edx: new target - // -- esi: pointer to last argument - // -- ecx: counter - // -- sp[0*kSystemPointerSize]: the hole (receiver) - // -- sp[1*kSystemPointerSize]: number of arguments (tagged) - // -- sp[2*kSystemPointerSize]: context - // ----------------------------------- - __ jmp(&entry); - __ bind(&loop); - __ push(Operand(esi, ecx, times_system_pointer_size, 0)); - __ bind(&entry); - __ dec(ecx); - __ j(greater_equal, &loop); + // Copy arguments to the expression stack. + __ PushArray(esi, eax, ecx); +#endif // Call the function. // eax: number of arguments (untagged) @@ -254,29 +245,34 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { // Restore new target. __ Pop(edx); - // Push the allocated receiver to the stack. We need two copies - // because we may have to return the original one and the calling - // conventions dictate that the called function pops the receiver. + // Push the allocated receiver to the stack. __ Push(eax); + +#ifdef V8_REVERSE_JSARGS + // We need two copies because we may have to return the original one + // and the calling conventions dictate that the called function pops the + // receiver. The second copy is pushed after the arguments, we saved in r8 + // since rax needs to store the number of arguments before + // InvokingFunction. + __ movd(xmm0, eax); + + // Set up pointer to first argument (skip receiver). + __ lea(edi, Operand(ebp, StandardFrameConstants::kCallerSPOffset + + kSystemPointerSize)); +#else + // We need two copies because we may have to return the original one + // and the calling conventions dictate that the called function pops the + // receiver. __ Push(eax); - // ----------- S t a t e ------------- - // -- edx: new target - // -- sp[0*kSystemPointerSize]: implicit receiver - // -- sp[1*kSystemPointerSize]: implicit receiver - // -- sp[2*kSystemPointerSize]: padding - // -- sp[3*kSystemPointerSize]: constructor function - // -- sp[4*kSystemPointerSize]: number of arguments (tagged) - // -- sp[5*kSystemPointerSize]: context - // ----------------------------------- + // Set up pointer to last argument. + __ lea(edi, Operand(ebp, StandardFrameConstants::kCallerSPOffset)); +#endif // Restore argument count. __ mov(eax, Operand(ebp, ConstructFrameConstants::kLengthOffset)); __ SmiUntag(eax); - // Set up pointer to last argument. - __ lea(edi, Operand(ebp, StandardFrameConstants::kCallerSPOffset)); - // Check if we have enough stack space to push all arguments. // Argument count in eax. Clobbers ecx. Label enough_stack_space, stack_overflow; @@ -292,27 +288,14 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { __ bind(&enough_stack_space); - // Copy arguments and receiver to the expression stack. - Label loop, entry; - __ mov(ecx, eax); - // ----------- S t a t e ------------- - // -- eax: number of arguments (untagged) - // -- edx: new target - // -- edi: pointer to last argument - // -- ecx: counter (tagged) - // -- sp[0*kSystemPointerSize]: implicit receiver - // -- sp[1*kSystemPointerSize]: implicit receiver - // -- sp[2*kSystemPointerSize]: padding - // -- sp[3*kSystemPointerSize]: constructor function - // -- sp[4*kSystemPointerSize]: number of arguments (tagged) - // -- sp[5*kSystemPointerSize]: context - // ----------------------------------- - __ jmp(&entry, Label::kNear); - __ bind(&loop); - __ Push(Operand(edi, ecx, times_system_pointer_size, 0)); - __ bind(&entry); - __ dec(ecx); - __ j(greater_equal, &loop); + // Copy arguments to the expression stack. + __ PushArray(edi, eax, ecx); + +#ifdef V8_REVERSE_JSARGS + // Push implicit receiver. + __ movd(ecx, xmm0); + __ Push(ecx); +#endif // Restore and and call the constructor function. __ mov(edi, Operand(ebp, ConstructFrameConstants::kConstructorOffset)); @@ -536,9 +519,13 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, // Load the previous frame pointer (edx) to access C arguments __ mov(scratch1, Operand(ebp, 0)); - // Push the function and the receiver onto the stack. + // Push the function. __ push(Operand(scratch1, EntryFrameConstants::kFunctionArgOffset)); + +#ifndef V8_REVERSE_JSARGS + // And the receiver onto the stack. __ push(Operand(scratch1, EntryFrameConstants::kReceiverArgOffset)); +#endif // Load the number of arguments and setup pointer to the arguments. __ mov(eax, Operand(scratch1, EntryFrameConstants::kArgcOffset)); @@ -558,6 +545,18 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, __ bind(&enough_stack_space); // Copy arguments to the stack in a loop. +#ifdef V8_REVERSE_JSARGS + Label loop, entry; + __ Move(ecx, eax); + __ jmp(&entry, Label::kNear); + __ bind(&loop); + // Push the parameter from argv. + __ mov(scratch2, Operand(scratch1, ecx, times_system_pointer_size, 0)); + __ push(Operand(scratch2, 0)); // dereference handle + __ bind(&entry); + __ dec(ecx); + __ j(greater_equal, &loop); +#else Label loop, entry; __ Move(ecx, Immediate(0)); __ jmp(&entry, Label::kNear); @@ -569,10 +568,16 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, __ bind(&entry); __ cmp(ecx, eax); __ j(not_equal, &loop); +#endif - // Load the previous frame pointer (ebx) to access C arguments + // Load the previous frame pointer to access C arguments __ mov(scratch2, Operand(ebp, 0)); +#ifdef V8_REVERSE_JSARGS + // Push the receiver onto the stack. + __ push(Operand(scratch2, EntryFrameConstants::kReceiverArgOffset)); +#endif + // Get the new.target and function from the frame. __ mov(edx, Operand(scratch2, EntryFrameConstants::kNewTargetArgOffset)); __ mov(edi, Operand(scratch2, EntryFrameConstants::kFunctionArgOffset)); @@ -662,15 +667,17 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { // Pop return address. __ PopReturnAddressTo(eax); +#ifndef V8_REVERSE_JSARGS // Push receiver. __ Push(FieldOperand(edx, JSGeneratorObject::kReceiverOffset)); +#endif // ----------- S t a t e ------------- // -- eax : return address // -- edx : the JSGeneratorObject to resume // -- edi : generator function // -- esi : generator context - // -- esp[0] : generator receiver + // -- esp[0] : generator receiver, if V8_REVERSE_JSARGS is not set // ----------------------------------- { @@ -682,6 +689,24 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { ecx, SharedFunctionInfo::kFormalParameterCountOffset)); __ mov(ebx, FieldOperand(edx, JSGeneratorObject::kParametersAndRegistersOffset)); +#ifdef V8_REVERSE_JSARGS + { + Label done_loop, loop; + __ mov(edi, ecx); + + __ bind(&loop); + __ dec(edi); + __ j(less, &done_loop); + __ Push( + FieldOperand(ebx, edi, times_tagged_size, FixedArray::kHeaderSize)); + __ jmp(&loop); + + __ bind(&done_loop); + } + + // Push receiver. + __ Push(FieldOperand(edx, JSGeneratorObject::kReceiverOffset)); +#else { Label done_loop, loop; __ Set(edi, 0); @@ -689,13 +714,14 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ bind(&loop); __ cmp(edi, ecx); __ j(greater_equal, &done_loop); - __ Push(FieldOperand(ebx, edi, times_system_pointer_size, - FixedArray::kHeaderSize)); + __ Push( + FieldOperand(ebx, edi, times_tagged_size, FixedArray::kHeaderSize)); __ add(edi, Immediate(1)); __ jmp(&loop); __ bind(&done_loop); } +#endif // Restore registers. __ mov(edi, FieldOperand(edx, JSGeneratorObject::kFunctionOffset)); @@ -1211,11 +1237,19 @@ static void Generate_InterpreterPushArgs(MacroAssembler* masm, Label loop_header, loop_check; __ jmp(&loop_check); __ bind(&loop_header); +#ifdef V8_REVERSE_JSARGS + __ Push(Operand(array_limit, 0)); + __ bind(&loop_check); + __ add(array_limit, Immediate(kSystemPointerSize)); + __ cmp(array_limit, start_address); + __ j(below_equal, &loop_header, Label::kNear); +#else __ Push(Operand(start_address, 0)); __ sub(start_address, Immediate(kSystemPointerSize)); __ bind(&loop_check); __ cmp(start_address, array_limit); __ j(above, &loop_header, Label::kNear); +#endif } // static @@ -1235,6 +1269,14 @@ void Builtins::Generate_InterpreterPushArgsThenCallImpl( const Register argv = ecx; Label stack_overflow; + +#ifdef V8_REVERSE_JSARGS + if (mode == InterpreterPushArgsMode::kWithFinalSpread) { + // The spread argument should not be pushed. + __ dec(eax); + } +#endif + // Add a stack check before pushing the arguments. Generate_StackOverflowCheck(masm, eax, scratch, &stack_overflow, true); @@ -1242,11 +1284,37 @@ void Builtins::Generate_InterpreterPushArgsThenCallImpl( // Compute the expected number of arguments. __ mov(scratch, eax); - __ add(scratch, Immediate(1)); // Add one for receiver. // Pop return address to allow tail-call after pushing arguments. __ PopReturnAddressTo(eax); +#ifdef V8_REVERSE_JSARGS + if (receiver_mode != ConvertReceiverMode::kNullOrUndefined) { + __ add(scratch, Immediate(1)); // Add one for receiver. + } + + // Find the address of the last argument. + __ shl(scratch, kSystemPointerSizeLog2); + __ neg(scratch); + __ add(scratch, argv); + + if (mode == InterpreterPushArgsMode::kWithFinalSpread) { + __ movd(xmm1, scratch); + Generate_InterpreterPushArgs(masm, scratch, argv); + // Pass the spread in the register ecx. + __ movd(ecx, xmm1); + __ mov(ecx, Operand(ecx, 0)); + } else { + Generate_InterpreterPushArgs(masm, scratch, argv); + } + + // Push "undefined" as the receiver arg if we need to. + if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { + __ PushRoot(RootIndex::kUndefinedValue); + } +#else + __ add(scratch, Immediate(1)); // Add one for receiver. + // Push "undefined" as the receiver arg if we need to. if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { __ PushRoot(RootIndex::kUndefinedValue); @@ -1259,18 +1327,22 @@ void Builtins::Generate_InterpreterPushArgsThenCallImpl( __ add(scratch, argv); Generate_InterpreterPushArgs(masm, scratch, argv); - // Call the target. - if (mode == InterpreterPushArgsMode::kWithFinalSpread) { __ Pop(ecx); // Pass the spread in a register - __ PushReturnAddressFrom(eax); - __ movd(eax, xmm0); // Restore number of arguments. + } +#endif + + __ PushReturnAddressFrom(eax); + __ movd(eax, xmm0); // Restore number of arguments. + + // Call the target. + if (mode == InterpreterPushArgsMode::kWithFinalSpread) { +#ifndef V8_REVERSE_JSARGS __ sub(eax, Immediate(1)); // Subtract one for spread +#endif __ Jump(BUILTIN_CODE(masm->isolate(), CallWithSpread), RelocInfo::CODE_TARGET); } else { - __ PushReturnAddressFrom(eax); - __ movd(eax, xmm0); // Restore number of arguments. __ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny), RelocInfo::CODE_TARGET); } @@ -1328,6 +1400,25 @@ void Generate_InterpreterPushZeroAndArgsAndReturnAddress( // Step 3 copy arguments to correct locations. // Slot meant for receiver contains return address. Reset it so that // we will not incorrectly interpret return address as an object. +#ifdef V8_REVERSE_JSARGS + __ mov(Operand(esp, (num_slots_to_move + 1) * kSystemPointerSize), + Immediate(0)); + __ mov(scratch1, Immediate(0)); + + Label loop_header, loop_check; + __ jmp(&loop_check); + __ bind(&loop_header); + __ mov(scratch2, Operand(start_addr, 0)); + __ mov(Operand(esp, scratch1, times_system_pointer_size, + (num_slots_to_move + 1) * kSystemPointerSize), + scratch2); + __ sub(start_addr, Immediate(kSystemPointerSize)); + __ bind(&loop_check); + __ inc(scratch1); + __ cmp(scratch1, eax); + __ j(less_equal, &loop_header, Label::kNear); + +#else __ mov(Operand(esp, num_args, times_system_pointer_size, (num_slots_to_move + 1) * kSystemPointerSize), Immediate(0)); @@ -1345,6 +1436,7 @@ void Generate_InterpreterPushZeroAndArgsAndReturnAddress( __ bind(&loop_check); __ cmp(scratch1, Immediate(0)); __ j(greater, &loop_header, Label::kNear); +#endif } } // end anonymous namespace @@ -1362,9 +1454,15 @@ void Builtins::Generate_InterpreterPushArgsThenConstructImpl( // -- esp[8] : the new target // -- esp[12] : the constructor // ----------------------------------- - Label stack_overflow; +#ifdef V8_REVERSE_JSARGS + if (mode == InterpreterPushArgsMode::kWithFinalSpread) { + // The spread argument should not be pushed. + __ dec(eax); + } +#endif + // Push arguments and move return address and stack spill slots to the top of // stack. The eax register is readonly. The ecx register will be modified. edx // and edi are used as scratch registers. @@ -1399,11 +1497,17 @@ void Builtins::Generate_InterpreterPushArgsThenConstructImpl( __ Drop(1); // The allocation site is unused. __ Pop(kJavaScriptCallNewTargetRegister); __ Pop(kJavaScriptCallTargetRegister); +#ifdef V8_REVERSE_JSARGS + // Pass the spread in the register ecx, overwriting ecx. + __ mov(ecx, Operand(ecx, 0)); + __ PushReturnAddressFrom(eax); + __ movd(eax, xmm0); // Reload number of arguments. +#else __ Pop(ecx); // Pop the spread (i.e. the first argument), overwriting ecx. __ PushReturnAddressFrom(eax); __ movd(eax, xmm0); // Reload number of arguments. __ sub(eax, Immediate(1)); // The actual argc thus decrements by one. - +#endif __ Jump(BUILTIN_CODE(masm->isolate(), ConstructWithSpread), RelocInfo::CODE_TARGET); } else { @@ -1705,9 +1809,35 @@ void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { // ... // esp[8 * n] : Argument 1 // esp[8 * (n + 1)] : Receiver (callable to call) - // + // NOTE: The order of args are reversed if V8_REVERSE_JSARGS // eax contains the number of arguments, n, not counting the receiver. - // + +#ifdef V8_REVERSE_JSARGS + // 1. Get the callable to call (passed as receiver) from the stack. + { + StackArgumentsAccessor args(eax); + __ mov(edi, args.GetReceiverOperand()); + } + + // 2. Save the return address and drop the callable. + __ PopReturnAddressTo(edx); + __ Pop(ecx); + + // 3. Make sure we have at least one argument. + { + Label done; + __ test(eax, eax); + __ j(not_zero, &done, Label::kNear); + __ PushRoot(RootIndex::kUndefinedValue); + __ inc(eax); + __ bind(&done); + } + + // 4. Push back the return address one slot down on the stack (overwriting the + // original callable), making the original first argument the new receiver. + __ PushReturnAddressFrom(edx); + __ dec(eax); // One fewer argument (first argument is new receiver). +#else // 1. Make sure we have at least one argument. { Label done; @@ -1741,6 +1871,7 @@ void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { __ pop(edx); // Discard copy of return address. __ dec(eax); // One fewer argument (first argument is new receiver). } +#endif // 4. Call the callable. __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); @@ -1956,6 +2087,56 @@ void Builtins::Generate_CallOrConstructVarargs(MacroAssembler* masm, Label stack_overflow; Generate_StackOverflowCheck(masm, kArgumentsLength, edx, &stack_overflow); +#ifdef V8_REVERSE_JSARGS + __ movd(xmm4, kArgumentsList); // Spill the arguments list. + + // Move the arguments already in the stack, + // including the receiver and the return address. + { + Label copy, check; + Register src = edx, current = edi, tmp = esi; + // Update stack pointer. + __ mov(src, esp); + __ lea(tmp, Operand(kArgumentsLength, times_system_pointer_size, 0)); + __ AllocateStackSpace(tmp); + // Include return address and receiver. + __ add(eax, Immediate(2)); + __ mov(current, Immediate(0)); + __ jmp(&check); + // Loop. + __ bind(©); + __ mov(tmp, Operand(src, current, times_system_pointer_size, 0)); + __ mov(Operand(esp, current, times_system_pointer_size, 0), tmp); + __ inc(current); + __ bind(&check); + __ cmp(current, eax); + __ j(less, ©); + __ lea(edx, Operand(esp, eax, times_system_pointer_size, 0)); + } + + __ movd(kArgumentsList, xmm4); // Recover arguments list. + + // Push additional arguments onto the stack. + { + __ Move(eax, Immediate(0)); + Label done, push, loop; + __ bind(&loop); + __ cmp(eax, kArgumentsLength); + __ j(equal, &done, Label::kNear); + // Turn the hole into undefined as we go. + __ mov(edi, FieldOperand(kArgumentsList, eax, times_tagged_size, + FixedArray::kHeaderSize)); + __ CompareRoot(edi, RootIndex::kTheHoleValue); + __ j(not_equal, &push, Label::kNear); + __ LoadRoot(edi, RootIndex::kUndefinedValue); + __ bind(&push); + __ mov(Operand(edx, 0), edi); + __ add(edx, Immediate(kSystemPointerSize)); + __ inc(eax); + __ jmp(&loop); + __ bind(&done); + } +#else // !V8_REVERSE_JSARGS // Push additional arguments onto the stack. { __ PopReturnAddressTo(edx); @@ -1965,7 +2146,7 @@ void Builtins::Generate_CallOrConstructVarargs(MacroAssembler* masm, __ cmp(eax, kArgumentsLength); __ j(equal, &done, Label::kNear); // Turn the hole into undefined as we go. - __ mov(edi, FieldOperand(kArgumentsList, eax, times_system_pointer_size, + __ mov(edi, FieldOperand(kArgumentsList, eax, times_tagged_size, FixedArray::kHeaderSize)); __ CompareRoot(edi, RootIndex::kTheHoleValue); __ j(not_equal, &push, Label::kNear); @@ -1977,6 +2158,7 @@ void Builtins::Generate_CallOrConstructVarargs(MacroAssembler* masm, __ bind(&done); __ PushReturnAddressFrom(edx); } +#endif // !V8_REVERSE_JSARGS // Restore eax, edi and edx. __ movd(esi, xmm3); // Restore the context. @@ -2203,7 +2385,6 @@ void Generate_PushBoundArguments(MacroAssembler* masm) { // -- edx : new.target (only in case of [[Construct]]) // -- edi : target (checked to be a JSBoundFunction) // ----------------------------------- - __ movd(xmm0, edx); // Spill edx. // Load [[BoundArguments]] into ecx and length of that into edx. @@ -2213,6 +2394,67 @@ void Generate_PushBoundArguments(MacroAssembler* masm) { __ SmiUntag(edx); __ test(edx, edx); __ j(zero, &no_bound_arguments); +#ifdef V8_REVERSE_JSARGS + { + // ----------- S t a t e ------------- + // -- eax : the number of arguments (not including the receiver) + // -- xmm0 : new.target (only in case of [[Construct]]) + // -- edi : target (checked to be a JSBoundFunction) + // -- ecx : the [[BoundArguments]] (implemented as FixedArray) + // -- edx : the number of [[BoundArguments]] + // ----------------------------------- + + // Check the stack for overflow. + { + Label done, stack_overflow; + Generate_StackOverflowCheck(masm, edx, ecx, &stack_overflow); + __ jmp(&done); + __ bind(&stack_overflow); + { + FrameScope frame(masm, StackFrame::MANUAL); + __ CallRuntime(Runtime::kThrowStackOverflow); + __ int3(); + } + __ bind(&done); + } + + // Spill context. + __ movd(xmm3, esi); + + // Save Return Adress and Receiver into registers. + __ pop(esi); + __ movd(xmm1, esi); + __ pop(esi); + __ movd(xmm2, esi); + + // Push [[BoundArguments]] to the stack. + { + Label loop; + __ mov(ecx, FieldOperand(edi, JSBoundFunction::kBoundArgumentsOffset)); + __ mov(edx, FieldOperand(ecx, FixedArray::kLengthOffset)); + __ SmiUntag(edx); + // Adjust effective number of arguments (eax contains the number of + // arguments from the call not including receiver plus the number of + // [[BoundArguments]]). + __ add(eax, edx); + __ bind(&loop); + __ dec(edx); + __ mov(esi, FieldOperand(ecx, edx, times_tagged_size, + FixedArray::kHeaderSize)); + __ push(esi); + __ j(greater, &loop); + } + + // Restore Receiver and Return Address. + __ movd(esi, xmm2); + __ push(esi); + __ movd(esi, xmm1); + __ push(esi); + + // Restore context. + __ movd(esi, xmm3); + } +#else // !V8_REVERSE_JSARGS { // ----------- S t a t e ------------- // -- eax : the number of arguments (not including the receiver) @@ -2241,7 +2483,6 @@ void Generate_PushBoundArguments(MacroAssembler* masm) { } __ bind(&done); } - #if V8_OS_WIN // Correctly allocate the stack space that was checked above. { @@ -2291,6 +2532,7 @@ void Generate_PushBoundArguments(MacroAssembler* masm) { // [[BoundArguments]]), so we need to subtract one for the return address. __ dec(eax); } +#endif // !V8_REVERSE_JSARGS __ bind(&no_bound_arguments); __ movd(edx, xmm0); // Reload edx. @@ -2518,7 +2760,11 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // Copy receiver and all expected arguments. const int offset = StandardFrameConstants::kCallerSPOffset; +#ifdef V8_REVERSE_JSARGS + __ lea(edi, Operand(ebp, ecx, times_system_pointer_size, offset)); +#else __ lea(edi, Operand(ebp, eax, times_system_pointer_size, offset)); +#endif __ mov(eax, -1); // account for receiver Label copy; @@ -2543,6 +2789,35 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // Remember expected arguments in xmm0. __ movd(xmm0, kExpectedNumberOfArgumentsRegister); +#ifdef V8_REVERSE_JSARGS + // Remember new target. + __ movd(xmm1, edx); + + // Fill remaining expected arguments with undefined values. + Label fill; + __ mov(edx, ecx); + __ sub(edx, eax); + __ bind(&fill); + __ Push(Immediate(masm->isolate()->factory()->undefined_value())); + __ dec(edx); + __ j(greater, &fill); + + // Copy receiver and all actual arguments. + const int offset = StandardFrameConstants::kCallerSPOffset; + __ lea(edi, Operand(ebp, eax, times_system_pointer_size, offset)); + __ mov(edx, Immediate(-1)); + + Label copy; + __ bind(©); + __ inc(edx); + __ push(Operand(edi, 0)); + __ sub(edi, Immediate(kSystemPointerSize)); + __ cmp(edx, eax); + __ j(less, ©); + + // Restore new.target + __ movd(edx, xmm1); +#else // !V8_REVERSE_JSARGS // Copy receiver and all actual arguments. const int offset = StandardFrameConstants::kCallerSPOffset; __ lea(edi, Operand(ebp, eax, times_system_pointer_size, offset)); @@ -2567,6 +2842,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ Push(Immediate(masm->isolate()->factory()->undefined_value())); __ cmp(eax, kExpectedNumberOfArgumentsRegister); __ j(less, &fill); +#endif // !V8_REVERSE_JSARGS // Restore expected arguments. __ movd(eax, xmm0); @@ -3153,6 +3429,7 @@ void Builtins::Generate_CallApiCallback(MacroAssembler* masm) { // -- esp[argc * 4] : first argument // -- esp[(argc + 1) * 4] : receiver // ----------------------------------- + // NOTE: The order of args are reversed if V8_REVERSE_JSARGS Register api_function_address = edx; Register argc = ecx; @@ -3222,8 +3499,13 @@ void Builtins::Generate_CallApiCallback(MacroAssembler* masm) { // FunctionCallbackInfo::values_ (points at the first varargs argument passed // on the stack). +#ifdef V8_REVERSE_JSARGS + __ lea(scratch, + Operand(scratch, (FCA::kArgsLength + 1) * kSystemPointerSize)); +#else __ lea(scratch, Operand(scratch, argc, times_system_pointer_size, (FCA::kArgsLength - 1) * kSystemPointerSize)); +#endif __ mov(ApiParameterOperand(kApiArgc + 1), scratch); // FunctionCallbackInfo::length_. diff --git a/deps/v8/src/builtins/ic-callable.tq b/deps/v8/src/builtins/ic-callable.tq new file mode 100644 index 00000000000000..95e107a9a69f89 --- /dev/null +++ b/deps/v8/src/builtins/ic-callable.tq @@ -0,0 +1,183 @@ +// Copyright 2020 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. + +namespace ic { +namespace callable { + +extern macro IncrementCallCount(FeedbackVector, uintptr): void; + +macro IsMonomorphic(feedback: MaybeObject, target: JSAny): bool { + return IsWeakReferenceToObject(feedback, target); +} + +macro InSameNativeContext(lhs: Context, rhs: Context): bool { + return LoadNativeContext(lhs) == LoadNativeContext(rhs); +} + +macro MaybeObjectToStrong(maybeObject: MaybeObject): + HeapObject labels IfCleared { + assert(IsWeakOrCleared(maybeObject)); + const weakObject = %RawDownCast>(maybeObject); + return WeakToStrong(weakObject) otherwise IfCleared; +} + +macro TryInitializeAsMonomorphic(implicit context: Context)( + maybeTarget: JSAny, feedbackVector: FeedbackVector, + slotId: uintptr): void labels TransitionToMegamorphic { + const targetHeapObject = + Cast(maybeTarget) otherwise TransitionToMegamorphic; + + let unwrappedTarget = targetHeapObject; + while (Is(unwrappedTarget)) { + unwrappedTarget = + UnsafeCast(unwrappedTarget).bound_target_function; + } + + const unwrappedTargetJSFunction = + Cast(unwrappedTarget) otherwise TransitionToMegamorphic; + if (!InSameNativeContext(unwrappedTargetJSFunction.context, context)) { + goto TransitionToMegamorphic; + } + + StoreWeakReferenceInFeedbackVector(feedbackVector, slotId, targetHeapObject); + ReportFeedbackUpdate(feedbackVector, slotId, 'Call:Initialize'); +} + +macro TransitionToMegamorphic(implicit context: Context)( + feedbackVector: FeedbackVector, slotId: uintptr): void { + StoreFeedbackVectorSlot(feedbackVector, slotId, kMegamorphicSymbol); + ReportFeedbackUpdate(feedbackVector, slotId, 'Call:TransitionMegamorphic'); +} + +macro CollectCallFeedback( + maybeTarget: JSAny, context: Context, + maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void { + const feedbackVector = + Cast(maybeFeedbackVector) otherwise return; + IncrementCallCount(feedbackVector, slotId); + + try { + const feedback: MaybeObject = + LoadFeedbackVectorSlot(feedbackVector, slotId); + if (IsMonomorphic(feedback, maybeTarget)) return; + if (IsMegamorphic(feedback)) return; + if (IsUninitialized(feedback)) goto TryInitializeAsMonomorphic; + + // If cleared, we have a new chance to become monomorphic. + const feedbackValue: HeapObject = + MaybeObjectToStrong(feedback) otherwise TryInitializeAsMonomorphic; + + // Try transitioning to a feedback cell. + // Check if {target}s feedback cell matches the {feedbackValue}. + const target = + Cast(maybeTarget) otherwise TransitionToMegamorphic; + const targetFeedbackCell: FeedbackCell = target.feedback_cell; + if (TaggedEqual(feedbackValue, targetFeedbackCell)) return; + + // Check if {target} and {feedbackValue} are both JSFunctions with + // the same feedback vector cell, and that those functions were + // actually compiled already. + const feedbackValueJSFunction = + Cast(feedbackValue) otherwise TransitionToMegamorphic; + const feedbackCell: FeedbackCell = feedbackValueJSFunction.feedback_cell; + if (!TaggedEqual(feedbackCell, targetFeedbackCell)) + goto TransitionToMegamorphic; + + StoreWeakReferenceInFeedbackVector(feedbackVector, slotId, feedbackCell); + ReportFeedbackUpdate(feedbackVector, slotId, 'Call:FeedbackVectorCell'); + } label TryInitializeAsMonomorphic { + TryInitializeAsMonomorphic(maybeTarget, feedbackVector, slotId) + otherwise TransitionToMegamorphic; + } label TransitionToMegamorphic { + TransitionToMegamorphic(feedbackVector, slotId); + } +} + +macro CollectInstanceOfFeedback( + maybeTarget: JSAny, context: Context, + maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void { + const feedbackVector = + Cast(maybeFeedbackVector) otherwise return; + // Note: The call count is not incremented. + + try { + const feedback: MaybeObject = + LoadFeedbackVectorSlot(feedbackVector, slotId); + if (IsMonomorphic(feedback, maybeTarget)) return; + if (IsMegamorphic(feedback)) return; + if (IsUninitialized(feedback)) goto TryInitializeAsMonomorphic; + + // If cleared, we have a new chance to become monomorphic. + const _feedbackValue: HeapObject = + MaybeObjectToStrong(feedback) otherwise TryInitializeAsMonomorphic; + + goto TransitionToMegamorphic; + } label TryInitializeAsMonomorphic { + TryInitializeAsMonomorphic(maybeTarget, feedbackVector, slotId) + otherwise TransitionToMegamorphic; + } label TransitionToMegamorphic { + TransitionToMegamorphic(feedbackVector, slotId); + } +} + +macro BothTaggedEqualArrayFunction(implicit context: Context)( + first: JSAny, second: JSAny): bool { + return TaggedEqual(first, second) && TaggedEqual(second, GetArrayFunction()); +} + +extern macro CreateAllocationSiteInFeedbackVector( + FeedbackVector, uintptr): AllocationSite; + +macro CollectConstructFeedback(implicit context: Context)( + target: JSAny, newTarget: JSAny, + maybeFeedbackVector: Undefined|FeedbackVector, + slotId: uintptr): never labels ConstructGeneric, + ConstructArray(AllocationSite) { + const feedbackVector = Cast(maybeFeedbackVector) + otherwise goto ConstructGeneric; + IncrementCallCount(feedbackVector, slotId); + + try { + const feedback: MaybeObject = + LoadFeedbackVectorSlot(feedbackVector, slotId); + if (IsMonomorphic(feedback, newTarget)) goto ConstructGeneric; + if (IsMegamorphic(feedback)) goto ConstructGeneric; + if (IsUninitialized(feedback)) goto TryInitializeAsMonomorphic; + + if (!IsWeakOrCleared(feedback)) { + const feedbackAsStrong = %RawDownCast(feedback); + if (Is(feedbackAsStrong)) { + if (BothTaggedEqualArrayFunction(target, newTarget)) { + goto ConstructArray(UnsafeCast(feedbackAsStrong)); + } + goto TransitionToMegamorphic; + } + } + + // If cleared, we have a new chance to become monomorphic. + const _feedbackValue: HeapObject = + MaybeObjectToStrong(feedback) otherwise TryInitializeAsMonomorphic; + + goto TransitionToMegamorphic; + } label TryInitializeAsMonomorphic { + if (BothTaggedEqualArrayFunction(target, newTarget)) { + // In this case we can skip unwrapping and context validation since we + // know the target is the current context's array function. + const allocationSite = + CreateAllocationSiteInFeedbackVector(feedbackVector, slotId); + ReportFeedbackUpdate( + feedbackVector, slotId, 'Construct:CreateAllocationSite'); + goto ConstructArray(allocationSite); + } + + TryInitializeAsMonomorphic(newTarget, feedbackVector, slotId) + otherwise TransitionToMegamorphic; + } label TransitionToMegamorphic { + TransitionToMegamorphic(feedbackVector, slotId); + } + goto ConstructGeneric; +} + +} // namespace callable +} // namespace ic diff --git a/deps/v8/src/builtins/ic.tq b/deps/v8/src/builtins/ic.tq new file mode 100644 index 00000000000000..f6fecc557f479f --- /dev/null +++ b/deps/v8/src/builtins/ic.tq @@ -0,0 +1,59 @@ +// Copyright 2020 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. + +namespace ic { + +// --- The public interface (forwards to the actual implementation). + +@export +macro CollectCallFeedback( + maybeTarget: JSAny, context: Context, + maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void { + callable::CollectCallFeedback( + maybeTarget, context, maybeFeedbackVector, slotId); +} + +@export +macro CollectInstanceOfFeedback( + maybeTarget: JSAny, context: Context, + maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void { + callable::CollectInstanceOfFeedback( + maybeTarget, context, maybeFeedbackVector, slotId); +} + +@export +macro CollectConstructFeedback(implicit context: Context)( + target: JSAny, newTarget: JSAny, + maybeFeedbackVector: Undefined|FeedbackVector, + slotId: uintptr): never labels ConstructGeneric, + ConstructArray(AllocationSite) { + callable::CollectConstructFeedback( + target, newTarget, maybeFeedbackVector, slotId) + otherwise ConstructGeneric, ConstructArray; +} + +// --- Common functionality. + +extern macro MegamorphicSymbolConstant(): Symbol; +extern macro UninitializedSymbolConstant(): Symbol; + +const kMegamorphicSymbol: Symbol = MegamorphicSymbolConstant(); +const kUninitializedSymbol: Symbol = UninitializedSymbolConstant(); + +macro IsMegamorphic(feedback: MaybeObject): bool { + return TaggedEqual(feedback, kMegamorphicSymbol); +} + +macro IsUninitialized(feedback: MaybeObject): bool { + return TaggedEqual(feedback, kUninitializedSymbol); +} + +extern macro LoadFeedbackVectorSlot(FeedbackVector, uintptr): MaybeObject; +extern macro StoreFeedbackVectorSlot( + FeedbackVector, uintptr, MaybeObject): void; +extern macro StoreWeakReferenceInFeedbackVector( + FeedbackVector, uintptr, HeapObject): MaybeObject; +extern macro ReportFeedbackUpdate(FeedbackVector, uintptr, constexpr string); + +} // namespace ic diff --git a/deps/v8/src/builtins/internal-coverage.tq b/deps/v8/src/builtins/internal-coverage.tq index 65cb207eaae8d0..07bfc40d8f277c 100644 --- a/deps/v8/src/builtins/internal-coverage.tq +++ b/deps/v8/src/builtins/internal-coverage.tq @@ -6,33 +6,34 @@ namespace internal_coverage { - macro GetCoverageInfo(implicit context: Context)(function: JSFunction): - CoverageInfo labels IfNoCoverageInfo { - const shared: SharedFunctionInfo = function.shared_function_info; - const debugInfo = Cast(shared.script_or_debug_info) - otherwise goto IfNoCoverageInfo; - - if (!SmiUntag(debugInfo.flags).has_coverage_info) goto IfNoCoverageInfo; - return UnsafeCast(debugInfo.coverage_info); - } - - macro IncrementBlockCount(implicit context: Context)( - coverageInfo: CoverageInfo, slot: Smi) { - assert(Convert(slot) < coverageInfo.slot_count); - ++coverageInfo.slots[slot].block_count; - } - - builtin IncBlockCounter(implicit context: Context)( - function: JSFunction, coverageArraySlotIndex: Smi): Undefined { - // It's quite possible that a function contains IncBlockCounter bytecodes, - // but no coverage info exists. This happens e.g. by selecting the - // best-effort coverage collection mode, which triggers deletion of all - // coverage infos in order to avoid memory leaks. - - const coverageInfo: CoverageInfo = - GetCoverageInfo(function) otherwise return Undefined; - IncrementBlockCount(coverageInfo, coverageArraySlotIndex); - return Undefined; - } +macro GetCoverageInfo(implicit context: Context)(function: JSFunction): + CoverageInfo labels IfNoCoverageInfo { + const shared: SharedFunctionInfo = function.shared_function_info; + const debugInfo = Cast(shared.script_or_debug_info) + otherwise goto IfNoCoverageInfo; + + if (!debugInfo.flags.has_coverage_info) goto IfNoCoverageInfo; + return UnsafeCast(debugInfo.coverage_info); +} + +macro IncrementBlockCount(implicit context: Context)( + coverageInfo: CoverageInfo, slot: Smi) { + assert(Convert(slot) < coverageInfo.slot_count); + ++coverageInfo.slots[slot].block_count; +} + +builtin IncBlockCounter( + implicit context: + Context)(function: JSFunction, coverageArraySlotIndex: Smi): Undefined { + // It's quite possible that a function contains IncBlockCounter bytecodes, + // but no coverage info exists. This happens e.g. by selecting the + // best-effort coverage collection mode, which triggers deletion of all + // coverage infos in order to avoid memory leaks. + + const coverageInfo: CoverageInfo = + GetCoverageInfo(function) otherwise return Undefined; + IncrementBlockCount(coverageInfo, coverageArraySlotIndex); + return Undefined; +} } // namespace internal_coverage diff --git a/deps/v8/src/builtins/iterator.tq b/deps/v8/src/builtins/iterator.tq index 272a2a7db83d5b..1354c434e7f51a 100644 --- a/deps/v8/src/builtins/iterator.tq +++ b/deps/v8/src/builtins/iterator.tq @@ -5,105 +5,98 @@ #include 'src/builtins/builtins-iterator-gen.h' namespace iterator { - // Returned from IteratorBuiltinsAssembler::GetIterator(). - @export - struct IteratorRecord { - // iteratorRecord.[[Iterator]] - object: JSReceiver; - - // iteratorRecord.[[NextMethod]] - next: JSAny; - } +// Returned from IteratorBuiltinsAssembler::GetIterator(). +@export +struct IteratorRecord { + // iteratorRecord.[[Iterator]] + object: JSReceiver; + + // iteratorRecord.[[NextMethod]] + next: JSAny; +} - extern macro IteratorBuiltinsAssembler::FastIterableToList( - implicit context: Context)(JSAny): JSArray labels Slow; - - extern macro IteratorBuiltinsAssembler::GetIteratorMethod( - implicit context: Context)(JSAny): JSAny; - extern macro IteratorBuiltinsAssembler::GetIterator( - implicit context: Context)(JSAny): IteratorRecord; - extern macro IteratorBuiltinsAssembler::GetIterator( - implicit context: Context)(JSAny, JSAny): IteratorRecord; - - extern macro IteratorBuiltinsAssembler::IteratorStep( - implicit context: Context)(IteratorRecord): JSReceiver - labels Done; - extern macro IteratorBuiltinsAssembler::IteratorStep( - implicit context: Context)(IteratorRecord, Map): JSReceiver - labels Done; - - extern macro IteratorBuiltinsAssembler::IteratorValue( - implicit context: Context)(JSReceiver): JSAny; - extern macro IteratorBuiltinsAssembler::IteratorValue( - implicit context: Context)(JSReceiver, Map): JSAny; - - extern macro IteratorBuiltinsAssembler::IteratorCloseOnException( - implicit context: Context)(IteratorRecord, JSAny): never; - - extern macro IteratorBuiltinsAssembler::IterableToList( - implicit context: Context)(JSAny, JSAny): JSArray; - - extern macro IteratorBuiltinsAssembler::StringListFromIterable( - implicit context: Context)(JSAny): JSArray; - - extern builtin IterableToListMayPreserveHoles(implicit context: - Context)(JSAny, JSAny); - extern builtin IterableToListWithSymbolLookup(implicit context: - Context)(JSAny); - - transitioning builtin GetIteratorWithFeedback( - context: Context, receiver: JSAny, loadSlot: TaggedIndex, - callSlot: TaggedIndex, feedback: Undefined|FeedbackVector): JSAny { - let iteratorMethod: JSAny; - typeswitch (feedback) { - case (Undefined): { - iteratorMethod = GetProperty(receiver, IteratorSymbolConstant()); - } - case (feedback: FeedbackVector): { - iteratorMethod = LoadIC( - context, receiver, IteratorSymbolConstant(), loadSlot, feedback); - } +extern macro IteratorBuiltinsAssembler::FastIterableToList( + implicit context: Context)(JSAny): JSArray labels Slow; + +extern macro IteratorBuiltinsAssembler::GetIteratorMethod( + implicit context: Context)(JSAny): JSAny; +extern macro IteratorBuiltinsAssembler::GetIterator(implicit context: Context)( + JSAny): IteratorRecord; +extern macro IteratorBuiltinsAssembler::GetIterator(implicit context: Context)( + JSAny, JSAny): IteratorRecord; + +extern macro IteratorBuiltinsAssembler::IteratorStep(implicit context: Context)( + IteratorRecord): JSReceiver + labels Done; +extern macro IteratorBuiltinsAssembler::IteratorStep(implicit context: Context)( + IteratorRecord, Map): JSReceiver + labels Done; + +extern macro IteratorBuiltinsAssembler::IteratorValue( + implicit context: Context)(JSReceiver): JSAny; +extern macro IteratorBuiltinsAssembler::IteratorValue( + implicit context: Context)(JSReceiver, Map): JSAny; + +extern macro IteratorBuiltinsAssembler::IterableToList( + implicit context: Context)(JSAny, JSAny): JSArray; + +extern macro IteratorBuiltinsAssembler::StringListFromIterable( + implicit context: Context)(JSAny): JSArray; + +extern builtin IterableToListWithSymbolLookup(implicit context: Context)(JSAny): + JSArray; +extern builtin IterableToFixedArrayWithSymbolLookupSlow( + implicit context: Context)(JSAny): FixedArray; + +transitioning builtin GetIteratorWithFeedback( + context: Context, receiver: JSAny, loadSlot: TaggedIndex, + callSlot: TaggedIndex, feedback: Undefined|FeedbackVector): JSAny { + let iteratorMethod: JSAny; + typeswitch (feedback) { + case (Undefined): { + iteratorMethod = GetProperty(receiver, IteratorSymbolConstant()); + } + case (feedback: FeedbackVector): { + iteratorMethod = LoadIC( + context, receiver, IteratorSymbolConstant(), loadSlot, feedback); } - // TODO(v8:10047): Use TaggedIndex here once TurboFan supports it. - const callSlotSmi: Smi = TaggedIndexToSmi(callSlot); - return CallIteratorWithFeedback( - context, receiver, iteratorMethod, callSlotSmi, feedback); } + // TODO(v8:10047): Use TaggedIndex here once TurboFan supports it. + const callSlotSmi: Smi = TaggedIndexToSmi(callSlot); + return CallIteratorWithFeedback( + context, receiver, iteratorMethod, callSlotSmi, feedback); +} - transitioning builtin CallIteratorWithFeedback( - context: Context, receiver: JSAny, iteratorMethod: JSAny, callSlot: Smi, - feedback: Undefined|FeedbackVector): JSAny { - const callSlotUnTagged: uintptr = Unsigned(SmiUntag(callSlot)); - CollectCallFeedback(iteratorMethod, context, feedback, callSlotUnTagged); - const iteratorCallable: Callable = Cast(iteratorMethod) - otherwise ThrowCalledNonCallable(iteratorMethod); - return Call(context, iteratorCallable, receiver); - } +transitioning builtin CallIteratorWithFeedback( + context: Context, receiver: JSAny, iteratorMethod: JSAny, callSlot: Smi, + feedback: Undefined|FeedbackVector): JSAny { + const callSlotUnTagged: uintptr = Unsigned(SmiUntag(callSlot)); + ic::CollectCallFeedback(iteratorMethod, context, feedback, callSlotUnTagged); + const iteratorCallable: Callable = Cast(iteratorMethod) + otherwise ThrowCalledNonCallable(iteratorMethod); + return Call(context, iteratorCallable, receiver); +} - transitioning - macro IteratorCloseOnException(implicit context: Context)( - iterator: IteratorRecord, exception: Object): never labels - IfException(Object) { - // Let return be ? GetMethod(iterator, "return"). - let method: JSAny; - try { - method = GetProperty(iterator.object, kReturnString); - } catch (e) { - goto IfException(e); - } +// https://tc39.es/ecma262/#sec-iteratorclose +@export +transitioning macro IteratorCloseOnException(implicit context: Context)( + iterator: IteratorRecord) { + try { + // 4. Let innerResult be GetMethod(iterator, "return"). + const method = GetProperty(iterator.object, kReturnString); - // If return is undefined, return Completion(completion). - if (method == Undefined || method == Null) goto IfException(exception); + // 5. If innerResult.[[Type]] is normal, then + // a. Let return be innerResult.[[Value]]. + // b. If return is undefined, return Completion(completion). + if (method == Undefined || method == Null) return; - // Let innerResult be Call(return, iterator, « »). + // c. Set innerResult to Call(return, iterator). // If an exception occurs, the original exception remains bound - try { - Call(context, method, iterator.object); - } catch (_e) { - goto IfException(exception); - } - - // (If completion.[[Type]] is throw) return Completion(completion). - goto IfException(exception); + Call(context, method, iterator.object); + } catch (_e) { + // Swallow the exception. } + + // (If completion.[[Type]] is throw) return Completion(completion). +} } diff --git a/deps/v8/src/builtins/math.tq b/deps/v8/src/builtins/math.tq index 8c9998906a533e..0586f432f5b0ec 100644 --- a/deps/v8/src/builtins/math.tq +++ b/deps/v8/src/builtins/math.tq @@ -4,468 +4,456 @@ namespace math { - extern transitioning builtin - NonNumberToNumber(implicit context: Context)(HeapObject): Number; - - transitioning macro ReduceToSmiOrFloat64(implicit context: Context)(x: JSAny): - never - labels SmiResult(Smi), Float64Result(float64) { - let x1: JSAny = x; - while (true) { - typeswitch (x1) { - case (s: Smi): { - goto SmiResult(s); - } - case (h: HeapNumber): { - goto Float64Result(Convert(h)); - } - case (a: JSAnyNotNumber): { - x1 = NonNumberToNumber(a); - } +extern transitioning builtin +NonNumberToNumber(implicit context: Context)(HeapObject): Number; + +transitioning macro ReduceToSmiOrFloat64(implicit context: Context)(x: JSAny): + never + labels SmiResult(Smi), Float64Result(float64) { + let x1: JSAny = x; + while (true) { + typeswitch (x1) { + case (s: Smi): { + goto SmiResult(s); + } + case (h: HeapNumber): { + goto Float64Result(Convert(h)); + } + case (a: JSAnyNotNumber): { + x1 = NonNumberToNumber(a); } } - VerifiedUnreachable(); } + VerifiedUnreachable(); +} - // ES6 #sec-math.abs - extern macro IsIntPtrAbsWithOverflowSupported(): constexpr bool; - extern macro TrySmiSub(Smi, Smi): Smi labels Overflow; - extern macro TrySmiAbs(Smi): Smi labels Overflow; - extern macro Float64Abs(float64): float64; - const kSmiMaxValuePlusOne: - constexpr float64 generates '0.0 - kSmiMinValue'; - - transitioning javascript builtin - MathAbs(js-implicit context: NativeContext)(x: JSAny): Number { +// ES6 #sec-math.abs +extern macro IsIntPtrAbsWithOverflowSupported(): constexpr bool; +extern macro TrySmiSub(Smi, Smi): Smi labels Overflow; +extern macro TrySmiAbs(Smi): Smi labels Overflow; +extern macro Float64Abs(float64): float64; +const kSmiMaxValuePlusOne: + constexpr float64 generates '0.0 - kSmiMinValue'; + +transitioning javascript builtin +MathAbs(js-implicit context: NativeContext)(x: JSAny): Number { + try { + ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result; + } label SmiResult(s: Smi) { try { - ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result; - } - label SmiResult(s: Smi) { - try { - if constexpr (IsIntPtrAbsWithOverflowSupported()) { - const result: Smi = TrySmiAbs(s) - otherwise SmiOverflow; - return result; + if constexpr (IsIntPtrAbsWithOverflowSupported()) { + const result: Smi = TrySmiAbs(s) + otherwise SmiOverflow; + return result; + } else { + if (0 <= s) { + return s; } else { - if (0 <= s) { - return s; - } else { - const result: Smi = TrySmiSub(0, s) otherwise SmiOverflow; - return result; - } + const result: Smi = TrySmiSub(0, s) otherwise SmiOverflow; + return result; } } - label SmiOverflow { - return NumberConstant(kSmiMaxValuePlusOne); - } - } - label Float64Result(f: float64) { - return Convert(Float64Abs(f)); + } label SmiOverflow { + return NumberConstant(kSmiMaxValuePlusOne); } + } label Float64Result(f: float64) { + return Convert(Float64Abs(f)); } +} - // ES6 #sec-math.ceil - extern macro Float64Ceil(float64): float64; - transitioning javascript builtin - MathCeil(js-implicit context: NativeContext)(x: JSAny): Number { - try { - ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result; - } - label SmiResult(s: Smi) { - return s; - } - label Float64Result(f: float64) { - return Convert(Float64Ceil(f)); - } +// ES6 #sec-math.ceil +extern macro Float64Ceil(float64): float64; +transitioning javascript builtin +MathCeil(js-implicit context: NativeContext)(x: JSAny): Number { + try { + ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result; + } label SmiResult(s: Smi) { + return s; + } label Float64Result(f: float64) { + return Convert(Float64Ceil(f)); } +} - // ES6 #sec-math.floor - extern macro Float64Floor(float64): float64; - transitioning javascript builtin - MathFloor(js-implicit context: NativeContext)(x: JSAny): Number { - try { - ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result; - } - label SmiResult(s: Smi) { - return s; - } - label Float64Result(f: float64) { - return Convert(Float64Floor(f)); - } +// ES6 #sec-math.floor +extern macro Float64Floor(float64): float64; +transitioning javascript builtin +MathFloor(js-implicit context: NativeContext)(x: JSAny): Number { + try { + ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result; + } label SmiResult(s: Smi) { + return s; + } label Float64Result(f: float64) { + return Convert(Float64Floor(f)); } +} - // ES6 #sec-math.round - extern macro Float64Round(float64): float64; - transitioning javascript builtin - MathRound(js-implicit context: NativeContext)(x: JSAny): Number { - try { - ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result; - } - label SmiResult(s: Smi) { - return s; - } - label Float64Result(f: float64) { - return Convert(Float64Round(f)); - } +// ES6 #sec-math.round +extern macro Float64Round(float64): float64; +transitioning javascript builtin +MathRound(js-implicit context: NativeContext)(x: JSAny): Number { + try { + ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result; + } label SmiResult(s: Smi) { + return s; + } label Float64Result(f: float64) { + return Convert(Float64Round(f)); } +} - // ES6 #sec-math.trunc - extern macro Float64Trunc(float64): float64; - transitioning javascript builtin - MathTrunc(js-implicit context: NativeContext)(x: JSAny): Number { - try { - ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result; - } - label SmiResult(s: Smi) { - return s; - } - label Float64Result(f: float64) { - return Convert(Float64Trunc(f)); - } +// ES6 #sec-math.trunc +extern macro Float64Trunc(float64): float64; +transitioning javascript builtin +MathTrunc(js-implicit context: NativeContext)(x: JSAny): Number { + try { + ReduceToSmiOrFloat64(x) otherwise SmiResult, Float64Result; + } label SmiResult(s: Smi) { + return s; + } label Float64Result(f: float64) { + return Convert(Float64Trunc(f)); } +} - // ES6 #sec-math.pow - extern macro Float64Pow(float64, float64): float64; - extern macro TruncateTaggedToFloat64(implicit context: Context)(JSAny): - float64; - - @export - macro MathPowImpl(implicit context: Context)(base: JSAny, exponent: JSAny): - Number { - const baseValue: float64 = TruncateTaggedToFloat64(base); - const exponentValue: float64 = TruncateTaggedToFloat64(exponent); - const result: float64 = Float64Pow(baseValue, exponentValue); - return Convert(result); - } +// ES6 #sec-math.pow +extern macro Float64Pow(float64, float64): float64; +extern macro TruncateTaggedToFloat64(implicit context: Context)(JSAny): float64; + +@export +macro MathPowImpl(implicit context: Context)(base: JSAny, exponent: JSAny): + Number { + const baseValue: float64 = TruncateTaggedToFloat64(base); + const exponentValue: float64 = TruncateTaggedToFloat64(exponent); + const result: float64 = Float64Pow(baseValue, exponentValue); + return Convert(result); +} - transitioning javascript builtin - MathPow(js-implicit context: NativeContext)(base: JSAny, exponent: JSAny): - Number { - return MathPowImpl(base, exponent); - } +transitioning javascript builtin +MathPow(js-implicit context: NativeContext)( + base: JSAny, exponent: JSAny): Number { + return MathPowImpl(base, exponent); +} - // ES6 #sec-math.max - extern macro Float64Max(float64, float64): float64; - transitioning javascript builtin - MathMax(js-implicit context: NativeContext)(...arguments): Number { - let result: float64 = MINUS_V8_INFINITY; - const argCount = arguments.length; - for (let i: intptr = 0; i < argCount; i++) { - const doubleValue = TruncateTaggedToFloat64(arguments[i]); - result = Float64Max(result, doubleValue); - } - return Convert(result); - } +// ES6 #sec-math.max +extern macro Float64Max(float64, float64): float64; +transitioning javascript builtin +MathMax(js-implicit context: NativeContext)(...arguments): Number { + let result: float64 = MINUS_V8_INFINITY; + const argCount = arguments.length; + for (let i: intptr = 0; i < argCount; i++) { + const doubleValue = TruncateTaggedToFloat64(arguments[i]); + result = Float64Max(result, doubleValue); + } + return Convert(result); +} - // ES6 #sec-math.min - extern macro Float64Min(float64, float64): float64; - transitioning javascript builtin - MathMin(js-implicit context: NativeContext)(...arguments): Number { - let result: float64 = V8_INFINITY; - const argCount = arguments.length; - for (let i: intptr = 0; i < argCount; i++) { - const doubleValue = TruncateTaggedToFloat64(arguments[i]); - result = Float64Min(result, doubleValue); - } - return Convert(result); - } +// ES6 #sec-math.min +extern macro Float64Min(float64, float64): float64; +transitioning javascript builtin +MathMin(js-implicit context: NativeContext)(...arguments): Number { + let result: float64 = V8_INFINITY; + const argCount = arguments.length; + for (let i: intptr = 0; i < argCount; i++) { + const doubleValue = TruncateTaggedToFloat64(arguments[i]); + result = Float64Min(result, doubleValue); + } + return Convert(result); +} - // ES6 #sec-math.acos - extern macro Float64Acos(float64): float64; +// ES6 #sec-math.acos +extern macro Float64Acos(float64): float64; - transitioning javascript builtin - MathAcos(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Acos(value)); - } +transitioning javascript builtin +MathAcos(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Acos(value)); +} - // ES6 #sec-math.acosh - extern macro Float64Acosh(float64): float64; +// ES6 #sec-math.acosh +extern macro Float64Acosh(float64): float64; - transitioning javascript builtin - MathAcosh(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Acosh(value)); - } +transitioning javascript builtin +MathAcosh(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Acosh(value)); +} - // ES6 #sec-math.asin - extern macro Float64Asin(float64): float64; +// ES6 #sec-math.asin +extern macro Float64Asin(float64): float64; - transitioning javascript builtin - MathAsin(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Asin(value)); - } +transitioning javascript builtin +MathAsin(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Asin(value)); +} - // ES6 #sec-math.asinh - extern macro Float64Asinh(float64): float64; +// ES6 #sec-math.asinh +extern macro Float64Asinh(float64): float64; - transitioning javascript builtin - MathAsinh(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Asinh(value)); - } +transitioning javascript builtin +MathAsinh(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Asinh(value)); +} - // ES6 #sec-math.atan - extern macro Float64Atan(float64): float64; +// ES6 #sec-math.atan +extern macro Float64Atan(float64): float64; - transitioning javascript builtin - MathAtan(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Atan(value)); - } +transitioning javascript builtin +MathAtan(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Atan(value)); +} - // ES6 #sec-math.atan2 - extern macro Float64Atan2(float64, float64): float64; +// ES6 #sec-math.atan2 +extern macro Float64Atan2(float64, float64): float64; - transitioning javascript builtin - MathAtan2(js-implicit context: NativeContext)(y: JSAny, x: JSAny): Number { - const yValue = Convert(ToNumber_Inline(y)); - const xValue = Convert(ToNumber_Inline(x)); - return Convert(Float64Atan2(yValue, xValue)); - } +transitioning javascript builtin +MathAtan2(js-implicit context: NativeContext)(y: JSAny, x: JSAny): Number { + const yValue = Convert(ToNumber_Inline(y)); + const xValue = Convert(ToNumber_Inline(x)); + return Convert(Float64Atan2(yValue, xValue)); +} - // ES6 #sec-math.atanh - extern macro Float64Atanh(float64): float64; +// ES6 #sec-math.atanh +extern macro Float64Atanh(float64): float64; - transitioning javascript builtin - MathAtanh(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Atanh(value)); - } +transitioning javascript builtin +MathAtanh(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Atanh(value)); +} - // ES6 #sec-math.cbrt - extern macro Float64Cbrt(float64): float64; +// ES6 #sec-math.cbrt +extern macro Float64Cbrt(float64): float64; - transitioning javascript builtin - MathCbrt(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Cbrt(value)); - } +transitioning javascript builtin +MathCbrt(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Cbrt(value)); +} - // ES6 #sec-math.clz32 - extern macro Word32Clz(int32): int32; +// ES6 #sec-math.clz32 +extern macro Word32Clz(int32): int32; - transitioning javascript builtin - MathClz32(js-implicit context: NativeContext)(x: JSAny): Number { - const value: int32 = Convert(ToNumber_Inline(x)); - return Convert(Word32Clz(value)); - } +transitioning javascript builtin +MathClz32(js-implicit context: NativeContext)(x: JSAny): Number { + const value: int32 = Convert(ToNumber_Inline(x)); + return Convert(Word32Clz(value)); +} - // ES6 #sec-math.cos - extern macro Float64Cos(float64): float64; +// ES6 #sec-math.cos +extern macro Float64Cos(float64): float64; - transitioning javascript builtin - MathCos(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Cos(value)); - } +transitioning javascript builtin +MathCos(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Cos(value)); +} - // ES6 #sec-math.cosh - extern macro Float64Cosh(float64): float64; +// ES6 #sec-math.cosh +extern macro Float64Cosh(float64): float64; - transitioning javascript builtin - MathCosh(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Cosh(value)); - } +transitioning javascript builtin +MathCosh(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Cosh(value)); +} - // ES6 #sec-math.exp - extern macro Float64Exp(float64): float64; +// ES6 #sec-math.exp +extern macro Float64Exp(float64): float64; - transitioning javascript builtin - MathExp(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Exp(value)); - } +transitioning javascript builtin +MathExp(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Exp(value)); +} - // ES6 #sec-math.expm1 - extern macro Float64Expm1(float64): float64; +// ES6 #sec-math.expm1 +extern macro Float64Expm1(float64): float64; - transitioning javascript builtin - MathExpm1(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Expm1(value)); - } +transitioning javascript builtin +MathExpm1(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Expm1(value)); +} - // ES6 #sec-math.fround - transitioning javascript builtin - MathFround(js-implicit context: NativeContext)(x: JSAny): Number { - const x32 = Convert(ToNumber_Inline(x)); - const x64 = Convert(x32); - return Convert(x64); - } +// ES6 #sec-math.fround +transitioning javascript builtin +MathFround(js-implicit context: NativeContext)(x: JSAny): Number { + const x32 = Convert(ToNumber_Inline(x)); + const x64 = Convert(x32); + return Convert(x64); +} - // ES6 #sec-math.imul - transitioning javascript builtin - MathImul(js-implicit context: NativeContext)(x: JSAny, y: JSAny): Number { - const x = Convert(ToNumber_Inline(x)); - const y = Convert(ToNumber_Inline(y)); - return Convert(x * y); - } +// ES6 #sec-math.imul +transitioning javascript builtin +MathImul(js-implicit context: NativeContext)(x: JSAny, y: JSAny): Number { + const x = Convert(ToNumber_Inline(x)); + const y = Convert(ToNumber_Inline(y)); + return Convert(x * y); +} - // ES6 #sec-math.log - extern macro Float64Log(float64): float64; +// ES6 #sec-math.log +extern macro Float64Log(float64): float64; - transitioning javascript builtin - MathLog(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Log(value)); - } +transitioning javascript builtin +MathLog(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Log(value)); +} - // ES6 #sec-math.log1p - extern macro Float64Log1p(float64): float64; +// ES6 #sec-math.log1p +extern macro Float64Log1p(float64): float64; - transitioning javascript builtin - MathLog1p(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Log1p(value)); - } +transitioning javascript builtin +MathLog1p(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Log1p(value)); +} - // ES6 #sec-math.log10 - extern macro Float64Log10(float64): float64; +// ES6 #sec-math.log10 +extern macro Float64Log10(float64): float64; - transitioning javascript builtin - MathLog10(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Log10(value)); - } +transitioning javascript builtin +MathLog10(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Log10(value)); +} - // ES6 #sec-math.log2 - extern macro Float64Log2(float64): float64; +// ES6 #sec-math.log2 +extern macro Float64Log2(float64): float64; - transitioning javascript builtin - MathLog2(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Log2(value)); - } +transitioning javascript builtin +MathLog2(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Log2(value)); +} - // ES6 #sec-math.sin - extern macro Float64Sin(float64): float64; +// ES6 #sec-math.sin +extern macro Float64Sin(float64): float64; - transitioning javascript builtin - MathSin(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Sin(value)); - } +transitioning javascript builtin +MathSin(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Sin(value)); +} - // ES6 #sec-math.sign - transitioning javascript builtin - MathSign(js-implicit context: NativeContext)(x: JSAny): Number { - const num = ToNumber_Inline(x); - const value = Convert(num); +// ES6 #sec-math.sign +transitioning javascript builtin +MathSign(js-implicit context: NativeContext)(x: JSAny): Number { + const num = ToNumber_Inline(x); + const value = Convert(num); - if (value < 0) { - return -1; - } else if (value > 0) { - return 1; - } else { - return num; - } + if (value < 0) { + return -1; + } else if (value > 0) { + return 1; + } else { + return num; } +} - // ES6 #sec-math.sinh - extern macro Float64Sinh(float64): float64; +// ES6 #sec-math.sinh +extern macro Float64Sinh(float64): float64; - transitioning javascript builtin - MathSinh(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Sinh(value)); - } +transitioning javascript builtin +MathSinh(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Sinh(value)); +} - // ES6 #sec-math.sqrt - extern macro Float64Sqrt(float64): float64; +// ES6 #sec-math.sqrt +extern macro Float64Sqrt(float64): float64; - transitioning javascript builtin - MathSqrt(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Sqrt(value)); - } +transitioning javascript builtin +MathSqrt(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Sqrt(value)); +} - // ES6 #sec-math.tan - extern macro Float64Tan(float64): float64; +// ES6 #sec-math.tan +extern macro Float64Tan(float64): float64; - transitioning javascript builtin - MathTan(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Tan(value)); - } +transitioning javascript builtin +MathTan(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Tan(value)); +} - // ES6 #sec-math.tanh - extern macro Float64Tanh(float64): float64; +// ES6 #sec-math.tanh +extern macro Float64Tanh(float64): float64; - transitioning javascript builtin - MathTanh(js-implicit context: NativeContext)(x: JSAny): Number { - const value = Convert(ToNumber_Inline(x)); - return Convert(Float64Tanh(value)); - } +transitioning javascript builtin +MathTanh(js-implicit context: NativeContext)(x: JSAny): Number { + const value = Convert(ToNumber_Inline(x)); + return Convert(Float64Tanh(value)); +} - // ES6 #sec-math.hypot - transitioning javascript builtin - MathHypot(js-implicit context: NativeContext, receiver: JSAny)(...arguments): - Number { - const length = arguments.length; - if (length == 0) { - return 0; - } - const absValues = AllocateZeroedFixedDoubleArray(length); - let oneArgIsNaN: bool = false; - let max: float64 = 0; - for (let i: intptr = 0; i < length; ++i) { - const value = Convert(ToNumber_Inline(arguments[i])); - if (Float64IsNaN(value)) { - oneArgIsNaN = true; - } else { - const absValue = Float64Abs(value); - absValues.floats[i] = Convert(absValue); - if (absValue > max) { - max = absValue; - } +// ES6 #sec-math.hypot +transitioning javascript builtin +MathHypot( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): Number { + const length = arguments.length; + if (length == 0) { + return 0; + } + const absValues = AllocateZeroedFixedDoubleArray(length); + let oneArgIsNaN: bool = false; + let max: float64 = 0; + for (let i: intptr = 0; i < length; ++i) { + const value = Convert(ToNumber_Inline(arguments[i])); + if (Float64IsNaN(value)) { + oneArgIsNaN = true; + } else { + const absValue = Float64Abs(value); + absValues.floats[i] = Convert(absValue); + if (absValue > max) { + max = absValue; } } - if (max == V8_INFINITY) { - return V8_INFINITY; - } else if (oneArgIsNaN) { - return kNaN; - } else if (max == 0) { - return 0; - } - assert(max > 0); - - // Kahan summation to avoid rounding errors. - // Normalize the numbers to the largest one to avoid overflow. - let sum: float64 = 0; - let compensation: float64 = 0; - for (let i: intptr = 0; i < length; ++i) { - const n = absValues.floats[i].ValueUnsafeAssumeNotHole() / max; - const summand = n * n - compensation; - const preliminary = sum + summand; - compensation = (preliminary - sum) - summand; - sum = preliminary; - } - return Convert(Float64Sqrt(sum) * max); } + if (max == V8_INFINITY) { + return V8_INFINITY; + } else if (oneArgIsNaN) { + return kNaN; + } else if (max == 0) { + return 0; + } + assert(max > 0); + + // Kahan summation to avoid rounding errors. + // Normalize the numbers to the largest one to avoid overflow. + let sum: float64 = 0; + let compensation: float64 = 0; + for (let i: intptr = 0; i < length; ++i) { + const n = absValues.floats[i].ValueUnsafeAssumeNotHole() / max; + const summand = n * n - compensation; + const preliminary = sum + summand; + compensation = (preliminary - sum) - summand; + sum = preliminary; + } + return Convert(Float64Sqrt(sum) * max); +} - // ES6 #sec-math.random - extern macro RefillMathRandom(NativeContext): Smi; - - transitioning javascript builtin - MathRandom(js-implicit context: NativeContext, receiver: JSAny)(): Number { - let smiIndex: Smi = - Cast(context[NativeContextSlot::MATH_RANDOM_INDEX_INDEX]) - otherwise unreachable; - if (smiIndex == 0) { - // refill math random. - smiIndex = RefillMathRandom(context); - } - const newSmiIndex: Smi = smiIndex - 1; - context[NativeContextSlot::MATH_RANDOM_INDEX_INDEX] = newSmiIndex; - - const array: FixedDoubleArray = Cast( - context[NativeContextSlot::MATH_RANDOM_CACHE_INDEX]) - otherwise unreachable; - const random: float64 = - array.floats[Convert(newSmiIndex)].ValueUnsafeAssumeNotHole(); - return AllocateHeapNumberWithValue(random); - } +// ES6 #sec-math.random +extern macro RefillMathRandom(NativeContext): Smi; + +transitioning javascript builtin +MathRandom(js-implicit context: NativeContext, receiver: JSAny)(): Number { + let smiIndex: Smi = + Cast(context[NativeContextSlot::MATH_RANDOM_INDEX_INDEX]) + otherwise unreachable; + if (smiIndex == 0) { + // refill math random. + smiIndex = RefillMathRandom(context); + } + const newSmiIndex: Smi = smiIndex - 1; + context[NativeContextSlot::MATH_RANDOM_INDEX_INDEX] = newSmiIndex; + + const array: FixedDoubleArray = Cast( + context[NativeContextSlot::MATH_RANDOM_CACHE_INDEX]) + otherwise unreachable; + const random: float64 = + array.floats[Convert(newSmiIndex)].ValueUnsafeAssumeNotHole(); + return AllocateHeapNumberWithValue(random); +} } diff --git a/deps/v8/src/builtins/mips/builtins-mips.cc b/deps/v8/src/builtins/mips/builtins-mips.cc index cb1a86db2f4734..c98961f2ad4556 100644 --- a/deps/v8/src/builtins/mips/builtins-mips.cc +++ b/deps/v8/src/builtins/mips/builtins-mips.cc @@ -911,16 +911,25 @@ static void MaybeOptimizeCode(MacroAssembler* masm, Register feedback_vector, // Advance the current bytecode offset. This simulates what all bytecode // handlers do upon completion of the underlying operation. Will bail out to a -// label if the bytecode (without prefix) is a return bytecode. +// label if the bytecode (without prefix) is a return bytecode. Will not advance +// the bytecode offset if the current bytecode is a JumpLoop, instead just +// re-executing the JumpLoop to jump to the correct bytecode. static void AdvanceBytecodeOffsetOrReturn(MacroAssembler* masm, Register bytecode_array, Register bytecode_offset, Register bytecode, Register scratch1, - Register scratch2, Label* if_return) { + Register scratch2, Register scratch3, + Label* if_return) { Register bytecode_size_table = scratch1; - DCHECK(!AreAliased(bytecode_array, bytecode_offset, bytecode_size_table, - bytecode)); + // The bytecode offset value will be increased by one in wide and extra wide + // cases. In the case of having a wide or extra wide JumpLoop bytecode, we + // will restore the original bytecode. In order to simplify the code, we have + // a backup of it. + Register original_bytecode_offset = scratch3; + DCHECK(!AreAliased(bytecode_array, bytecode_offset, bytecode, + bytecode_size_table, original_bytecode_offset)); + __ Move(original_bytecode_offset, bytecode_offset); __ li(bytecode_size_table, ExternalReference::bytecode_size_table_address()); // Check if the bytecode is a Wide or ExtraWide prefix bytecode. @@ -959,10 +968,23 @@ static void AdvanceBytecodeOffsetOrReturn(MacroAssembler* masm, RETURN_BYTECODE_LIST(JUMP_IF_EQUAL) #undef JUMP_IF_EQUAL + // If this is a JumpLoop, re-execute it to perform the jump to the beginning + // of the loop. + Label end, not_jump_loop; + __ Branch(¬_jump_loop, ne, bytecode, + Operand(static_cast(interpreter::Bytecode::kJumpLoop))); + // We need to restore the original bytecode_offset since we might have + // increased it to skip the wide / extra-wide prefix bytecode. + __ Move(bytecode_offset, original_bytecode_offset); + __ jmp(&end); + + __ bind(¬_jump_loop); // Otherwise, load the size of the current bytecode and advance the offset. __ Lsa(scratch2, bytecode_size_table, bytecode, 2); __ lw(scratch2, MemOperand(scratch2)); __ Addu(bytecode_offset, bytecode_offset, scratch2); + + __ bind(&end); } // Generate code for entering a JS function with the interpreter. @@ -1134,7 +1156,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { __ lbu(a1, MemOperand(a1)); AdvanceBytecodeOffsetOrReturn(masm, kInterpreterBytecodeArrayRegister, kInterpreterBytecodeOffsetRegister, a1, a2, a3, - &do_return); + t0, &do_return); __ jmp(&do_dispatch); __ bind(&do_return); @@ -1412,7 +1434,7 @@ void Builtins::Generate_InterpreterEnterBytecodeAdvance(MacroAssembler* masm) { Label if_return; AdvanceBytecodeOffsetOrReturn(masm, kInterpreterBytecodeArrayRegister, kInterpreterBytecodeOffsetRegister, a1, a2, a3, - &if_return); + t0, &if_return); __ bind(&enter_bytecode); // Convert new bytecode offset to a Smi and save in the stackframe. diff --git a/deps/v8/src/builtins/mips64/builtins-mips64.cc b/deps/v8/src/builtins/mips64/builtins-mips64.cc index baf2d5bfec80e2..babe084bb057ce 100644 --- a/deps/v8/src/builtins/mips64/builtins-mips64.cc +++ b/deps/v8/src/builtins/mips64/builtins-mips64.cc @@ -930,15 +930,25 @@ static void MaybeOptimizeCode(MacroAssembler* masm, Register feedback_vector, // Advance the current bytecode offset. This simulates what all bytecode // handlers do upon completion of the underlying operation. Will bail out to a -// label if the bytecode (without prefix) is a return bytecode. +// label if the bytecode (without prefix) is a return bytecode. Will not advance +// the bytecode offset if the current bytecode is a JumpLoop, instead just +// re-executing the JumpLoop to jump to the correct bytecode. static void AdvanceBytecodeOffsetOrReturn(MacroAssembler* masm, Register bytecode_array, Register bytecode_offset, Register bytecode, Register scratch1, - Register scratch2, Label* if_return) { + Register scratch2, Register scratch3, + Label* if_return) { Register bytecode_size_table = scratch1; - DCHECK(!AreAliased(bytecode_array, bytecode_offset, bytecode_size_table, - bytecode)); + + // The bytecode offset value will be increased by one in wide and extra wide + // cases. In the case of having a wide or extra wide JumpLoop bytecode, we + // will restore the original bytecode. In order to simplify the code, we have + // a backup of it. + Register original_bytecode_offset = scratch3; + DCHECK(!AreAliased(bytecode_array, bytecode_offset, bytecode, + bytecode_size_table, original_bytecode_offset)); + __ Move(original_bytecode_offset, bytecode_offset); __ li(bytecode_size_table, ExternalReference::bytecode_size_table_address()); // Check if the bytecode is a Wide or ExtraWide prefix bytecode. @@ -977,10 +987,23 @@ static void AdvanceBytecodeOffsetOrReturn(MacroAssembler* masm, RETURN_BYTECODE_LIST(JUMP_IF_EQUAL) #undef JUMP_IF_EQUAL + // If this is a JumpLoop, re-execute it to perform the jump to the beginning + // of the loop. + Label end, not_jump_loop; + __ Branch(¬_jump_loop, ne, bytecode, + Operand(static_cast(interpreter::Bytecode::kJumpLoop))); + // We need to restore the original bytecode_offset since we might have + // increased it to skip the wide / extra-wide prefix bytecode. + __ Move(bytecode_offset, original_bytecode_offset); + __ jmp(&end); + + __ bind(¬_jump_loop); // Otherwise, load the size of the current bytecode and advance the offset. __ Dlsa(scratch2, bytecode_size_table, bytecode, 2); __ Lw(scratch2, MemOperand(scratch2)); __ Daddu(bytecode_offset, bytecode_offset, scratch2); + + __ bind(&end); } // Generate code for entering a JS function with the interpreter. @@ -1153,7 +1176,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { __ Lbu(a1, MemOperand(a1)); AdvanceBytecodeOffsetOrReturn(masm, kInterpreterBytecodeArrayRegister, kInterpreterBytecodeOffsetRegister, a1, a2, a3, - &do_return); + a4, &do_return); __ jmp(&do_dispatch); __ bind(&do_return); @@ -1430,7 +1453,7 @@ void Builtins::Generate_InterpreterEnterBytecodeAdvance(MacroAssembler* masm) { Label if_return; AdvanceBytecodeOffsetOrReturn(masm, kInterpreterBytecodeArrayRegister, kInterpreterBytecodeOffsetRegister, a1, a2, a3, - &if_return); + a4, &if_return); __ bind(&enter_bytecode); // Convert new bytecode offset to a Smi and save in the stackframe. diff --git a/deps/v8/src/builtins/number.tq b/deps/v8/src/builtins/number.tq index 958cd5f5f65fb2..98680cf5533c5f 100644 --- a/deps/v8/src/builtins/number.tq +++ b/deps/v8/src/builtins/number.tq @@ -3,73 +3,71 @@ // LICENSE file. namespace runtime { - extern transitioning runtime - DoubleToStringWithRadix(implicit context: Context)(Number, Number): String; +extern transitioning runtime +DoubleToStringWithRadix(implicit context: Context)(Number, Number): String; } // namespace runtime namespace number { - extern macro NaNStringConstant(): String; - extern macro ZeroStringConstant(): String; - extern macro InfinityStringConstant(): String; - extern macro MinusInfinityStringConstant(): String; +extern macro NaNStringConstant(): String; +extern macro ZeroStringConstant(): String; +extern macro InfinityStringConstant(): String; +extern macro MinusInfinityStringConstant(): String; - const kAsciiZero: constexpr int32 = 48; // '0' (ascii) - const kAsciiLowerCaseA: constexpr int32 = 97; // 'a' (ascii) +const kAsciiZero: constexpr int32 = 48; // '0' (ascii) +const kAsciiLowerCaseA: constexpr int32 = 97; // 'a' (ascii) - transitioning macro ThisNumberValue(implicit context: Context)( - receiver: JSAny, method: constexpr string): Number { - return UnsafeCast( - ToThisValue(receiver, PrimitiveType::kNumber, method)); - } - - // https://tc39.github.io/ecma262/#sec-number.prototype.tostring - transitioning javascript builtin NumberPrototypeToString( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): String { - // 1. Let x be ? thisNumberValue(this value). - const x = ThisNumberValue(receiver, 'Number.prototype.toString'); +transitioning macro ThisNumberValue(implicit context: Context)( + receiver: JSAny, method: constexpr string): Number { + return UnsafeCast( + ToThisValue(receiver, PrimitiveType::kNumber, method)); +} - // 2. If radix is not present, let radixNumber be 10. - // 3. Else if radix is undefined, let radixNumber be 10. - // 4. Else, let radixNumber be ? ToInteger(radix). - const radix: JSAny = arguments[0]; - const radixNumber: Number = - radix == Undefined ? 10 : ToInteger_Inline(radix); +// https://tc39.github.io/ecma262/#sec-number.prototype.tostring +transitioning javascript builtin NumberPrototypeToString( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + // 1. Let x be ? thisNumberValue(this value). + const x = ThisNumberValue(receiver, 'Number.prototype.toString'); - // 5. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception. - if (radixNumber < 2 || radixNumber > 36) { - ThrowRangeError(MessageTemplate::kToRadixFormatRange); - } + // 2. If radix is not present, let radixNumber be 10. + // 3. Else if radix is undefined, let radixNumber be 10. + // 4. Else, let radixNumber be ? ToInteger(radix). + const radix: JSAny = arguments[0]; + const radixNumber: Number = radix == Undefined ? 10 : ToInteger_Inline(radix); - // 6. If radixNumber = 10, return ! ToString(x). - if (radixNumber == 10) { - return NumberToString(x); - } + // 5. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception. + if (radixNumber < 2 || radixNumber > 36) { + ThrowRangeError(MessageTemplate::kToRadixFormatRange); + } - // 7. Return the String representation of this Number - // value using the radix specified by radixNumber. + // 6. If radixNumber = 10, return ! ToString(x). + if (radixNumber == 10) { + return NumberToString(x); + } - // Fast case where the result is a one character string. - if (TaggedIsPositiveSmi(x) && x < radixNumber) { - let charCode = Convert(UnsafeCast(x)); - if (charCode < 10) { - charCode += kAsciiZero; - } else { - charCode = charCode - 10 + kAsciiLowerCaseA; - } - return StringFromSingleCharCode(charCode); - } + // 7. Return the String representation of this Number + // value using the radix specified by radixNumber. - if (x == -0) { - return ZeroStringConstant(); - } else if (NumberIsNaN(x)) { - return NaNStringConstant(); - } else if (x == V8_INFINITY) { - return InfinityStringConstant(); - } else if (x == MINUS_V8_INFINITY) { - return MinusInfinityStringConstant(); + // Fast case where the result is a one character string. + if (TaggedIsPositiveSmi(x) && x < radixNumber) { + let charCode = Convert(UnsafeCast(x)); + if (charCode < 10) { + charCode += kAsciiZero; + } else { + charCode = charCode - 10 + kAsciiLowerCaseA; } + return StringFromSingleCharCode(charCode); + } - return runtime::DoubleToStringWithRadix(x, radixNumber); + if (x == -0) { + return ZeroStringConstant(); + } else if (NumberIsNaN(x)) { + return NaNStringConstant(); + } else if (x == V8_INFINITY) { + return InfinityStringConstant(); + } else if (x == MINUS_V8_INFINITY) { + return MinusInfinityStringConstant(); } + + return runtime::DoubleToStringWithRadix(x, radixNumber); +} } diff --git a/deps/v8/src/builtins/object-fromentries.tq b/deps/v8/src/builtins/object-fromentries.tq index 2dbe9beacf1a05..32d4dea1574943 100644 --- a/deps/v8/src/builtins/object-fromentries.tq +++ b/deps/v8/src/builtins/object-fromentries.tq @@ -4,65 +4,63 @@ namespace object { - transitioning macro ObjectFromEntriesFastCase(implicit context: Context)( - iterable: JSAny): JSObject labels IfSlow { - typeswitch (iterable) { - case (array: FastJSArrayWithNoCustomIteration): { - const elements: FixedArray = - Cast(array.elements) otherwise IfSlow; - const length: Smi = array.length; - const result: JSObject = NewJSObject(); +transitioning macro ObjectFromEntriesFastCase(implicit context: Context)( + iterable: JSAny): JSObject labels IfSlow { + typeswitch (iterable) { + case (array: FastJSArrayWithNoCustomIteration): { + const elements: FixedArray = + Cast(array.elements) otherwise IfSlow; + const length: Smi = array.length; + const result: JSObject = NewJSObject(); - for (let k: Smi = 0; k < length; ++k) { - const value: JSAny = array::LoadElementOrUndefined(elements, k); - const pair: KeyValuePair = - collections::LoadKeyValuePairNoSideEffects(value) - otherwise IfSlow; - // Bail out if ToPropertyKey will attempt to load and call - // Symbol.toPrimitive, toString, and valueOf, which could - // invalidate assumptions about the iterable. - if (Is(pair.key)) goto IfSlow; - FastCreateDataProperty(result, pair.key, pair.value); - } - return result; - } - case (JSAny): { - goto IfSlow; + for (let k: Smi = 0; k < length; ++k) { + const value: JSAny = array::LoadElementOrUndefined(elements, k); + const pair: KeyValuePair = + collections::LoadKeyValuePairNoSideEffects(value) + otherwise IfSlow; + // Bail out if ToPropertyKey will attempt to load and call + // Symbol.toPrimitive, toString, and valueOf, which could + // invalidate assumptions about the iterable. + if (Is(pair.key)) goto IfSlow; + FastCreateDataProperty(result, pair.key, pair.value); } + return result; + } + case (JSAny): { + goto IfSlow; } } +} - transitioning javascript builtin - ObjectFromEntries(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): JSAny { - const iterable: JSAny = arguments[0]; +transitioning javascript builtin +ObjectFromEntries( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const iterable: JSAny = arguments[0]; + try { + if (IsNullOrUndefined(iterable)) goto Throw; + return ObjectFromEntriesFastCase(iterable) otherwise IfSlow; + } label IfSlow { + const result: JSObject = NewJSObject(); + const fastIteratorResultMap: Map = GetIteratorResultMap(); + let i: iterator::IteratorRecord = iterator::GetIterator(iterable); try { - if (IsNullOrUndefined(iterable)) goto Throw; - return ObjectFromEntriesFastCase(iterable) otherwise IfSlow; - } - label IfSlow { - const result: JSObject = NewJSObject(); - const fastIteratorResultMap: Map = GetIteratorResultMap(); - let i: iterator::IteratorRecord = iterator::GetIterator(iterable); - try { - assert(!IsNullOrUndefined(i.object)); - while (true) { - const step: JSReceiver = - iterator::IteratorStep(i, fastIteratorResultMap) - otherwise return result; - const iteratorValue: JSAny = - iterator::IteratorValue(step, fastIteratorResultMap); - const pair: KeyValuePair = - collections::LoadKeyValuePair(iteratorValue); - FastCreateDataProperty(result, pair.key, pair.value); - } - return result; - } catch (e) deferred { - iterator::IteratorCloseOnException(i, e); + assert(!IsNullOrUndefined(i.object)); + while (true) { + const step: JSReceiver = + iterator::IteratorStep(i, fastIteratorResultMap) + otherwise return result; + const iteratorValue: JSAny = + iterator::IteratorValue(step, fastIteratorResultMap); + const pair: KeyValuePair = collections::LoadKeyValuePair(iteratorValue); + FastCreateDataProperty(result, pair.key, pair.value); } + return result; + } catch (e) deferred { + iterator::IteratorCloseOnException(i); + ReThrow(context, e); } - label Throw deferred { - ThrowTypeError(MessageTemplate::kNotIterable); - } + } label Throw deferred { + ThrowTypeError(MessageTemplate::kNotIterable); } +} } // namespace object diff --git a/deps/v8/src/builtins/object.tq b/deps/v8/src/builtins/object.tq index c6d3e922797a75..931972024cf97f 100644 --- a/deps/v8/src/builtins/object.tq +++ b/deps/v8/src/builtins/object.tq @@ -3,179 +3,199 @@ // found in the LICENSE file. namespace runtime { - extern transitioning runtime - ObjectIsExtensible(implicit context: Context)(JSAny): JSAny; +extern transitioning runtime +ObjectIsExtensible(implicit context: Context)(JSAny): JSAny; - extern transitioning runtime - JSReceiverPreventExtensionsThrow(implicit context: Context)(JSReceiver): - JSAny; +extern transitioning runtime +JSReceiverPreventExtensionsThrow(implicit context: Context)(JSReceiver): JSAny; - extern transitioning runtime - JSReceiverPreventExtensionsDontThrow(implicit context: Context)(JSReceiver): - JSAny; +extern transitioning runtime +JSReceiverPreventExtensionsDontThrow(implicit context: Context)(JSReceiver): + JSAny; - extern transitioning runtime - JSReceiverGetPrototypeOf(implicit context: Context)(JSReceiver): JSAny; +extern transitioning runtime +JSReceiverGetPrototypeOf(implicit context: Context)(JSReceiver): JSAny; - extern transitioning runtime - JSReceiverSetPrototypeOfThrow(implicit context: Context)(JSReceiver, JSAny): - JSAny; +extern transitioning runtime +JSReceiverSetPrototypeOfThrow(implicit context: Context)( + JSReceiver, JSAny): JSAny; - extern transitioning runtime - JSReceiverSetPrototypeOfDontThrow(implicit context: - Context)(JSReceiver, JSAny): JSAny; +extern transitioning runtime +JSReceiverSetPrototypeOfDontThrow(implicit context: Context)( + JSReceiver, JSAny): JSAny; - extern transitioning runtime ObjectCreate(implicit context: - Context)(JSAny, JSAny): JSAny; +extern transitioning runtime ObjectCreate(implicit context: Context)( + JSAny, JSAny): JSAny; } // namespace runtime namespace object { - transitioning macro - ObjectIsExtensibleImpl(implicit context: Context)(object: JSAny): JSAny { - const objectJSReceiver = Cast(object) otherwise return False; - const objectJSProxy = Cast(objectJSReceiver) - otherwise return runtime::ObjectIsExtensible(objectJSReceiver); - return proxy::ProxyIsExtensible(objectJSProxy); - } - - transitioning macro - ObjectPreventExtensionsThrow(implicit context: Context)(object: JSAny): - JSAny { - const objectJSReceiver = Cast(object) otherwise return object; - const objectJSProxy = Cast(objectJSReceiver) - otherwise return runtime::JSReceiverPreventExtensionsThrow( - objectJSReceiver); - proxy::ProxyPreventExtensions(objectJSProxy, True); - return objectJSReceiver; - } - - transitioning macro - ObjectPreventExtensionsDontThrow(implicit context: Context)(object: JSAny): - JSAny { - const objectJSReceiver = Cast(object) otherwise return False; - const objectJSProxy = Cast(objectJSReceiver) - otherwise return runtime::JSReceiverPreventExtensionsDontThrow( - objectJSReceiver); - return proxy::ProxyPreventExtensions(objectJSProxy, False); - } - - transitioning macro - ObjectGetPrototypeOfImpl(implicit context: Context)(object: JSAny): JSAny { - const objectJSReceiver: JSReceiver = ToObject_Inline(context, object); - return object::JSReceiverGetPrototypeOf(objectJSReceiver); - } - - transitioning macro - JSReceiverGetPrototypeOf(implicit context: Context)(object: JSReceiver): - JSAny { - const objectJSProxy = Cast(object) - otherwise return runtime::JSReceiverGetPrototypeOf(object); - return proxy::ProxyGetPrototypeOf(objectJSProxy); - } - - transitioning macro - ObjectSetPrototypeOfThrow(implicit context: Context)( - object: JSAny, proto: JSReceiver|Null): JSAny { - const objectJSReceiver = Cast(object) otherwise return object; - const objectJSProxy = Cast(objectJSReceiver) - otherwise return runtime::JSReceiverSetPrototypeOfThrow( - objectJSReceiver, proto); - proxy::ProxySetPrototypeOf(objectJSProxy, proto, True); - return objectJSReceiver; - } - - transitioning macro - ObjectSetPrototypeOfDontThrow(implicit context: Context)( - object: JSAny, proto: JSReceiver|Null): JSAny { - const objectJSReceiver = Cast(object) otherwise return False; - const objectJSProxy = Cast(objectJSReceiver) - otherwise return runtime::JSReceiverSetPrototypeOfDontThrow( - objectJSReceiver, proto); - return proxy::ProxySetPrototypeOf(objectJSProxy, proto, False); - } - - transitioning builtin CreateObjectWithoutProperties( - implicit context: Context)(prototype: JSAny): JSAny { - const nativeContext = LoadNativeContext(context); - - try { - let map: Map; - let properties: NameDictionary|EmptyFixedArray; - typeswitch (prototype) { - case (Null): { - map = UnsafeCast( - nativeContext - [NativeContextSlot::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP]); - properties = AllocateNameDictionary(kNameDictionaryInitialCapacity); - } - case (prototype: JSReceiver): { - properties = kEmptyFixedArray; - const objectFunction = UnsafeCast( - nativeContext[NativeContextSlot::OBJECT_FUNCTION_INDEX]); - map = UnsafeCast(objectFunction.prototype_or_initial_map); - if (prototype != map.prototype) { - const prototypeInfo = - prototype.map.PrototypeInfo() otherwise Runtime; - typeswitch (prototypeInfo.object_create_map) { - case (Undefined): { - goto Runtime; - } - case (weak_map: Weak): { - map = WeakToStrong(weak_map) otherwise Runtime; - } +transitioning macro +ObjectIsExtensibleImpl(implicit context: Context)(object: JSAny): JSAny { + const objectJSReceiver = Cast(object) otherwise return False; + const objectJSProxy = Cast(objectJSReceiver) + otherwise return runtime::ObjectIsExtensible(objectJSReceiver); + return proxy::ProxyIsExtensible(objectJSProxy); +} + +transitioning macro +ObjectPreventExtensionsThrow(implicit context: Context)(object: JSAny): JSAny { + const objectJSReceiver = Cast(object) otherwise return object; + const objectJSProxy = Cast(objectJSReceiver) + otherwise return runtime::JSReceiverPreventExtensionsThrow(objectJSReceiver); + proxy::ProxyPreventExtensions(objectJSProxy, True); + return objectJSReceiver; +} + +transitioning macro +ObjectPreventExtensionsDontThrow(implicit context: Context)(object: JSAny): + JSAny { + const objectJSReceiver = Cast(object) otherwise return False; + const objectJSProxy = Cast(objectJSReceiver) + otherwise return runtime::JSReceiverPreventExtensionsDontThrow( + objectJSReceiver); + return proxy::ProxyPreventExtensions(objectJSProxy, False); +} + +transitioning macro +ObjectGetPrototypeOfImpl(implicit context: Context)(object: JSAny): JSAny { + const objectJSReceiver: JSReceiver = ToObject_Inline(context, object); + return object::JSReceiverGetPrototypeOf(objectJSReceiver); +} + +transitioning macro +JSReceiverGetPrototypeOf(implicit context: Context)(object: JSReceiver): JSAny { + const objectJSProxy = Cast(object) + otherwise return runtime::JSReceiverGetPrototypeOf(object); + return proxy::ProxyGetPrototypeOf(objectJSProxy); +} + +transitioning macro +ObjectSetPrototypeOfThrow(implicit context: Context)( + object: JSAny, proto: JSReceiver|Null): JSAny { + const objectJSReceiver = Cast(object) otherwise return object; + const objectJSProxy = Cast(objectJSReceiver) + otherwise return runtime::JSReceiverSetPrototypeOfThrow( + objectJSReceiver, proto); + proxy::ProxySetPrototypeOf(objectJSProxy, proto, True); + return objectJSReceiver; +} + +transitioning macro +ObjectSetPrototypeOfDontThrow(implicit context: Context)( + object: JSAny, proto: JSReceiver|Null): JSAny { + const objectJSReceiver = Cast(object) otherwise return False; + const objectJSProxy = Cast(objectJSReceiver) + otherwise return runtime::JSReceiverSetPrototypeOfDontThrow( + objectJSReceiver, proto); + return proxy::ProxySetPrototypeOf(objectJSProxy, proto, False); +} + +transitioning builtin CreateObjectWithoutProperties(implicit context: Context)( + prototype: JSAny): JSAny { + const nativeContext = LoadNativeContext(context); + + try { + let map: Map; + let properties: NameDictionary|EmptyFixedArray; + typeswitch (prototype) { + case (Null): { + map = UnsafeCast( + nativeContext + [NativeContextSlot::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP]); + properties = AllocateNameDictionary(kNameDictionaryInitialCapacity); + } + case (prototype: JSReceiver): { + properties = kEmptyFixedArray; + const objectFunction = UnsafeCast( + nativeContext[NativeContextSlot::OBJECT_FUNCTION_INDEX]); + map = UnsafeCast(objectFunction.prototype_or_initial_map); + if (prototype != map.prototype) { + const prototypeInfo = prototype.map.PrototypeInfo() otherwise Runtime; + typeswitch (prototypeInfo.object_create_map) { + case (Undefined): { + goto Runtime; + } + case (weak_map: Weak): { + map = WeakToStrong(weak_map) otherwise Runtime; } } } - case (JSAny): { - goto Runtime; - } - } - return AllocateJSObjectFromMap(map, properties); - } - label Runtime deferred { - return runtime::ObjectCreate(prototype, Undefined); - } - } - - // ES6 section 19.1.2.11 Object.isExtensible ( O ) - transitioning javascript builtin - ObjectIsExtensible(js-implicit context: NativeContext)(object: JSAny): JSAny { - return object::ObjectIsExtensibleImpl(object); - } - - // ES6 section 19.1.2.18 Object.preventExtensions ( O ) - transitioning javascript builtin - ObjectPreventExtensions(js-implicit context: NativeContext)(object: JSAny): - JSAny { - return object::ObjectPreventExtensionsThrow(object); - } - - // ES6 section 19.1.2.9 Object.getPrototypeOf ( O ) - transitioning javascript builtin - ObjectGetPrototypeOf(js-implicit context: NativeContext)(object: JSAny): - JSAny { - return object::ObjectGetPrototypeOfImpl(object); - } - - // ES6 section 19.1.2.21 Object.setPrototypeOf ( O, proto ) - transitioning javascript builtin ObjectSetPrototypeOf( - js-implicit context: NativeContext)(object: JSAny, proto: JSAny): JSAny { - // 1. Set O to ? RequireObjectCoercible(O). - RequireObjectCoercible(object, 'Object.setPrototypeOf'); - - // 2. If Type(proto) is neither Object nor Null, throw a TypeError - // exception. - // 3. If Type(O) is not Object, return O. - // 4. Let status be ? O.[[SetPrototypeOf]](proto). - // 5. If status is false, throw a TypeError exception. - // 6. Return O. - typeswitch (proto) { - case (proto: JSReceiver|Null): { - return object::ObjectSetPrototypeOfThrow(object, proto); } case (JSAny): { - ThrowTypeError(MessageTemplate::kProtoObjectOrNull, proto); + goto Runtime; } } + return AllocateJSObjectFromMap(map, properties); + } label Runtime deferred { + return runtime::ObjectCreate(prototype, Undefined); } +} + +// ES6 section 19.1.2.11 Object.isExtensible ( O ) +transitioning javascript builtin +ObjectIsExtensible(js-implicit context: NativeContext)(object: JSAny): JSAny { + return object::ObjectIsExtensibleImpl(object); +} + +// ES6 section 19.1.2.18 Object.preventExtensions ( O ) +transitioning javascript builtin +ObjectPreventExtensions(js-implicit context: NativeContext)(object: JSAny): + JSAny { + return object::ObjectPreventExtensionsThrow(object); +} + +// ES6 section 19.1.2.9 Object.getPrototypeOf ( O ) +transitioning javascript builtin +ObjectGetPrototypeOf(js-implicit context: NativeContext)(object: JSAny): JSAny { + return object::ObjectGetPrototypeOfImpl(object); +} + +// ES6 section 19.1.2.21 Object.setPrototypeOf ( O, proto ) +transitioning javascript builtin ObjectSetPrototypeOf( + js-implicit context: NativeContext)(object: JSAny, proto: JSAny): JSAny { + // 1. Set O to ? RequireObjectCoercible(O). + RequireObjectCoercible(object, 'Object.setPrototypeOf'); + + // 2. If Type(proto) is neither Object nor Null, throw a TypeError + // exception. + // 3. If Type(O) is not Object, return O. + // 4. Let status be ? O.[[SetPrototypeOf]](proto). + // 5. If status is false, throw a TypeError exception. + // 6. Return O. + typeswitch (proto) { + case (proto: JSReceiver|Null): { + return object::ObjectSetPrototypeOfThrow(object, proto); + } + case (JSAny): { + ThrowTypeError(MessageTemplate::kProtoObjectOrNull, proto); + } + } +} + +// ES #sec-object.prototype.tostring +transitioning javascript builtin ObjectPrototypeToString( + js-implicit context: Context, receiver: JSAny)(): String { + return ObjectToString(context, receiver); +} + +// ES #sec-object.prototype.valueof +transitioning javascript builtin ObjectPrototypeValueOf( + js-implicit context: Context, receiver: JSAny)(): JSReceiver { + // 1. Return ? ToObject(this value). + return ToObject_Inline(context, receiver); +} + +// ES #sec-object.prototype.tolocalestring +transitioning javascript builtin ObjectPrototypeToLocaleString( + js-implicit context: Context, receiver: JSAny)(): JSAny { + // 1. Let O be the this value. + // 2. Return ? Invoke(O, "toString"). + if (receiver == Null || receiver == Undefined) deferred { + ThrowTypeError( + MessageTemplate::kCalledOnNullOrUndefined, + 'Object.prototype.toLocaleString'); + } + const method = GetProperty(receiver, 'toString'); + return Call(context, method, receiver); +} } // namespace object diff --git a/deps/v8/src/builtins/ppc/builtins-ppc.cc b/deps/v8/src/builtins/ppc/builtins-ppc.cc index 460d7492970726..367838f82cc87d 100644 --- a/deps/v8/src/builtins/ppc/builtins-ppc.cc +++ b/deps/v8/src/builtins/ppc/builtins-ppc.cc @@ -87,7 +87,7 @@ void Generate_StackOverflowCheck(MacroAssembler* masm, Register num_args, // here which will cause scratch to become negative. __ sub(scratch, sp, scratch); // Check if the arguments will overflow the stack. - __ ShiftLeftImm(r0, num_args, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(r0, num_args, Operand(kSystemPointerSizeLog2)); __ cmp(scratch, r0); __ ble(stack_overflow); // Signed comparison. } @@ -130,16 +130,16 @@ void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) { // -- r6: new target // -- r7: pointer to last argument // -- cr0: condition indicating whether r3 is zero - // -- sp[0*kPointerSize]: the hole (receiver) - // -- sp[1*kPointerSize]: number of arguments (tagged) - // -- sp[2*kPointerSize]: context + // -- sp[0*kSystemPointerSize]: the hole (receiver) + // -- sp[1*kSystemPointerSize]: number of arguments (tagged) + // -- sp[2*kSystemPointerSize]: context // ----------------------------------- __ beq(&no_args, cr0); - __ ShiftLeftImm(scratch, r3, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(scratch, r3, Operand(kSystemPointerSizeLog2)); __ sub(sp, sp, scratch); __ mtctr(r3); __ bind(&loop); - __ subi(scratch, scratch, Operand(kPointerSize)); + __ subi(scratch, scratch, Operand(kSystemPointerSize)); __ LoadPX(r0, MemOperand(r7, scratch)); __ StorePX(r0, MemOperand(sp, scratch)); __ bdnz(&loop); @@ -166,7 +166,7 @@ void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) { __ SmiToPtrArrayOffset(r4, r4); __ add(sp, sp, r4); - __ addi(sp, sp, Operand(kPointerSize)); + __ addi(sp, sp, Operand(kSystemPointerSize)); __ blr(); __ bind(&stack_overflow); @@ -202,14 +202,15 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { __ Push(r6); // ----------- S t a t e ------------- - // -- sp[0*kPointerSize]: new target - // -- sp[1*kPointerSize]: padding - // -- r4 and sp[2*kPointerSize]: constructor function - // -- sp[3*kPointerSize]: number of arguments (tagged) - // -- sp[4*kPointerSize]: context + // -- sp[0*kSystemPointerSize]: new target + // -- sp[1*kSystemPointerSize]: padding + // -- r4 and sp[2*kSystemPointerSize]: constructor function + // -- sp[3*kSystemPointerSize]: number of arguments (tagged) + // -- sp[4*kSystemPointerSize]: context // ----------------------------------- - __ LoadP(r7, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset)); + __ LoadTaggedPointerField( + r7, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset)); __ lwz(r7, FieldMemOperand(r7, SharedFunctionInfo::kFlagsOffset)); __ DecodeField(r7); __ JumpIfIsInRange(r7, kDefaultDerivedConstructor, kDerivedConstructor, @@ -228,11 +229,11 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r3: receiver - // -- Slot 4 / sp[0*kPointerSize]: new target - // -- Slot 3 / sp[1*kPointerSize]: padding - // -- Slot 2 / sp[2*kPointerSize]: constructor function - // -- Slot 1 / sp[3*kPointerSize]: number of arguments (tagged) - // -- Slot 0 / sp[4*kPointerSize]: context + // -- Slot 4 / sp[0*kSystemPointerSize]: new target + // -- Slot 3 / sp[1*kSystemPointerSize]: padding + // -- Slot 2 / sp[2*kSystemPointerSize]: constructor function + // -- Slot 1 / sp[3*kSystemPointerSize]: number of arguments (tagged) + // -- Slot 0 / sp[4*kSystemPointerSize]: context // ----------------------------------- // Deoptimizer enters here. masm->isolate()->heap()->SetConstructStubCreateDeoptPCOffset( @@ -248,12 +249,12 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r6: new target - // -- sp[0*kPointerSize]: implicit receiver - // -- sp[1*kPointerSize]: implicit receiver - // -- sp[2*kPointerSize]: padding - // -- sp[3*kPointerSize]: constructor function - // -- sp[4*kPointerSize]: number of arguments (tagged) - // -- sp[5*kPointerSize]: context + // -- sp[0*kSystemPointerSize]: implicit receiver + // -- sp[1*kSystemPointerSize]: implicit receiver + // -- sp[2*kSystemPointerSize]: padding + // -- sp[3*kSystemPointerSize]: constructor function + // -- sp[4*kSystemPointerSize]: number of arguments (tagged) + // -- sp[5*kSystemPointerSize]: context // ----------------------------------- // Restore constructor function and argument count. @@ -284,20 +285,20 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { // -- r6: new target // -- r7: pointer to last argument // -- cr0: condition indicating whether r3 is zero - // -- sp[0*kPointerSize]: implicit receiver - // -- sp[1*kPointerSize]: implicit receiver - // -- sp[2*kPointerSize]: padding - // -- r4 and sp[3*kPointerSize]: constructor function - // -- sp[4*kPointerSize]: number of arguments (tagged) - // -- sp[5*kPointerSize]: context + // -- sp[0*kSystemPointerSize]: implicit receiver + // -- sp[1*kSystemPointerSize]: implicit receiver + // -- sp[2*kSystemPointerSize]: padding + // -- r4 and sp[3*kSystemPointerSize]: constructor function + // -- sp[4*kSystemPointerSize]: number of arguments (tagged) + // -- sp[5*kSystemPointerSize]: context // ----------------------------------- __ cmpi(r3, Operand::Zero()); __ beq(&no_args); - __ ShiftLeftImm(r9, r3, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(r9, r3, Operand(kSystemPointerSizeLog2)); __ sub(sp, sp, r9); __ mtctr(r3); __ bind(&loop); - __ subi(r9, r9, Operand(kPointerSize)); + __ subi(r9, r9, Operand(kSystemPointerSize)); __ LoadPX(r0, MemOperand(r7, r9)); __ StorePX(r0, MemOperand(sp, r9)); __ bdnz(&loop); @@ -311,11 +312,11 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r0: constructor result - // -- sp[0*kPointerSize]: implicit receiver - // -- sp[1*kPointerSize]: padding - // -- sp[2*kPointerSize]: constructor function - // -- sp[3*kPointerSize]: number of arguments - // -- sp[4*kPointerSize]: context + // -- sp[0*kSystemPointerSize]: implicit receiver + // -- sp[1*kSystemPointerSize]: padding + // -- sp[2*kSystemPointerSize]: constructor function + // -- sp[3*kSystemPointerSize]: number of arguments + // -- sp[4*kSystemPointerSize]: context // ----------------------------------- // Store offset of return address for deoptimizer. @@ -366,7 +367,7 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { __ SmiToPtrArrayOffset(r4, r4); __ add(sp, sp, r4); - __ addi(sp, sp, Operand(kPointerSize)); + __ addi(sp, sp, Operand(kSystemPointerSize)); __ blr(); } @@ -381,8 +382,9 @@ static void GetSharedFunctionInfoBytecode(MacroAssembler* masm, __ CompareObjectType(sfi_data, scratch1, scratch1, INTERPRETER_DATA_TYPE); __ bne(&done); - __ LoadP(sfi_data, - FieldMemOperand(sfi_data, InterpreterData::kBytecodeArrayOffset)); + __ LoadTaggedPointerField( + sfi_data, + FieldMemOperand(sfi_data, InterpreterData::kBytecodeArrayOffset)); __ bind(&done); } @@ -396,14 +398,16 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ AssertGeneratorObject(r4); // Store input value into generator object. - __ StoreP(r3, FieldMemOperand(r4, JSGeneratorObject::kInputOrDebugPosOffset), - r0); + __ StoreTaggedField( + r3, FieldMemOperand(r4, JSGeneratorObject::kInputOrDebugPosOffset), r0); __ RecordWriteField(r4, JSGeneratorObject::kInputOrDebugPosOffset, r3, r6, kLRHasNotBeenSaved, kDontSaveFPRegs); // Load suspended function and context. - __ LoadP(r7, FieldMemOperand(r4, JSGeneratorObject::kFunctionOffset)); - __ LoadP(cp, FieldMemOperand(r7, JSFunction::kContextOffset)); + __ LoadTaggedPointerField( + r7, FieldMemOperand(r4, JSGeneratorObject::kFunctionOffset)); + __ LoadTaggedPointerField(cp, + FieldMemOperand(r7, JSFunction::kContextOffset)); // Flood function if we are stepping. Label prepare_step_in_if_stepping, prepare_step_in_suspended_generator; @@ -436,7 +440,8 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ blt(&stack_overflow); // Push receiver. - __ LoadP(scratch, FieldMemOperand(r4, JSGeneratorObject::kReceiverOffset)); + __ LoadTaggedPointerField( + scratch, FieldMemOperand(r4, JSGeneratorObject::kReceiverOffset)); __ Push(scratch); // ----------- S t a t e ------------- @@ -448,23 +453,26 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { // ----------------------------------- // Copy the function arguments from the generator object's register file. - __ LoadP(r6, FieldMemOperand(r7, JSFunction::kSharedFunctionInfoOffset)); + __ LoadTaggedPointerField( + r6, FieldMemOperand(r7, JSFunction::kSharedFunctionInfoOffset)); __ LoadHalfWord( r6, FieldMemOperand(r6, SharedFunctionInfo::kFormalParameterCountOffset)); - __ LoadP(r5, FieldMemOperand( - r4, JSGeneratorObject::kParametersAndRegistersOffset)); + __ LoadTaggedPointerField( + r5, + FieldMemOperand(r4, JSGeneratorObject::kParametersAndRegistersOffset)); { Label loop, done_loop; __ cmpi(r6, Operand::Zero()); __ ble(&done_loop); - // setup r9 to first element address - kPointerSize + // setup r9 to first element address - kTaggedSize __ addi(r9, r5, - Operand(FixedArray::kHeaderSize - kHeapObjectTag - kPointerSize)); + Operand(FixedArray::kHeaderSize - kHeapObjectTag - kTaggedSize)); __ mtctr(r6); __ bind(&loop); - __ LoadPU(scratch, MemOperand(r9, kPointerSize)); + __ LoadAnyTaggedField(scratch, MemOperand(r9, kTaggedSize)); + __ addi(r9, r9, Operand(kTaggedSize)); __ push(scratch); __ bdnz(&loop); @@ -473,8 +481,10 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { // Underlying function needs to have bytecode available. if (FLAG_debug_code) { - __ LoadP(r6, FieldMemOperand(r7, JSFunction::kSharedFunctionInfoOffset)); - __ LoadP(r6, FieldMemOperand(r6, SharedFunctionInfo::kFunctionDataOffset)); + __ LoadTaggedPointerField( + r6, FieldMemOperand(r7, JSFunction::kSharedFunctionInfoOffset)); + __ LoadTaggedPointerField( + r6, FieldMemOperand(r6, SharedFunctionInfo::kFunctionDataOffset)); GetSharedFunctionInfoBytecode(masm, r6, r3); __ CompareObjectType(r6, r6, r6, BYTECODE_ARRAY_TYPE); __ Assert(eq, AbortReason::kMissingBytecodeArray); @@ -488,7 +498,7 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ mr(r6, r4); __ mr(r4, r7); static_assert(kJavaScriptCallCodeStartRegister == r5, "ABI mismatch"); - __ LoadP(r5, FieldMemOperand(r4, JSFunction::kCodeOffset)); + __ LoadTaggedPointerField(r5, FieldMemOperand(r4, JSFunction::kCodeOffset)); __ JumpCodeObject(r5); } @@ -500,7 +510,8 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ PushRoot(RootIndex::kTheHoleValue); __ CallRuntime(Runtime::kDebugOnFunctionCall); __ Pop(r4); - __ LoadP(r7, FieldMemOperand(r4, JSGeneratorObject::kFunctionOffset)); + __ LoadTaggedPointerField( + r7, FieldMemOperand(r4, JSGeneratorObject::kFunctionOffset)); } __ b(&stepping_prepared); @@ -510,7 +521,8 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ Push(r4); __ CallRuntime(Runtime::kDebugPrepareStepInSuspendedGenerator); __ Pop(r4); - __ LoadP(r7, FieldMemOperand(r4, JSGeneratorObject::kFunctionOffset)); + __ LoadTaggedPointerField( + r7, FieldMemOperand(r4, JSGeneratorObject::kFunctionOffset)); } __ b(&stepping_prepared); @@ -560,7 +572,7 @@ void Generate_JSEntryVariant(MacroAssembler* masm, StackFrame::Type type, // PPC LINUX ABI: // preserve LR in pre-reserved slot in caller's frame __ mflr(r0); - __ StoreP(r0, MemOperand(sp, kStackFrameLRSlot * kPointerSize)); + __ StoreP(r0, MemOperand(sp, kStackFrameLRSlot * kSystemPointerSize)); // Save callee saved registers on the stack. __ MultiPush(kCalleeSaved); @@ -695,7 +707,7 @@ void Generate_JSEntryVariant(MacroAssembler* masm, StackFrame::Type type, __ MultiPop(kCalleeSaved); // Return - __ LoadP(r0, MemOperand(sp, kStackFrameLRSlot * kPointerSize)); + __ LoadP(r0, MemOperand(sp, kStackFrameLRSlot * kSystemPointerSize)); __ mtlr(r0); __ blr(); } @@ -729,7 +741,7 @@ static void Generate_CheckStackOverflow(MacroAssembler* masm, Register argc, // here which will cause scratch1 to become negative. __ sub(scratch1, sp, scratch1); // Check if the arguments will overflow the stack. - __ ShiftLeftImm(scratch2, argc, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(scratch2, argc, Operand(kSystemPointerSizeLog2)); __ cmp(scratch1, scratch2); __ bgt(&okay); // Signed comparison. @@ -787,13 +799,13 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, // r3: argc // r8: argv, i.e. points to first arg Label loop, entry; - __ ShiftLeftImm(r0, r3, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(r0, r3, Operand(kSystemPointerSizeLog2)); __ add(r5, r8, r0); // r5 points past last arg. __ b(&entry); __ bind(&loop); __ LoadP(r9, MemOperand(r8)); // read next parameter - __ addi(r8, r8, Operand(kPointerSize)); + __ addi(r8, r8, Operand(kSystemPointerSize)); __ LoadP(r0, MemOperand(r9)); // dereference handle __ push(r0); // push parameter __ bind(&entry); @@ -851,8 +863,8 @@ static void ReplaceClosureCodeWithOptimizedCode(MacroAssembler* masm, Register scratch1, Register scratch2) { // Store code entry in the closure. - __ StoreP(optimized_code, FieldMemOperand(closure, JSFunction::kCodeOffset), - r0); + __ StoreTaggedField(optimized_code, + FieldMemOperand(closure, JSFunction::kCodeOffset), r0); __ mr(scratch1, optimized_code); // Write barrier clobbers scratch1 below. __ RecordWriteField(closure, JSFunction::kCodeOffset, scratch1, scratch2, kLRHasNotBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET, @@ -900,8 +912,9 @@ static void TailCallOptimizedCodeSlot(MacroAssembler* masm, // Check if the optimized code is marked for deopt. If it is, call the // runtime to clear it. Label found_deoptimized_code; - __ LoadP(scratch, FieldMemOperand(optimized_code_entry, - Code::kCodeDataContainerOffset)); + __ LoadTaggedPointerField( + scratch, + FieldMemOperand(optimized_code_entry, Code::kCodeDataContainerOffset)); __ LoadWordArith( scratch, FieldMemOperand(scratch, CodeDataContainer::kKindSpecificFlagsOffset)); @@ -1057,10 +1070,12 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { // Get the bytecode array from the function object and load it into // kInterpreterBytecodeArrayRegister. - __ LoadP(r3, FieldMemOperand(closure, JSFunction::kSharedFunctionInfoOffset)); + __ LoadTaggedPointerField( + r3, FieldMemOperand(closure, JSFunction::kSharedFunctionInfoOffset)); // Load original bytecode array or the debug copy. - __ LoadP(kInterpreterBytecodeArrayRegister, - FieldMemOperand(r3, SharedFunctionInfo::kFunctionDataOffset)); + __ LoadTaggedPointerField( + kInterpreterBytecodeArrayRegister, + FieldMemOperand(r3, SharedFunctionInfo::kFunctionDataOffset)); GetSharedFunctionInfoBytecode(masm, kInterpreterBytecodeArrayRegister, r7); // The bytecode array could have been flushed from the shared function info, @@ -1071,15 +1086,17 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { __ bne(&compile_lazy); // Load the feedback vector from the closure. - __ LoadP(feedback_vector, - FieldMemOperand(closure, JSFunction::kFeedbackCellOffset)); - __ LoadP(feedback_vector, - FieldMemOperand(feedback_vector, Cell::kValueOffset)); + __ LoadTaggedPointerField( + feedback_vector, + FieldMemOperand(closure, JSFunction::kFeedbackCellOffset)); + __ LoadTaggedPointerField( + feedback_vector, FieldMemOperand(feedback_vector, Cell::kValueOffset)); Label push_stack_frame; // Check if feedback vector is valid. If valid, check for optimized code // and update invocation count. Otherwise, setup the stack frame. - __ LoadP(r7, FieldMemOperand(feedback_vector, HeapObject::kMapOffset)); + __ LoadTaggedPointerField( + r7, FieldMemOperand(feedback_vector, HeapObject::kMapOffset)); __ LoadHalfWord(r7, FieldMemOperand(r7, Map::kInstanceTypeOffset)); __ cmpi(r7, Operand(FEEDBACK_VECTOR_TYPE)); __ bne(&push_stack_frame); @@ -1087,9 +1104,10 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { Register optimized_code_entry = r7; // Read off the optimized code slot in the feedback vector. - __ LoadP(optimized_code_entry, - FieldMemOperand(feedback_vector, - FeedbackVector::kOptimizedCodeWeakOrSmiOffset)); + __ LoadAnyTaggedField( + optimized_code_entry, + FieldMemOperand(feedback_vector, + FeedbackVector::kOptimizedCodeWeakOrSmiOffset)); // Check if the optimized code slot is not empty. Label optimized_code_slot_not_empty; __ CmpSmiLiteral(optimized_code_entry, @@ -1156,7 +1174,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { // TODO(rmcilroy): Consider doing more than one push per loop iteration. Label loop, no_args; __ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kUndefinedValue); - __ ShiftRightImm(r5, r5, Operand(kPointerSizeLog2), SetRC); + __ ShiftRightImm(r5, r5, Operand(kSystemPointerSizeLog2), SetRC); __ beq(&no_args, cr0); __ mtctr(r5); __ bind(&loop); @@ -1174,7 +1192,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { BytecodeArray::kIncomingNewTargetOrGeneratorRegisterOffset)); __ cmpi(r8, Operand::Zero()); __ beq(&no_incoming_new_target_or_generator_register); - __ ShiftLeftImm(r8, r8, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(r8, r8, Operand(kSystemPointerSizeLog2)); __ StorePX(r6, MemOperand(fp, r8)); __ bind(&no_incoming_new_target_or_generator_register); @@ -1197,7 +1215,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { ExternalReference::interpreter_dispatch_table_address(masm->isolate())); __ lbzx(r6, MemOperand(kInterpreterBytecodeArrayRegister, kInterpreterBytecodeOffsetRegister)); - __ ShiftLeftImm(r6, r6, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(r6, r6, Operand(kSystemPointerSizeLog2)); __ LoadPX(kJavaScriptCallCodeStartRegister, MemOperand(kInterpreterDispatchTableRegister, r6)); __ Call(kJavaScriptCallCodeStartRegister); @@ -1282,10 +1300,10 @@ static void Generate_InterpreterPushArgs(MacroAssembler* masm, Label loop, skip; __ cmpi(count, Operand::Zero()); __ beq(&skip); - __ addi(index, index, Operand(kPointerSize)); // Bias up for LoadPU + __ addi(index, index, Operand(kSystemPointerSize)); // Bias up for LoadPU __ mtctr(count); __ bind(&loop); - __ LoadPU(scratch, MemOperand(index, -kPointerSize)); + __ LoadPU(scratch, MemOperand(index, -kSystemPointerSize)); __ push(scratch); __ bdnz(&loop); __ bind(&skip); @@ -1409,15 +1427,17 @@ static void Generate_InterpreterEnterBytecode(MacroAssembler* masm) { // get the custom trampoline, otherwise grab the entry address of the global // trampoline. __ LoadP(r5, MemOperand(fp, StandardFrameConstants::kFunctionOffset)); - __ LoadP(r5, FieldMemOperand(r5, JSFunction::kSharedFunctionInfoOffset)); - __ LoadP(r5, FieldMemOperand(r5, SharedFunctionInfo::kFunctionDataOffset)); + __ LoadTaggedPointerField( + r5, FieldMemOperand(r5, JSFunction::kSharedFunctionInfoOffset)); + __ LoadTaggedPointerField( + r5, FieldMemOperand(r5, SharedFunctionInfo::kFunctionDataOffset)); __ CompareObjectType(r5, kInterpreterDispatchTableRegister, kInterpreterDispatchTableRegister, INTERPRETER_DATA_TYPE); __ bne(&builtin_trampoline); - __ LoadP(r5, - FieldMemOperand(r5, InterpreterData::kInterpreterTrampolineOffset)); + __ LoadTaggedPointerField( + r5, FieldMemOperand(r5, InterpreterData::kInterpreterTrampolineOffset)); __ addi(r5, r5, Operand(Code::kHeaderSize - kHeapObjectTag)); __ b(&trampoline_loaded); @@ -1472,7 +1492,7 @@ static void Generate_InterpreterEnterBytecode(MacroAssembler* masm) { Register scratch = temps.Acquire(); __ lbzx(ip, MemOperand(kInterpreterBytecodeArrayRegister, kInterpreterBytecodeOffsetRegister)); - __ ShiftLeftImm(scratch, scratch, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(scratch, scratch, Operand(kSystemPointerSizeLog2)); __ LoadPX(kJavaScriptCallCodeStartRegister, MemOperand(kInterpreterDispatchTableRegister, scratch)); __ Jump(kJavaScriptCallCodeStartRegister); @@ -1538,9 +1558,10 @@ void Generate_ContinueToBuiltinHelper(MacroAssembler* masm, // Overwrite the hole inserted by the deoptimizer with the return value from // the LAZY deopt point. __ StoreP( - r3, MemOperand( - sp, config->num_allocatable_general_registers() * kPointerSize + - BuiltinContinuationFrameConstants::kFixedFrameSize)); + r3, + MemOperand(sp, config->num_allocatable_general_registers() * + kSystemPointerSize + + BuiltinContinuationFrameConstants::kFixedFrameSize)); } for (int i = allocatable_register_count - 1; i >= 0; --i) { int code = config->GetAllocatableGeneralCode(i); @@ -1591,8 +1612,8 @@ void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) { } DCHECK_EQ(kInterpreterAccumulatorRegister.code(), r3.code()); - __ LoadP(r3, MemOperand(sp, 0 * kPointerSize)); - __ addi(sp, sp, Operand(1 * kPointerSize)); + __ LoadP(r3, MemOperand(sp, 0 * kSystemPointerSize)); + __ addi(sp, sp, Operand(1 * kSystemPointerSize)); __ Ret(); } @@ -1616,7 +1637,8 @@ void Builtins::Generate_InterpreterOnStackReplacement(MacroAssembler* masm) { // Load deoptimization data from the code object. // = [#deoptimization_data_offset] - __ LoadP(r4, FieldMemOperand(r3, Code::kDeoptimizationDataOffset)); + __ LoadTaggedPointerField( + r4, FieldMemOperand(r3, Code::kDeoptimizationDataOffset)); { ConstantPoolUnavailableScope constant_pool_unavailable(masm); @@ -1628,10 +1650,9 @@ void Builtins::Generate_InterpreterOnStackReplacement(MacroAssembler* masm) { // Load the OSR entrypoint offset from the deoptimization data. // = [#header_size + #osr_pc_offset] - __ LoadP(r4, - FieldMemOperand(r4, FixedArray::OffsetOfElementAt( - DeoptimizationData::kOsrPcOffsetIndex))); - __ SmiUntag(r4); + __ SmiUntagField( + r4, FieldMemOperand(r4, FixedArray::OffsetOfElementAt( + DeoptimizationData::kOsrPcOffsetIndex))); // Compute the target address = code start + osr_offset __ add(r0, r3, r4); @@ -1659,16 +1680,16 @@ void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) { Register arg_size = r8; Register new_sp = r6; Register scratch = r7; - __ ShiftLeftImm(arg_size, r3, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(arg_size, r3, Operand(kSystemPointerSizeLog2)); __ add(new_sp, sp, arg_size); __ LoadRoot(scratch, RootIndex::kUndefinedValue); __ mr(r5, scratch); __ LoadP(r4, MemOperand(new_sp, 0)); // receiver - __ cmpi(arg_size, Operand(kPointerSize)); + __ cmpi(arg_size, Operand(kSystemPointerSize)); __ blt(&skip); - __ LoadP(scratch, MemOperand(new_sp, 1 * -kPointerSize)); // thisArg + __ LoadP(scratch, MemOperand(new_sp, 1 * -kSystemPointerSize)); // thisArg __ beq(&skip); - __ LoadP(r5, MemOperand(new_sp, 2 * -kPointerSize)); // argArray + __ LoadP(r5, MemOperand(new_sp, 2 * -kSystemPointerSize)); // argArray __ bind(&skip); __ mr(sp, new_sp); __ StoreP(scratch, MemOperand(sp, 0)); @@ -1717,7 +1738,7 @@ void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { // 2. Get the callable to call (passed as receiver) from the stack. // r3: actual number of arguments - __ ShiftLeftImm(r5, r3, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(r5, r3, Operand(kSystemPointerSizeLog2)); __ LoadPX(r4, MemOperand(sp, r5)); // 3. Shift arguments and return address one slot down on the stack @@ -1733,9 +1754,9 @@ void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { __ mtctr(r3); __ bind(&loop); - __ LoadP(scratch, MemOperand(r5, -kPointerSize)); + __ LoadP(scratch, MemOperand(r5, -kSystemPointerSize)); __ StoreP(scratch, MemOperand(r5)); - __ subi(r5, r5, Operand(kPointerSize)); + __ subi(r5, r5, Operand(kSystemPointerSize)); __ bdnz(&loop); // Adjust the actual number of arguments and remove the top element // (which is a copy of the last argument). @@ -1764,19 +1785,20 @@ void Builtins::Generate_ReflectApply(MacroAssembler* masm) { Register arg_size = r8; Register new_sp = r6; Register scratch = r7; - __ ShiftLeftImm(arg_size, r3, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(arg_size, r3, Operand(kSystemPointerSizeLog2)); __ add(new_sp, sp, arg_size); __ LoadRoot(r4, RootIndex::kUndefinedValue); __ mr(scratch, r4); __ mr(r5, r4); - __ cmpi(arg_size, Operand(kPointerSize)); + __ cmpi(arg_size, Operand(kSystemPointerSize)); __ blt(&skip); - __ LoadP(r4, MemOperand(new_sp, 1 * -kPointerSize)); // target + __ LoadP(r4, MemOperand(new_sp, 1 * -kSystemPointerSize)); // target __ beq(&skip); - __ LoadP(scratch, MemOperand(new_sp, 2 * -kPointerSize)); // thisArgument - __ cmpi(arg_size, Operand(2 * kPointerSize)); + __ LoadP(scratch, + MemOperand(new_sp, 2 * -kSystemPointerSize)); // thisArgument + __ cmpi(arg_size, Operand(2 * kSystemPointerSize)); __ beq(&skip); - __ LoadP(r5, MemOperand(new_sp, 3 * -kPointerSize)); // argumentsList + __ LoadP(r5, MemOperand(new_sp, 3 * -kSystemPointerSize)); // argumentsList __ bind(&skip); __ mr(sp, new_sp); __ StoreP(scratch, MemOperand(sp, 0)); @@ -1814,21 +1836,21 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { Label skip; Register arg_size = r8; Register new_sp = r7; - __ ShiftLeftImm(arg_size, r3, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(arg_size, r3, Operand(kSystemPointerSizeLog2)); __ add(new_sp, sp, arg_size); __ LoadRoot(r4, RootIndex::kUndefinedValue); __ mr(r5, r4); __ mr(r6, r4); __ StoreP(r4, MemOperand(new_sp, 0)); // receiver (undefined) - __ cmpi(arg_size, Operand(kPointerSize)); + __ cmpi(arg_size, Operand(kSystemPointerSize)); __ blt(&skip); - __ LoadP(r4, MemOperand(new_sp, 1 * -kPointerSize)); // target + __ LoadP(r4, MemOperand(new_sp, 1 * -kSystemPointerSize)); // target __ mr(r6, r4); // new.target defaults to target __ beq(&skip); - __ LoadP(r5, MemOperand(new_sp, 2 * -kPointerSize)); // argumentsList - __ cmpi(arg_size, Operand(2 * kPointerSize)); + __ LoadP(r5, MemOperand(new_sp, 2 * -kSystemPointerSize)); // argumentsList + __ cmpi(arg_size, Operand(2 * kSystemPointerSize)); __ beq(&skip); - __ LoadP(r6, MemOperand(new_sp, 3 * -kPointerSize)); // new.target + __ LoadP(r6, MemOperand(new_sp, 3 * -kSystemPointerSize)); // new.target __ bind(&skip); __ mr(sp, new_sp); } @@ -1875,7 +1897,7 @@ static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) { // Get the number of arguments passed (as a smi), tear down the frame and // then tear down the parameters. __ LoadP(r4, MemOperand(fp, ArgumentsAdaptorFrameConstants::kLengthOffset)); - int stack_adjustment = kPointerSize; // adjust for receiver + int stack_adjustment = kSystemPointerSize; // adjust for receiver __ LeaveFrame(StackFrame::ARGUMENTS_ADAPTOR, stack_adjustment); __ SmiToPtrArrayOffset(r0, r4); __ add(sp, sp, r0); @@ -1898,7 +1920,8 @@ void Builtins::Generate_CallOrConstructVarargs(MacroAssembler* masm, // Allow r5 to be a FixedArray, or a FixedDoubleArray if r7 == 0. Label ok, fail; __ AssertNotSmi(r5); - __ LoadP(scratch, FieldMemOperand(r5, HeapObject::kMapOffset)); + __ LoadTaggedPointerField(scratch, + FieldMemOperand(r5, HeapObject::kMapOffset)); __ LoadHalfWord(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset)); __ cmpi(scratch, Operand(FIXED_ARRAY_TYPE)); @@ -1924,10 +1947,11 @@ void Builtins::Generate_CallOrConstructVarargs(MacroAssembler* masm, __ cmpi(r7, Operand::Zero()); __ beq(&no_args); __ addi(r5, r5, - Operand(FixedArray::kHeaderSize - kHeapObjectTag - kPointerSize)); + Operand(FixedArray::kHeaderSize - kHeapObjectTag - kTaggedSize)); __ mtctr(r7); __ bind(&loop); - __ LoadPU(scratch, MemOperand(r5, kPointerSize)); + __ LoadTaggedPointerField(scratch, MemOperand(r5, kTaggedSize)); + __ addi(r5, r5, Operand(kTaggedSize)); __ CompareRoot(scratch, RootIndex::kTheHoleValue); __ bne(&skip); __ LoadRoot(scratch, RootIndex::kUndefinedValue); @@ -1961,7 +1985,8 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm, if (mode == CallOrConstructMode::kConstruct) { Label new_target_constructor, new_target_not_constructor; __ JumpIfSmi(r6, &new_target_not_constructor); - __ LoadP(scratch, FieldMemOperand(r6, HeapObject::kMapOffset)); + __ LoadTaggedPointerField(scratch, + FieldMemOperand(r6, HeapObject::kMapOffset)); __ lbz(scratch, FieldMemOperand(scratch, Map::kBitFieldOffset)); __ TestBit(scratch, Map::Bits1::IsConstructorBit::kShift, r0); __ bne(&new_target_constructor, cr0); @@ -1985,7 +2010,8 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm, __ beq(&arguments_adaptor); { __ LoadP(r8, MemOperand(fp, StandardFrameConstants::kFunctionOffset)); - __ LoadP(r8, FieldMemOperand(r8, JSFunction::kSharedFunctionInfoOffset)); + __ LoadTaggedPointerField( + r8, FieldMemOperand(r8, JSFunction::kSharedFunctionInfoOffset)); __ LoadHalfWord( r8, FieldMemOperand(r8, SharedFunctionInfo::kFormalParameterCountOffset)); @@ -2011,11 +2037,11 @@ void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm, // Forward the arguments from the caller frame. { Label loop; - __ addi(r7, r7, Operand(kPointerSize)); + __ addi(r7, r7, Operand(kSystemPointerSize)); __ add(r3, r3, r8); __ bind(&loop); { - __ ShiftLeftImm(scratch, r8, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(scratch, r8, Operand(kSystemPointerSizeLog2)); __ LoadPX(scratch, MemOperand(r7, scratch)); __ push(scratch); __ subi(r8, r8, Operand(1)); @@ -2045,7 +2071,8 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList) // Check that the function is not a "classConstructor". Label class_constructor; - __ LoadP(r5, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset)); + __ LoadTaggedPointerField( + r5, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset)); __ lwz(r6, FieldMemOperand(r5, SharedFunctionInfo::kFlagsOffset)); __ TestBitMask(r6, SharedFunctionInfo::IsClassConstructorBit::kMask, r0); __ bne(&class_constructor, cr0); @@ -2053,7 +2080,8 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, // Enter the context of the function; ToObject has to run in the function // context, and we also need to take the global proxy from the function // context in case of conversion. - __ LoadP(cp, FieldMemOperand(r4, JSFunction::kContextOffset)); + __ LoadTaggedPointerField(cp, + FieldMemOperand(r4, JSFunction::kContextOffset)); // We need to convert the receiver for non-native sloppy mode functions. Label done_convert; __ andi(r0, r6, @@ -2073,7 +2101,7 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, __ LoadGlobalProxy(r6); } else { Label convert_to_object, convert_receiver; - __ ShiftLeftImm(r6, r3, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(r6, r3, Operand(kSystemPointerSizeLog2)); __ LoadPX(r6, MemOperand(sp, r6)); __ JumpIfSmi(r6, &convert_to_object); STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); @@ -2107,10 +2135,11 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, __ Pop(r3, r4); __ SmiUntag(r3); } - __ LoadP(r5, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset)); + __ LoadTaggedPointerField( + r5, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset)); __ bind(&convert_receiver); } - __ ShiftLeftImm(r7, r3, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(r7, r3, Operand(kSystemPointerSizeLog2)); __ StorePX(r6, MemOperand(sp, r7)); } __ bind(&done_convert); @@ -2146,9 +2175,9 @@ void Generate_PushBoundArguments(MacroAssembler* masm) { // Load [[BoundArguments]] into r5 and length of that into r7. Label no_bound_arguments; - __ LoadP(r5, FieldMemOperand(r4, JSBoundFunction::kBoundArgumentsOffset)); - __ LoadP(r7, FieldMemOperand(r5, FixedArray::kLengthOffset)); - __ SmiUntag(r7, SetRC); + __ LoadTaggedPointerField( + r5, FieldMemOperand(r4, JSBoundFunction::kBoundArgumentsOffset)); + __ SmiUntagField(r7, FieldMemOperand(r5, FixedArray::kLengthOffset), SetRC); __ beq(&no_bound_arguments, cr0); { // ----------- S t a t e ------------- @@ -2163,9 +2192,8 @@ void Generate_PushBoundArguments(MacroAssembler* masm) { // Reserve stack space for the [[BoundArguments]]. { Label done; - __ mr(scratch, sp); // preserve previous stack pointer - __ ShiftLeftImm(r10, r7, Operand(kPointerSizeLog2)); - __ sub(sp, sp, r10); + __ ShiftLeftImm(r10, r7, Operand(kSystemPointerSizeLog2)); + __ sub(r0, sp, r10); // Check the stack for overflow. We are not trying to catch interruptions // (i.e. debug break and preemption) here, so check the "real stack // limit". @@ -2173,11 +2201,9 @@ void Generate_PushBoundArguments(MacroAssembler* masm) { UseScratchRegisterScope temps(masm); Register scratch = temps.Acquire(); LoadStackLimit(masm, scratch, StackLimitKind::kRealStackLimit); - __ cmpl(sp, scratch); + __ cmpl(r0, scratch); } __ bgt(&done); // Signed comparison. - // Restore the stack pointer. - __ mr(sp, scratch); { FrameScope scope(masm, StackFrame::MANUAL); __ EnterFrame(StackFrame::INTERNAL); @@ -2186,6 +2212,9 @@ void Generate_PushBoundArguments(MacroAssembler* masm) { __ bind(&done); } + __ mr(scratch, sp); + __ mr(sp, r0); + // Relocate arguments down the stack. // -- r3 : the number of arguments (not including the receiver) // -- r9 : the previous stack pointer @@ -2199,7 +2228,7 @@ void Generate_PushBoundArguments(MacroAssembler* masm) { __ bind(&loop); __ LoadPX(r0, MemOperand(scratch, r8)); __ StorePX(r0, MemOperand(sp, r8)); - __ addi(r8, r8, Operand(kPointerSize)); + __ addi(r8, r8, Operand(kSystemPointerSize)); __ bdnz(&loop); __ bind(&skip); } @@ -2207,13 +2236,15 @@ void Generate_PushBoundArguments(MacroAssembler* masm) { // Copy [[BoundArguments]] to the stack (below the arguments). { Label loop; - __ addi(r5, r5, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ ShiftLeftImm(r10, r7, Operand(kTaggedSizeLog2)); + __ addi(r10, r10, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); __ add(r5, r5, r10); __ mtctr(r7); __ bind(&loop); - __ LoadPU(r0, MemOperand(r5, -kPointerSize)); - __ StorePX(r0, MemOperand(sp, r8)); - __ addi(r8, r8, Operand(kPointerSize)); + __ LoadAnyTaggedField(ip, MemOperand(r5, -kTaggedSize), r0); + __ StorePX(ip, MemOperand(sp, r8)); + __ addi(r8, r8, Operand(kSystemPointerSize)); + __ addi(r5, r5, Operand(-kTaggedSize)); __ bdnz(&loop); __ add(r3, r3, r7); } @@ -2232,16 +2263,17 @@ void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm) { __ AssertBoundFunction(r4); // Patch the receiver to [[BoundThis]]. - __ LoadP(r6, FieldMemOperand(r4, JSBoundFunction::kBoundThisOffset)); - __ ShiftLeftImm(r0, r3, Operand(kPointerSizeLog2)); + __ LoadAnyTaggedField(r6, + FieldMemOperand(r4, JSBoundFunction::kBoundThisOffset)); + __ ShiftLeftImm(r0, r3, Operand(kSystemPointerSizeLog2)); __ StorePX(r6, MemOperand(sp, r0)); // Push the [[BoundArguments]] onto the stack. Generate_PushBoundArguments(masm); // Call the [[BoundTargetFunction]] via the Call builtin. - __ LoadP(r4, - FieldMemOperand(r4, JSBoundFunction::kBoundTargetFunctionOffset)); + __ LoadTaggedPointerField( + r4, FieldMemOperand(r4, JSBoundFunction::kBoundTargetFunctionOffset)); __ Jump(BUILTIN_CODE(masm->isolate(), Call_ReceiverIsAny), RelocInfo::CODE_TARGET); } @@ -2275,7 +2307,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) { // 2. Call to something else, which might have a [[Call]] internal method (if // not we raise an exception). // Overwrite the original receiver the (original) target. - __ ShiftLeftImm(r8, r3, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(r8, r3, Operand(kSystemPointerSizeLog2)); __ StorePX(r4, MemOperand(sp, r8)); // Let the "call_as_function_delegate" take care of the rest. __ LoadNativeContextSlot(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, r4); @@ -2309,7 +2341,8 @@ void Builtins::Generate_ConstructFunction(MacroAssembler* masm) { Label call_generic_stub; // Jump to JSBuiltinsConstructStub or JSConstructStubGeneric. - __ LoadP(r7, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset)); + __ LoadTaggedPointerField( + r7, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset)); __ lwz(r7, FieldMemOperand(r7, SharedFunctionInfo::kFlagsOffset)); __ mov(ip, Operand(SharedFunctionInfo::ConstructAsBuiltinBit::kMask)); __ and_(r7, r7, ip, SetRC); @@ -2338,15 +2371,15 @@ void Builtins::Generate_ConstructBoundFunction(MacroAssembler* masm) { // Patch new.target to [[BoundTargetFunction]] if new.target equals target. Label skip; - __ cmp(r4, r6); + __ CompareTagged(r4, r6); __ bne(&skip); - __ LoadP(r6, - FieldMemOperand(r4, JSBoundFunction::kBoundTargetFunctionOffset)); + __ LoadTaggedPointerField( + r6, FieldMemOperand(r4, JSBoundFunction::kBoundTargetFunctionOffset)); __ bind(&skip); // Construct the [[BoundTargetFunction]] via the Construct builtin. - __ LoadP(r4, - FieldMemOperand(r4, JSBoundFunction::kBoundTargetFunctionOffset)); + __ LoadTaggedPointerField( + r4, FieldMemOperand(r4, JSBoundFunction::kBoundTargetFunctionOffset)); __ Jump(BUILTIN_CODE(masm->isolate(), Construct), RelocInfo::CODE_TARGET); } @@ -2364,7 +2397,7 @@ void Builtins::Generate_Construct(MacroAssembler* masm) { __ JumpIfSmi(r4, &non_constructor); // Check if target has a [[Construct]] internal method. - __ LoadP(r7, FieldMemOperand(r4, HeapObject::kMapOffset)); + __ LoadTaggedPointerField(r7, FieldMemOperand(r4, HeapObject::kMapOffset)); __ lbz(r5, FieldMemOperand(r7, Map::kBitFieldOffset)); __ TestBit(r5, Map::Bits1::IsConstructorBit::kShift, r0); __ beq(&non_constructor, cr0); @@ -2390,7 +2423,7 @@ void Builtins::Generate_Construct(MacroAssembler* masm) { __ bind(&non_proxy); { // Overwrite the original receiver with the (original) target. - __ ShiftLeftImm(r8, r3, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(r8, r3, Operand(kSystemPointerSizeLog2)); __ StorePX(r4, MemOperand(sp, r8)); // Let the "call_as_constructor_delegate" take care of the rest. __ LoadNativeContextSlot(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, r4); @@ -2416,7 +2449,8 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { Label dont_adapt_arguments, stack_overflow, skip_adapt_arguments; __ cmpli(r5, Operand(kDontAdaptArgumentsSentinel)); __ beq(&dont_adapt_arguments); - __ LoadP(r7, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset)); + __ LoadTaggedPointerField( + r7, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset)); __ lwz(r7, FieldMemOperand(r7, SharedFunctionInfo::kFlagsOffset)); __ TestBitMask(r7, SharedFunctionInfo::IsSafeToSkipArgumentsAdaptorBit::kMask, r0); @@ -2444,8 +2478,8 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ SmiToPtrArrayOffset(r3, r3); __ add(r3, r3, fp); // adjust for return address and receiver - __ addi(r3, r3, Operand(2 * kPointerSize)); - __ ShiftLeftImm(r7, r5, Operand(kPointerSizeLog2)); + __ addi(r3, r3, Operand(2 * kSystemPointerSize)); + __ ShiftLeftImm(r7, r5, Operand(kSystemPointerSizeLog2)); __ sub(r7, r3, r7); // Copy the arguments (including the receiver) to the new stack frame. @@ -2460,7 +2494,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ LoadP(r0, MemOperand(r3, 0)); __ push(r0); __ cmp(r3, r7); // Compare before moving to next argument. - __ subi(r3, r3, Operand(kPointerSize)); + __ subi(r3, r3, Operand(kSystemPointerSize)); __ bne(©); __ b(&invoke); @@ -2488,10 +2522,10 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { Label copy; __ bind(©); // Adjust load for return address and receiver. - __ LoadP(r0, MemOperand(r3, 2 * kPointerSize)); + __ LoadP(r0, MemOperand(r3, 2 * kSystemPointerSize)); __ push(r0); __ cmp(r3, fp); // Compare before moving to next argument. - __ subi(r3, r3, Operand(kPointerSize)); + __ subi(r3, r3, Operand(kSystemPointerSize)); __ bne(©); // Fill the remaining expected arguments with undefined. @@ -2499,12 +2533,12 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // r5: expected number of arguments // r6: new target (passed through to callee) __ LoadRoot(r0, RootIndex::kUndefinedValue); - __ ShiftLeftImm(r7, r5, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(r7, r5, Operand(kSystemPointerSizeLog2)); __ sub(r7, fp, r7); // Adjust for frame. __ subi(r7, r7, Operand(ArgumentsAdaptorFrameConstants::kFixedFrameSizeFromFp + - kPointerSize)); + kSystemPointerSize)); Label fill; __ bind(&fill); @@ -2520,7 +2554,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // r4 : function (passed through to callee) // r6 : new target (passed through to callee) static_assert(kJavaScriptCallCodeStartRegister == r5, "ABI mismatch"); - __ LoadP(r5, FieldMemOperand(r4, JSFunction::kCodeOffset)); + __ LoadTaggedPointerField(r5, FieldMemOperand(r4, JSFunction::kCodeOffset)); __ CallCodeObject(r5); // Store offset of return address for deoptimizer. @@ -2549,7 +2583,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // Remove superfluous parameters from the stack. __ sub(r7, r3, r5); __ mr(r3, r5); - __ ShiftLeftImm(r7, r7, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(r7, r7, Operand(kSystemPointerSizeLog2)); __ add(sp, sp, r7); __ b(&dont_adapt_arguments); } @@ -2572,8 +2606,9 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // Dont adapt arguments. // ------------------------------------------- __ bind(&dont_adapt_arguments); + __ RecordComment("-- Call without adapting args --"); static_assert(kJavaScriptCallCodeStartRegister == r5, "ABI mismatch"); - __ LoadP(r5, FieldMemOperand(r4, JSFunction::kCodeOffset)); + __ LoadTaggedPointerField(r5, FieldMemOperand(r4, JSFunction::kCodeOffset)); __ JumpCodeObject(r5); __ bind(&stack_overflow); @@ -2663,9 +2698,9 @@ void Builtins::Generate_CEntry(MacroAssembler* masm, int result_size, __ mr(r4, r5); } else { // Compute the argv pointer. - __ ShiftLeftImm(r4, r3, Operand(kPointerSizeLog2)); + __ ShiftLeftImm(r4, r3, Operand(kSystemPointerSizeLog2)); __ add(r4, r4, sp); - __ subi(r4, r4, Operand(kPointerSize)); + __ subi(r4, r4, Operand(kSystemPointerSize)); } // Enter the exit frame that transitions from JavaScript to C++. @@ -2701,7 +2736,8 @@ void Builtins::Generate_CEntry(MacroAssembler* masm, int result_size, // buffer as implicit first argument. __ mr(r5, r4); __ mr(r4, r3); - __ addi(r3, sp, Operand((kStackFrameExtraParamSlot + 1) * kPointerSize)); + __ addi(r3, sp, + Operand((kStackFrameExtraParamSlot + 1) * kSystemPointerSize)); isolate_reg = r6; } @@ -2713,7 +2749,7 @@ void Builtins::Generate_CEntry(MacroAssembler* masm, int result_size, // If return value is on the stack, pop it to registers. if (needs_return_buffer) { - __ LoadP(r4, MemOperand(r3, kPointerSize)); + __ LoadP(r4, MemOperand(r3, kSystemPointerSize)); __ LoadP(r3, MemOperand(r3)); } @@ -2828,7 +2864,7 @@ void Builtins::Generate_DoubleToI(MacroAssembler* masm) { __ Push(result_reg, scratch); // Account for saved regs. - int argument_offset = 2 * kPointerSize; + int argument_offset = 2 * kSystemPointerSize; // Load double input. __ lfd(double_scratch, MemOperand(sp, argument_offset)); @@ -2850,7 +2886,7 @@ void Builtins::Generate_DoubleToI(MacroAssembler* masm) { __ Push(scratch_high, scratch_low); // Account for saved regs. - argument_offset += 2 * kPointerSize; + argument_offset += 2 * kSystemPointerSize; __ lwz(scratch_high, MemOperand(sp, argument_offset + Register::kExponentOffset)); @@ -2921,7 +2957,7 @@ void Builtins::Generate_DoubleToI(MacroAssembler* masm) { __ bind(&done); __ Pop(scratch_high, scratch_low); // Account for saved regs. - argument_offset -= 2 * kPointerSize; + argument_offset -= 2 * kSystemPointerSize; __ bind(&fastpath_done); __ StoreP(result_reg, MemOperand(sp, argument_offset)); @@ -3092,33 +3128,33 @@ void Builtins::Generate_CallApiCallback(MacroAssembler* masm) { // Set up FunctionCallbackInfo's implicit_args on the stack as follows: // // Target state: - // sp[0 * kPointerSize]: kHolder - // sp[1 * kPointerSize]: kIsolate - // sp[2 * kPointerSize]: undefined (kReturnValueDefaultValue) - // sp[3 * kPointerSize]: undefined (kReturnValue) - // sp[4 * kPointerSize]: kData - // sp[5 * kPointerSize]: undefined (kNewTarget) + // sp[0 * kSystemPointerSize]: kHolder + // sp[1 * kSystemPointerSize]: kIsolate + // sp[2 * kSystemPointerSize]: undefined (kReturnValueDefaultValue) + // sp[3 * kSystemPointerSize]: undefined (kReturnValue) + // sp[4 * kSystemPointerSize]: kData + // sp[5 * kSystemPointerSize]: undefined (kNewTarget) // Reserve space on the stack. - __ subi(sp, sp, Operand(FCA::kArgsLength * kPointerSize)); + __ subi(sp, sp, Operand(FCA::kArgsLength * kSystemPointerSize)); // kHolder. - __ StoreP(holder, MemOperand(sp, 0 * kPointerSize)); + __ StoreP(holder, MemOperand(sp, 0 * kSystemPointerSize)); // kIsolate. __ Move(scratch, ExternalReference::isolate_address(masm->isolate())); - __ StoreP(scratch, MemOperand(sp, 1 * kPointerSize)); + __ StoreP(scratch, MemOperand(sp, 1 * kSystemPointerSize)); // kReturnValueDefaultValue and kReturnValue. __ LoadRoot(scratch, RootIndex::kUndefinedValue); - __ StoreP(scratch, MemOperand(sp, 2 * kPointerSize)); - __ StoreP(scratch, MemOperand(sp, 3 * kPointerSize)); + __ StoreP(scratch, MemOperand(sp, 2 * kSystemPointerSize)); + __ StoreP(scratch, MemOperand(sp, 3 * kSystemPointerSize)); // kData. - __ StoreP(call_data, MemOperand(sp, 4 * kPointerSize)); + __ StoreP(call_data, MemOperand(sp, 4 * kSystemPointerSize)); // kNewTarget. - __ StoreP(scratch, MemOperand(sp, 5 * kPointerSize)); + __ StoreP(scratch, MemOperand(sp, 5 * kSystemPointerSize)); // Keep a pointer to kHolder (= implicit_args) in a scratch register. // We use it below to set up the FunctionCallbackInfo object. @@ -3140,31 +3176,34 @@ void Builtins::Generate_CallApiCallback(MacroAssembler* masm) { // FunctionCallbackInfo::implicit_args_ (points at kHolder as set up above). // Arguments are after the return address (pushed by EnterExitFrame()). - __ StoreP(scratch, - MemOperand(sp, (kStackFrameExtraParamSlot + 1) * kPointerSize)); + __ StoreP(scratch, MemOperand(sp, (kStackFrameExtraParamSlot + 1) * + kSystemPointerSize)); // FunctionCallbackInfo::values_ (points at the first varargs argument passed // on the stack). - __ addi(scratch, scratch, Operand((FCA::kArgsLength - 1) * kPointerSize)); - __ ShiftLeftImm(ip, argc, Operand(kPointerSizeLog2)); + __ addi(scratch, scratch, + Operand((FCA::kArgsLength - 1) * kSystemPointerSize)); + __ ShiftLeftImm(ip, argc, Operand(kSystemPointerSizeLog2)); __ add(scratch, scratch, ip); - __ StoreP(scratch, - MemOperand(sp, (kStackFrameExtraParamSlot + 2) * kPointerSize)); + __ StoreP(scratch, MemOperand(sp, (kStackFrameExtraParamSlot + 2) * + kSystemPointerSize)); // FunctionCallbackInfo::length_. - __ stw(argc, MemOperand(sp, (kStackFrameExtraParamSlot + 3) * kPointerSize)); + __ stw(argc, + MemOperand(sp, (kStackFrameExtraParamSlot + 3) * kSystemPointerSize)); // We also store the number of bytes to drop from the stack after returning // from the API function here. __ mov(scratch, - Operand((FCA::kArgsLength + 1 /* receiver */) * kPointerSize)); - __ ShiftLeftImm(ip, argc, Operand(kPointerSizeLog2)); + Operand((FCA::kArgsLength + 1 /* receiver */) * kSystemPointerSize)); + __ ShiftLeftImm(ip, argc, Operand(kSystemPointerSizeLog2)); __ add(scratch, scratch, ip); - __ StoreP(scratch, - MemOperand(sp, (kStackFrameExtraParamSlot + 4) * kPointerSize)); + __ StoreP(scratch, MemOperand(sp, (kStackFrameExtraParamSlot + 4) * + kSystemPointerSize)); // v8::InvocationCallback's argument. - __ addi(r3, sp, Operand((kStackFrameExtraParamSlot + 1) * kPointerSize)); + __ addi(r3, sp, + Operand((kStackFrameExtraParamSlot + 1) * kSystemPointerSize)); ExternalReference thunk_ref = ExternalReference::invoke_function_callback(); @@ -3172,11 +3211,11 @@ void Builtins::Generate_CallApiCallback(MacroAssembler* masm) { // TODO(jgruber): Document what these arguments are. static constexpr int kStackSlotsAboveFCA = 2; MemOperand return_value_operand( - fp, (kStackSlotsAboveFCA + FCA::kReturnValueOffset) * kPointerSize); + fp, (kStackSlotsAboveFCA + FCA::kReturnValueOffset) * kSystemPointerSize); static constexpr int kUseStackSpaceOperand = 0; MemOperand stack_space_operand( - sp, (kStackFrameExtraParamSlot + 4) * kPointerSize); + sp, (kStackFrameExtraParamSlot + 4) * kSystemPointerSize); AllowExternalCallThatCantCauseGC scope(masm); CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, @@ -3210,14 +3249,16 @@ void Builtins::Generate_CallApiGetter(MacroAssembler* masm) { __ push(receiver); // Push data from AccessorInfo. - __ LoadP(scratch, FieldMemOperand(callback, AccessorInfo::kDataOffset)); + __ LoadAnyTaggedField(scratch, + FieldMemOperand(callback, AccessorInfo::kDataOffset)); __ push(scratch); __ LoadRoot(scratch, RootIndex::kUndefinedValue); __ Push(scratch, scratch); __ Move(scratch, ExternalReference::isolate_address(masm->isolate())); __ Push(scratch, holder); __ Push(Smi::zero()); // should_throw_on_error -> false - __ LoadP(scratch, FieldMemOperand(callback, AccessorInfo::kNameOffset)); + __ LoadTaggedPointerField( + scratch, FieldMemOperand(callback, AccessorInfo::kNameOffset)); __ push(scratch); // v8::PropertyCallbackInfo::args_ array and name handle. @@ -3225,20 +3266,20 @@ void Builtins::Generate_CallApiGetter(MacroAssembler* masm) { // Load address of v8::PropertyAccessorInfo::args_ array and name handle. __ mr(r3, sp); // r3 = Handle - __ addi(r4, r3, Operand(1 * kPointerSize)); // r4 = v8::PCI::args_ + __ addi(r4, r3, Operand(1 * kSystemPointerSize)); // r4 = v8::PCI::args_ -// If ABI passes Handles (pointer-sized struct) in a register: -// -// Create 2 extra slots on stack: -// [0] space for DirectCEntryStub's LR save -// [1] AccessorInfo& -// -// Otherwise: -// -// Create 3 extra slots on stack: -// [0] space for DirectCEntryStub's LR save -// [1] copy of Handle (first arg) -// [2] AccessorInfo& + // If ABI passes Handles (pointer-sized struct) in a register: + // + // Create 2 extra slots on stack: + // [0] space for DirectCEntryStub's LR save + // [1] AccessorInfo& + // + // Otherwise: + // + // Create 3 extra slots on stack: + // [0] space for DirectCEntryStub's LR save + // [1] copy of Handle (first arg) + // [2] AccessorInfo& if (ABI_PASSES_HANDLES_IN_REGS) { accessorInfoSlot = kStackFrameExtraParamSlot + 1; apiStackSpace = 2; @@ -3253,26 +3294,28 @@ void Builtins::Generate_CallApiGetter(MacroAssembler* masm) { if (!ABI_PASSES_HANDLES_IN_REGS) { // pass 1st arg by reference - __ StoreP(r3, MemOperand(sp, arg0Slot * kPointerSize)); - __ addi(r3, sp, Operand(arg0Slot * kPointerSize)); + __ StoreP(r3, MemOperand(sp, arg0Slot * kSystemPointerSize)); + __ addi(r3, sp, Operand(arg0Slot * kSystemPointerSize)); } // Create v8::PropertyCallbackInfo object on the stack and initialize // it's args_ field. - __ StoreP(r4, MemOperand(sp, accessorInfoSlot * kPointerSize)); - __ addi(r4, sp, Operand(accessorInfoSlot * kPointerSize)); + __ StoreP(r4, MemOperand(sp, accessorInfoSlot * kSystemPointerSize)); + __ addi(r4, sp, Operand(accessorInfoSlot * kSystemPointerSize)); // r4 = v8::PropertyCallbackInfo& ExternalReference thunk_ref = ExternalReference::invoke_accessor_getter_callback(); - __ LoadP(scratch, FieldMemOperand(callback, AccessorInfo::kJsGetterOffset)); + __ LoadTaggedPointerField( + scratch, FieldMemOperand(callback, AccessorInfo::kJsGetterOffset)); __ LoadP(api_function_address, FieldMemOperand(scratch, Foreign::kForeignAddressOffset)); // +3 is to skip prolog, return address and name handle. MemOperand return_value_operand( - fp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize); + fp, + (PropertyCallbackArguments::kReturnValueOffset + 3) * kSystemPointerSize); MemOperand* const kUseStackSpaceConstant = nullptr; CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, kStackUnwindSpace, kUseStackSpaceConstant, @@ -3285,16 +3328,17 @@ void Builtins::Generate_DirectCEntry(MacroAssembler* masm) { // Place the return address on the stack, making the call // GC safe. The RegExp backend also relies on this. __ mflr(r0); - __ StoreP(r0, MemOperand(sp, kStackFrameExtraParamSlot * kPointerSize)); + __ StoreP(r0, MemOperand(sp, kStackFrameExtraParamSlot * kSystemPointerSize)); if (ABI_USES_FUNCTION_DESCRIPTORS) { // AIX/PPC64BE Linux use a function descriptor; - __ LoadP(ToRegister(ABI_TOC_REGISTER), MemOperand(temp2, kPointerSize)); + __ LoadP(ToRegister(ABI_TOC_REGISTER), + MemOperand(temp2, kSystemPointerSize)); __ LoadP(temp2, MemOperand(temp2, 0)); // Instruction address } __ Call(temp2); // Call the C++ function. - __ LoadP(r0, MemOperand(sp, kStackFrameExtraParamSlot * kPointerSize)); + __ LoadP(r0, MemOperand(sp, kStackFrameExtraParamSlot * kSystemPointerSize)); __ mtlr(r0); __ blr(); } diff --git a/deps/v8/src/builtins/promise-abstract-operations.tq b/deps/v8/src/builtins/promise-abstract-operations.tq index 95ca356a0ceb85..9cf6da102b8eec 100644 --- a/deps/v8/src/builtins/promise-abstract-operations.tq +++ b/deps/v8/src/builtins/promise-abstract-operations.tq @@ -6,544 +6,534 @@ #include 'src/builtins/builtins-promise-gen.h' namespace runtime { - extern transitioning runtime - RejectPromise(implicit context: Context)(JSPromise, JSAny, Boolean): JSAny; +extern transitioning runtime +RejectPromise(implicit context: Context)(JSPromise, JSAny, Boolean): JSAny; - extern transitioning runtime - PromiseRevokeReject(implicit context: Context)(JSPromise): JSAny; +extern transitioning runtime +PromiseRevokeReject(implicit context: Context)(JSPromise): JSAny; - extern transitioning runtime - PromiseRejectAfterResolved(implicit context: Context)(JSPromise, JSAny): - JSAny; +extern transitioning runtime +PromiseRejectAfterResolved(implicit context: Context)(JSPromise, JSAny): JSAny; - extern transitioning runtime - PromiseResolveAfterResolved(implicit context: Context)(JSPromise, JSAny): - JSAny; +extern transitioning runtime +PromiseResolveAfterResolved(implicit context: Context)(JSPromise, JSAny): JSAny; - extern transitioning runtime - PromiseRejectEventFromStack(implicit context: Context)(JSPromise, JSAny): - JSAny; +extern transitioning runtime +PromiseRejectEventFromStack(implicit context: Context)(JSPromise, JSAny): JSAny; } // https://tc39.es/ecma262/#sec-promise-abstract-operations namespace promise { - extern macro AllocateFunctionWithMapAndContext( - Map, SharedFunctionInfo, Context): JSFunction; - - extern macro PromiseReactionMapConstant(): Map; - extern macro PromiseFulfillReactionJobTaskMapConstant(): Map; - extern macro PromiseRejectReactionJobTaskMapConstant(): Map; - extern transitioning builtin - ResolvePromise(Context, JSPromise, JSAny): JSAny; - - extern transitioning builtin - EnqueueMicrotask(Context, Microtask): Undefined; - - macro - ExtractHandlerContextInternal(implicit context: Context)(handler: Callable| - Undefined): - Context labels NotFound { - let iter: JSAny = handler; - while (true) { - typeswitch (iter) { - case (b: JSBoundFunction): { - iter = b.bound_target_function; - } - case (p: JSProxy): { - iter = p.target; - } - case (f: JSFunction): { - return f.context; - } - case (JSAny): { - break; - } +extern macro AllocateFunctionWithMapAndContext( + Map, SharedFunctionInfo, Context): JSFunction; + +extern macro PromiseReactionMapConstant(): Map; +extern macro PromiseFulfillReactionJobTaskMapConstant(): Map; +extern macro PromiseRejectReactionJobTaskMapConstant(): Map; +extern transitioning builtin +ResolvePromise(Context, JSPromise, JSAny): JSAny; + +extern transitioning builtin +EnqueueMicrotask(Context, Microtask): Undefined; + +macro +ExtractHandlerContextInternal(implicit context: Context)( + handler: Callable|Undefined): Context labels NotFound { + let iter: JSAny = handler; + while (true) { + typeswitch (iter) { + case (b: JSBoundFunction): { + iter = b.bound_target_function; + } + case (p: JSProxy): { + iter = p.target; + } + case (f: JSFunction): { + return f.context; + } + case (JSAny): { + break; } } - goto NotFound; } + goto NotFound; +} - macro - ExtractHandlerContext(implicit context: Context)(handler: Callable| - Undefined): Context { - try { - return ExtractHandlerContextInternal(handler) otherwise NotFound; - } - label NotFound deferred { - return context; - } +macro +ExtractHandlerContext(implicit context: Context)(handler: Callable| + Undefined): Context { + try { + return ExtractHandlerContextInternal(handler) otherwise NotFound; + } label NotFound deferred { + return context; } +} - macro - ExtractHandlerContext(implicit context: Context)( - primary: Callable|Undefined, secondary: Callable|Undefined): Context { - try { - return ExtractHandlerContextInternal(primary) otherwise NotFound; - } - label NotFound deferred { - return ExtractHandlerContextInternal(secondary) otherwise Default; - } - label Default deferred { - return context; - } +macro +ExtractHandlerContext(implicit context: Context)( + primary: Callable|Undefined, secondary: Callable|Undefined): Context { + try { + return ExtractHandlerContextInternal(primary) otherwise NotFound; + } label NotFound deferred { + return ExtractHandlerContextInternal(secondary) otherwise Default; + } label Default deferred { + return context; } +} - transitioning macro MorphAndEnqueuePromiseReaction(implicit context: Context)( - promiseReaction: PromiseReaction, argument: JSAny, - reactionType: constexpr PromiseReactionType): void { - let primaryHandler: Callable|Undefined; - let secondaryHandler: Callable|Undefined; - if constexpr (reactionType == kPromiseReactionFulfill) { - primaryHandler = promiseReaction.fulfill_handler; - secondaryHandler = promiseReaction.reject_handler; - } else { - StaticAssert(reactionType == kPromiseReactionReject); - primaryHandler = promiseReaction.reject_handler; - secondaryHandler = promiseReaction.fulfill_handler; - } - - // According to HTML, we use the context of the appropriate handler as the - // context of the microtask. See step 3 of HTML's EnqueueJob: - // https://html.spec.whatwg.org/C/#enqueuejob(queuename,-job,-arguments) - const handlerContext: Context = - ExtractHandlerContext(primaryHandler, secondaryHandler); +transitioning macro MorphAndEnqueuePromiseReaction(implicit context: Context)( + promiseReaction: PromiseReaction, argument: JSAny, + reactionType: constexpr PromiseReactionType): void { + let primaryHandler: Callable|Undefined; + let secondaryHandler: Callable|Undefined; + if constexpr (reactionType == kPromiseReactionFulfill) { + primaryHandler = promiseReaction.fulfill_handler; + secondaryHandler = promiseReaction.reject_handler; + } else { + StaticAssert(reactionType == kPromiseReactionReject); + primaryHandler = promiseReaction.reject_handler; + secondaryHandler = promiseReaction.fulfill_handler; + } - // Morph {current} from a PromiseReaction into a PromiseReactionJobTask - // and schedule that on the microtask queue. We try to minimize the number - // of stores here to avoid screwing up the store buffer. + // According to HTML, we use the context of the appropriate handler as the + // context of the microtask. See step 3 of HTML's EnqueueJob: + // https://html.spec.whatwg.org/C/#enqueuejob(queuename,-job,-arguments) + const handlerContext: Context = + ExtractHandlerContext(primaryHandler, secondaryHandler); + + // Morph {current} from a PromiseReaction into a PromiseReactionJobTask + // and schedule that on the microtask queue. We try to minimize the number + // of stores here to avoid screwing up the store buffer. + StaticAssert( + kPromiseReactionSize == + kPromiseReactionJobTaskSizeOfAllPromiseReactionJobTasks); + if constexpr (reactionType == kPromiseReactionFulfill) { + * UnsafeConstCast(& promiseReaction.map) = + PromiseFulfillReactionJobTaskMapConstant(); + const promiseReactionJobTask = + UnsafeCast(promiseReaction); + promiseReactionJobTask.argument = argument; + promiseReactionJobTask.context = handlerContext; + EnqueueMicrotask(handlerContext, promiseReactionJobTask); StaticAssert( - kPromiseReactionSize == - kPromiseReactionJobTaskSizeOfAllPromiseReactionJobTasks); - if constexpr (reactionType == kPromiseReactionFulfill) { - * UnsafeConstCast(& promiseReaction.map) = - PromiseFulfillReactionJobTaskMapConstant(); - const promiseReactionJobTask = - UnsafeCast(promiseReaction); - promiseReactionJobTask.argument = argument; - promiseReactionJobTask.context = handlerContext; - EnqueueMicrotask(handlerContext, promiseReactionJobTask); - StaticAssert( - kPromiseReactionFulfillHandlerOffset == - kPromiseReactionJobTaskHandlerOffset); - StaticAssert( - kPromiseReactionPromiseOrCapabilityOffset == - kPromiseReactionJobTaskPromiseOrCapabilityOffset); - } else { - StaticAssert(reactionType == kPromiseReactionReject); - * UnsafeConstCast(& promiseReaction.map) = - PromiseRejectReactionJobTaskMapConstant(); - const promiseReactionJobTask = - UnsafeCast(promiseReaction); - promiseReactionJobTask.argument = argument; - promiseReactionJobTask.context = handlerContext; - promiseReactionJobTask.handler = primaryHandler; - EnqueueMicrotask(handlerContext, promiseReactionJobTask); - StaticAssert( - kPromiseReactionPromiseOrCapabilityOffset == - kPromiseReactionJobTaskPromiseOrCapabilityOffset); - } + kPromiseReactionFulfillHandlerOffset == + kPromiseReactionJobTaskHandlerOffset); + StaticAssert( + kPromiseReactionPromiseOrCapabilityOffset == + kPromiseReactionJobTaskPromiseOrCapabilityOffset); + } else { + StaticAssert(reactionType == kPromiseReactionReject); + * UnsafeConstCast(& promiseReaction.map) = + PromiseRejectReactionJobTaskMapConstant(); + const promiseReactionJobTask = + UnsafeCast(promiseReaction); + promiseReactionJobTask.argument = argument; + promiseReactionJobTask.context = handlerContext; + promiseReactionJobTask.handler = primaryHandler; + EnqueueMicrotask(handlerContext, promiseReactionJobTask); + StaticAssert( + kPromiseReactionPromiseOrCapabilityOffset == + kPromiseReactionJobTaskPromiseOrCapabilityOffset); } +} - // https://tc39.es/ecma262/#sec-triggerpromisereactions - transitioning macro TriggerPromiseReactions(implicit context: Context)( - reactions: Zero|PromiseReaction, argument: JSAny, - reactionType: constexpr PromiseReactionType): void { - // We need to reverse the {reactions} here, since we record them on the - // JSPromise in the reverse order. - let current = reactions; - let reversed: Zero|PromiseReaction = kZero; - - // As an additional safety net against misuse of the V8 Extras API, we - // sanity check the {reactions} to make sure that they are actually - // PromiseReaction instances and not actual JavaScript values (which - // would indicate that we're rejecting or resolving an already settled - // promise), see https://crbug.com/931640 for details on this. - while (true) { - typeswitch (current) { - case (Zero): { - break; - } - case (currentReaction: PromiseReaction): { - current = currentReaction.next; - currentReaction.next = reversed; - reversed = currentReaction; - } +// https://tc39.es/ecma262/#sec-triggerpromisereactions +transitioning macro TriggerPromiseReactions(implicit context: Context)( + reactions: Zero|PromiseReaction, argument: JSAny, + reactionType: constexpr PromiseReactionType): void { + // We need to reverse the {reactions} here, since we record them on the + // JSPromise in the reverse order. + let current = reactions; + let reversed: Zero|PromiseReaction = kZero; + + // As an additional safety net against misuse of the V8 Extras API, we + // sanity check the {reactions} to make sure that they are actually + // PromiseReaction instances and not actual JavaScript values (which + // would indicate that we're rejecting or resolving an already settled + // promise), see https://crbug.com/931640 for details on this. + while (true) { + typeswitch (current) { + case (Zero): { + break; + } + case (currentReaction: PromiseReaction): { + current = currentReaction.next; + currentReaction.next = reversed; + reversed = currentReaction; } } - // Morph the {reactions} into PromiseReactionJobTasks and push them - // onto the microtask queue. - current = reversed; - while (true) { - typeswitch (current) { - case (Zero): { - break; - } - case (currentReaction: PromiseReaction): { - current = currentReaction.next; - MorphAndEnqueuePromiseReaction( - currentReaction, argument, reactionType); - } + } + // Morph the {reactions} into PromiseReactionJobTasks and push them + // onto the microtask queue. + current = reversed; + while (true) { + typeswitch (current) { + case (Zero): { + break; + } + case (currentReaction: PromiseReaction): { + current = currentReaction.next; + MorphAndEnqueuePromiseReaction(currentReaction, argument, reactionType); } } } +} - // https://tc39.es/ecma262/#sec-fulfillpromise - transitioning builtin - FulfillPromise(implicit context: Context)(promise: JSPromise, value: JSAny): - Undefined { - // Assert: The value of promise.[[PromiseState]] is "pending". - assert(promise.Status() == PromiseState::kPending); +// https://tc39.es/ecma262/#sec-fulfillpromise +transitioning builtin +FulfillPromise(implicit context: Context)( + promise: JSPromise, value: JSAny): Undefined { + // Assert: The value of promise.[[PromiseState]] is "pending". + assert(promise.Status() == PromiseState::kPending); - // 2. Let reactions be promise.[[PromiseFulfillReactions]]. - const reactions = - UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result); + // 2. Let reactions be promise.[[PromiseFulfillReactions]]. + const reactions = + UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result); - // 3. Set promise.[[PromiseResult]] to value. - // 4. Set promise.[[PromiseFulfillReactions]] to undefined. - // 5. Set promise.[[PromiseRejectReactions]] to undefined. - promise.reactions_or_result = value; + // 3. Set promise.[[PromiseResult]] to value. + // 4. Set promise.[[PromiseFulfillReactions]] to undefined. + // 5. Set promise.[[PromiseRejectReactions]] to undefined. + promise.reactions_or_result = value; - // 6. Set promise.[[PromiseState]] to "fulfilled". - promise.SetStatus(PromiseState::kFulfilled); + // 6. Set promise.[[PromiseState]] to "fulfilled". + promise.SetStatus(PromiseState::kFulfilled); + + // 7. Return TriggerPromiseReactions(reactions, value). + TriggerPromiseReactions(reactions, value, kPromiseReactionFulfill); + return Undefined; +} - // 7. Return TriggerPromiseReactions(reactions, value). - TriggerPromiseReactions(reactions, value, kPromiseReactionFulfill); - return Undefined; +extern macro PromiseBuiltinsAssembler:: + IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(): bool; + +// https://tc39.es/ecma262/#sec-rejectpromise +transitioning builtin +RejectPromise(implicit context: Context)( + promise: JSPromise, reason: JSAny, debugEvent: Boolean): JSAny { + // If promise hook is enabled or the debugger is active, let + // the runtime handle this operation, which greatly reduces + // the complexity here and also avoids a couple of back and + // forth between JavaScript and C++ land. + if (IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() || + !promise.HasHandler()) { + // 7. If promise.[[PromiseIsHandled]] is false, perform + // HostPromiseRejectionTracker(promise, "reject"). + // We don't try to handle rejecting {promise} without handler + // here, but we let the C++ code take care of this completely. + return runtime::RejectPromise(promise, reason, debugEvent); } - extern macro PromiseBuiltinsAssembler:: - IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(): bool; - - // https://tc39.es/ecma262/#sec-rejectpromise - transitioning builtin - RejectPromise(implicit context: Context)( - promise: JSPromise, reason: JSAny, debugEvent: Boolean): JSAny { - // If promise hook is enabled or the debugger is active, let - // the runtime handle this operation, which greatly reduces - // the complexity here and also avoids a couple of back and - // forth between JavaScript and C++ land. - if (IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() || - !promise.HasHandler()) { - // 7. If promise.[[PromiseIsHandled]] is false, perform - // HostPromiseRejectionTracker(promise, "reject"). - // We don't try to handle rejecting {promise} without handler - // here, but we let the C++ code take care of this completely. - return runtime::RejectPromise(promise, reason, debugEvent); - } + // 2. Let reactions be promise.[[PromiseRejectReactions]]. + const reactions = + UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result); - // 2. Let reactions be promise.[[PromiseRejectReactions]]. - const reactions = - UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result); + // 3. Set promise.[[PromiseResult]] to reason. + // 4. Set promise.[[PromiseFulfillReactions]] to undefined. + // 5. Set promise.[[PromiseRejectReactions]] to undefined. + promise.reactions_or_result = reason; - // 3. Set promise.[[PromiseResult]] to reason. - // 4. Set promise.[[PromiseFulfillReactions]] to undefined. - // 5. Set promise.[[PromiseRejectReactions]] to undefined. - promise.reactions_or_result = reason; + // 6. Set promise.[[PromiseState]] to "rejected". + promise.SetStatus(PromiseState::kRejected); - // 6. Set promise.[[PromiseState]] to "rejected". - promise.SetStatus(PromiseState::kRejected); + // 8. Return TriggerPromiseReactions(reactions, reason). + TriggerPromiseReactions(reactions, reason, kPromiseReactionReject); + return Undefined; +} - // 8. Return TriggerPromiseReactions(reactions, reason). - TriggerPromiseReactions(reactions, reason, kPromiseReactionReject); - return Undefined; - } +const kPromiseCapabilitySize: + constexpr int31 generates 'PromiseCapability::kSize'; +const kPromiseBuiltinsCapabilitiesContextLength: constexpr int31 + generates 'PromiseBuiltins::kCapabilitiesContextLength'; +const kPromiseBuiltinsCapabilitySlot: constexpr ContextSlot + generates 'PromiseBuiltins::kCapabilitySlot'; +const kPromiseBuiltinsPromiseSlot: constexpr ContextSlot + generates 'PromiseBuiltins::kPromiseSlot'; +const kPromiseBuiltinsAlreadyResolvedSlot: constexpr ContextSlot + generates 'PromiseBuiltins::kAlreadyResolvedSlot'; +const kPromiseBuiltinsDebugEventSlot: constexpr ContextSlot + generates 'PromiseBuiltins::kDebugEventSlot'; + +@export +macro CreatePromiseCapabilitiesExecutorContext( + nativeContext: NativeContext, capability: PromiseCapability): Context { + const executorContext = AllocateSyntheticFunctionContext( + nativeContext, kPromiseBuiltinsCapabilitiesContextLength); + + executorContext[kPromiseBuiltinsCapabilitySlot] = capability; + return executorContext; +} - const kPromiseCapabilitySize: - constexpr int31 generates 'PromiseCapability::kSize'; - const kPromiseBuiltinsCapabilitiesContextLength: constexpr int31 - generates 'PromiseBuiltins::kCapabilitiesContextLength'; - const kPromiseBuiltinsCapabilitySlot: constexpr ContextSlot - generates 'PromiseBuiltins::kCapabilitySlot'; - const kPromiseBuiltinsPromiseSlot: constexpr ContextSlot - generates 'PromiseBuiltins::kPromiseSlot'; - const kPromiseBuiltinsAlreadyResolvedSlot: constexpr ContextSlot - generates 'PromiseBuiltins::kAlreadyResolvedSlot'; - const kPromiseBuiltinsDebugEventSlot: constexpr ContextSlot - generates 'PromiseBuiltins::kDebugEventSlot'; - - @export - macro CreatePromiseCapabilitiesExecutorContext( - nativeContext: NativeContext, capability: PromiseCapability): Context { - const executorContext = AllocateSyntheticFunctionContext( - nativeContext, kPromiseBuiltinsCapabilitiesContextLength); - - executorContext[kPromiseBuiltinsCapabilitySlot] = capability; - return executorContext; - } +@export +macro CreatePromiseCapability( + promise: JSReceiver|Undefined, resolve: JSFunction|Undefined, + reject: JSFunction|Undefined): PromiseCapability { + return new PromiseCapability{ + map: kPromiseCapabilityMap, + promise: promise, + resolve: resolve, + reject: reject + }; +} - @export - macro CreatePromiseCapability( - promise: JSReceiver|Undefined, resolve: JSFunction|Undefined, - reject: JSFunction|Undefined): PromiseCapability { - return new PromiseCapability{ - map: kPromiseCapabilityMap, - promise: promise, - resolve: resolve, - reject: reject - }; - } +@export +struct PromiseResolvingFunctions { + resolve: JSFunction; + reject: JSFunction; +} - @export - struct PromiseResolvingFunctions { - resolve: JSFunction; - reject: JSFunction; - } +@export +macro CreatePromiseResolvingFunctions(implicit context: Context)( + promise: JSPromise, debugEvent: Object, nativeContext: NativeContext): + PromiseResolvingFunctions { + const promiseContext = CreatePromiseResolvingFunctionsContext( + promise, debugEvent, nativeContext); + const map = UnsafeCast( + nativeContext + [NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]); + const resolveInfo = PromiseCapabilityDefaultResolveSharedFunConstant(); + + const resolve: JSFunction = + AllocateFunctionWithMapAndContext(map, resolveInfo, promiseContext); + const rejectInfo = PromiseCapabilityDefaultRejectSharedFunConstant(); + const reject: JSFunction = + AllocateFunctionWithMapAndContext(map, rejectInfo, promiseContext); + return PromiseResolvingFunctions{resolve: resolve, reject: reject}; +} - @export - macro CreatePromiseResolvingFunctions(implicit context: Context)( - promise: JSPromise, debugEvent: Object, nativeContext: NativeContext): - PromiseResolvingFunctions { - const promiseContext = CreatePromiseResolvingFunctionsContext( - promise, debugEvent, nativeContext); - const map = UnsafeCast( +transitioning macro +InnerNewPromiseCapability(implicit context: Context)( + constructor: HeapObject, debugEvent: Object): PromiseCapability { + const nativeContext = LoadNativeContext(context); + if (TaggedEqual( + constructor, + nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX])) { + const promise = NewJSPromise(); + + const pair = + CreatePromiseResolvingFunctions(promise, debugEvent, nativeContext); + + return CreatePromiseCapability(promise, pair.resolve, pair.reject); + } else { + // We have to create the capability before the associated promise + // because the builtin PromiseConstructor uses the executor. + const capability = CreatePromiseCapability(Undefined, Undefined, Undefined); + const executorContext = + CreatePromiseCapabilitiesExecutorContext(nativeContext, capability); + + const executorInfo = PromiseGetCapabilitiesExecutorSharedFunConstant(); + const functionMap = UnsafeCast( nativeContext [NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]); - const resolveInfo = UnsafeCast( - nativeContext[NativeContextSlot:: - PROMISE_CAPABILITY_DEFAULT_RESOLVE_SHARED_FUN_INDEX]); - const resolve: JSFunction = - AllocateFunctionWithMapAndContext(map, resolveInfo, promiseContext); - const rejectInfo = UnsafeCast( - nativeContext[NativeContextSlot:: - PROMISE_CAPABILITY_DEFAULT_REJECT_SHARED_FUN_INDEX]); - const reject: JSFunction = - AllocateFunctionWithMapAndContext(map, rejectInfo, promiseContext); - return PromiseResolvingFunctions{resolve: resolve, reject: reject}; - } + const executor = AllocateFunctionWithMapAndContext( + functionMap, executorInfo, executorContext); - transitioning macro - InnerNewPromiseCapability(implicit context: Context)( - constructor: HeapObject, debugEvent: Object): PromiseCapability { - const nativeContext = LoadNativeContext(context); - if (TaggedEqual( - constructor, - nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX])) { - const promise = NewJSPromise(); - - const pair = - CreatePromiseResolvingFunctions(promise, debugEvent, nativeContext); - - return CreatePromiseCapability(promise, pair.resolve, pair.reject); - } else { - // We have to create the capability before the associated promise - // because the builtin PromiseConstructor uses the executor. - const capability = - CreatePromiseCapability(Undefined, Undefined, Undefined); - const executorContext = - CreatePromiseCapabilitiesExecutorContext(nativeContext, capability); - - const executorInfo = UnsafeCast( - nativeContext[NativeContextSlot:: - PROMISE_GET_CAPABILITIES_EXECUTOR_SHARED_FUN]); - const functionMap = UnsafeCast( - nativeContext - [NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]); - const executor = AllocateFunctionWithMapAndContext( - functionMap, executorInfo, executorContext); - - const promiseConstructor = UnsafeCast(constructor); - const promise = Construct(promiseConstructor, executor); - capability.promise = promise; - - if (!TaggedIsCallable(capability.resolve) || - !TaggedIsCallable(capability.reject)) { - ThrowTypeError(MessageTemplate::kPromiseNonCallable); - } - return capability; + const promiseConstructor = UnsafeCast(constructor); + const promise = Construct(promiseConstructor, executor); + capability.promise = promise; + + if (!Is(capability.resolve) || !Is(capability.reject)) { + ThrowTypeError(MessageTemplate::kPromiseNonCallable); } + return capability; } +} - // https://tc39.es/ecma262/#sec-newpromisecapability - transitioning builtin - NewPromiseCapability(implicit context: Context)( - maybeConstructor: Object, debugEvent: Object): PromiseCapability { - typeswitch (maybeConstructor) { - case (Smi): { +// https://tc39.es/ecma262/#sec-newpromisecapability +transitioning builtin +NewPromiseCapability(implicit context: Context)( + maybeConstructor: Object, debugEvent: Object): PromiseCapability { + typeswitch (maybeConstructor) { + case (Smi): { + ThrowTypeError(MessageTemplate::kNotConstructor, maybeConstructor); + } + case (constructor: HeapObject): { + if (!IsConstructor(constructor)) { ThrowTypeError(MessageTemplate::kNotConstructor, maybeConstructor); } - case (constructor: HeapObject): { - if (!IsConstructor(constructor)) { - ThrowTypeError(MessageTemplate::kNotConstructor, maybeConstructor); - } - return InnerNewPromiseCapability(constructor, debugEvent); - } + return InnerNewPromiseCapability(constructor, debugEvent); } } +} - // https://tc39.es/ecma262/#sec-promise-reject-functions - transitioning javascript builtin - PromiseCapabilityDefaultReject( - js-implicit context: NativeContext, - receiver: JSAny)(reason: JSAny): JSAny { - // 2. Let promise be F.[[Promise]]. - const promise = UnsafeCast(context[kPromiseBuiltinsPromiseSlot]); - - // 3. Let alreadyResolved be F.[[AlreadyResolved]]. - const alreadyResolved = - UnsafeCast(context[kPromiseBuiltinsAlreadyResolvedSlot]); - - // 4. If alreadyResolved.[[Value]] is true, return undefined. - if (alreadyResolved == True) { - return runtime::PromiseRejectAfterResolved(promise, reason); - } +// https://tc39.es/ecma262/#sec-promise-reject-functions +transitioning javascript builtin +PromiseCapabilityDefaultReject( + js-implicit context: NativeContext, receiver: JSAny)(reason: JSAny): JSAny { + // 2. Let promise be F.[[Promise]]. + const promise = UnsafeCast(context[kPromiseBuiltinsPromiseSlot]); - // 5. Set alreadyResolved.[[Value]] to true. - context[kPromiseBuiltinsAlreadyResolvedSlot] = True; + // 3. Let alreadyResolved be F.[[AlreadyResolved]]. + const alreadyResolved = + UnsafeCast(context[kPromiseBuiltinsAlreadyResolvedSlot]); - // 6. Return RejectPromise(promise, reason). - const debugEvent = - UnsafeCast(context[kPromiseBuiltinsDebugEventSlot]); - return RejectPromise(promise, reason, debugEvent); + // 4. If alreadyResolved.[[Value]] is true, return undefined. + if (alreadyResolved == True) { + return runtime::PromiseRejectAfterResolved(promise, reason); } - // https://tc39.es/ecma262/#sec-promise-resolve-functions - transitioning javascript builtin - PromiseCapabilityDefaultResolve( - js-implicit context: NativeContext, - receiver: JSAny)(resolution: JSAny): JSAny { - // 2. Let promise be F.[[Promise]]. - const promise = UnsafeCast(context[kPromiseBuiltinsPromiseSlot]); - - // 3. Let alreadyResolved be F.[[AlreadyResolved]]. - const alreadyResolved = - UnsafeCast(context[kPromiseBuiltinsAlreadyResolvedSlot]); - - // 4. If alreadyResolved.[[Value]] is true, return undefined. - if (alreadyResolved == True) { - return runtime::PromiseResolveAfterResolved(promise, resolution); - } + // 5. Set alreadyResolved.[[Value]] to true. + context[kPromiseBuiltinsAlreadyResolvedSlot] = True; - // 5. Set alreadyResolved.[[Value]] to true. - context[kPromiseBuiltinsAlreadyResolvedSlot] = True; + // 6. Return RejectPromise(promise, reason). + const debugEvent = + UnsafeCast(context[kPromiseBuiltinsDebugEventSlot]); + return RejectPromise(promise, reason, debugEvent); +} - // The rest of the logic (and the catch prediction) is - // encapsulated in the dedicated ResolvePromise builtin. - return ResolvePromise(context, promise, resolution); +// https://tc39.es/ecma262/#sec-promise-resolve-functions +transitioning javascript builtin +PromiseCapabilityDefaultResolve( + js-implicit context: NativeContext, + receiver: JSAny)(resolution: JSAny): JSAny { + // 2. Let promise be F.[[Promise]]. + const promise = UnsafeCast(context[kPromiseBuiltinsPromiseSlot]); + + // 3. Let alreadyResolved be F.[[AlreadyResolved]]. + const alreadyResolved = + UnsafeCast(context[kPromiseBuiltinsAlreadyResolvedSlot]); + + // 4. If alreadyResolved.[[Value]] is true, return undefined. + if (alreadyResolved == True) { + return runtime::PromiseResolveAfterResolved(promise, resolution); } - @export - transitioning macro PerformPromiseThenImpl(implicit context: Context)( - promise: JSPromise, onFulfilled: Callable|Undefined, - onRejected: Callable|Undefined, - resultPromiseOrCapability: JSPromise|PromiseCapability|Undefined): void { - if (promise.Status() == PromiseState::kPending) { - // The {promise} is still in "Pending" state, so we just record a new - // PromiseReaction holding both the onFulfilled and onRejected callbacks. - // Once the {promise} is resolved we decide on the concrete handler to - // push onto the microtask queue. - const handlerContext = ExtractHandlerContext(onFulfilled, onRejected); - const promiseReactions = - UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result); - const reaction = NewPromiseReaction( - handlerContext, promiseReactions, resultPromiseOrCapability, - onFulfilled, onRejected); - promise.reactions_or_result = reaction; - } else { - const reactionsOrResult = promise.reactions_or_result; - let microtask: PromiseReactionJobTask; - let handlerContext: Context; - if (promise.Status() == PromiseState::kFulfilled) { - handlerContext = ExtractHandlerContext(onFulfilled, onRejected); - microtask = NewPromiseFulfillReactionJobTask( - handlerContext, reactionsOrResult, onFulfilled, + // 5. Set alreadyResolved.[[Value]] to true. + context[kPromiseBuiltinsAlreadyResolvedSlot] = True; + + // The rest of the logic (and the catch prediction) is + // encapsulated in the dedicated ResolvePromise builtin. + return ResolvePromise(context, promise, resolution); +} + +@export +transitioning macro PerformPromiseThenImpl(implicit context: Context)( + promise: JSPromise, onFulfilled: Callable|Undefined, + onRejected: Callable|Undefined, + resultPromiseOrCapability: JSPromise|PromiseCapability|Undefined): void { + if (promise.Status() == PromiseState::kPending) { + // The {promise} is still in "Pending" state, so we just record a new + // PromiseReaction holding both the onFulfilled and onRejected callbacks. + // Once the {promise} is resolved we decide on the concrete handler to + // push onto the microtask queue. + const handlerContext = ExtractHandlerContext(onFulfilled, onRejected); + const promiseReactions = + UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result); + const reaction = NewPromiseReaction( + handlerContext, promiseReactions, resultPromiseOrCapability, + onFulfilled, onRejected); + promise.reactions_or_result = reaction; + } else { + const reactionsOrResult = promise.reactions_or_result; + let microtask: PromiseReactionJobTask; + let handlerContext: Context; + if (promise.Status() == PromiseState::kFulfilled) { + handlerContext = ExtractHandlerContext(onFulfilled, onRejected); + microtask = NewPromiseFulfillReactionJobTask( + handlerContext, reactionsOrResult, onFulfilled, + resultPromiseOrCapability); + } else + deferred { + assert(promise.Status() == PromiseState::kRejected); + handlerContext = ExtractHandlerContext(onRejected, onFulfilled); + microtask = NewPromiseRejectReactionJobTask( + handlerContext, reactionsOrResult, onRejected, resultPromiseOrCapability); - } else - deferred { - assert(promise.Status() == PromiseState::kRejected); - handlerContext = ExtractHandlerContext(onRejected, onFulfilled); - microtask = NewPromiseRejectReactionJobTask( - handlerContext, reactionsOrResult, onRejected, - resultPromiseOrCapability); - if (!promise.HasHandler()) { - runtime::PromiseRevokeReject(promise); - } + if (!promise.HasHandler()) { + runtime::PromiseRevokeReject(promise); } - EnqueueMicrotask(handlerContext, microtask); - } - promise.SetHasHandler(); + } + EnqueueMicrotask(handlerContext, microtask); } + promise.SetHasHandler(); +} - // https://tc39.es/ecma262/#sec-performpromisethen - transitioning builtin - PerformPromiseThen(implicit context: Context)( - promise: JSPromise, onFulfilled: Callable|Undefined, - onRejected: Callable|Undefined, - resultPromise: JSPromise|Undefined): JSAny { - PerformPromiseThenImpl(promise, onFulfilled, onRejected, resultPromise); - return resultPromise; - } +// https://tc39.es/ecma262/#sec-performpromisethen +transitioning builtin +PerformPromiseThen(implicit context: Context)( + promise: JSPromise, onFulfilled: Callable|Undefined, + onRejected: Callable|Undefined, resultPromise: JSPromise|Undefined): JSAny { + PerformPromiseThenImpl(promise, onFulfilled, onRejected, resultPromise); + return resultPromise; +} - // https://tc39.es/ecma262/#sec-promise-reject-functions - transitioning javascript builtin - PromiseReject(js-implicit context: NativeContext, receiver: JSAny)( - reason: JSAny): JSAny { - // 1. Let C be the this value. - // 2. If Type(C) is not Object, throw a TypeError exception. - const receiver = Cast(receiver) otherwise - ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'PromiseReject'); - - const promiseFun = context[NativeContextSlot::PROMISE_FUNCTION_INDEX]; - if (promiseFun == receiver) { - const promise = NewJSPromise(PromiseState::kRejected, reason); - runtime::PromiseRejectEventFromStack(promise, reason); - return promise; - } else { - // 3. Let promiseCapability be ? NewPromiseCapability(C). - const capability = NewPromiseCapability(receiver, True); - - // 4. Perform ? Call(promiseCapability.[[Reject]], undefined, « r »). - const reject = UnsafeCast(capability.reject); - Call(context, reject, Undefined, reason); - - // 5. Return promiseCapability.[[Promise]]. - return capability.promise; - } - } +// https://tc39.es/ecma262/#sec-promise-reject-functions +transitioning javascript builtin +PromiseReject( + js-implicit context: NativeContext, receiver: JSAny)(reason: JSAny): JSAny { + // 1. Let C be the this value. + // 2. If Type(C) is not Object, throw a TypeError exception. + const receiver = Cast(receiver) otherwise + ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'PromiseReject'); + + const promiseFun = context[NativeContextSlot::PROMISE_FUNCTION_INDEX]; + if (promiseFun == receiver) { + const promise = NewJSPromise(PromiseState::kRejected, reason); + runtime::PromiseRejectEventFromStack(promise, reason); + return promise; + } else { + // 3. Let promiseCapability be ? NewPromiseCapability(C). + const capability = NewPromiseCapability(receiver, True); - const kPromiseExecutorAlreadyInvoked: constexpr MessageTemplate - generates 'MessageTemplate::kPromiseExecutorAlreadyInvoked'; - - // https://tc39.es/ecma262/#sec-getcapabilitiesexecutor-functions - transitioning javascript builtin - PromiseGetCapabilitiesExecutor( - js-implicit context: NativeContext, - receiver: JSAny)(resolve: JSAny, reject: JSAny): JSAny { - const capability = - UnsafeCast(context[kPromiseBuiltinsCapabilitySlot]); - if (capability.resolve != Undefined || capability.reject != Undefined) - deferred { - ThrowTypeError(kPromiseExecutorAlreadyInvoked); - } + // 4. Perform ? Call(promiseCapability.[[Reject]], undefined, « r »). + const reject = UnsafeCast(capability.reject); + Call(context, reject, Undefined, reason); - capability.resolve = resolve; - capability.reject = reject; - return Undefined; + // 5. Return promiseCapability.[[Promise]]. + return capability.promise; } +} - transitioning macro CallResolve(implicit context: Context)( - constructor: Constructor, resolve: JSAny, value: JSAny): JSAny { - // Undefined can never be a valid value for the resolve function, - // instead it is used as a special marker for the fast path. - if (resolve == Undefined) { - return PromiseResolve(constructor, value); - } else - deferred { - return Call(context, UnsafeCast(resolve), constructor, value); - } - } +const kPromiseExecutorAlreadyInvoked: constexpr MessageTemplate + generates 'MessageTemplate::kPromiseExecutorAlreadyInvoked'; + +// https://tc39.es/ecma262/#sec-getcapabilitiesexecutor-functions +transitioning javascript builtin +PromiseGetCapabilitiesExecutor( + js-implicit context: NativeContext, receiver: JSAny)( + resolve: JSAny, reject: JSAny): JSAny { + const capability = + UnsafeCast(context[kPromiseBuiltinsCapabilitySlot]); + if (capability.resolve != Undefined || capability.reject != Undefined) + deferred { + ThrowTypeError(kPromiseExecutorAlreadyInvoked); + } - transitioning javascript builtin - PromiseConstructorLazyDeoptContinuation( - js-implicit context: NativeContext, receiver: JSAny)( - promise: JSAny, reject: JSAny, exception: JSAny|TheHole, - _result: JSAny): JSAny { - typeswitch (exception) { - case (TheHole): { - } - case (e: JSAny): { - Call(context, reject, Undefined, e); - } + capability.resolve = resolve; + capability.reject = reject; + return Undefined; +} + +transitioning macro CallResolve(implicit context: Context)( + constructor: Constructor, resolve: JSAny, value: JSAny): JSAny { + // Undefined can never be a valid value for the resolve function, + // instead it is used as a special marker for the fast path. + if (resolve == Undefined) { + return PromiseResolve(constructor, value); + } else + deferred { + return Call(context, UnsafeCast(resolve), constructor, value); + } +} + +transitioning javascript builtin +PromiseConstructorLazyDeoptContinuation( + js-implicit context: NativeContext, receiver: JSAny)( + promise: JSAny, reject: JSAny, exception: JSAny|TheHole, + _result: JSAny): JSAny { + typeswitch (exception) { + case (TheHole): { + } + case (e: JSAny): { + Call(context, reject, Undefined, e); } - return promise; } + return promise; +} + +extern macro PromiseCapabilityDefaultRejectSharedFunConstant(): + SharedFunctionInfo; +extern macro PromiseCapabilityDefaultResolveSharedFunConstant(): + SharedFunctionInfo; +extern macro PromiseGetCapabilitiesExecutorSharedFunConstant(): + SharedFunctionInfo; } diff --git a/deps/v8/src/builtins/promise-all-element-closure.tq b/deps/v8/src/builtins/promise-all-element-closure.tq index c320b24f036c1c..0b870ea3b185bc 100644 --- a/deps/v8/src/builtins/promise-all-element-closure.tq +++ b/deps/v8/src/builtins/promise-all-element-closure.tq @@ -8,182 +8,180 @@ namespace promise { - struct PromiseAllWrapResultAsFulfilledFunctor { - macro Call(_nativeContext: NativeContext, value: JSAny): JSAny { - return value; - } +struct PromiseAllWrapResultAsFulfilledFunctor { + macro Call(_nativeContext: NativeContext, value: JSAny): JSAny { + return value; } +} - struct PromiseAllSettledWrapResultAsFulfilledFunctor { - transitioning - macro Call(implicit context: Context)( - nativeContext: NativeContext, value: JSAny): JSAny { - // TODO(gsathya): Optimize the creation using a cached map to - // prevent transitions here. - // 9. Let obj be ! ObjectCreate(%ObjectPrototype%). - const objectFunction = UnsafeCast( - nativeContext[NativeContextSlot::OBJECT_FUNCTION_INDEX]); - const objectFunctionMap = - UnsafeCast(objectFunction.prototype_or_initial_map); - const obj = AllocateJSObjectFromMap(objectFunctionMap); - - // 10. Perform ! CreateDataProperty(obj, "status", "fulfilled"). - FastCreateDataProperty( - obj, StringConstant('status'), StringConstant('fulfilled')); - - // 11. Perform ! CreateDataProperty(obj, "value", x). - FastCreateDataProperty(obj, StringConstant('value'), value); - return obj; - } +struct PromiseAllSettledWrapResultAsFulfilledFunctor { + transitioning + macro Call(implicit context: Context)( + nativeContext: NativeContext, value: JSAny): JSAny { + // TODO(gsathya): Optimize the creation using a cached map to + // prevent transitions here. + // 9. Let obj be ! ObjectCreate(%ObjectPrototype%). + const objectFunction = UnsafeCast( + nativeContext[NativeContextSlot::OBJECT_FUNCTION_INDEX]); + const objectFunctionMap = + UnsafeCast(objectFunction.prototype_or_initial_map); + const obj = AllocateJSObjectFromMap(objectFunctionMap); + + // 10. Perform ! CreateDataProperty(obj, "status", "fulfilled"). + FastCreateDataProperty( + obj, StringConstant('status'), StringConstant('fulfilled')); + + // 11. Perform ! CreateDataProperty(obj, "value", x). + FastCreateDataProperty(obj, StringConstant('value'), value); + return obj; } +} - struct PromiseAllSettledWrapResultAsRejectedFunctor { - transitioning - macro Call(implicit context: Context)( - nativeContext: NativeContext, value: JSAny): JSAny { - // TODO(gsathya): Optimize the creation using a cached map to - // prevent transitions here. - // 9. Let obj be ! ObjectCreate(%ObjectPrototype%). - const objectFunction = UnsafeCast( - nativeContext[NativeContextSlot::OBJECT_FUNCTION_INDEX]); - const objectFunctionMap = - UnsafeCast(objectFunction.prototype_or_initial_map); - const obj = AllocateJSObjectFromMap(objectFunctionMap); - - // 10. Perform ! CreateDataProperty(obj, "status", "rejected"). - FastCreateDataProperty( - obj, StringConstant('status'), StringConstant('rejected')); - - // 11. Perform ! CreateDataProperty(obj, "reason", x). - FastCreateDataProperty(obj, StringConstant('reason'), value); - return obj; - } +struct PromiseAllSettledWrapResultAsRejectedFunctor { + transitioning + macro Call(implicit context: Context)( + nativeContext: NativeContext, value: JSAny): JSAny { + // TODO(gsathya): Optimize the creation using a cached map to + // prevent transitions here. + // 9. Let obj be ! ObjectCreate(%ObjectPrototype%). + const objectFunction = UnsafeCast( + nativeContext[NativeContextSlot::OBJECT_FUNCTION_INDEX]); + const objectFunctionMap = + UnsafeCast(objectFunction.prototype_or_initial_map); + const obj = AllocateJSObjectFromMap(objectFunctionMap); + + // 10. Perform ! CreateDataProperty(obj, "status", "rejected"). + FastCreateDataProperty( + obj, StringConstant('status'), StringConstant('rejected')); + + // 11. Perform ! CreateDataProperty(obj, "reason", x). + FastCreateDataProperty(obj, StringConstant('reason'), value); + return obj; } +} - extern macro LoadJSReceiverIdentityHash(Object): intptr labels IfNoHash; +extern macro LoadJSReceiverIdentityHash(Object): intptr labels IfNoHash; - extern enum PromiseAllResolveElementContextSlots extends int31 - constexpr 'PromiseBuiltins::PromiseAllResolveElementContextSlots' { - kPromiseAllResolveElementRemainingSlot, - kPromiseAllResolveElementCapabilitySlot, - kPromiseAllResolveElementValuesArraySlot, - kPromiseAllResolveElementLength - } - extern operator '[]=' macro StoreContextElement( - Context, constexpr PromiseAllResolveElementContextSlots, Object): void; - extern operator '[]' macro LoadContextElement( - Context, constexpr PromiseAllResolveElementContextSlots): Object; - - const kPropertyArrayNoHashSentinel: constexpr int31 - generates 'PropertyArray::kNoHashSentinel'; - - const kPropertyArrayHashFieldMax: constexpr int31 - generates 'PropertyArray::HashField::kMax'; - - transitioning macro PromiseAllResolveElementClosure( - implicit context: - Context)(value: JSAny, function: JSFunction, wrapResultFunctor: F): - JSAny { - // We use the {function}s context as the marker to remember whether this - // resolve element closure was already called. It points to the resolve - // element context (which is a FunctionContext) until it was called the - // first time, in which case we make it point to the native context here - // to mark this resolve element closure as done. - if (IsNativeContext(context)) deferred { - return Undefined; - } +extern enum PromiseAllResolveElementContextSlots extends int31 +constexpr 'PromiseBuiltins::PromiseAllResolveElementContextSlots' { + kPromiseAllResolveElementRemainingSlot, + kPromiseAllResolveElementCapabilitySlot, + kPromiseAllResolveElementValuesArraySlot, + kPromiseAllResolveElementLength +} +extern operator '[]=' macro StoreContextElement( + Context, constexpr PromiseAllResolveElementContextSlots, Object): void; +extern operator '[]' macro LoadContextElement( + Context, constexpr PromiseAllResolveElementContextSlots): Object; + +const kPropertyArrayNoHashSentinel: constexpr int31 + generates 'PropertyArray::kNoHashSentinel'; + +const kPropertyArrayHashFieldMax: constexpr int31 + generates 'PropertyArray::HashField::kMax'; + +transitioning macro PromiseAllResolveElementClosure( + implicit context: Context)( + value: JSAny, function: JSFunction, wrapResultFunctor: F): JSAny { + // We use the {function}s context as the marker to remember whether this + // resolve element closure was already called. It points to the resolve + // element context (which is a FunctionContext) until it was called the + // first time, in which case we make it point to the native context here + // to mark this resolve element closure as done. + if (IsNativeContext(context)) deferred { + return Undefined; + } - assert( - context.length == - PromiseAllResolveElementContextSlots::kPromiseAllResolveElementLength); - const nativeContext = LoadNativeContext(context); - function.context = nativeContext; - - // Update the value depending on whether Promise.all or - // Promise.allSettled is called. - const updatedValue = wrapResultFunctor.Call(nativeContext, value); - - // Determine the index from the {function}. - assert(kPropertyArrayNoHashSentinel == 0); - const identityHash = - LoadJSReceiverIdentityHash(function) otherwise unreachable; - assert(identityHash > 0); - const index = identityHash - 1; - - // Check if we need to grow the [[ValuesArray]] to store {value} at {index}. - const valuesArray = UnsafeCast( - context[PromiseAllResolveElementContextSlots:: - kPromiseAllResolveElementValuesArraySlot]); - const elements = UnsafeCast(valuesArray.elements); - const valuesLength = Convert(valuesArray.length); - if (index < valuesLength) { - // The {index} is in bounds of the {values_array}, - // just store the {value} and continue. + assert( + context.length == + PromiseAllResolveElementContextSlots::kPromiseAllResolveElementLength); + const nativeContext = LoadNativeContext(context); + function.context = nativeContext; + + // Update the value depending on whether Promise.all or + // Promise.allSettled is called. + const updatedValue = wrapResultFunctor.Call(nativeContext, value); + + // Determine the index from the {function}. + assert(kPropertyArrayNoHashSentinel == 0); + const identityHash = + LoadJSReceiverIdentityHash(function) otherwise unreachable; + assert(identityHash > 0); + const index = identityHash - 1; + + // Check if we need to grow the [[ValuesArray]] to store {value} at {index}. + const valuesArray = UnsafeCast( + context[PromiseAllResolveElementContextSlots:: + kPromiseAllResolveElementValuesArraySlot]); + const elements = UnsafeCast(valuesArray.elements); + const valuesLength = Convert(valuesArray.length); + if (index < valuesLength) { + // The {index} is in bounds of the {values_array}, + // just store the {value} and continue. + elements.objects[index] = updatedValue; + } else { + // Check if we need to grow the backing store. + const newLength = index + 1; + const elementsLength = elements.length_intptr; + if (index < elementsLength) { + // The {index} is within bounds of the {elements} backing store, so + // just store the {value} and update the "length" of the {values_array}. + valuesArray.length = Convert(newLength); elements.objects[index] = updatedValue; - } else { - // Check if we need to grow the backing store. - const newLength = index + 1; - const elementsLength = elements.length_intptr; - if (index < elementsLength) { - // The {index} is within bounds of the {elements} backing store, so - // just store the {value} and update the "length" of the {values_array}. + } else + deferred { + // We need to grow the backing store to fit the {index} as well. + const newElementsLength = IntPtrMin( + CalculateNewElementsCapacity(newLength), + kPropertyArrayHashFieldMax + 1); + assert(index < newElementsLength); + assert(elementsLength < newElementsLength); + const newElements = + ExtractFixedArray(elements, 0, elementsLength, newElementsLength); + newElements.objects[index] = updatedValue; + + // Update backing store and "length" on {values_array}. + valuesArray.elements = newElements; valuesArray.length = Convert(newLength); - elements.objects[index] = updatedValue; - } else - deferred { - // We need to grow the backing store to fit the {index} as well. - const newElementsLength = IntPtrMin( - CalculateNewElementsCapacity(newLength), - kPropertyArrayHashFieldMax + 1); - assert(index < newElementsLength); - assert(elementsLength < newElementsLength); - const newElements = - ExtractFixedArray(elements, 0, elementsLength, newElementsLength); - newElements.objects[index] = updatedValue; - - // Update backing store and "length" on {values_array}. - valuesArray.elements = newElements; - valuesArray.length = Convert(newLength); - } - } - let remainingElementsCount = - UnsafeCast(context[PromiseAllResolveElementContextSlots:: - kPromiseAllResolveElementRemainingSlot]); - remainingElementsCount = remainingElementsCount - 1; - context[PromiseAllResolveElementContextSlots:: - kPromiseAllResolveElementRemainingSlot] = - remainingElementsCount; - if (remainingElementsCount == 0) { - const capability = UnsafeCast( - context[PromiseAllResolveElementContextSlots:: - kPromiseAllResolveElementCapabilitySlot]); - const resolve = UnsafeCast(capability.resolve); - Call(context, resolve, Undefined, valuesArray); - } - return Undefined; + } } - - transitioning javascript builtin - PromiseAllResolveElementClosure( - js-implicit context: Context, receiver: JSAny, - target: JSFunction)(value: JSAny): JSAny { - return PromiseAllResolveElementClosure( - value, target, PromiseAllWrapResultAsFulfilledFunctor{}); + let remainingElementsCount = + UnsafeCast(context[PromiseAllResolveElementContextSlots:: + kPromiseAllResolveElementRemainingSlot]); + remainingElementsCount = remainingElementsCount - 1; + context[PromiseAllResolveElementContextSlots:: + kPromiseAllResolveElementRemainingSlot] = remainingElementsCount; + if (remainingElementsCount == 0) { + const capability = UnsafeCast( + context[PromiseAllResolveElementContextSlots:: + kPromiseAllResolveElementCapabilitySlot]); + const resolve = UnsafeCast(capability.resolve); + Call(context, resolve, Undefined, valuesArray); } + return Undefined; +} - transitioning javascript builtin - PromiseAllSettledResolveElementClosure( - js-implicit context: Context, receiver: JSAny, - target: JSFunction)(value: JSAny): JSAny { - return PromiseAllResolveElementClosure( - value, target, PromiseAllSettledWrapResultAsFulfilledFunctor{}); - } +transitioning javascript builtin +PromiseAllResolveElementClosure( + js-implicit context: Context, receiver: JSAny, + target: JSFunction)(value: JSAny): JSAny { + return PromiseAllResolveElementClosure( + value, target, PromiseAllWrapResultAsFulfilledFunctor{}); +} - transitioning javascript builtin - PromiseAllSettledRejectElementClosure( - js-implicit context: Context, receiver: JSAny, - target: JSFunction)(value: JSAny): JSAny { - return PromiseAllResolveElementClosure( - value, target, PromiseAllSettledWrapResultAsRejectedFunctor{}); - } +transitioning javascript builtin +PromiseAllSettledResolveElementClosure( + js-implicit context: Context, receiver: JSAny, + target: JSFunction)(value: JSAny): JSAny { + return PromiseAllResolveElementClosure( + value, target, PromiseAllSettledWrapResultAsFulfilledFunctor{}); +} + +transitioning javascript builtin +PromiseAllSettledRejectElementClosure( + js-implicit context: Context, receiver: JSAny, + target: JSFunction)(value: JSAny): JSAny { + return PromiseAllResolveElementClosure( + value, target, PromiseAllSettledWrapResultAsRejectedFunctor{}); +} } diff --git a/deps/v8/src/builtins/promise-all.tq b/deps/v8/src/builtins/promise-all.tq index 19a16d8da858f1..b7fad88f6fc891 100644 --- a/deps/v8/src/builtins/promise-all.tq +++ b/deps/v8/src/builtins/promise-all.tq @@ -6,165 +6,160 @@ #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( - 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; - } +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( + 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( - nativeContext - [NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]); - const resolveInfo = - UnsafeCast(nativeContext[slotIndex]); - const resolve = AllocateFunctionWithMapAndContext( - map, resolveInfo, resolveElementContext); - - assert(kPropertyArrayNoHashSentinel == 0); - resolve.properties_or_hash = index; - return resolve; - } +macro CreatePromiseAllResolveElementFunction(implicit context: Context)( + resolveElementContext: Context, index: Smi, nativeContext: NativeContext, + resolveFunction: SharedFunctionInfo): JSFunction { + assert(index > 0); + assert(index < kPropertyArrayHashFieldMax); + + const map = UnsafeCast( + nativeContext + [NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]); + const resolve = AllocateFunctionWithMapAndContext( + map, resolveFunction, 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; - } +@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(); - } +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 PromiseAllResolveElementFunctor { + macro Call(implicit context: Context)( + resolveElementContext: Context, nativeContext: NativeContext, index: Smi, + _capability: PromiseCapability): Callable { + return CreatePromiseAllResolveElementFunction( + resolveElementContext, index, nativeContext, + PromiseAllResolveElementSharedFunConstant()); } +} - struct PromiseAllRejectElementFunctor { - macro Call(implicit context: Context)( - _resolveElementContext: Context, _nativeContext: NativeContext, - _index: Smi, capability: PromiseCapability): Callable { - return UnsafeCast(capability.reject); - } +struct PromiseAllRejectElementFunctor { + macro Call(implicit context: Context)( + _resolveElementContext: Context, _nativeContext: NativeContext, + _index: Smi, capability: PromiseCapability): Callable { + return UnsafeCast(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 PromiseAllSettledResolveElementFunctor { + macro Call(implicit context: Context)( + resolveElementContext: Context, nativeContext: NativeContext, index: Smi, + _capability: PromiseCapability): Callable { + return CreatePromiseAllResolveElementFunction( + resolveElementContext, index, nativeContext, + PromiseAllSettledResolveElementSharedFunConstant()); } +} - 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); - } +struct PromiseAllSettledRejectElementFunctor { + macro Call(implicit context: Context)( + resolveElementContext: Context, nativeContext: NativeContext, index: Smi, + _capability: PromiseCapability): Callable { + return CreatePromiseAllResolveElementFunction( + resolveElementContext, index, nativeContext, + PromiseAllSettledRejectElementSharedFunConstant()); } +} - transitioning macro PerformPromiseAll(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); - } +transitioning macro PerformPromiseAll( + 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); + const resolveElementContext = + CreatePromiseAllResolveElementContext(capability, nativeContext); - let index: Smi = 1; + 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; + // 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 { - 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(promiseResolve) - otherwise ThrowTypeError( - MessageTemplate::kCalledNonCallable, 'resolve'); - } + if (!IsPromiseResolveLookupChainIntact(nativeContext, constructor)) { + let promiseResolve: JSAny; + + // 5. Let _promiseResolve_ be ? Get(_constructor_, `"resolve"`). + promiseResolve = GetProperty(constructor, kResolveString); - const fastIteratorResultMap = UnsafeCast( - nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]); - while (true) { - let nextValue: JSAny; + // 6. If IsCallable(_promiseResolve_) is *false*, throw a *TypeError* + // exception. + promiseResolveFunction = + Cast(promiseResolve) otherwise ThrowTypeError( + MessageTemplate::kCalledNonCallable, 'resolve'); + } + const fastIteratorResultMap = UnsafeCast( + nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]); + while (true) { + let nextValue: JSAny; + try { // Let next be IteratorStep(iteratorRecord.[[Iterator]]). // If next is an abrupt completion, set iteratorRecord.[[Done]] to // true. ReturnIfAbrupt(next). @@ -176,209 +171,207 @@ namespace promise { // to true. // ReturnIfAbrupt(nextValue). nextValue = iterator::IteratorValue(next, fastIteratorResultMap); + } catch (e) { + goto Reject(e); + } - // 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(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(nextValue) || - !IsPromiseThenLookupChainIntact( - nativeContext, UnsafeCast(nextValue).map)) { - try { - // Let nextPromise be ? Call(constructor, _promiseResolve_, « - // nextValue »). - const nextPromise = CallResolve( - UnsafeCast(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(thenResult)) deferred { - SetPropertyStrict( - context, thenResult, kPromiseHandledBySymbol, promise); - } - } catch (e) deferred { - iterator::IteratorCloseOnException(iter, e) otherwise Reject; + // 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 below 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 anyway. + ThrowRangeError( + MessageTemplate::kTooManyElementsInPromiseCombinator, 'all'); + } + + // Set remainingElementsCount.[[Value]] to + // remainingElementsCount.[[Value]] + 1. + const remainingElementsCount = UnsafeCast( + 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(nextValue) || + !IsPromiseThenLookupChainIntact( + nativeContext, UnsafeCast(nextValue).map)) { + // Let nextPromise be ? Call(constructor, _promiseResolve_, « + // nextValue »). + const nextPromise = CallResolve( + UnsafeCast(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(thenResult)) deferred { + SetPropertyStrict( + context, thenResult, kPromiseHandledBySymbol, promise); } - } else { - PerformPromiseThenImpl( - UnsafeCast(nextValue), resolveElementFun, - rejectElementFun, Undefined); - } - - // Set index to index + 1. - index += 1; + } else { + PerformPromiseThenImpl( + UnsafeCast(nextValue), resolveElementFun, + rejectElementFun, Undefined); } + + // Set index to index + 1. + index += 1; } + } catch (e) deferred { + iterator::IteratorCloseOnException(iter); + goto Reject(e); } - label Done {} - - // Set iteratorRecord.[[Done]] to true. - // Set remainingElementsCount.[[Value]] to - // remainingElementsCount.[[Value]] - 1. - let remainingElementsCount = UnsafeCast( + } label Done {} + + // Set iteratorRecord.[[Done]] to true. + // Set remainingElementsCount.[[Value]] to + // remainingElementsCount.[[Value]] - 1. + let remainingElementsCount = UnsafeCast( + 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( 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( + kPromiseAllResolveElementValuesArraySlot]); + const oldElements = UnsafeCast(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( resolveElementContext[PromiseAllResolveElementContextSlots:: kPromiseAllResolveElementValuesArraySlot]); - const oldElements = UnsafeCast(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( - resolveElementContext - [PromiseAllResolveElementContextSlots:: - kPromiseAllResolveElementValuesArraySlot]); - Call(nativeContext, UnsafeCast(resolve), Undefined, valuesArray); - } - - // Return resultCapability.[[Promise]]. - return promise; - } + Call(nativeContext, UnsafeCast(resolve), Undefined, valuesArray); + } - transitioning macro GeneratePromiseAll(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(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); + // Return resultCapability.[[Promise]]. + return promise; +} - 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(e); - const reject = UnsafeCast(capability.reject); - Call(context, reject, Undefined, e); - return capability.promise; - } +transitioning macro GeneratePromiseAll( + 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(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 { + // 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(e); + const reject = UnsafeCast(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.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{}); - } +// 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{}); +} + +extern macro PromiseAllResolveElementSharedFunConstant(): SharedFunctionInfo; +extern macro PromiseAllSettledRejectElementSharedFunConstant(): + SharedFunctionInfo; +extern macro PromiseAllSettledResolveElementSharedFunConstant(): + SharedFunctionInfo; } diff --git a/deps/v8/src/builtins/promise-any.tq b/deps/v8/src/builtins/promise-any.tq new file mode 100644 index 00000000000000..1046ed0a89c4cf --- /dev/null +++ b/deps/v8/src/builtins/promise-any.tq @@ -0,0 +1,372 @@ +// Copyright 2020 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-gen.h' + +namespace promise { +extern enum PromiseAnyRejectElementContextSlots extends int31 +constexpr 'PromiseBuiltins::PromiseAnyRejectElementContextSlots' { + kPromiseAnyRejectElementRemainingSlot, + kPromiseAnyRejectElementCapabilitySlot, + kPromiseAnyRejectElementErrorsArraySlot, + kPromiseAnyRejectElementLength +} + +extern operator '[]=' macro StoreContextElement( + Context, constexpr PromiseAnyRejectElementContextSlots, Object): void; +extern operator '[]' macro LoadContextElement( + Context, constexpr PromiseAnyRejectElementContextSlots): Object; + +// Creates the context used by all Promise.any reject element closures, +// together with the errors array. Since all closures for a single Promise.any +// 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). See Promise.all which uses the same approach. +transitioning macro CreatePromiseAnyRejectElementContext( + implicit context: Context)( + capability: PromiseCapability, nativeContext: NativeContext): Context { + const rejectContext = AllocateSyntheticFunctionContext( + nativeContext, + PromiseAnyRejectElementContextSlots::kPromiseAnyRejectElementLength); + rejectContext[PromiseAnyRejectElementContextSlots:: + kPromiseAnyRejectElementRemainingSlot] = SmiConstant(1); + rejectContext[PromiseAnyRejectElementContextSlots:: + kPromiseAnyRejectElementCapabilitySlot] = capability; + // Will be set later. + rejectContext[PromiseAnyRejectElementContextSlots:: + kPromiseAnyRejectElementErrorsArraySlot] = Undefined; + return rejectContext; +} + +macro CreatePromiseAnyRejectElementFunction(implicit context: Context)( + rejectElementContext: Context, index: Smi, + nativeContext: NativeContext): JSFunction { + assert(index > 0); + assert(index < kPropertyArrayHashFieldMax); + const map = UnsafeCast( + nativeContext + [NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]); + const rejectInfo = PromiseAnyRejectElementSharedFunConstant(); + const reject = + AllocateFunctionWithMapAndContext(map, rejectInfo, rejectElementContext); + assert(kPropertyArrayNoHashSentinel == 0); + reject.properties_or_hash = index; + return reject; +} + +// https://tc39.es/proposal-promise-any/#sec-promise.any-reject-element-functions +transitioning javascript builtin +PromiseAnyRejectElementClosure( + js-implicit context: Context, receiver: JSAny, + target: JSFunction)(value: JSAny): JSAny { + // 1. Let F be the active function object. + + // 2. Let alreadyCalled be F.[[AlreadyCalled]]. + + // 3. If alreadyCalled.[[Value]] is true, return undefined. + + // We use the function's context as the marker to remember whether this + // reject element closure was already called. It points to the reject + // element context (which is a FunctionContext) until it was called the + // first time, in which case we make it point to the native context here + // to mark this reject element closure as done. + if (IsNativeContext(context)) deferred { + return Undefined; + } + + assert( + context.length == + PromiseAnyRejectElementContextSlots::kPromiseAnyRejectElementLength); + + // 4. Set alreadyCalled.[[Value]] to true. + const nativeContext = LoadNativeContext(context); + target.context = nativeContext; + + // 5. Let index be F.[[Index]]. + assert(kPropertyArrayNoHashSentinel == 0); + const identityHash = LoadJSReceiverIdentityHash(target) otherwise unreachable; + assert(identityHash > 0); + const index = identityHash - 1; + + // 6. Let errors be F.[[Errors]]. + if (context[PromiseAnyRejectElementContextSlots:: + kPromiseAnyRejectElementErrorsArraySlot] == Undefined) { + // We're going to reject the Promise with a more fundamental error (e.g., + // something went wrong with iterating the Promises). We don't need to + // construct the "errors" array. + return Undefined; + } + + const errorsArray = UnsafeCast( + context[PromiseAnyRejectElementContextSlots:: + kPromiseAnyRejectElementErrorsArraySlot]); + + // 7. Let promiseCapability be F.[[Capability]]. + + // 8. Let remainingElementsCount be F.[[RemainingElements]]. + let remainingElementsCount = + UnsafeCast(context[PromiseAnyRejectElementContextSlots:: + kPromiseAnyRejectElementRemainingSlot]); + // 9. Set errors[index] to x. + errorsArray.objects[index] = value; + + // 10. Set remainingElementsCount.[[Value]] to + // remainingElementsCount.[[Value]] - 1. + remainingElementsCount = remainingElementsCount - 1; + context[PromiseAnyRejectElementContextSlots:: + kPromiseAnyRejectElementRemainingSlot] = remainingElementsCount; + + // 11. If remainingElementsCount.[[Value]] is 0, then + if (remainingElementsCount == 0) { + // a. Let error be a newly created AggregateError object. + + // b. Set error.[[AggregateErrors]] to errors. + const error = ConstructAggregateError(errorsArray); + // c. Return ? Call(promiseCapability.[[Reject]], undefined, « error »). + const capability = UnsafeCast( + context[PromiseAnyRejectElementContextSlots:: + kPromiseAnyRejectElementCapabilitySlot]); + Call(context, UnsafeCast(capability.reject), Undefined, error); + } + + // 12. Return undefined. + return Undefined; +} + +transitioning macro PerformPromiseAny(implicit context: Context)( + iteratorRecord: iterator::IteratorRecord, constructor: Constructor, + resultCapability: PromiseCapability): JSAny labels +Reject(Object) { + // 1. Assert: ! IsConstructor(constructor) is true. + // 2. Assert: resultCapability is a PromiseCapability Record. + + const nativeContext = LoadNativeContext(context); + + // 3. Let errors be a new empty List. + let growableErrorsArray = growable_fixed_array::NewGrowableFixedArray(); + + // 4. Let remainingElementsCount be a new Record { [[Value]]: 1 }. + const rejectElementContext = + CreatePromiseAnyRejectElementContext(resultCapability, nativeContext); + + // 5. Let index be 0. + // (We subtract 1 in the PromiseAnyRejectElementClosure). + let index: Smi = 1; + + try { + // 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; + if (!IsPromiseResolveLookupChainIntact(nativeContext, constructor)) + deferred { + // 6. Let promiseResolve be ? Get(constructor, `"resolve"`). + const promiseResolve = GetProperty(constructor, kResolveString); + // 7. If IsCallable(promiseResolve) is false, throw a + // TypeError exception. + promiseResolveFunction = Cast(promiseResolve) + otherwise ThrowTypeError( + MessageTemplate::kCalledNonCallable, 'resolve'); + } + const fastIteratorResultMap = UnsafeCast( + nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]); + // 8. Repeat, + while (true) { + let nextValue: JSAny; + try { + // a. Let next be IteratorStep(iteratorRecord). + + // b. If next is an abrupt completion, set + // iteratorRecord.[[Done]] to true. + + // c. ReturnIfAbrupt(next). + + // d. if next is false, then [continues below in "Done"] + const next: JSReceiver = iterator::IteratorStep( + iteratorRecord, fastIteratorResultMap) otherwise goto Done; + // e. Let nextValue be IteratorValue(next). + + // f. If nextValue is an abrupt completion, set + // iteratorRecord.[[Done]] to true. + + // g. ReturnIfAbrupt(nextValue). + nextValue = iterator::IteratorValue(next, fastIteratorResultMap); + } catch (e) { + goto Reject(e); + } + + // We store the indices as identity hash on the reject element + // closures. Thus, we need this limit. + if (index == kPropertyArrayHashFieldMax) { + // If there are too many elements (currently more than + // 2**21-1), raise a RangeError here (which is caught later 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 + // anyway. + ThrowRangeError( + MessageTemplate::kTooManyElementsInPromiseCombinator, 'any'); + } + + // h. Append undefined to errors. + growableErrorsArray.Push(Undefined); + + let nextPromise: JSAny; + // i. Let nextPromise be ? Call(constructor, promiseResolve, + // «nextValue »). + nextPromise = CallResolve(constructor, promiseResolveFunction, nextValue); + + // j. Let steps be the algorithm steps defined in Promise.any + // Reject Element Functions. + + // k. Let rejectElement be ! CreateBuiltinFunction(steps, « + // [[AlreadyCalled]], [[Index]], + // [[Errors]], [[Capability]], [[RemainingElements]] »). + + // l. Set rejectElement.[[AlreadyCalled]] to a new Record { + // [[Value]]: false }. + + // m. Set rejectElement.[[Index]] to index. + + // n. Set rejectElement.[[Errors]] to errors. + + // o. Set rejectElement.[[Capability]] to resultCapability. + + // p. Set rejectElement.[[RemainingElements]] to + // remainingElementsCount. + const rejectElement = CreatePromiseAnyRejectElementFunction( + rejectElementContext, index, nativeContext); + // q. Set remainingElementsCount.[[Value]] to + // remainingElementsCount.[[Value]] + 1. + const remainingElementsCount = UnsafeCast( + rejectElementContext[PromiseAnyRejectElementContextSlots:: + kPromiseAnyRejectElementRemainingSlot]); + rejectElementContext[PromiseAnyRejectElementContextSlots:: + kPromiseAnyRejectElementRemainingSlot] = + remainingElementsCount + 1; + + // r. Perform ? Invoke(nextPromise, "then", « + // resultCapability.[[Resolve]], rejectElement »). + let thenResult: JSAny; + + const then = GetProperty(nextPromise, kThenString); + thenResult = Call( + context, then, nextPromise, + UnsafeCast(resultCapability.resolve), rejectElement); + + // s. Increase index by 1. + index += 1; + + // For catch prediction, mark that rejections here are + // semantically handled by the combined Promise. + if (IsDebugActive() && Is(thenResult)) deferred { + SetPropertyStrict( + context, thenResult, kPromiseHandledBySymbol, + resultCapability.promise); + SetPropertyStrict( + context, rejectElement, kPromiseForwardingHandlerSymbol, True); + } + } + } catch (e) deferred { + iterator::IteratorCloseOnException(iteratorRecord); + goto Reject(e); + } label Done {} + + // (8.d) + // i. Set iteratorRecord.[[Done]] to true. + // ii. Set remainingElementsCount.[[Value]] to + // remainingElementsCount.[[Value]] - 1. + let remainingElementsCount = UnsafeCast( + rejectElementContext[PromiseAnyRejectElementContextSlots:: + kPromiseAnyRejectElementRemainingSlot]); + remainingElementsCount -= 1; + rejectElementContext[PromiseAnyRejectElementContextSlots:: + kPromiseAnyRejectElementRemainingSlot] = + remainingElementsCount; + + const errorsArray = growableErrorsArray.ToFixedArray(); + rejectElementContext[PromiseAnyRejectElementContextSlots:: + kPromiseAnyRejectElementErrorsArraySlot] = + errorsArray; + + // iii. If remainingElementsCount.[[Value]] is 0, then + if (remainingElementsCount == 0) deferred { + // 1. Let error be a newly created AggregateError object. + // 2. Set error.[[AggregateErrors]] to errors. + const error = ConstructAggregateError(errorsArray); + // 3. Return ThrowCompletion(error). + goto Reject(error); + } + // iv. Return resultCapability.[[Promise]]. + return resultCapability.promise; +} + +// https://tc39.es/proposal-promise-any/#sec-promise.any +transitioning javascript builtin +PromiseAny( + js-implicit context: Context, receiver: JSAny)(iterable: JSAny): JSAny { + // 1. Let C be the this value. + const receiver = Cast(receiver) + otherwise ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'Promise.any'); + + // 2. Let promiseCapability be ? NewPromiseCapability(C). + const capability = NewPromiseCapability(receiver, False); + + // NewPromiseCapability guarantees that receiver is Constructor + assert(Is(receiver)); + const constructor = UnsafeCast(receiver); + + try { + let iteratorRecord: iterator::IteratorRecord; + try { + // 3. Let iteratorRecord be GetIterator(iterable). + + // 4. IfAbruptRejectPromise(iteratorRecord, promiseCapability). + // (catch below) + iteratorRecord = iterator::GetIterator(iterable); + + // 5. Let result be PerformPromiseAny(iteratorRecord, C, + // promiseCapability). + + // 6. If result is an abrupt completion, then + + // a. If iteratorRecord.[[Done]] is false, set result to + // IteratorClose(iteratorRecord, result). + + // b. IfAbruptRejectPromise(result, promiseCapability). + + // [Iterator closing handled by PerformPromiseAny] + + // 7. Return Completion(result). + return PerformPromiseAny(iteratorRecord, constructor, capability) + otherwise Reject; + } catch (e) deferred { + goto Reject(e); + } + } label Reject(e: Object) deferred { + // Exception must be bound to a JS value. + assert(e != TheHole); + Call( + context, UnsafeCast(capability.reject), Undefined, + UnsafeCast(e)); + return capability.promise; + } +} + +transitioning macro ConstructAggregateError(implicit context: Context)( + errorsArray: FixedArray): JSObject { + const obj: JSAggregateError = error::ConstructInternalAggregateErrorHelper( + context, SmiConstant(MessageTemplate::kAllPromisesRejected)); + obj.errors = errorsArray; + return obj; +} + +extern macro PromiseAnyRejectElementSharedFunConstant(): SharedFunctionInfo; +} diff --git a/deps/v8/src/builtins/promise-constructor.tq b/deps/v8/src/builtins/promise-constructor.tq index dc0e077485cabe..dbf1fe2f4ded26 100644 --- a/deps/v8/src/builtins/promise-constructor.tq +++ b/deps/v8/src/builtins/promise-constructor.tq @@ -6,104 +6,104 @@ #include 'src/builtins/builtins-promise-gen.h' namespace runtime { - extern transitioning runtime - DebugPushPromise(implicit context: Context)(JSAny): JSAny; +extern transitioning runtime +DebugPushPromise(implicit context: Context)(JSAny): JSAny; - extern transitioning runtime - DebugPopPromise(implicit context: Context)(): JSAny; +extern transitioning runtime +DebugPopPromise(implicit context: Context)(): JSAny; - extern transitioning runtime - PromiseHookInit(implicit context: Context)(Object, Object): JSAny; +extern transitioning runtime +PromiseHookInit(implicit context: Context)(Object, Object): JSAny; } // https://tc39.es/ecma262/#sec-promise-constructor namespace promise { - extern runtime IncrementUseCounter(Context, Smi): void; - type UseCounterFeature extends int31 - constexpr 'v8::Isolate::UseCounterFeature'; - const kPromiseConstructorReturnedUndefined: constexpr UseCounterFeature - generates 'v8::Isolate::kPromiseConstructorReturnedUndefined'; - - extern macro - IsDebugActive(): bool; - - transitioning macro - HasAccessCheckFailed(implicit context: Context)( - nativeContext: NativeContext, promiseFun: JSAny, executor: JSAny): bool { - BranchIfAccessCheckFailed(nativeContext, promiseFun, executor) - otherwise return true; - return false; - } +extern runtime IncrementUseCounter(Context, Smi): void; +type UseCounterFeature extends int31 +constexpr 'v8::Isolate::UseCounterFeature'; +const kPromiseConstructorReturnedUndefined: constexpr UseCounterFeature + generates 'v8::Isolate::kPromiseConstructorReturnedUndefined'; + +extern macro +IsDebugActive(): bool; + +transitioning macro +HasAccessCheckFailed(implicit context: Context)( + nativeContext: NativeContext, promiseFun: JSAny, executor: JSAny): bool { + BranchIfAccessCheckFailed(nativeContext, promiseFun, executor) + otherwise return true; + return false; +} - extern macro ConstructorBuiltinsAssembler::EmitFastNewObject( - Context, JSFunction, JSReceiver): JSObject; +extern macro ConstructorBuiltinsAssembler::EmitFastNewObject( + Context, JSFunction, JSReceiver): JSObject; - extern macro - PromiseBuiltinsAssembler::IsPromiseHookEnabledOrHasAsyncEventDelegate(): bool; +extern macro +PromiseBuiltinsAssembler::IsPromiseHookEnabledOrHasAsyncEventDelegate(): bool; - // https://tc39.es/ecma262/#sec-promise-executor - transitioning javascript builtin - PromiseConstructor( - js-implicit context: NativeContext, receiver: JSAny, - newTarget: JSAny)(executor: JSAny): JSAny { - // 1. If NewTarget is undefined, throw a TypeError exception. - if (newTarget == Undefined) { - ThrowTypeError(MessageTemplate::kNotAPromise, newTarget); - } +// https://tc39.es/ecma262/#sec-promise-executor +transitioning javascript builtin +PromiseConstructor( + js-implicit context: NativeContext, receiver: JSAny, + newTarget: JSAny)(executor: JSAny): JSAny { + // 1. If NewTarget is undefined, throw a TypeError exception. + if (newTarget == Undefined) { + ThrowTypeError(MessageTemplate::kNotAPromise, newTarget); + } - // 2. If IsCallable(executor) is false, throw a TypeError exception. - if (!TaggedIsCallable(executor)) { - ThrowTypeError(MessageTemplate::kResolverNotAFunction, executor); - } + // 2. If IsCallable(executor) is false, throw a TypeError exception. + if (!Is(executor)) { + ThrowTypeError(MessageTemplate::kResolverNotAFunction, executor); + } - const promiseFun = UnsafeCast( - context[NativeContextSlot::PROMISE_FUNCTION_INDEX]); + const promiseFun = UnsafeCast( + context[NativeContextSlot::PROMISE_FUNCTION_INDEX]); - // Silently fail if the stack looks fishy. - if (HasAccessCheckFailed(context, promiseFun, executor)) { - IncrementUseCounter( - context, SmiConstant(kPromiseConstructorReturnedUndefined)); - return Undefined; - } + // Silently fail if the stack looks fishy. + if (HasAccessCheckFailed(context, promiseFun, executor)) { + IncrementUseCounter( + context, SmiConstant(kPromiseConstructorReturnedUndefined)); + return Undefined; + } - let result: JSPromise; - if (promiseFun == newTarget) { - result = NewJSPromise(); - } else { - result = UnsafeCast(EmitFastNewObject( - context, promiseFun, UnsafeCast(newTarget))); - PromiseInit(result); - if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) { - runtime::PromiseHookInit(result, Undefined); - } + let result: JSPromise; + if (promiseFun == newTarget) { + result = NewJSPromise(); + } else { + result = UnsafeCast(EmitFastNewObject( + context, promiseFun, UnsafeCast(newTarget))); + PromiseInit(result); + if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) { + runtime::PromiseHookInit(result, Undefined); } + } - const isDebugActive = IsDebugActive(); - if (isDebugActive) runtime::DebugPushPromise(result); - - const funcs = CreatePromiseResolvingFunctions(result, True, context); - const resolve = funcs.resolve; - const reject = funcs.reject; - try { - Call(context, UnsafeCast(executor), Undefined, resolve, reject); - } catch (e) { - Call(context, reject, Undefined, e); - } + const isDebugActive = IsDebugActive(); + if (isDebugActive) runtime::DebugPushPromise(result); - if (isDebugActive) runtime::DebugPopPromise(); - return result; + const funcs = CreatePromiseResolvingFunctions(result, True, context); + const resolve = funcs.resolve; + const reject = funcs.reject; + try { + Call(context, UnsafeCast(executor), Undefined, resolve, reject); + } catch (e) { + Call(context, reject, Undefined, e); } - // Promise.prototype.catch ( onRejected ) - // https://tc39.es/ecma262/#sec-promise.prototype.catch - transitioning javascript builtin - PromisePrototypeCatch(js-implicit context: Context, receiver: JSAny)( - onRejected: JSAny): JSAny { - // 1. Let promise be the this value. - // 2. Return ? Invoke(promise, "then", « undefined, onRejected »). - const nativeContext = LoadNativeContext(context); - return UnsafeCast( - InvokeThen(nativeContext, receiver, Undefined, onRejected)); - } + if (isDebugActive) runtime::DebugPopPromise(); + return result; +} + +// Promise.prototype.catch ( onRejected ) +// https://tc39.es/ecma262/#sec-promise.prototype.catch +transitioning javascript builtin +PromisePrototypeCatch( + js-implicit context: Context, receiver: JSAny)(onRejected: JSAny): JSAny { + // 1. Let promise be the this value. + // 2. Return ? Invoke(promise, "then", « undefined, onRejected »). + const nativeContext = LoadNativeContext(context); + return UnsafeCast( + InvokeThen(nativeContext, receiver, Undefined, onRejected)); +} } diff --git a/deps/v8/src/builtins/promise-finally.tq b/deps/v8/src/builtins/promise-finally.tq index 32028b819dabdf..48928ca4ce7ef3 100644 --- a/deps/v8/src/builtins/promise-finally.tq +++ b/deps/v8/src/builtins/promise-finally.tq @@ -7,201 +7,201 @@ namespace promise { - // TODO(joshualitt): The below ContextSlots are only available on synthetic - // contexts created by the promise pipeline for use in the promise pipeline. - // However, with Torque we should type the context and its slots to prevent - // accidentially using these slots on contexts which don't support them. - const kPromiseBuiltinsValueSlot: constexpr ContextSlot - generates 'PromiseBuiltins::kValueSlot'; - const kPromiseBuiltinsOnFinallySlot: constexpr ContextSlot - generates 'PromiseBuiltins::kOnFinallySlot'; - const kPromiseBuiltinsConstructorSlot: constexpr ContextSlot - generates 'PromiseBuiltins::kConstructorSlot'; - const kPromiseBuiltinsPromiseValueThunkOrReasonContextLength: constexpr int31 - generates 'PromiseBuiltins::kPromiseValueThunkOrReasonContextLength'; - const kPromiseBuiltinsPromiseFinallyContextLength: constexpr int31 - generates 'PromiseBuiltins::kPromiseFinallyContextLength'; - - transitioning javascript builtin - PromiseValueThunkFinally(js-implicit context: Context, receiver: JSAny)(): - JSAny { - return UnsafeCast(context[kPromiseBuiltinsValueSlot]); - } +// TODO(joshualitt): The below ContextSlots are only available on synthetic +// contexts created by the promise pipeline for use in the promise pipeline. +// However, with Torque we should type the context and its slots to prevent +// accidentially using these slots on contexts which don't support them. +const kPromiseBuiltinsValueSlot: constexpr ContextSlot + generates 'PromiseBuiltins::kValueSlot'; +const kPromiseBuiltinsOnFinallySlot: constexpr ContextSlot + generates 'PromiseBuiltins::kOnFinallySlot'; +const kPromiseBuiltinsConstructorSlot: constexpr ContextSlot + generates 'PromiseBuiltins::kConstructorSlot'; +const kPromiseBuiltinsPromiseValueThunkOrReasonContextLength: constexpr int31 + generates 'PromiseBuiltins::kPromiseValueThunkOrReasonContextLength'; +const kPromiseBuiltinsPromiseFinallyContextLength: constexpr int31 + generates 'PromiseBuiltins::kPromiseFinallyContextLength'; + +transitioning javascript builtin +PromiseValueThunkFinally( + js-implicit context: Context, receiver: JSAny)(): JSAny { + return UnsafeCast(context[kPromiseBuiltinsValueSlot]); +} - transitioning javascript builtin - PromiseThrowerFinally(js-implicit context: Context, receiver: JSAny)(): - never { - const reason = UnsafeCast(context[kPromiseBuiltinsValueSlot]); - Throw(reason); - } +transitioning javascript builtin +PromiseThrowerFinally(js-implicit context: Context, receiver: JSAny)(): never { + const reason = UnsafeCast(context[kPromiseBuiltinsValueSlot]); + Throw(reason); +} - macro CreateThrowerFunction(implicit context: Context)( - nativeContext: NativeContext, reason: JSAny): JSFunction { - const throwerContext = AllocateSyntheticFunctionContext( - nativeContext, kPromiseBuiltinsPromiseValueThunkOrReasonContextLength); - throwerContext[kPromiseBuiltinsValueSlot] = reason; - const map = UnsafeCast( - nativeContext - [NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]); - const throwerInfo = UnsafeCast( - nativeContext[NativeContextSlot::PROMISE_THROWER_FINALLY_SHARED_FUN]); - return AllocateFunctionWithMapAndContext(map, throwerInfo, throwerContext); - } +macro CreateThrowerFunction(implicit context: Context)( + nativeContext: NativeContext, reason: JSAny): JSFunction { + const throwerContext = AllocateSyntheticFunctionContext( + nativeContext, kPromiseBuiltinsPromiseValueThunkOrReasonContextLength); + throwerContext[kPromiseBuiltinsValueSlot] = reason; + const map = UnsafeCast( + nativeContext + [NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]); + const throwerInfo = PromiseThrowerFinallySharedFunConstant(); + return AllocateFunctionWithMapAndContext(map, throwerInfo, throwerContext); +} - transitioning javascript builtin - PromiseCatchFinally(js-implicit context: Context, receiver: JSAny)( - reason: JSAny): JSAny { - // 1. Let onFinally be F.[[OnFinally]]. - // 2. Assert: IsCallable(onFinally) is true. - const onFinally = - UnsafeCast(context[kPromiseBuiltinsOnFinallySlot]); +transitioning javascript builtin +PromiseCatchFinally( + js-implicit context: Context, receiver: JSAny)(reason: JSAny): JSAny { + // 1. Let onFinally be F.[[OnFinally]]. + // 2. Assert: IsCallable(onFinally) is true. + const onFinally = + UnsafeCast(context[kPromiseBuiltinsOnFinallySlot]); - // 3. Let result be ? Call(onFinally). - const result = Call(context, onFinally, Undefined); + // 3. Let result be ? Call(onFinally). + const result = Call(context, onFinally, Undefined); - // 4. Let C be F.[[Constructor]]. - const constructor = - UnsafeCast(context[kPromiseBuiltinsConstructorSlot]); + // 4. Let C be F.[[Constructor]]. + const constructor = + UnsafeCast(context[kPromiseBuiltinsConstructorSlot]); - // 5. Assert: IsConstructor(C) is true. - assert(IsConstructor(constructor)); + // 5. Assert: IsConstructor(C) is true. + assert(IsConstructor(constructor)); - // 6. Let promise be ? PromiseResolve(C, result). - const promise = PromiseResolve(constructor, result); + // 6. Let promise be ? PromiseResolve(C, result). + const promise = PromiseResolve(constructor, result); - // 7. Let thrower be equivalent to a function that throws reason. - const nativeContext = LoadNativeContext(context); - const thrower = CreateThrowerFunction(nativeContext, reason); + // 7. Let thrower be equivalent to a function that throws reason. + const nativeContext = LoadNativeContext(context); + const thrower = CreateThrowerFunction(nativeContext, reason); - // 8. Return ? Invoke(promise, "then", « thrower »). - return UnsafeCast(InvokeThen(nativeContext, promise, thrower)); - } + // 8. Return ? Invoke(promise, "then", « thrower »). + return UnsafeCast(InvokeThen(nativeContext, promise, thrower)); +} - macro CreateValueThunkFunction(implicit context: Context)( - nativeContext: NativeContext, value: JSAny): JSFunction { - const valueThunkContext = AllocateSyntheticFunctionContext( - nativeContext, kPromiseBuiltinsPromiseValueThunkOrReasonContextLength); - valueThunkContext[kPromiseBuiltinsValueSlot] = value; - const map = UnsafeCast( - nativeContext - [NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]); - const valueThunkInfo = UnsafeCast( - nativeContext - [NativeContextSlot::PROMISE_VALUE_THUNK_FINALLY_SHARED_FUN]); - return AllocateFunctionWithMapAndContext( - map, valueThunkInfo, valueThunkContext); - } +macro CreateValueThunkFunction(implicit context: Context)( + nativeContext: NativeContext, value: JSAny): JSFunction { + const valueThunkContext = AllocateSyntheticFunctionContext( + nativeContext, kPromiseBuiltinsPromiseValueThunkOrReasonContextLength); + valueThunkContext[kPromiseBuiltinsValueSlot] = value; + const map = UnsafeCast( + nativeContext + [NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]); + const valueThunkInfo = PromiseValueThunkFinallySharedFunConstant(); + return AllocateFunctionWithMapAndContext( + map, valueThunkInfo, valueThunkContext); +} - transitioning javascript builtin - PromiseThenFinally(js-implicit context: Context, receiver: JSAny)( - value: JSAny): JSAny { - // 1. Let onFinally be F.[[OnFinally]]. - // 2. Assert: IsCallable(onFinally) is true. - const onFinally = - UnsafeCast(context[kPromiseBuiltinsOnFinallySlot]); +transitioning javascript builtin +PromiseThenFinally( + js-implicit context: Context, receiver: JSAny)(value: JSAny): JSAny { + // 1. Let onFinally be F.[[OnFinally]]. + // 2. Assert: IsCallable(onFinally) is true. + const onFinally = + UnsafeCast(context[kPromiseBuiltinsOnFinallySlot]); - // 3. Let result be ? Call(onFinally). - const result = Call(context, onFinally, Undefined); + // 3. Let result be ? Call(onFinally). + const result = Call(context, onFinally, Undefined); - // 4. Let C be F.[[Constructor]]. - const constructor = - UnsafeCast(context[kPromiseBuiltinsConstructorSlot]); + // 4. Let C be F.[[Constructor]]. + const constructor = + UnsafeCast(context[kPromiseBuiltinsConstructorSlot]); - // 5. Assert: IsConstructor(C) is true. - assert(IsConstructor(constructor)); + // 5. Assert: IsConstructor(C) is true. + assert(IsConstructor(constructor)); - // 6. Let promise be ? PromiseResolve(C, result). - const promise = PromiseResolve(constructor, result); + // 6. Let promise be ? PromiseResolve(C, result). + const promise = PromiseResolve(constructor, result); - // 7. Let valueThunk be equivalent to a function that returns value. - const nativeContext = LoadNativeContext(context); - const valueThunk = CreateValueThunkFunction(nativeContext, value); + // 7. Let valueThunk be equivalent to a function that returns value. + const nativeContext = LoadNativeContext(context); + const valueThunk = CreateValueThunkFunction(nativeContext, value); - // 8. Return ? Invoke(promise, "then", « valueThunk »). - return UnsafeCast(InvokeThen(nativeContext, promise, valueThunk)); - } + // 8. Return ? Invoke(promise, "then", « valueThunk »). + return UnsafeCast(InvokeThen(nativeContext, promise, valueThunk)); +} - struct PromiseFinallyFunctions { - then_finally: JSFunction; - catch_finally: JSFunction; - } +struct PromiseFinallyFunctions { + then_finally: JSFunction; + catch_finally: JSFunction; +} - macro CreatePromiseFinallyFunctions(implicit context: Context)( - nativeContext: NativeContext, onFinally: Callable, - constructor: JSReceiver): PromiseFinallyFunctions { - const promiseContext = AllocateSyntheticFunctionContext( - nativeContext, kPromiseBuiltinsPromiseFinallyContextLength); - promiseContext[kPromiseBuiltinsOnFinallySlot] = onFinally; - promiseContext[kPromiseBuiltinsConstructorSlot] = constructor; - const map = UnsafeCast( - nativeContext - [NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]); - const thenFinallyInfo = UnsafeCast( - nativeContext[NativeContextSlot::PROMISE_THEN_FINALLY_SHARED_FUN]); - const thenFinally = - AllocateFunctionWithMapAndContext(map, thenFinallyInfo, promiseContext); - const catchFinallyInfo = UnsafeCast( - nativeContext[NativeContextSlot::PROMISE_CATCH_FINALLY_SHARED_FUN]); - const catchFinally = AllocateFunctionWithMapAndContext( - map, catchFinallyInfo, promiseContext); - return PromiseFinallyFunctions{ - then_finally: thenFinally, - catch_finally: catchFinally - }; - } +macro CreatePromiseFinallyFunctions(implicit context: Context)( + nativeContext: NativeContext, onFinally: Callable, + constructor: JSReceiver): PromiseFinallyFunctions { + const promiseContext = AllocateSyntheticFunctionContext( + nativeContext, kPromiseBuiltinsPromiseFinallyContextLength); + promiseContext[kPromiseBuiltinsOnFinallySlot] = onFinally; + promiseContext[kPromiseBuiltinsConstructorSlot] = constructor; + const map = UnsafeCast( + nativeContext + [NativeContextSlot::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX]); + const thenFinallyInfo = PromiseThenFinallySharedFunConstant(); + const thenFinally = + AllocateFunctionWithMapAndContext(map, thenFinallyInfo, promiseContext); + const catchFinallyInfo = PromiseCatchFinallySharedFunConstant(); + const catchFinally = + AllocateFunctionWithMapAndContext(map, catchFinallyInfo, promiseContext); + return PromiseFinallyFunctions{ + then_finally: thenFinally, + catch_finally: catchFinally + }; +} - transitioning javascript builtin - PromisePrototypeFinally(js-implicit context: Context, receiver: JSAny)( - onFinally: JSAny): JSAny { - // 1. Let promise be the this value. - // 2. If Type(promise) is not Object, throw a TypeError exception. - const jsReceiver = Cast(receiver) otherwise ThrowTypeError( - MessageTemplate::kCalledOnNonObject, 'Promise.prototype.finally'); - - // 3. Let C be ? SpeciesConstructor(promise, %Promise%). - const nativeContext = LoadNativeContext(context); - const promiseFun = UnsafeCast( - nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]); - - let constructor: JSReceiver = promiseFun; - const receiverMap = jsReceiver.map; - if (!IsJSPromiseMap(receiverMap) || - !IsPromiseSpeciesLookupChainIntact(nativeContext, receiverMap)) - deferred { - constructor = SpeciesConstructor(jsReceiver, promiseFun); - } - - // 4. Assert: IsConstructor(C) is true. - assert(IsConstructor(constructor)); - - // 5. If IsCallable(onFinally) is not true, - // a. Let thenFinally be onFinally. - // b. Let catchFinally be onFinally. - // 6. Else, - // a. Let thenFinally be a new built-in function object as defined - // in ThenFinally Function. - // b. Let catchFinally be a new built-in function object as - // defined in CatchFinally Function. - // c. Set thenFinally and catchFinally's [[Constructor]] internal - // slots to C. - // d. Set thenFinally and catchFinally's [[OnFinally]] internal - // slots to onFinally. - let thenFinally: JSAny; - let catchFinally: JSAny; - if (!TaggedIsSmi(onFinally) && - IsCallable(UnsafeCast(onFinally))) { - const pair = CreatePromiseFinallyFunctions( - nativeContext, UnsafeCast(onFinally), constructor); +transitioning javascript builtin +PromisePrototypeFinally( + js-implicit context: Context, receiver: JSAny)(onFinally: JSAny): JSAny { + // 1. Let promise be the this value. + // 2. If Type(promise) is not Object, throw a TypeError exception. + const jsReceiver = Cast(receiver) otherwise ThrowTypeError( + MessageTemplate::kCalledOnNonObject, 'Promise.prototype.finally'); + + // 3. Let C be ? SpeciesConstructor(promise, %Promise%). + const nativeContext = LoadNativeContext(context); + const promiseFun = UnsafeCast( + nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]); + + let constructor: JSReceiver = promiseFun; + const receiverMap = jsReceiver.map; + if (!IsJSPromiseMap(receiverMap) || + !IsPromiseSpeciesLookupChainIntact(nativeContext, receiverMap)) + deferred { + constructor = SpeciesConstructor(jsReceiver, promiseFun); + } + + // 4. Assert: IsConstructor(C) is true. + assert(IsConstructor(constructor)); + + // 5. If IsCallable(onFinally) is not true, + // a. Let thenFinally be onFinally. + // b. Let catchFinally be onFinally. + // 6. Else, + // a. Let thenFinally be a new built-in function object as defined + // in ThenFinally Function. + // b. Let catchFinally be a new built-in function object as + // defined in CatchFinally Function. + // c. Set thenFinally and catchFinally's [[Constructor]] internal + // slots to C. + // d. Set thenFinally and catchFinally's [[OnFinally]] internal + // slots to onFinally. + let thenFinally: JSAny; + let catchFinally: JSAny; + typeswitch (onFinally) { + case (onFinally: Callable): { + const pair = + CreatePromiseFinallyFunctions(nativeContext, onFinally, constructor); thenFinally = pair.then_finally; catchFinally = pair.catch_finally; - } else - deferred { - thenFinally = onFinally; - catchFinally = onFinally; - } - - // 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »). - return UnsafeCast( - InvokeThen(nativeContext, receiver, thenFinally, catchFinally)); + } + case (JSAny): deferred { + thenFinally = onFinally; + catchFinally = onFinally; + } } + + // 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »). + return UnsafeCast( + InvokeThen(nativeContext, receiver, thenFinally, catchFinally)); +} + +extern macro PromiseCatchFinallySharedFunConstant(): SharedFunctionInfo; +extern macro PromiseThenFinallySharedFunConstant(): SharedFunctionInfo; +extern macro PromiseThrowerFinallySharedFunConstant(): SharedFunctionInfo; +extern macro PromiseValueThunkFinallySharedFunConstant(): SharedFunctionInfo; } diff --git a/deps/v8/src/builtins/promise-jobs.tq b/deps/v8/src/builtins/promise-jobs.tq index ee049605e1afb4..6c64baf22d19ba 100644 --- a/deps/v8/src/builtins/promise-jobs.tq +++ b/deps/v8/src/builtins/promise-jobs.tq @@ -6,68 +6,68 @@ // https://tc39.es/ecma262/#sec-promise-jobs namespace promise { - extern macro IsJSPromiseMap(Map): bool; +extern macro IsJSPromiseMap(Map): bool; - // https://tc39.es/ecma262/#sec-promiseresolvethenablejob - transitioning builtin - PromiseResolveThenableJob(implicit context: Context)( - promiseToResolve: JSPromise, thenable: JSReceiver, then: JSAny): JSAny { - // We can use a simple optimization here if we know that {then} is the - // initial Promise.prototype.then method, and {thenable} is a JSPromise - // whose - // @@species lookup chain is intact: We can connect {thenable} and - // {promise_to_resolve} directly in that case and avoid the allocation of a - // temporary JSPromise and the closures plus context. +// https://tc39.es/ecma262/#sec-promiseresolvethenablejob +transitioning builtin +PromiseResolveThenableJob(implicit context: Context)( + promiseToResolve: JSPromise, thenable: JSReceiver, then: JSAny): JSAny { + // We can use a simple optimization here if we know that {then} is the + // initial Promise.prototype.then method, and {thenable} is a JSPromise + // whose + // @@species lookup chain is intact: We can connect {thenable} and + // {promise_to_resolve} directly in that case and avoid the allocation of a + // temporary JSPromise and the closures plus context. + // + // We take the generic (slow-)path if a PromiseHook is enabled or the + // debugger is active, to make sure we expose spec compliant behavior. + const nativeContext = LoadNativeContext(context); + const promiseThen = nativeContext[NativeContextSlot::PROMISE_THEN_INDEX]; + const thenableMap = thenable.map; + if (TaggedEqual(then, promiseThen) && IsJSPromiseMap(thenableMap) && + !IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() && + IsPromiseSpeciesLookupChainIntact(nativeContext, thenableMap)) { + // We know that the {thenable} is a JSPromise, which doesn't require + // any special treatment and that {then} corresponds to the initial + // Promise.prototype.then method. So instead of allocating a temporary + // JSPromise to connect the {thenable} with the {promise_to_resolve}, + // we can directly schedule the {promise_to_resolve} with default + // handlers onto the {thenable} promise. This does not only save the + // JSPromise allocation, but also avoids the allocation of the two + // resolving closures and the shared context. // - // We take the generic (slow-)path if a PromiseHook is enabled or the - // debugger is active, to make sure we expose spec compliant behavior. - const nativeContext = LoadNativeContext(context); - const promiseThen = nativeContext[NativeContextSlot::PROMISE_THEN_INDEX]; - const thenableMap = thenable.map; - if (TaggedEqual(then, promiseThen) && IsJSPromiseMap(thenableMap) && - !IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() && - IsPromiseSpeciesLookupChainIntact(nativeContext, thenableMap)) { - // We know that the {thenable} is a JSPromise, which doesn't require - // any special treatment and that {then} corresponds to the initial - // Promise.prototype.then method. So instead of allocating a temporary - // JSPromise to connect the {thenable} with the {promise_to_resolve}, - // we can directly schedule the {promise_to_resolve} with default - // handlers onto the {thenable} promise. This does not only save the - // JSPromise allocation, but also avoids the allocation of the two - // resolving closures and the shared context. - // - // What happens normally in this case is - // - // resolve, reject = CreateResolvingFunctions(promise_to_resolve) - // result_capability = NewPromiseCapability(%Promise%) - // PerformPromiseThen(thenable, resolve, reject, result_capability) - // - // which means that PerformPromiseThen will either schedule a new - // PromiseReaction with resolve and reject or a PromiseReactionJob - // with resolve or reject based on the state of {thenable}. And - // resolve or reject will just invoke the default [[Resolve]] or - // [[Reject]] functions on the {promise_to_resolve}. - // - // This is the same as just doing - // - // PerformPromiseThen(thenable, undefined, undefined, - // promise_to_resolve) - // - // which performs exactly the same (observable) steps. - return PerformPromiseThen( - UnsafeCast(thenable), UndefinedConstant(), - UndefinedConstant(), promiseToResolve); - } else { - const funcs = CreatePromiseResolvingFunctions( - promiseToResolve, False, nativeContext); - const resolve = funcs.resolve; - const reject = funcs.reject; - try { - return Call( - context, UnsafeCast(then), thenable, resolve, reject); - } catch (e) { - return Call(context, UnsafeCast(reject), Undefined, e); - } + // What happens normally in this case is + // + // resolve, reject = CreateResolvingFunctions(promise_to_resolve) + // result_capability = NewPromiseCapability(%Promise%) + // PerformPromiseThen(thenable, resolve, reject, result_capability) + // + // which means that PerformPromiseThen will either schedule a new + // PromiseReaction with resolve and reject or a PromiseReactionJob + // with resolve or reject based on the state of {thenable}. And + // resolve or reject will just invoke the default [[Resolve]] or + // [[Reject]] functions on the {promise_to_resolve}. + // + // This is the same as just doing + // + // PerformPromiseThen(thenable, undefined, undefined, + // promise_to_resolve) + // + // which performs exactly the same (observable) steps. + return PerformPromiseThen( + UnsafeCast(thenable), UndefinedConstant(), + UndefinedConstant(), promiseToResolve); + } else { + const funcs = + CreatePromiseResolvingFunctions(promiseToResolve, False, nativeContext); + const resolve = funcs.resolve; + const reject = funcs.reject; + try { + return Call( + context, UnsafeCast(then), thenable, resolve, reject); + } catch (e) { + return Call(context, UnsafeCast(reject), Undefined, e); } } } +} diff --git a/deps/v8/src/builtins/promise-misc.tq b/deps/v8/src/builtins/promise-misc.tq index 61461de29fe091..7ed2f7909a88c1 100644 --- a/deps/v8/src/builtins/promise-misc.tq +++ b/deps/v8/src/builtins/promise-misc.tq @@ -6,246 +6,267 @@ #include 'src/builtins/builtins-promise-gen.h' namespace runtime { - extern transitioning runtime - AllowDynamicFunction(implicit context: Context)(JSAny): JSAny; +extern transitioning runtime +AllowDynamicFunction(implicit context: Context)(JSAny): JSAny; } // Unsafe functions that should be used very carefully. namespace promise_internal { - extern macro PromiseBuiltinsAssembler::ZeroOutEmbedderOffsets(JSPromise): - void; +extern macro PromiseBuiltinsAssembler::ZeroOutEmbedderOffsets(JSPromise): void; - extern macro PromiseBuiltinsAssembler::AllocateJSPromise(Context): HeapObject; +extern macro PromiseBuiltinsAssembler::AllocateJSPromise(Context): HeapObject; } namespace promise { - extern macro IsFunctionWithPrototypeSlotMap(Map): bool; +extern macro IsFunctionWithPrototypeSlotMap(Map): bool; - @export - macro PromiseHasHandler(promise: JSPromise): bool { - return promise.HasHandler(); - } +@export +macro PromiseHasHandler(promise: JSPromise): bool { + return promise.HasHandler(); +} - @export - macro PromiseInit(promise: JSPromise): void { - assert(PromiseState::kPending == 0); - promise.reactions_or_result = kZero; - promise.flags = 0; - promise_internal::ZeroOutEmbedderOffsets(promise); - } +@export +macro PromiseInit(promise: JSPromise): void { + promise.reactions_or_result = kZero; + promise.flags = SmiTag(JSPromiseFlags{ + status: PromiseState::kPending, + has_handler: false, + handled_hint: false, + async_task_id: 0 + }); + promise_internal::ZeroOutEmbedderOffsets(promise); +} - macro InnerNewJSPromise(implicit context: Context)(): JSPromise { - const nativeContext = LoadNativeContext(context); - const promiseFun = UnsafeCast( - nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]); - assert(IsFunctionWithPrototypeSlotMap(promiseFun.map)); - const promiseMap = UnsafeCast(promiseFun.prototype_or_initial_map); - const promiseHeapObject = promise_internal::AllocateJSPromise(context); - * UnsafeConstCast(& promiseHeapObject.map) = promiseMap; - const promise = UnsafeCast(promiseHeapObject); - promise.properties_or_hash = kEmptyFixedArray; - promise.elements = kEmptyFixedArray; - promise.reactions_or_result = kZero; - promise.flags = 0; - return promise; - } +macro InnerNewJSPromise(implicit context: Context)(): JSPromise { + const nativeContext = LoadNativeContext(context); + const promiseFun = UnsafeCast( + nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]); + assert(IsFunctionWithPrototypeSlotMap(promiseFun.map)); + const promiseMap = UnsafeCast(promiseFun.prototype_or_initial_map); + const promiseHeapObject = promise_internal::AllocateJSPromise(context); + * UnsafeConstCast(& promiseHeapObject.map) = promiseMap; + const promise = UnsafeCast(promiseHeapObject); + promise.properties_or_hash = kEmptyFixedArray; + promise.elements = kEmptyFixedArray; + promise.reactions_or_result = kZero; + promise.flags = SmiTag(JSPromiseFlags{ + status: PromiseState::kPending, + has_handler: false, + handled_hint: false, + async_task_id: 0 + }); + return promise; +} - macro NewPromiseFulfillReactionJobTask(implicit context: Context)( - handlerContext: Context, argument: Object, handler: Callable|Undefined, - promiseOrCapability: JSPromise|PromiseCapability| - Undefined): PromiseFulfillReactionJobTask { - const nativeContext = LoadNativeContext(handlerContext); - return new PromiseFulfillReactionJobTask{ - map: PromiseFulfillReactionJobTaskMapConstant(), - argument, - context: handlerContext, - handler, - promise_or_capability: promiseOrCapability, - continuation_preserved_embedder_data: nativeContext - [NativeContextSlot::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX] - }; - } +macro NewPromiseFulfillReactionJobTask(implicit context: Context)( + handlerContext: Context, argument: Object, handler: Callable|Undefined, + promiseOrCapability: JSPromise|PromiseCapability| + Undefined): PromiseFulfillReactionJobTask { + const nativeContext = LoadNativeContext(handlerContext); + return new PromiseFulfillReactionJobTask{ + map: PromiseFulfillReactionJobTaskMapConstant(), + argument, + context: handlerContext, + handler, + promise_or_capability: promiseOrCapability, + continuation_preserved_embedder_data: nativeContext + [NativeContextSlot::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX] + }; +} - macro NewPromiseRejectReactionJobTask(implicit context: Context)( - handlerContext: Context, argument: Object, handler: Callable|Undefined, - promiseOrCapability: JSPromise|PromiseCapability| - Undefined): PromiseRejectReactionJobTask { - const nativeContext = LoadNativeContext(handlerContext); - return new PromiseRejectReactionJobTask{ - map: PromiseRejectReactionJobTaskMapConstant(), - argument, - context: handlerContext, - handler, - promise_or_capability: promiseOrCapability, - continuation_preserved_embedder_data: nativeContext - [NativeContextSlot::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX] - }; - } +macro NewPromiseRejectReactionJobTask(implicit context: Context)( + handlerContext: Context, argument: Object, handler: Callable|Undefined, + promiseOrCapability: JSPromise|PromiseCapability| + Undefined): PromiseRejectReactionJobTask { + const nativeContext = LoadNativeContext(handlerContext); + return new PromiseRejectReactionJobTask{ + map: PromiseRejectReactionJobTaskMapConstant(), + argument, + context: handlerContext, + handler, + promise_or_capability: promiseOrCapability, + continuation_preserved_embedder_data: nativeContext + [NativeContextSlot::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX] + }; +} - // These allocate and initialize a promise with pending state and - // undefined fields. - // - // This uses the given parent as the parent promise for the promise - // init hook. - @export - transitioning macro NewJSPromise(implicit context: Context)(parent: Object): - JSPromise { - const instance = InnerNewJSPromise(); - PromiseInit(instance); - if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) { - runtime::PromiseHookInit(instance, parent); - } - return instance; +// These allocate and initialize a promise with pending state and +// undefined fields. +// +// This uses the given parent as the parent promise for the promise +// init hook. +@export +transitioning macro NewJSPromise(implicit context: Context)(parent: Object): + JSPromise { + const instance = InnerNewJSPromise(); + PromiseInit(instance); + if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) { + runtime::PromiseHookInit(instance, parent); } + return instance; +} - // This uses undefined as the parent promise for the promise init - // hook. - @export - transitioning macro NewJSPromise(implicit context: Context)(): JSPromise { - return NewJSPromise(Undefined); - } +// This uses undefined as the parent promise for the promise init +// hook. +@export +transitioning macro NewJSPromise(implicit context: Context)(): JSPromise { + return NewJSPromise(Undefined); +} - // This allocates and initializes a promise with the given state and - // fields. - @export - transitioning macro NewJSPromise(implicit context: Context)( - status: constexpr PromiseState, result: JSAny): JSPromise { - assert(status != PromiseState::kPending); - assert(kJSPromiseStatusShift == 0); - - const instance = InnerNewJSPromise(); - instance.reactions_or_result = result; - instance.SetStatus(status); - promise_internal::ZeroOutEmbedderOffsets(instance); - - if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) { - runtime::PromiseHookInit(instance, Undefined); - } - return instance; - } +// This allocates and initializes a promise with the given state and +// fields. +@export +transitioning macro NewJSPromise(implicit context: Context)( + status: constexpr PromiseState, result: JSAny): JSPromise { + assert(status != PromiseState::kPending); - macro NewPromiseReaction(implicit context: Context)( - handlerContext: Context, next: Zero|PromiseReaction, - promiseOrCapability: JSPromise|PromiseCapability|Undefined, - fulfillHandler: Callable|Undefined, - rejectHandler: Callable|Undefined): PromiseReaction { - const nativeContext = LoadNativeContext(handlerContext); - return new PromiseReaction{ - map: PromiseReactionMapConstant(), - next: next, - reject_handler: rejectHandler, - fulfill_handler: fulfillHandler, - promise_or_capability: promiseOrCapability, - continuation_preserved_embedder_data: nativeContext - [NativeContextSlot::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX] - }; - } + const instance = InnerNewJSPromise(); + instance.reactions_or_result = result; + instance.SetStatus(status); + promise_internal::ZeroOutEmbedderOffsets(instance); - extern macro PromiseResolveThenableJobTaskMapConstant(): Map; - - macro NewPromiseResolveThenableJobTask(implicit context: Context)( - promiseToResolve: JSPromise, then: JSReceiver, thenable: JSReceiver, - thenContext: Context): PromiseResolveThenableJobTask { - return new PromiseResolveThenableJobTask{ - map: PromiseResolveThenableJobTaskMapConstant(), - context: thenContext, - promise_to_resolve: promiseToResolve, - then: then, - thenable: thenable - }; + if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) { + runtime::PromiseHookInit(instance, Undefined); } + return instance; +} - struct InvokeThenOneArgFunctor { - transitioning - macro Call( - nativeContext: NativeContext, then: JSAny, receiver: JSAny, arg1: JSAny, - _arg2: JSAny): JSAny { - return Call(nativeContext, then, receiver, arg1); - } - } +macro NewPromiseReaction(implicit context: Context)( + handlerContext: Context, next: Zero|PromiseReaction, + promiseOrCapability: JSPromise|PromiseCapability|Undefined, + fulfillHandler: Callable|Undefined, + rejectHandler: Callable|Undefined): PromiseReaction { + const nativeContext = LoadNativeContext(handlerContext); + return new PromiseReaction{ + map: PromiseReactionMapConstant(), + next: next, + reject_handler: rejectHandler, + fulfill_handler: fulfillHandler, + promise_or_capability: promiseOrCapability, + continuation_preserved_embedder_data: nativeContext + [NativeContextSlot::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX] + }; +} - struct InvokeThenTwoArgFunctor { - transitioning - macro Call( - nativeContext: NativeContext, then: JSAny, receiver: JSAny, arg1: JSAny, - arg2: JSAny): JSAny { - return Call(nativeContext, then, receiver, arg1, arg2); - } - } +extern macro PromiseResolveThenableJobTaskMapConstant(): Map; - transitioning - macro InvokeThen(implicit context: Context)( - nativeContext: NativeContext, receiver: JSAny, arg1: JSAny, arg2: JSAny, - callFunctor: F): JSAny { - // We can skip the "then" lookup on {receiver} if it's [[Prototype]] - // is the (initial) Promise.prototype and the Promise#then protector - // is intact, as that guards the lookup path for the "then" property - // on JSPromise instances which have the (initial) %PromisePrototype%. - if (!Is(receiver) && - IsPromiseThenLookupChainIntact( - nativeContext, UnsafeCast(receiver).map)) { - const then = UnsafeCast( - nativeContext[NativeContextSlot::PROMISE_THEN_INDEX]); - return callFunctor.Call(nativeContext, then, receiver, arg1, arg2); - } else - deferred { - const then = UnsafeCast(GetProperty(receiver, kThenString)); - return callFunctor.Call(nativeContext, then, receiver, arg1, arg2); - } - } +// https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob +macro NewPromiseResolveThenableJobTask(implicit context: Context)( + promiseToResolve: JSPromise, thenable: JSReceiver, + then: Callable): PromiseResolveThenableJobTask { + // 2. Let getThenRealmResult be GetFunctionRealm(then). + // 3. If getThenRealmResult is a normal completion, then let thenRealm be + // getThenRealmResult.[[Value]]. + // 4. Otherwise, let thenRealm be null. + // + // The only cases where |thenRealm| can be null is when |then| is a revoked + // Proxy object, which would throw when it is called anyway. So instead of + // setting the context to null as the spec does, we just use the current + // realm. + const thenContext: Context = ExtractHandlerContext(then); + const nativeContext = LoadNativeContext(thenContext); + + // 1. Let job be a new Job abstract closure with no parameters that + // captures promiseToResolve, thenable, and then... + // 5. Return { [[Job]]: job, [[Realm]]: thenRealm }. + return new PromiseResolveThenableJobTask{ + map: PromiseResolveThenableJobTaskMapConstant(), + context: nativeContext, + promise_to_resolve: promiseToResolve, + thenable, + then + }; +} +struct InvokeThenOneArgFunctor { transitioning - macro InvokeThen(implicit context: Context)( - nativeContext: NativeContext, receiver: JSAny, arg: JSAny): JSAny { - return InvokeThen( - nativeContext, receiver, arg, Undefined, InvokeThenOneArgFunctor{}); + macro Call( + nativeContext: NativeContext, then: JSAny, receiver: JSAny, arg1: JSAny, + _arg2: JSAny): JSAny { + return Call(nativeContext, then, receiver, arg1); } +} +struct InvokeThenTwoArgFunctor { transitioning - macro InvokeThen(implicit context: Context)( - nativeContext: NativeContext, receiver: JSAny, arg1: JSAny, + macro Call( + nativeContext: NativeContext, then: JSAny, receiver: JSAny, arg1: JSAny, arg2: JSAny): JSAny { - return InvokeThen( - nativeContext, receiver, arg1, arg2, InvokeThenTwoArgFunctor{}); + return Call(nativeContext, then, receiver, arg1, arg2); } +} - transitioning - macro BranchIfAccessCheckFailed(implicit context: Context)( - nativeContext: NativeContext, promiseConstructor: JSAny, - executor: JSAny): void labels IfNoAccess { - try { - // If executor is a bound function, load the bound function until we've - // reached an actual function. - let foundExecutor = executor; - while (true) { - typeswitch (foundExecutor) { - case (f: JSFunction): { - // Load the context from the function and compare it to the Promise - // constructor's context. If they match, everything is fine, - // otherwise, bail out to the runtime. - const functionContext = f.context; - const nativeFunctionContext = LoadNativeContext(functionContext); - if (TaggedEqual(nativeContext, nativeFunctionContext)) { - goto HasAccess; - } else { - goto CallRuntime; - } - } - case (b: JSBoundFunction): { - foundExecutor = b.bound_target_function; - } - case (Object): { +transitioning +macro InvokeThen(implicit context: Context)( + nativeContext: NativeContext, receiver: JSAny, arg1: JSAny, arg2: JSAny, + callFunctor: F): JSAny { + // We can skip the "then" lookup on {receiver} if it's [[Prototype]] + // is the (initial) Promise.prototype and the Promise#then protector + // is intact, as that guards the lookup path for the "then" property + // on JSPromise instances which have the (initial) %PromisePrototype%. + if (!Is(receiver) && + IsPromiseThenLookupChainIntact( + nativeContext, UnsafeCast(receiver).map)) { + const then = + UnsafeCast(nativeContext[NativeContextSlot::PROMISE_THEN_INDEX]); + return callFunctor.Call(nativeContext, then, receiver, arg1, arg2); + } else + deferred { + const then = UnsafeCast(GetProperty(receiver, kThenString)); + return callFunctor.Call(nativeContext, then, receiver, arg1, arg2); + } +} + +transitioning +macro InvokeThen(implicit context: Context)( + nativeContext: NativeContext, receiver: JSAny, arg: JSAny): JSAny { + return InvokeThen( + nativeContext, receiver, arg, Undefined, InvokeThenOneArgFunctor{}); +} + +transitioning +macro InvokeThen(implicit context: Context)( + nativeContext: NativeContext, receiver: JSAny, arg1: JSAny, + arg2: JSAny): JSAny { + return InvokeThen( + nativeContext, receiver, arg1, arg2, InvokeThenTwoArgFunctor{}); +} + +transitioning +macro BranchIfAccessCheckFailed(implicit context: Context)( + nativeContext: NativeContext, promiseConstructor: JSAny, + executor: JSAny): void labels IfNoAccess { + try { + // If executor is a bound function, load the bound function until we've + // reached an actual function. + let foundExecutor = executor; + while (true) { + typeswitch (foundExecutor) { + case (f: JSFunction): { + // Load the context from the function and compare it to the Promise + // constructor's context. If they match, everything is fine, + // otherwise, bail out to the runtime. + const functionContext = f.context; + const nativeFunctionContext = LoadNativeContext(functionContext); + if (TaggedEqual(nativeContext, nativeFunctionContext)) { + goto HasAccess; + } else { goto CallRuntime; } } + case (b: JSBoundFunction): { + foundExecutor = b.bound_target_function; + } + case (Object): { + goto CallRuntime; + } } } - label CallRuntime deferred { - const result = runtime::AllowDynamicFunction(promiseConstructor); - if (result != True) { - goto IfNoAccess; - } + } label CallRuntime deferred { + const result = runtime::AllowDynamicFunction(promiseConstructor); + if (result != True) { + goto IfNoAccess; } - label HasAccess {} - } + } label HasAccess {} +} } diff --git a/deps/v8/src/builtins/promise-race.tq b/deps/v8/src/builtins/promise-race.tq index 7e56a08c8443ad..27d2038398ab4b 100644 --- a/deps/v8/src/builtins/promise-race.tq +++ b/deps/v8/src/builtins/promise-race.tq @@ -6,126 +6,124 @@ namespace promise { - extern macro PromiseForwardingHandlerSymbolConstant(): Symbol; - const kPromiseForwardingHandlerSymbol: Symbol = - PromiseForwardingHandlerSymbolConstant(); - extern macro PromiseHandledBySymbolConstant(): Symbol; - const kPromiseHandledBySymbol: Symbol = PromiseHandledBySymbolConstant(); - extern macro ResolveStringConstant(): String; - const kResolveString: String = ResolveStringConstant(); - extern macro SetPropertyStrict(Context, Object, Object, Object): Object; - extern macro IsPromiseResolveProtectorCellInvalid(): bool; +extern macro PromiseForwardingHandlerSymbolConstant(): Symbol; +const kPromiseForwardingHandlerSymbol: Symbol = + PromiseForwardingHandlerSymbolConstant(); +extern macro PromiseHandledBySymbolConstant(): Symbol; +const kPromiseHandledBySymbol: Symbol = PromiseHandledBySymbolConstant(); +extern macro ResolveStringConstant(): String; +const kResolveString: String = ResolveStringConstant(); +extern macro SetPropertyStrict(Context, Object, Object, Object): Object; +extern macro IsPromiseResolveProtectorCellInvalid(): bool; - macro IsPromiseResolveLookupChainIntact(implicit context: Context)( - nativeContext: NativeContext, constructor: JSReceiver): bool { - if (IsForceSlowPath()) return false; - const promiseFun = UnsafeCast( - nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]); - return promiseFun == constructor && !IsPromiseResolveProtectorCellInvalid(); - } +macro IsPromiseResolveLookupChainIntact(implicit context: Context)( + nativeContext: NativeContext, constructor: JSReceiver): bool { + if (IsForceSlowPath()) return false; + const promiseFun = UnsafeCast( + nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]); + return promiseFun == constructor && !IsPromiseResolveProtectorCellInvalid(); +} - // https://tc39.es/ecma262/#sec-promise.race - transitioning javascript builtin - PromiseRace(js-implicit context: Context, receiver: JSAny)(iterable: JSAny): - JSAny { - const receiver = Cast(receiver) - otherwise ThrowTypeError( - MessageTemplate::kCalledOnNonObject, 'Promise.race'); +// https://tc39.es/ecma262/#sec-promise.race +transitioning javascript builtin +PromiseRace( + js-implicit context: Context, receiver: JSAny)(iterable: JSAny): JSAny { + const receiver = Cast(receiver) + otherwise ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'Promise.race'); - // 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); - const resolve = capability.resolve; - const reject = capability.reject; - const promise = capability.promise; + // 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); + const resolve = capability.resolve; + const reject = capability.reject; + const promise = capability.promise; - // For catch prediction, don't treat the .then calls as handling it; - // instead, recurse outwards. - if (IsDebugActive()) deferred { - SetPropertyStrict( - context, reject, kPromiseForwardingHandlerSymbol, True); - } + // For catch prediction, don't treat the .then calls as handling it; + // instead, recurse outwards. + if (IsDebugActive()) deferred { + SetPropertyStrict(context, reject, kPromiseForwardingHandlerSymbol, True); + } + try { + // Let iterator be GetIterator(iterable). + // IfAbruptRejectPromise(iterator, promiseCapability). + let i: iterator::IteratorRecord; try { - // Let iterator be GetIterator(iterable). - // IfAbruptRejectPromise(iterator, promiseCapability). - let i: iterator::IteratorRecord; - try { - i = iterator::GetIterator(iterable); - } catch (e) deferred { - goto Reject(e); - } + i = iterator::GetIterator(iterable); + } catch (e) deferred { + goto Reject(e); + } - // Let result be PerformPromiseRace(iteratorRecord, C, promiseCapability). - try { - // 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. - const nativeContext = LoadNativeContext(context); - let promiseResolveFunction: JSAny = Undefined; - if (!IsPromiseResolveLookupChainIntact(nativeContext, receiver)) - deferred { - // 3. Let _promiseResolve_ be ? Get(_constructor_, `"resolve"`). - const resolve = GetProperty(receiver, kResolveString); + // Let result be PerformPromiseRace(iteratorRecord, C, promiseCapability). + try { + // 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. + const nativeContext = LoadNativeContext(context); + let promiseResolveFunction: JSAny = Undefined; + if (!IsPromiseResolveLookupChainIntact(nativeContext, receiver)) + deferred { + // 3. Let _promiseResolve_ be ? Get(_constructor_, `"resolve"`). + const resolve = GetProperty(receiver, kResolveString); - // 4. If IsCallable(_promiseResolve_) is *false*, throw a - // *TypeError* exception. - promiseResolveFunction = Cast(resolve) - otherwise ThrowTypeError( - MessageTemplate::kCalledNonCallable, 'resolve'); - } + // 4. If IsCallable(_promiseResolve_) is *false*, throw a + // *TypeError* exception. + promiseResolveFunction = Cast(resolve) + otherwise ThrowTypeError( + MessageTemplate::kCalledNonCallable, 'resolve'); + } - const fastIteratorResultMap = UnsafeCast( - nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]); - while (true) { - let nextValue: JSAny; - try { - // Let next be IteratorStep(iteratorRecord.[[Iterator]]). - // If next is an abrupt completion, set iteratorRecord.[[Done]] to - // true. ReturnIfAbrupt(next). - const next: JSReceiver = iterator::IteratorStep( - i, fastIteratorResultMap) otherwise return promise; + const fastIteratorResultMap = UnsafeCast( + nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]); + while (true) { + let nextValue: JSAny; + try { + // Let next be IteratorStep(iteratorRecord.[[Iterator]]). + // If next is an abrupt completion, set iteratorRecord.[[Done]] to + // true. ReturnIfAbrupt(next). + const next: JSReceiver = iterator::IteratorStep( + i, fastIteratorResultMap) otherwise return promise; - // Let nextValue be IteratorValue(next). - // If nextValue is an abrupt completion, set iteratorRecord.[[Done]] - // to true. - // ReturnIfAbrupt(nextValue). - nextValue = iterator::IteratorValue(next, fastIteratorResultMap); - } catch (e) { - goto Reject(e); - } - // Let nextPromise be ? Call(constructor, _promiseResolve_, « - // nextValue »). - const nextPromise = CallResolve( - UnsafeCast(receiver), promiseResolveFunction, - nextValue); + // Let nextValue be IteratorValue(next). + // If nextValue is an abrupt completion, set iteratorRecord.[[Done]] + // to true. + // ReturnIfAbrupt(nextValue). + nextValue = iterator::IteratorValue(next, fastIteratorResultMap); + } catch (e) { + goto Reject(e); + } + // Let nextPromise be ? Call(constructor, _promiseResolve_, « + // nextValue »). + const nextPromise = CallResolve( + UnsafeCast(receiver), promiseResolveFunction, + nextValue); - // Perform ? Invoke(nextPromise, "then", « resolveElement, - // resultCapability.[[Reject]] »). - const then = GetProperty(nextPromise, kThenString); - const thenResult = Call( - context, then, nextPromise, UnsafeCast(resolve), - UnsafeCast(reject)); + // Perform ? Invoke(nextPromise, "then", « resolveElement, + // resultCapability.[[Reject]] »). + const then = GetProperty(nextPromise, kThenString); + const thenResult = Call( + context, then, nextPromise, UnsafeCast(resolve), + UnsafeCast(reject)); - // For catch prediction, mark that rejections here are semantically - // handled by the combined Promise. - if (IsDebugActive() && !Is(promise)) deferred { - SetPropertyStrict( - context, thenResult, kPromiseHandledBySymbol, promise); - } - } - } catch (e) deferred { - iterator::IteratorCloseOnException(i, e) otherwise Reject; + // For catch prediction, mark that rejections here are semantically + // handled by the combined Promise. + if (IsDebugActive() && !Is(promise)) deferred { + SetPropertyStrict( + context, thenResult, kPromiseHandledBySymbol, promise); + } } + } catch (e) deferred { + iterator::IteratorCloseOnException(i); + goto Reject(e); } - label Reject(exception: Object) deferred { - Call( - context, UnsafeCast(reject), Undefined, - UnsafeCast(exception)); - return promise; - } - unreachable; + } label Reject(exception: Object) deferred { + Call( + context, UnsafeCast(reject), Undefined, + UnsafeCast(exception)); + return promise; } + unreachable; +} } diff --git a/deps/v8/src/builtins/promise-reaction-job.tq b/deps/v8/src/builtins/promise-reaction-job.tq index f17886c0d18643..1e89da02617dc9 100644 --- a/deps/v8/src/builtins/promise-reaction-job.tq +++ b/deps/v8/src/builtins/promise-reaction-job.tq @@ -6,118 +6,118 @@ namespace promise { - transitioning - macro RejectPromiseReactionJob( - context: Context, - promiseOrCapability: JSPromise|PromiseCapability|Undefined, reason: JSAny, - reactionType: constexpr PromiseReactionType): JSAny { - if constexpr (reactionType == kPromiseReactionReject) { - typeswitch (promiseOrCapability) { - case (promise: JSPromise): { - // For fast native promises we can skip the indirection via the - // promiseCapability.[[Reject]] function and run the resolve logic - // directly from here. - return RejectPromise(promise, reason, False); - } - case (Undefined): { - return Undefined; - } - case (capability: PromiseCapability): { - // In the general case we need to call the (user provided) - // promiseCapability.[[Reject]] function. - const reject = UnsafeCast(capability.reject); - return Call(context, reject, Undefined, reason); - } - } - } else { - StaticAssert(reactionType == kPromiseReactionFulfill); - // We have to call out to the dedicated PromiseRejectReactionJob - // builtin here, instead of just doing the work inline, as otherwise - // the catch predictions in the debugger will be wrong, which just - // walks the stack and checks for certain builtins. - return PromiseRejectReactionJob(reason, Undefined, promiseOrCapability); - } - } - - transitioning - macro FuflfillPromiseReactionJob( - context: Context, - promiseOrCapability: JSPromise|PromiseCapability|Undefined, result: JSAny, - reactionType: constexpr PromiseReactionType): JSAny { +transitioning +macro RejectPromiseReactionJob( + context: Context, + promiseOrCapability: JSPromise|PromiseCapability|Undefined, reason: JSAny, + reactionType: constexpr PromiseReactionType): JSAny { + if constexpr (reactionType == kPromiseReactionReject) { typeswitch (promiseOrCapability) { case (promise: JSPromise): { // For fast native promises we can skip the indirection via the - // promiseCapability.[[Resolve]] function and run the resolve logic + // promiseCapability.[[Reject]] function and run the resolve logic // directly from here. - return ResolvePromise(context, promise, result); + return RejectPromise(promise, reason, False); } case (Undefined): { return Undefined; } case (capability: PromiseCapability): { // In the general case we need to call the (user provided) - // promiseCapability.[[Resolve]] function. - const resolve = UnsafeCast(capability.resolve); - try { - return Call(context, resolve, Undefined, result); - } catch (e) { - return RejectPromiseReactionJob( - context, promiseOrCapability, e, reactionType); - } + // promiseCapability.[[Reject]] function. + const reject = UnsafeCast(capability.reject); + return Call(context, reject, Undefined, reason); } } + } else { + StaticAssert(reactionType == kPromiseReactionFulfill); + // We have to call out to the dedicated PromiseRejectReactionJob + // builtin here, instead of just doing the work inline, as otherwise + // the catch predictions in the debugger will be wrong, which just + // walks the stack and checks for certain builtins. + return PromiseRejectReactionJob(reason, Undefined, promiseOrCapability); } +} - // https://tc39.es/ecma262/#sec-promisereactionjob - transitioning - macro PromiseReactionJob( - context: Context, argument: JSAny, handler: Callable|Undefined, - promiseOrCapability: JSPromise|PromiseCapability|Undefined, - reactionType: constexpr PromiseReactionType): JSAny { - if (handler == Undefined) { - if constexpr (reactionType == kPromiseReactionFulfill) { - return FuflfillPromiseReactionJob( - context, promiseOrCapability, argument, reactionType); - } else { - StaticAssert(reactionType == kPromiseReactionReject); - return RejectPromiseReactionJob( - context, promiseOrCapability, argument, reactionType); - } - } else { +transitioning +macro FuflfillPromiseReactionJob( + context: Context, + promiseOrCapability: JSPromise|PromiseCapability|Undefined, result: JSAny, + reactionType: constexpr PromiseReactionType): JSAny { + typeswitch (promiseOrCapability) { + case (promise: JSPromise): { + // For fast native promises we can skip the indirection via the + // promiseCapability.[[Resolve]] function and run the resolve logic + // directly from here. + return ResolvePromise(context, promise, result); + } + case (Undefined): { + return Undefined; + } + case (capability: PromiseCapability): { + // In the general case we need to call the (user provided) + // promiseCapability.[[Resolve]] function. + const resolve = UnsafeCast(capability.resolve); try { - const result = - Call(context, UnsafeCast(handler), Undefined, argument); - if (promiseOrCapability == Undefined) { - // There's no [[Capability]] for this promise reaction job, which - // means that this is a specification-internal operation (aka - // await) where the result does not matter (see the specification - // change in https://github.com/tc39/ecma262/pull/1146 for - // details). - return Undefined; - } else { - return FuflfillPromiseReactionJob( - context, promiseOrCapability, result, reactionType); - } + return Call(context, resolve, Undefined, result); } catch (e) { return RejectPromiseReactionJob( context, promiseOrCapability, e, reactionType); } } } +} - transitioning builtin - PromiseFulfillReactionJob(implicit context: Context)( - value: JSAny, handler: Callable|Undefined, - promiseOrCapability: JSPromise|PromiseCapability|Undefined): JSAny { - return PromiseReactionJob( - context, value, handler, promiseOrCapability, kPromiseReactionFulfill); +// https://tc39.es/ecma262/#sec-promisereactionjob +transitioning +macro PromiseReactionJob( + context: Context, argument: JSAny, handler: Callable|Undefined, + promiseOrCapability: JSPromise|PromiseCapability|Undefined, + reactionType: constexpr PromiseReactionType): JSAny { + if (handler == Undefined) { + if constexpr (reactionType == kPromiseReactionFulfill) { + return FuflfillPromiseReactionJob( + context, promiseOrCapability, argument, reactionType); + } else { + StaticAssert(reactionType == kPromiseReactionReject); + return RejectPromiseReactionJob( + context, promiseOrCapability, argument, reactionType); + } + } else { + try { + const result = + Call(context, UnsafeCast(handler), Undefined, argument); + if (promiseOrCapability == Undefined) { + // There's no [[Capability]] for this promise reaction job, which + // means that this is a specification-internal operation (aka + // await) where the result does not matter (see the specification + // change in https://github.com/tc39/ecma262/pull/1146 for + // details). + return Undefined; + } else { + return FuflfillPromiseReactionJob( + context, promiseOrCapability, result, reactionType); + } + } catch (e) { + return RejectPromiseReactionJob( + context, promiseOrCapability, e, reactionType); + } } +} - transitioning builtin - PromiseRejectReactionJob(implicit context: Context)( - reason: JSAny, handler: Callable|Undefined, - promiseOrCapability: JSPromise|PromiseCapability|Undefined): JSAny { - return PromiseReactionJob( - context, reason, handler, promiseOrCapability, kPromiseReactionReject); - } +transitioning builtin +PromiseFulfillReactionJob(implicit context: Context)( + value: JSAny, handler: Callable|Undefined, + promiseOrCapability: JSPromise|PromiseCapability|Undefined): JSAny { + return PromiseReactionJob( + context, value, handler, promiseOrCapability, kPromiseReactionFulfill); +} + +transitioning builtin +PromiseRejectReactionJob(implicit context: Context)( + reason: JSAny, handler: Callable|Undefined, + promiseOrCapability: JSPromise|PromiseCapability|Undefined): JSAny { + return PromiseReactionJob( + context, reason, handler, promiseOrCapability, kPromiseReactionReject); +} } diff --git a/deps/v8/src/builtins/promise-resolve.tq b/deps/v8/src/builtins/promise-resolve.tq index 0fc98b556b2297..dbb60720c04416 100644 --- a/deps/v8/src/builtins/promise-resolve.tq +++ b/deps/v8/src/builtins/promise-resolve.tq @@ -5,190 +5,180 @@ #include 'src/builtins/builtins-promise-gen.h' namespace runtime { - extern transitioning runtime - ResolvePromise(implicit context: Context)(JSPromise, JSAny): JSAny; +extern transitioning runtime +ResolvePromise(implicit context: Context)(JSPromise, JSAny): JSAny; } namespace promise { - extern macro ConstructorStringConstant(): String; - const kConstructorString: String = ConstructorStringConstant(); - - // https://tc39.es/ecma262/#sec-promise.resolve - transitioning javascript builtin - PromiseResolveTrampoline(js-implicit context: NativeContext, receiver: JSAny)( - value: JSAny): JSAny { - // 1. Let C be the this value. - // 2. If Type(C) is not Object, throw a TypeError exception. - const receiver = Cast(receiver) otherwise - ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'PromiseResolve'); - - // 3. Return ? PromiseResolve(C, x). - return PromiseResolve(receiver, value); - } - - transitioning builtin - PromiseResolve(implicit context: - Context)(constructor: JSReceiver, value: JSAny): JSAny { - const nativeContext = LoadNativeContext(context); - const promiseFun = nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]; - try { - // Check if {value} is a JSPromise. - const value = Cast(value) otherwise NeedToAllocate; - - // We can skip the "constructor" lookup on {value} if it's [[Prototype]] - // is the (initial) Promise.prototype and the @@species protector is - // intact, as that guards the lookup path for "constructor" on - // JSPromise instances which have the (initial) Promise.prototype. - const promisePrototype = - nativeContext[NativeContextSlot::PROMISE_PROTOTYPE_INDEX]; - if (value.map.prototype != promisePrototype) { - goto SlowConstructor; - } - - if (IsPromiseSpeciesProtectorCellInvalid()) goto SlowConstructor; +extern macro ConstructorStringConstant(): String; +const kConstructorString: String = ConstructorStringConstant(); + +// https://tc39.es/ecma262/#sec-promise.resolve +transitioning javascript builtin +PromiseResolveTrampoline( + js-implicit context: NativeContext, receiver: JSAny)(value: JSAny): JSAny { + // 1. Let C be the this value. + // 2. If Type(C) is not Object, throw a TypeError exception. + const receiver = Cast(receiver) otherwise + ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'PromiseResolve'); + + // 3. Return ? PromiseResolve(C, x). + return PromiseResolve(receiver, value); +} - // If the {constructor} is the Promise function, we just immediately - // return the {value} here and don't bother wrapping it into a - // native Promise. - if (promiseFun != constructor) goto SlowConstructor; - return value; - } - label SlowConstructor deferred { - // At this point, value or/and constructor are not native promises, but - // they could be of the same subclass. - const valueConstructor = GetProperty(value, kConstructorString); - if (valueConstructor != constructor) goto NeedToAllocate; - return value; - } - label NeedToAllocate { - if (promiseFun == constructor) { - // This adds a fast path for native promises that don't need to - // create NewPromiseCapability. - const result = NewJSPromise(); - ResolvePromise(context, result, value); - return result; - } else - deferred { - const capability = NewPromiseCapability(constructor, True); - const resolve = UnsafeCast(capability.resolve); - Call(context, resolve, Undefined, value); - return capability.promise; - } +transitioning builtin +PromiseResolve(implicit context: Context)( + constructor: JSReceiver, value: JSAny): JSAny { + const nativeContext = LoadNativeContext(context); + const promiseFun = nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]; + try { + // Check if {value} is a JSPromise. + const value = Cast(value) otherwise NeedToAllocate; + + // We can skip the "constructor" lookup on {value} if it's [[Prototype]] + // is the (initial) Promise.prototype and the @@species protector is + // intact, as that guards the lookup path for "constructor" on + // JSPromise instances which have the (initial) Promise.prototype. + const promisePrototype = + nativeContext[NativeContextSlot::PROMISE_PROTOTYPE_INDEX]; + if (value.map.prototype != promisePrototype) { + goto SlowConstructor; } - } - - extern macro IsJSReceiverMap(Map): bool; - extern macro IsPromiseThenProtectorCellInvalid(): bool; - - extern macro ThenStringConstant(): String; - - const kThenString: String = ThenStringConstant(); - - transitioning builtin - ResolvePromise(implicit context: - Context)(promise: JSPromise, resolution: JSAny): JSAny { - // 6. If SameValue(resolution, promise) is true, then - // If promise hook is enabled or the debugger is active, let - // the runtime handle this operation, which greatly reduces - // the complexity here and also avoids a couple of back and - // forth between JavaScript and C++ land. - // We also let the runtime handle it if promise == resolution. - // We can use pointer comparison here, since the {promise} is guaranteed - // to be a JSPromise inside this function and thus is reference comparable. - if (IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() || - TaggedEqual(promise, resolution)) + if (IsPromiseSpeciesProtectorCellInvalid()) goto SlowConstructor; + + // If the {constructor} is the Promise function, we just immediately + // return the {value} here and don't bother wrapping it into a + // native Promise. + if (promiseFun != constructor) goto SlowConstructor; + return value; + } label SlowConstructor deferred { + // At this point, value or/and constructor are not native promises, but + // they could be of the same subclass. + const valueConstructor = GetProperty(value, kConstructorString); + if (valueConstructor != constructor) goto NeedToAllocate; + return value; + } label NeedToAllocate { + if (promiseFun == constructor) { + // This adds a fast path for native promises that don't need to + // create NewPromiseCapability. + const result = NewJSPromise(); + ResolvePromise(context, result, value); + return result; + } else deferred { - return runtime::ResolvePromise(promise, resolution); - } - - let then: Object = Undefined; - try { - // 7. If Type(resolution) is not Object, then - // 7.b Return FulfillPromise(promise, resolution). - if (TaggedIsSmi(resolution)) { - return FulfillPromise(promise, resolution); + const capability = NewPromiseCapability(constructor, True); + const resolve = UnsafeCast(capability.resolve); + Call(context, resolve, Undefined, value); + return capability.promise; } + } +} - const heapResolution = UnsafeCast(resolution); - const resolutionMap = heapResolution.map; - if (!IsJSReceiverMap(resolutionMap)) { - return FulfillPromise(promise, resolution); - } +extern macro IsJSReceiverMap(Map): bool; + +extern macro IsPromiseThenProtectorCellInvalid(): bool; + +extern macro ThenStringConstant(): String; + +const kThenString: String = ThenStringConstant(); + +// https://tc39.es/ecma262/#sec-promise-resolve-functions +transitioning builtin +ResolvePromise(implicit context: Context)( + promise: JSPromise, resolution: JSAny): JSAny { + // 7. If SameValue(resolution, promise) is true, then + // If promise hook is enabled or the debugger is active, let + // the runtime handle this operation, which greatly reduces + // the complexity here and also avoids a couple of back and + // forth between JavaScript and C++ land. + // We also let the runtime handle it if promise == resolution. + // We can use pointer comparison here, since the {promise} is guaranteed + // to be a JSPromise inside this function and thus is reference comparable. + if (IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() || + TaggedEqual(promise, resolution)) + deferred { + return runtime::ResolvePromise(promise, resolution); + } - // We can skip the "then" lookup on {resolution} if its [[Prototype]] - // is the (initial) Promise.prototype and the Promise#then protector - // is intact, as that guards the lookup path for the "then" property - // on JSPromise instances which have the (initial) %PromisePrototype%. - if (IsForceSlowPath()) { - goto Slow; - } + let then: Object = Undefined; + try { + // 8. If Type(resolution) is not Object, then + // 8.a Return FulfillPromise(promise, resolution). + if (TaggedIsSmi(resolution)) { + return FulfillPromise(promise, resolution); + } - if (IsPromiseThenProtectorCellInvalid()) { - goto Slow; - } + const heapResolution = UnsafeCast(resolution); + const resolutionMap = heapResolution.map; + if (!IsJSReceiverMap(resolutionMap)) { + return FulfillPromise(promise, resolution); + } - const nativeContext = LoadNativeContext(context); - if (!IsJSPromiseMap(resolutionMap)) { - // We can skip the lookup of "then" if the {resolution} is a (newly - // created) IterResultObject, as the Promise#then() protector also - // ensures that the intrinsic %ObjectPrototype% doesn't contain any - // "then" property. This helps to avoid negative lookups on iterator - // results from async generators. - assert(IsJSReceiverMap(resolutionMap)); - assert(!IsPromiseThenProtectorCellInvalid()); - if (resolutionMap == - nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]) { - return FulfillPromise(promise, resolution); - } else { - goto Slow; - } - } + // We can skip the "then" lookup on {resolution} if its [[Prototype]] + // is the (initial) Promise.prototype and the Promise#then protector + // is intact, as that guards the lookup path for the "then" property + // on JSPromise instances which have the (initial) %PromisePrototype%. + if (IsForceSlowPath()) { + goto Slow; + } - const promisePrototype = - nativeContext[NativeContextSlot::PROMISE_PROTOTYPE_INDEX]; - if (resolutionMap.prototype == promisePrototype) { - // The {resolution} is a native Promise in this case. - then = nativeContext[NativeContextSlot::PROMISE_THEN_INDEX]; - goto Enqueue; - } + if (IsPromiseThenProtectorCellInvalid()) { goto Slow; } - label Slow deferred { - // 8. Let then be Get(resolution, "then"). - // 9. If then is an abrupt completion, then - // 9.a Return RejectPromise(promise, then.[[Value]]). - try { - then = GetProperty(resolution, kThenString); - } catch (e) { - return RejectPromise(promise, e, False); - } - // 11. If IsCallable(thenAction) is false, then - if (TaggedIsSmi(then)) { + const nativeContext = LoadNativeContext(context); + if (!IsJSPromiseMap(resolutionMap)) { + // We can skip the lookup of "then" if the {resolution} is a (newly + // created) IterResultObject, as the Promise#then() protector also + // ensures that the intrinsic %ObjectPrototype% doesn't contain any + // "then" property. This helps to avoid negative lookups on iterator + // results from async generators. + assert(IsJSReceiverMap(resolutionMap)); + assert(!IsPromiseThenProtectorCellInvalid()); + if (resolutionMap == + nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]) { return FulfillPromise(promise, resolution); + } else { + goto Slow; } + } - if (!IsCallable(UnsafeCast(then))) { - return FulfillPromise(promise, resolution); - } + const promisePrototype = + nativeContext[NativeContextSlot::PROMISE_PROTOTYPE_INDEX]; + if (resolutionMap.prototype == promisePrototype) { + // The {resolution} is a native Promise in this case. + then = nativeContext[NativeContextSlot::PROMISE_THEN_INDEX]; goto Enqueue; } - label Enqueue { - // 12. Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob, - // «promise, resolution, thenAction»). - - // According to HTML, we use the context of the then function - // (|thenAction|) as the context of the microtask. See step 3 of HTML's - // EnqueueJob: - // https://html.spec.whatwg.org/C/#enqueuejob(queuename,-job,-arguments) - const thenContext: Context = - ExtractHandlerContext(UnsafeCast(then)); - const nativeContext = LoadNativeContext(thenContext); - const task = NewPromiseResolveThenableJobTask( - promise, UnsafeCast(then), - UnsafeCast(resolution), nativeContext); - return EnqueueMicrotask(nativeContext, task); + goto Slow; + } label Slow deferred { + // 9. Let then be Get(resolution, "then"). + // 10. If then is an abrupt completion, then + try { + then = GetProperty(resolution, kThenString); + } catch (e) { + // a. Return RejectPromise(promise, then.[[Value]]). + return RejectPromise(promise, e, False); } + + // 11. Let thenAction be then.[[Value]]. + // 12. If IsCallable(thenAction) is false, then + if (!Is(then)) { + // a. Return FulfillPromise(promise, resolution). + return FulfillPromise(promise, resolution); + } + goto Enqueue; + } label Enqueue { + // 13. Let job be NewPromiseResolveThenableJob(promise, resolution, + // thenAction). + const task = NewPromiseResolveThenableJobTask( + promise, UnsafeCast(resolution), + UnsafeCast(then)); + + // 14. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]). + // 15. Return undefined. + return EnqueueMicrotask(task.context, task); } } +} diff --git a/deps/v8/src/builtins/promise-then.tq b/deps/v8/src/builtins/promise-then.tq index 45f8fd0c81f6d0..3de6d277d840ca 100644 --- a/deps/v8/src/builtins/promise-then.tq +++ b/deps/v8/src/builtins/promise-then.tq @@ -6,74 +6,69 @@ namespace promise { - macro - IsPromiseSpeciesLookupChainIntact( - nativeContext: NativeContext, promiseMap: Map): bool { - const promisePrototype = - nativeContext[NativeContextSlot::PROMISE_PROTOTYPE_INDEX]; - if (IsForceSlowPath()) return false; - if (promiseMap.prototype != promisePrototype) return false; - return !IsPromiseSpeciesProtectorCellInvalid(); - } - - // https://tc39.es/ecma262/#sec-promise.prototype.then - transitioning javascript builtin - PromisePrototypeThen(js-implicit context: NativeContext, receiver: JSAny)( - onFulfilled: JSAny, onRejected: JSAny): JSAny { - // 1. Let promise be the this value. - // 2. If IsPromise(promise) is false, throw a TypeError exception. - const promise = Cast(receiver) otherwise ThrowTypeError( - MessageTemplate::kIncompatibleMethodReceiver, 'Promise.prototype.then', - receiver); +macro +IsPromiseSpeciesLookupChainIntact( + nativeContext: NativeContext, promiseMap: Map): bool { + const promisePrototype = + nativeContext[NativeContextSlot::PROMISE_PROTOTYPE_INDEX]; + if (IsForceSlowPath()) return false; + if (promiseMap.prototype != promisePrototype) return false; + return !IsPromiseSpeciesProtectorCellInvalid(); +} - // 3. Let C be ? SpeciesConstructor(promise, %Promise%). - const promiseFun = UnsafeCast( - context[NativeContextSlot::PROMISE_FUNCTION_INDEX]); +// https://tc39.es/ecma262/#sec-promise.prototype.then +transitioning javascript builtin +PromisePrototypeThen(js-implicit context: NativeContext, receiver: JSAny)( + onFulfilled: JSAny, onRejected: JSAny): JSAny { + // 1. Let promise be the this value. + // 2. If IsPromise(promise) is false, throw a TypeError exception. + const promise = Cast(receiver) otherwise ThrowTypeError( + MessageTemplate::kIncompatibleMethodReceiver, 'Promise.prototype.then', + receiver); - // 4. Let resultCapability be ? NewPromiseCapability(C). - let resultPromiseOrCapability: JSPromise|PromiseCapability; - let resultPromise: JSAny; - try { - if (IsPromiseSpeciesLookupChainIntact(context, promise.map)) { - goto AllocateAndInit; - } + // 3. Let C be ? SpeciesConstructor(promise, %Promise%). + const promiseFun = UnsafeCast( + context[NativeContextSlot::PROMISE_FUNCTION_INDEX]); - const constructor = SpeciesConstructor(promise, promiseFun); - if (TaggedEqual(constructor, promiseFun)) { - goto AllocateAndInit; - } else { - const promiseCapability = NewPromiseCapability(constructor, True); - resultPromiseOrCapability = promiseCapability; - resultPromise = promiseCapability.promise; - } + // 4. Let resultCapability be ? NewPromiseCapability(C). + let resultPromiseOrCapability: JSPromise|PromiseCapability; + let resultPromise: JSAny; + try { + if (IsPromiseSpeciesLookupChainIntact(context, promise.map)) { + goto AllocateAndInit; } - label AllocateAndInit { - const resultJSPromise = NewJSPromise(promise); - resultPromiseOrCapability = resultJSPromise; - resultPromise = resultJSPromise; + + const constructor = SpeciesConstructor(promise, promiseFun); + if (TaggedEqual(constructor, promiseFun)) { + goto AllocateAndInit; + } else { + const promiseCapability = NewPromiseCapability(constructor, True); + resultPromiseOrCapability = promiseCapability; + resultPromise = promiseCapability.promise; } + } label AllocateAndInit { + const resultJSPromise = NewJSPromise(promise); + resultPromiseOrCapability = resultJSPromise; + resultPromise = resultJSPromise; + } - // We do some work of the PerformPromiseThen operation here, in that - // we check the handlers and turn non-callable handlers into undefined. - // This is because this is the one and only callsite of PerformPromiseThen - // that has to do this. + // We do some work of the PerformPromiseThen operation here, in that + // we check the handlers and turn non-callable handlers into undefined. + // This is because this is the one and only callsite of PerformPromiseThen + // that has to do this. - // 3. If IsCallable(onFulfilled) is false, then - // a. Set onFulfilled to undefined. - const onFulfilled = TaggedIsCallable(onFulfilled) ? - UnsafeCast(onFulfilled) : - Undefined; + // 3. If IsCallable(onFulfilled) is false, then + // a. Set onFulfilled to undefined. + const onFulfilled = CastOrDefault(onFulfilled, Undefined); - // 4. If IsCallable(onRejected) is false, then - // a. Set onRejected to undefined. - const onRejected = TaggedIsCallable(onRejected) ? - UnsafeCast(onRejected) : - Undefined; + // 4. If IsCallable(onRejected) is false, then + // a. Set onRejected to undefined. + const onRejected = CastOrDefault(onRejected, Undefined); - // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, - // resultCapability). - PerformPromiseThenImpl( - promise, onFulfilled, onRejected, resultPromiseOrCapability); - return resultPromise; - } + // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, + // resultCapability). + PerformPromiseThenImpl( + promise, onFulfilled, onRejected, resultPromiseOrCapability); + return resultPromise; +} } diff --git a/deps/v8/src/builtins/proxy-constructor.tq b/deps/v8/src/builtins/proxy-constructor.tq index ef886e4f2878c2..ea31ff6db87fd4 100644 --- a/deps/v8/src/builtins/proxy-constructor.tq +++ b/deps/v8/src/builtins/proxy-constructor.tq @@ -6,55 +6,40 @@ namespace proxy { - // ES #sec-proxy-constructor - // https://tc39.github.io/ecma262/#sec-proxy-constructor - transitioning javascript builtin - ProxyConstructor( - js-implicit context: NativeContext, receiver: JSAny, - newTarget: JSAny)(target: JSAny, handler: JSAny): JSProxy { - try { - // 1. If NewTarget is undefined, throw a TypeError exception. - if (newTarget == Undefined) { - ThrowTypeError(MessageTemplate::kConstructorNotFunction, 'Proxy'); - } - - // 2. Return ? ProxyCreate(target, handler). - // https://tc39.github.io/ecma262/#sec-proxycreate - // 1. If Type(target) is not Object, throw a TypeError exception. - // 2. If target is a Proxy exotic object and target.[[ProxyHandler]] is - // null, throw a TypeError exception. - // 3. If Type(handler) is not Object, throw a TypeError exception. - // 4. If handler is a Proxy exotic object and handler.[[ProxyHandler]] - // is null, throw a TypeError exception. - const targetJSReceiver = - Cast(target) otherwise ThrowProxyNonObject; - if (IsRevokedProxy(targetJSReceiver)) { - goto ThrowProxyHandlerOrTargetRevoked; - } +// ES #sec-proxy-constructor +// https://tc39.github.io/ecma262/#sec-proxy-constructor +transitioning javascript builtin +ProxyConstructor( + js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny)( + target: JSAny, handler: JSAny): JSProxy { + try { + // 1. If NewTarget is undefined, throw a TypeError exception. + if (newTarget == Undefined) { + ThrowTypeError(MessageTemplate::kConstructorNotFunction, 'Proxy'); + } - const handlerJSReceiver = - Cast(handler) otherwise ThrowProxyNonObject; - if (IsRevokedProxy(handlerJSReceiver)) { - goto ThrowProxyHandlerOrTargetRevoked; - } + // 2. Return ? ProxyCreate(target, handler). + // https://tc39.github.io/ecma262/#sec-proxycreate + // 1. If Type(target) is not Object, throw a TypeError exception. + // 2. If Type(handler) is not Object, throw a TypeError exception. + const targetJSReceiver = + Cast(target) otherwise ThrowProxyNonObject; + const handlerJSReceiver = + Cast(handler) otherwise ThrowProxyNonObject; - // 5. Let P be a newly created object. - // 6. Set P's essential internal methods (except for [[Call]] and - // [[Construct]]) to the definitions specified in 9.5. - // 7. If IsCallable(target) is true, then - // a. Set P.[[Call]] as specified in 9.5.12. - // b. If IsConstructor(target) is true, then - // 1. Set P.[[Construct]] as specified in 9.5.13. - // 8. Set P.[[ProxyTarget]] to target. - // 9. Set P.[[ProxyHandler]] to handler. - // 10. Return P. - return AllocateProxy(targetJSReceiver, handlerJSReceiver); - } - label ThrowProxyNonObject deferred { - ThrowTypeError(MessageTemplate::kProxyNonObject); - } - label ThrowProxyHandlerOrTargetRevoked deferred { - ThrowTypeError(MessageTemplate::kProxyHandlerOrTargetRevoked); - } + // 5. Let P be a newly created object. + // 6. Set P's essential internal methods (except for [[Call]] and + // [[Construct]]) to the definitions specified in 9.5. + // 7. If IsCallable(target) is true, then + // a. Set P.[[Call]] as specified in 9.5.12. + // b. If IsConstructor(target) is true, then + // 1. Set P.[[Construct]] as specified in 9.5.13. + // 8. Set P.[[ProxyTarget]] to target. + // 9. Set P.[[ProxyHandler]] to handler. + // 10. Return P. + return AllocateProxy(targetJSReceiver, handlerJSReceiver); + } label ThrowProxyNonObject deferred { + ThrowTypeError(MessageTemplate::kProxyNonObject); } } +} diff --git a/deps/v8/src/builtins/proxy-delete-property.tq b/deps/v8/src/builtins/proxy-delete-property.tq index b068f3afb1a19b..45914a6ed55950 100644 --- a/deps/v8/src/builtins/proxy-delete-property.tq +++ b/deps/v8/src/builtins/proxy-delete-property.tq @@ -6,66 +6,64 @@ namespace proxy { - // ES #sec-proxy-object-internal-methods-and-internal-slots-delete-p - // https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-delete-p - transitioning builtin - ProxyDeleteProperty(implicit context: Context)( - proxy: JSProxy, name: PropertyKey, languageMode: LanguageModeSmi): JSAny { - const kTrapName: constexpr string = 'deleteProperty'; - // Handle deeply nested proxy. - PerformStackCheck(); - // 1. Assert: IsPropertyKey(P) is true. - assert(TaggedIsNotSmi(name)); - assert(IsName(name)); - assert(!IsPrivateSymbol(name)); +// ES #sec-proxy-object-internal-methods-and-internal-slots-delete-p +// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-delete-p +transitioning builtin +ProxyDeleteProperty(implicit context: Context)( + proxy: JSProxy, name: PropertyKey, languageMode: LanguageModeSmi): JSAny { + const kTrapName: constexpr string = 'deleteProperty'; + // Handle deeply nested proxy. + PerformStackCheck(); + // 1. Assert: IsPropertyKey(P) is true. + assert(TaggedIsNotSmi(name)); + assert(IsName(name)); + assert(!IsPrivateSymbol(name)); - try { - // 2. Let handler be O.[[ProxyHandler]]. - // 3. If handler is null, throw a TypeError exception. - // 4. Assert: Type(handler) is Object. - assert(proxy.handler == Null || Is(proxy.handler)); - const handler = - Cast(proxy.handler) otherwise ThrowProxyHandlerRevoked; + try { + // 2. Let handler be O.[[ProxyHandler]]. + // 3. If handler is null, throw a TypeError exception. + // 4. Assert: Type(handler) is Object. + assert(proxy.handler == Null || Is(proxy.handler)); + const handler = + Cast(proxy.handler) otherwise ThrowProxyHandlerRevoked; - // 5. Let target be O.[[ProxyTarget]]. - const target = UnsafeCast(proxy.target); + // 5. Let target be O.[[ProxyTarget]]. + const target = UnsafeCast(proxy.target); - // 6. Let trap be ? GetMethod(handler, "deleteProperty"). - // 7. If trap is undefined, then (see 7.a below). - const trap: Callable = GetMethod(handler, kTrapName) - otherwise goto TrapUndefined(target); + // 6. Let trap be ? GetMethod(handler, "deleteProperty"). + // 7. If trap is undefined, then (see 7.a below). + const trap: Callable = GetMethod(handler, kTrapName) + otherwise goto TrapUndefined(target); - // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, - // « target, P »)). - const trapResult = Call(context, trap, handler, target, name); + // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, + // « target, P »)). + const trapResult = Call(context, trap, handler, target, name); - // 9. If booleanTrapResult is false, return false. - if (!ToBoolean(trapResult)) { - const strictValue: LanguageModeSmi = LanguageMode::kStrict; - if (languageMode == strictValue) { - ThrowTypeError( - MessageTemplate::kProxyTrapReturnedFalsishFor, kTrapName, name); - } - return False; + // 9. If booleanTrapResult is false, return false. + if (!ToBoolean(trapResult)) { + const strictValue: LanguageModeSmi = LanguageMode::kStrict; + if (languageMode == strictValue) { + ThrowTypeError( + MessageTemplate::kProxyTrapReturnedFalsishFor, kTrapName, name); } + return False; + } - // 10. Let targetDesc be ? target.[[GetOwnProperty]](P). - // 11. If targetDesc is undefined, return true. - // 12. If targetDesc.[[Configurable]] is false, throw a TypeError - // exception. - // 13. Let extensibleTarget be ? IsExtensible(target). - // 14. If extensibleTarget is false, throw a TypeError exception. - CheckDeleteTrapResult(target, proxy, name); + // 10. Let targetDesc be ? target.[[GetOwnProperty]](P). + // 11. If targetDesc is undefined, return true. + // 12. If targetDesc.[[Configurable]] is false, throw a TypeError + // exception. + // 13. Let extensibleTarget be ? IsExtensible(target). + // 14. If extensibleTarget is false, throw a TypeError exception. + CheckDeleteTrapResult(target, proxy, name); - // 15. Return true. - return True; - } - label TrapUndefined(target: JSAny) { - // 7.a. Return ? target.[[Delete]](P). - return DeleteProperty(target, name, languageMode); - } - label ThrowProxyHandlerRevoked deferred { - ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName); - } + // 15. Return true. + return True; + } label TrapUndefined(target: JSAny) { + // 7.a. Return ? target.[[Delete]](P). + return DeleteProperty(target, name, languageMode); + } label ThrowProxyHandlerRevoked deferred { + ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName); } } +} diff --git a/deps/v8/src/builtins/proxy-get-property.tq b/deps/v8/src/builtins/proxy-get-property.tq index 7138648a851d4e..2d6a1edee68869 100644 --- a/deps/v8/src/builtins/proxy-get-property.tq +++ b/deps/v8/src/builtins/proxy-get-property.tq @@ -6,60 +6,59 @@ namespace proxy { - extern transitioning builtin GetPropertyWithReceiver( - implicit context: Context)(JSAny, Name, JSAny, Smi): JSAny; +extern transitioning builtin GetPropertyWithReceiver(implicit context: Context)( + JSAny, Name, JSAny, Smi): JSAny; - // ES #sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver - // https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver - transitioning builtin - ProxyGetProperty(implicit context: Context)( - proxy: JSProxy, name: PropertyKey, receiverValue: JSAny, - onNonExistent: Smi): JSAny { - PerformStackCheck(); - // 1. Assert: IsPropertyKey(P) is true. - assert(TaggedIsNotSmi(name)); - assert(IsName(name)); - assert(!IsPrivateSymbol(name)); +// ES #sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver +// https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver +transitioning builtin +ProxyGetProperty(implicit context: Context)( + proxy: JSProxy, name: PropertyKey, receiverValue: JSAny, + onNonExistent: Smi): JSAny { + PerformStackCheck(); + // 1. Assert: IsPropertyKey(P) is true. + assert(TaggedIsNotSmi(name)); + assert(IsName(name)); + assert(!IsPrivateSymbol(name)); - // 2. Let handler be O.[[ProxyHandler]]. - // 3. If handler is null, throw a TypeError exception. - // 4. Assert: Type(handler) is Object. - let handler: JSReceiver; - typeswitch (proxy.handler) { - case (Null): { - ThrowTypeError(MessageTemplate::kProxyRevoked, 'get'); - } - case (h: JSReceiver): { - handler = h; - } + // 2. Let handler be O.[[ProxyHandler]]. + // 3. If handler is null, throw a TypeError exception. + // 4. Assert: Type(handler) is Object. + let handler: JSReceiver; + typeswitch (proxy.handler) { + case (Null): { + ThrowTypeError(MessageTemplate::kProxyRevoked, 'get'); } + case (h: JSReceiver): { + handler = h; + } + } - // 5. Let target be O.[[ProxyTarget]]. - const target = Cast(proxy.target) otherwise unreachable; + // 5. Let target be O.[[ProxyTarget]]. + const target = Cast(proxy.target) otherwise unreachable; - // 6. Let trap be ? GetMethod(handler, "get"). - // 7. If trap is undefined, then (see 7.a below). - // 7.a. Return ? target.[[Get]](P, Receiver). - const trap: Callable = GetMethod(handler, 'get') - otherwise return GetPropertyWithReceiver( - target, name, receiverValue, onNonExistent); + // 6. Let trap be ? GetMethod(handler, "get"). + // 7. If trap is undefined, then (see 7.a below). + // 7.a. Return ? target.[[Get]](P, Receiver). + const trap: Callable = GetMethod(handler, 'get') + otherwise return GetPropertyWithReceiver( + target, name, receiverValue, onNonExistent); - // 8. Let trapResult be ? Call(trap, handler, « target, P, Receiver »). - const trapResult = - Call(context, trap, handler, target, name, receiverValue); + // 8. Let trapResult be ? Call(trap, handler, « target, P, Receiver »). + const trapResult = Call(context, trap, handler, target, name, receiverValue); - // 9. Let targetDesc be ? target.[[GetOwnProperty]](P). - // 10. If targetDesc is not undefined and targetDesc.[[Configurable]] is - // false, then - // a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] - // is false, then - // i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a - // TypeError exception. - // b. If IsAccessorDescriptor(targetDesc) is true and targetDesc.[[Get]] - // is undefined, then - // i. If trapResult is not undefined, throw a TypeError exception. - // 11. Return trapResult. - CheckGetSetTrapResult(target, proxy, name, trapResult, kProxyGet); - return trapResult; - } + // 9. Let targetDesc be ? target.[[GetOwnProperty]](P). + // 10. If targetDesc is not undefined and targetDesc.[[Configurable]] is + // false, then + // a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] + // is false, then + // i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a + // TypeError exception. + // b. If IsAccessorDescriptor(targetDesc) is true and targetDesc.[[Get]] + // is undefined, then + // i. If trapResult is not undefined, throw a TypeError exception. + // 11. Return trapResult. + CheckGetSetTrapResult(target, proxy, name, trapResult, kProxyGet); + return trapResult; +} } diff --git a/deps/v8/src/builtins/proxy-get-prototype-of.tq b/deps/v8/src/builtins/proxy-get-prototype-of.tq index 8c556e27c2bf45..152489ecb6a25a 100644 --- a/deps/v8/src/builtins/proxy-get-prototype-of.tq +++ b/deps/v8/src/builtins/proxy-get-prototype-of.tq @@ -6,65 +6,62 @@ namespace proxy { - // ES #sec-proxy-object-internal-methods-and-internal-slots-isextensible - // https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-isextensible - transitioning builtin - ProxyGetPrototypeOf(implicit context: Context)(proxy: JSProxy): JSAny { - PerformStackCheck(); - const kTrapName: constexpr string = 'getPrototypeOf'; - try { - // 1. Let handler be O.[[ProxyHandler]]. - // 2. If handler is null, throw a TypeError exception. - // 3. Assert: Type(handler) is Object. - assert(proxy.handler == Null || Is(proxy.handler)); - const handler = - Cast(proxy.handler) otherwise ThrowProxyHandlerRevoked; +// ES #sec-proxy-object-internal-methods-and-internal-slots-isextensible +// https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-isextensible +transitioning builtin +ProxyGetPrototypeOf(implicit context: Context)(proxy: JSProxy): JSAny { + PerformStackCheck(); + const kTrapName: constexpr string = 'getPrototypeOf'; + try { + // 1. Let handler be O.[[ProxyHandler]]. + // 2. If handler is null, throw a TypeError exception. + // 3. Assert: Type(handler) is Object. + assert(proxy.handler == Null || Is(proxy.handler)); + const handler = + Cast(proxy.handler) otherwise ThrowProxyHandlerRevoked; - // 4. Let target be O.[[ProxyTarget]]. - const target = proxy.target; + // 4. Let target be O.[[ProxyTarget]]. + const target = proxy.target; - // 5. Let trap be ? GetMethod(handler, "getPrototypeOf"). - // 6. If trap is undefined, then (see 6.a below). - const trap: Callable = GetMethod(handler, kTrapName) - otherwise goto TrapUndefined(target); + // 5. Let trap be ? GetMethod(handler, "getPrototypeOf"). + // 6. If trap is undefined, then (see 6.a below). + const trap: Callable = GetMethod(handler, kTrapName) + otherwise goto TrapUndefined(target); - // 7. Let handlerProto be ? Call(trap, handler, « target »). - const handlerProto = Call(context, trap, handler, target); + // 7. Let handlerProto be ? Call(trap, handler, « target »). + const handlerProto = Call(context, trap, handler, target); - // 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError - // exception. - if (!Is(handlerProto) && handlerProto != Null) { - goto ThrowProxyGetPrototypeOfInvalid; - } + // 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError + // exception. + if (!Is(handlerProto) && handlerProto != Null) { + goto ThrowProxyGetPrototypeOfInvalid; + } - // 9. Let extensibleTarget be ? IsExtensible(target). - // 10. If extensibleTarget is true, return handlerProto. - const extensibleTarget: JSAny = object::ObjectIsExtensibleImpl(target); - assert(extensibleTarget == True || extensibleTarget == False); - if (extensibleTarget == True) { - return handlerProto; - } + // 9. Let extensibleTarget be ? IsExtensible(target). + // 10. If extensibleTarget is true, return handlerProto. + const extensibleTarget: JSAny = object::ObjectIsExtensibleImpl(target); + assert(extensibleTarget == True || extensibleTarget == False); + if (extensibleTarget == True) { + return handlerProto; + } - // 11. Let targetProto be ? target.[[GetPrototypeOf]](). - const targetProto = object::ObjectGetPrototypeOfImpl(target); + // 11. Let targetProto be ? target.[[GetPrototypeOf]](). + const targetProto = object::ObjectGetPrototypeOfImpl(target); - // 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError - // exception. - // 13. Return handlerProto. - if (SameValue(targetProto, handlerProto)) { - return handlerProto; - } - ThrowTypeError(MessageTemplate::kProxyGetPrototypeOfNonExtensible); - } - label TrapUndefined(target: JSAny) { - // 6.a. Return ? target.[[GetPrototypeOf]](). - return object::ObjectGetPrototypeOfImpl(target); - } - label ThrowProxyHandlerRevoked deferred { - ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName); - } - label ThrowProxyGetPrototypeOfInvalid deferred { - ThrowTypeError(MessageTemplate::kProxyGetPrototypeOfInvalid); + // 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError + // exception. + // 13. Return handlerProto. + if (SameValue(targetProto, handlerProto)) { + return handlerProto; } + ThrowTypeError(MessageTemplate::kProxyGetPrototypeOfNonExtensible); + } label TrapUndefined(target: JSAny) { + // 6.a. Return ? target.[[GetPrototypeOf]](). + return object::ObjectGetPrototypeOfImpl(target); + } label ThrowProxyHandlerRevoked deferred { + ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName); + } label ThrowProxyGetPrototypeOfInvalid deferred { + ThrowTypeError(MessageTemplate::kProxyGetPrototypeOfInvalid); } } +} diff --git a/deps/v8/src/builtins/proxy-has-property.tq b/deps/v8/src/builtins/proxy-has-property.tq index 286314666714dd..488f6fabb31cc6 100644 --- a/deps/v8/src/builtins/proxy-has-property.tq +++ b/deps/v8/src/builtins/proxy-has-property.tq @@ -6,52 +6,50 @@ namespace proxy { - // ES #sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p - // https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p - transitioning builtin ProxyHasProperty(implicit context: Context)( - proxy: JSProxy, name: PropertyKey): JSAny { - assert(IsJSProxy(proxy)); - - PerformStackCheck(); - - // 1. Assert: IsPropertyKey(P) is true. - assert(IsName(name)); - assert(!IsPrivateSymbol(name)); - - try { - // 2. Let handler be O.[[ProxyHandler]]. - // 3. If handler is null, throw a TypeError exception. - // 4. Assert: Type(handler) is Object. - assert(proxy.handler == Null || Is(proxy.handler)); - const handler = - Cast(proxy.handler) otherwise ThrowProxyHandlerRevoked; - - // 5. Let target be O.[[ProxyTarget]]. - const target = Cast(proxy.target) otherwise unreachable; - - // 6. Let trap be ? GetMethod(handler, "has"). - // 7. If trap is undefined, then (see 7.a below). - const trap: Callable = GetMethod(handler, 'has') - otherwise goto TrapUndefined(target); - - // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « - // target»)). - // 9. If booleanTrapResult is false, then (see 9.a. in - // CheckHasTrapResult). - // 10. Return booleanTrapResult. - const trapResult = Call(context, trap, handler, target, name); - if (ToBoolean(trapResult)) { - return True; - } - CheckHasTrapResult(target, proxy, name); - return False; - } - label TrapUndefined(target: JSAny) { - // 7.a. Return ? target.[[HasProperty]](P). - tail HasProperty(target, name); - } - label ThrowProxyHandlerRevoked deferred { - ThrowTypeError(MessageTemplate::kProxyRevoked, 'has'); +// ES #sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p +// https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p +transitioning builtin ProxyHasProperty(implicit context: Context)( + proxy: JSProxy, name: PropertyKey): JSAny { + assert(IsJSProxy(proxy)); + + PerformStackCheck(); + + // 1. Assert: IsPropertyKey(P) is true. + assert(IsName(name)); + assert(!IsPrivateSymbol(name)); + + try { + // 2. Let handler be O.[[ProxyHandler]]. + // 3. If handler is null, throw a TypeError exception. + // 4. Assert: Type(handler) is Object. + assert(proxy.handler == Null || Is(proxy.handler)); + const handler = + Cast(proxy.handler) otherwise ThrowProxyHandlerRevoked; + + // 5. Let target be O.[[ProxyTarget]]. + const target = Cast(proxy.target) otherwise unreachable; + + // 6. Let trap be ? GetMethod(handler, "has"). + // 7. If trap is undefined, then (see 7.a below). + const trap: Callable = GetMethod(handler, 'has') + otherwise goto TrapUndefined(target); + + // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « + // target»)). + // 9. If booleanTrapResult is false, then (see 9.a. in + // CheckHasTrapResult). + // 10. Return booleanTrapResult. + const trapResult = Call(context, trap, handler, target, name); + if (ToBoolean(trapResult)) { + return True; } + CheckHasTrapResult(target, proxy, name); + return False; + } label TrapUndefined(target: JSAny) { + // 7.a. Return ? target.[[HasProperty]](P). + tail HasProperty(target, name); + } label ThrowProxyHandlerRevoked deferred { + ThrowTypeError(MessageTemplate::kProxyRevoked, 'has'); } } +} diff --git a/deps/v8/src/builtins/proxy-is-extensible.tq b/deps/v8/src/builtins/proxy-is-extensible.tq index 9c0d45c5294950..a7c2c56d441b33 100644 --- a/deps/v8/src/builtins/proxy-is-extensible.tq +++ b/deps/v8/src/builtins/proxy-is-extensible.tq @@ -6,52 +6,50 @@ namespace proxy { - // ES #sec-proxy-object-internal-methods-and-internal-slots-isextensible - // https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-isextensible - transitioning builtin ProxyIsExtensible(implicit context: - Context)(proxy: JSProxy): JSAny { - PerformStackCheck(); - const kTrapName: constexpr string = 'isExtensible'; - try { - // 1. Let handler be O.[[ProxyHandler]]. - // 2. If handler is null, throw a TypeError exception. - // 3. Assert: Type(handler) is Object. - assert(proxy.handler == Null || Is(proxy.handler)); - const handler = - Cast(proxy.handler) otherwise ThrowProxyHandlerRevoked; - - // 4. Let target be O.[[ProxyTarget]]. - const target = proxy.target; - - // 5. Let trap be ? GetMethod(handler, "isExtensible"). - // 6. If trap is undefined, then (see 6.a below). - const trap: Callable = GetMethod(handler, kTrapName) - otherwise goto TrapUndefined(target); - - // 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « - // target»)). - const trapResult = ToBoolean(Call(context, trap, handler, target)); - - // 8. Let targetResult be ? IsExtensible(target). - const targetResult: bool = - ToBoolean(object::ObjectIsExtensibleImpl(target)); - - // 9. If SameValue(booleanTrapResult, targetResult) is false, throw a - // TypeError exception. - if (trapResult != targetResult) { - ThrowTypeError( - MessageTemplate::kProxyIsExtensibleInconsistent, - SelectBooleanConstant(targetResult)); - } - // 10. Return booleanTrapResult. - return SelectBooleanConstant(trapResult); - } - label TrapUndefined(target: JSAny) { - // 6.a. Return ? IsExtensible(target). - return object::ObjectIsExtensibleImpl(target); - } - label ThrowProxyHandlerRevoked deferred { - ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName); +// ES #sec-proxy-object-internal-methods-and-internal-slots-isextensible +// https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-isextensible +transitioning builtin ProxyIsExtensible(implicit context: Context)( + proxy: JSProxy): JSAny { + PerformStackCheck(); + const kTrapName: constexpr string = 'isExtensible'; + try { + // 1. Let handler be O.[[ProxyHandler]]. + // 2. If handler is null, throw a TypeError exception. + // 3. Assert: Type(handler) is Object. + assert(proxy.handler == Null || Is(proxy.handler)); + const handler = + Cast(proxy.handler) otherwise ThrowProxyHandlerRevoked; + + // 4. Let target be O.[[ProxyTarget]]. + const target = proxy.target; + + // 5. Let trap be ? GetMethod(handler, "isExtensible"). + // 6. If trap is undefined, then (see 6.a below). + const trap: Callable = GetMethod(handler, kTrapName) + otherwise goto TrapUndefined(target); + + // 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « + // target»)). + const trapResult = ToBoolean(Call(context, trap, handler, target)); + + // 8. Let targetResult be ? IsExtensible(target). + const targetResult: bool = + ToBoolean(object::ObjectIsExtensibleImpl(target)); + + // 9. If SameValue(booleanTrapResult, targetResult) is false, throw a + // TypeError exception. + if (trapResult != targetResult) { + ThrowTypeError( + MessageTemplate::kProxyIsExtensibleInconsistent, + SelectBooleanConstant(targetResult)); } + // 10. Return booleanTrapResult. + return SelectBooleanConstant(trapResult); + } label TrapUndefined(target: JSAny) { + // 6.a. Return ? IsExtensible(target). + return object::ObjectIsExtensibleImpl(target); + } label ThrowProxyHandlerRevoked deferred { + ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName); } } +} diff --git a/deps/v8/src/builtins/proxy-prevent-extensions.tq b/deps/v8/src/builtins/proxy-prevent-extensions.tq index 10bd1f45f0145e..a5a3d93da4498c 100644 --- a/deps/v8/src/builtins/proxy-prevent-extensions.tq +++ b/deps/v8/src/builtins/proxy-prevent-extensions.tq @@ -6,61 +6,59 @@ namespace proxy { - // ES #sec-proxy-object-internal-methods-and-internal-slots-preventextensions - // https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-preventextensions - transitioning builtin - ProxyPreventExtensions(implicit context: - Context)(proxy: JSProxy, doThrow: Boolean): JSAny { - PerformStackCheck(); - const kTrapName: constexpr string = 'preventExtensions'; - try { - // 1. Let handler be O.[[ProxyHandler]]. - // 2. If handler is null, throw a TypeError exception. - // 3. Assert: Type(handler) is Object. - assert(proxy.handler == Null || Is(proxy.handler)); - const handler = - Cast(proxy.handler) otherwise ThrowProxyHandlerRevoked; +// ES #sec-proxy-object-internal-methods-and-internal-slots-preventextensions +// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-preventextensions +transitioning builtin +ProxyPreventExtensions(implicit context: Context)( + proxy: JSProxy, doThrow: Boolean): JSAny { + PerformStackCheck(); + const kTrapName: constexpr string = 'preventExtensions'; + try { + // 1. Let handler be O.[[ProxyHandler]]. + // 2. If handler is null, throw a TypeError exception. + // 3. Assert: Type(handler) is Object. + assert(proxy.handler == Null || Is(proxy.handler)); + const handler = + Cast(proxy.handler) otherwise ThrowProxyHandlerRevoked; - // 4. Let target be O.[[ProxyTarget]]. - const target = proxy.target; + // 4. Let target be O.[[ProxyTarget]]. + const target = proxy.target; - // 5. Let trap be ? GetMethod(handler, "preventExtensions"). - // 6. If trap is undefined, then (see 6.a below). - const trap: Callable = GetMethod(handler, kTrapName) - otherwise goto TrapUndefined(target); + // 5. Let trap be ? GetMethod(handler, "preventExtensions"). + // 6. If trap is undefined, then (see 6.a below). + const trap: Callable = GetMethod(handler, kTrapName) + otherwise goto TrapUndefined(target); - // 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « - // target»)). - const trapResult = Call(context, trap, handler, target); + // 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « + // target»)). + const trapResult = Call(context, trap, handler, target); - // 8. If booleanTrapResult is true, then - // 8.a. Let extensibleTarget be ? IsExtensible(target). - // 8.b If extensibleTarget is true, throw a TypeError exception. - if (ToBoolean(trapResult)) { - const extensibleTarget: JSAny = object::ObjectIsExtensibleImpl(target); - assert(extensibleTarget == True || extensibleTarget == False); - if (extensibleTarget == True) { - ThrowTypeError(MessageTemplate::kProxyPreventExtensionsExtensible); - } - } else { - if (doThrow == True) { - ThrowTypeError(MessageTemplate::kProxyTrapReturnedFalsish, kTrapName); - } - return False; + // 8. If booleanTrapResult is true, then + // 8.a. Let extensibleTarget be ? IsExtensible(target). + // 8.b If extensibleTarget is true, throw a TypeError exception. + if (ToBoolean(trapResult)) { + const extensibleTarget: JSAny = object::ObjectIsExtensibleImpl(target); + assert(extensibleTarget == True || extensibleTarget == False); + if (extensibleTarget == True) { + ThrowTypeError(MessageTemplate::kProxyPreventExtensionsExtensible); } - - // 9. Return booleanTrapResult. - return True; - } - label TrapUndefined(target: JSAny) { - // 6.a. Return ? target.[[PreventExtensions]](). + } else { if (doThrow == True) { - return object::ObjectPreventExtensionsThrow(target); + ThrowTypeError(MessageTemplate::kProxyTrapReturnedFalsish, kTrapName); } - return object::ObjectPreventExtensionsDontThrow(target); + return False; } - label ThrowProxyHandlerRevoked deferred { - ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName); + + // 9. Return booleanTrapResult. + return True; + } label TrapUndefined(target: JSAny) { + // 6.a. Return ? target.[[PreventExtensions]](). + if (doThrow == True) { + return object::ObjectPreventExtensionsThrow(target); } + return object::ObjectPreventExtensionsDontThrow(target); + } label ThrowProxyHandlerRevoked deferred { + ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName); } +} } // namespace proxy diff --git a/deps/v8/src/builtins/proxy-revocable.tq b/deps/v8/src/builtins/proxy-revocable.tq index 2b853afefea618..989db1c04c6129 100644 --- a/deps/v8/src/builtins/proxy-revocable.tq +++ b/deps/v8/src/builtins/proxy-revocable.tq @@ -6,48 +6,35 @@ namespace proxy { - extern macro ProxiesCodeStubAssembler::AllocateProxyRevokeFunction( - implicit context: Context)(JSProxy): JSFunction; - - // Proxy.revocable(target, handler) - // https://tc39.github.io/ecma262/#sec-proxy.revocable - transitioning javascript builtin - ProxyRevocable(js-implicit context: NativeContext)( - target: JSAny, handler: JSAny): JSProxyRevocableResult { - try { - const targetJSReceiver = - Cast(target) otherwise ThrowProxyNonObject; - if (IsRevokedProxy(targetJSReceiver)) { - goto ThrowProxyHandlerOrTargetRevoked; - } - - const handlerJSReceiver = - Cast(handler) otherwise ThrowProxyNonObject; - if (IsRevokedProxy(handlerJSReceiver)) { - goto ThrowProxyHandlerOrTargetRevoked; - } - - // 1. Let p be ? ProxyCreate(target, handler). - const proxy: JSProxy = AllocateProxy(targetJSReceiver, handlerJSReceiver); - - // 2. Let steps be the algorithm steps defined in Proxy Revocation - // Functions. - // 3. Let revoker be CreateBuiltinFunction(steps, « [[RevocableProxy]] »). - // 4. Set revoker.[[RevocableProxy]] to p. - const revoke: JSFunction = AllocateProxyRevokeFunction(proxy); - - // 5. Let result be ObjectCreate(%ObjectPrototype%). - // 6. Perform CreateDataProperty(result, "proxy", p). - // 7. Perform CreateDataProperty(result, "revoke", revoker). - // 8. Return result. - return NewJSProxyRevocableResult(proxy, revoke); - } - label ThrowProxyNonObject deferred { - ThrowTypeError(MessageTemplate::kProxyNonObject, 'Proxy.revocable'); - } - label ThrowProxyHandlerOrTargetRevoked deferred { - ThrowTypeError( - MessageTemplate::kProxyHandlerOrTargetRevoked, 'Proxy.revocable'); - } +extern macro ProxiesCodeStubAssembler::AllocateProxyRevokeFunction( + implicit context: Context)(JSProxy): JSFunction; + +// Proxy.revocable(target, handler) +// https://tc39.github.io/ecma262/#sec-proxy.revocable +transitioning javascript builtin +ProxyRevocable(js-implicit context: NativeContext)( + target: JSAny, handler: JSAny): JSProxyRevocableResult { + try { + // 1. Let p be ? ProxyCreate(target, handler). + const targetJSReceiver = + Cast(target) otherwise ThrowProxyNonObject; + const handlerJSReceiver = + Cast(handler) otherwise ThrowProxyNonObject; + const proxy: JSProxy = AllocateProxy(targetJSReceiver, handlerJSReceiver); + + // 2. Let steps be the algorithm steps defined in Proxy Revocation + // Functions. + // 3. Let revoker be CreateBuiltinFunction(steps, « [[RevocableProxy]] »). + // 4. Set revoker.[[RevocableProxy]] to p. + const revoke: JSFunction = AllocateProxyRevokeFunction(proxy); + + // 5. Let result be ObjectCreate(%ObjectPrototype%). + // 6. Perform CreateDataProperty(result, "proxy", p). + // 7. Perform CreateDataProperty(result, "revoke", revoker). + // 8. Return result. + return NewJSProxyRevocableResult(proxy, revoke); + } label ThrowProxyNonObject deferred { + ThrowTypeError(MessageTemplate::kProxyNonObject, 'Proxy.revocable'); } } +} diff --git a/deps/v8/src/builtins/proxy-revoke.tq b/deps/v8/src/builtins/proxy-revoke.tq index 7300f4d7179e70..5d2071b931bc8b 100644 --- a/deps/v8/src/builtins/proxy-revoke.tq +++ b/deps/v8/src/builtins/proxy-revoke.tq @@ -6,31 +6,31 @@ namespace proxy { - // Proxy Revocation Functions - // https://tc39.github.io/ecma262/#sec-proxy-revocation-functions - transitioning javascript builtin - ProxyRevoke(js-implicit context: NativeContext)(): Undefined { - // 1. Let p be F.[[RevocableProxy]]. - const proxyObject: Object = context[PROXY_SLOT]; - - // 2. If p is null, return undefined - if (proxyObject == Null) { - return Undefined; - } +// Proxy Revocation Functions +// https://tc39.github.io/ecma262/#sec-proxy-revocation-functions +transitioning javascript builtin +ProxyRevoke(js-implicit context: NativeContext)(): Undefined { + // 1. Let p be F.[[RevocableProxy]]. + const proxyObject: Object = context[PROXY_SLOT]; + + // 2. If p is null, return undefined + if (proxyObject == Null) { + return Undefined; + } - // 3. Set F.[[RevocableProxy]] to null. - context[PROXY_SLOT] = Null; + // 3. Set F.[[RevocableProxy]] to null. + context[PROXY_SLOT] = Null; - // 4. Assert: p is a Proxy object. - const proxy: JSProxy = UnsafeCast(proxyObject); + // 4. Assert: p is a Proxy object. + const proxy: JSProxy = UnsafeCast(proxyObject); - // 5. Set p.[[ProxyTarget]] to null. - proxy.target = Null; + // 5. Set p.[[ProxyTarget]] to null. + proxy.target = Null; - // 6. Set p.[[ProxyHandler]] to null. - proxy.handler = Null; + // 6. Set p.[[ProxyHandler]] to null. + proxy.handler = Null; - // 7. Return undefined. - return Undefined; - } + // 7. Return undefined. + return Undefined; +} } diff --git a/deps/v8/src/builtins/proxy-set-property.tq b/deps/v8/src/builtins/proxy-set-property.tq index 2d084eac7a5666..49f55fcd336527 100644 --- a/deps/v8/src/builtins/proxy-set-property.tq +++ b/deps/v8/src/builtins/proxy-set-property.tq @@ -6,84 +6,82 @@ namespace proxy { - extern transitioning runtime - SetPropertyWithReceiver(implicit context: - Context)(Object, Name, Object, Object): void; +extern transitioning runtime +SetPropertyWithReceiver(implicit context: Context)( + Object, Name, Object, Object): void; - transitioning macro CallThrowTypeErrorIfStrict(implicit context: Context)( - message: constexpr MessageTemplate) { - ThrowTypeErrorIfStrict(SmiConstant(message), Null, Null); - } +transitioning macro CallThrowTypeErrorIfStrict(implicit context: Context)( + message: constexpr MessageTemplate) { + ThrowTypeErrorIfStrict(SmiConstant(message), Null, Null); +} - // ES #sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver - // https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver - transitioning builtin - ProxySetProperty(implicit context: Context)( - proxy: JSProxy, name: PropertyKey|PrivateSymbol, value: JSAny, - receiverValue: JSAny): JSAny { - // 1. Assert: IsPropertyKey(P) is true. - assert(TaggedIsNotSmi(name)); - assert(IsName(name)); +// ES #sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver +// https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver +transitioning builtin +ProxySetProperty(implicit context: Context)( + proxy: JSProxy, name: PropertyKey|PrivateSymbol, value: JSAny, + receiverValue: JSAny): JSAny { + // 1. Assert: IsPropertyKey(P) is true. + assert(TaggedIsNotSmi(name)); + assert(IsName(name)); - let key: PropertyKey; - typeswitch (name) { - case (PrivateSymbol): { - CallThrowTypeErrorIfStrict(MessageTemplate::kProxyPrivate); - return Undefined; - } - case (name: PropertyKey): { - key = name; - } + let key: PropertyKey; + typeswitch (name) { + case (PrivateSymbol): { + CallThrowTypeErrorIfStrict(MessageTemplate::kProxyPrivate); + return Undefined; } + case (name: PropertyKey): { + key = name; + } + } - try { - // 2. Let handler be O.[[ProxyHandler]]. - // 3. If handler is null, throw a TypeError exception. - // 4. Assert: Type(handler) is Object. - assert(proxy.handler == Null || Is(proxy.handler)); - const handler = - Cast(proxy.handler) otherwise ThrowProxyHandlerRevoked; + try { + // 2. Let handler be O.[[ProxyHandler]]. + // 3. If handler is null, throw a TypeError exception. + // 4. Assert: Type(handler) is Object. + assert(proxy.handler == Null || Is(proxy.handler)); + const handler = + Cast(proxy.handler) otherwise ThrowProxyHandlerRevoked; - // 5. Let target be O.[[ProxyTarget]]. - const target = UnsafeCast(proxy.target); + // 5. Let target be O.[[ProxyTarget]]. + const target = UnsafeCast(proxy.target); - // 6. Let trap be ? GetMethod(handler, "set"). - // 7. If trap is undefined, then (see 7.a below). - const trap: Callable = GetMethod(handler, 'set') - otherwise goto TrapUndefined(target); + // 6. Let trap be ? GetMethod(handler, "set"). + // 7. If trap is undefined, then (see 7.a below). + const trap: Callable = GetMethod(handler, 'set') + otherwise goto TrapUndefined(target); - // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, - // « target, P, V, Receiver »)). - // 9. If booleanTrapResult is false, return false. - // 10. Let targetDesc be ? target.[[GetOwnProperty]](P). - // 11. If targetDesc is not undefined and targetDesc.[[Configurable]] is - // false, then - // a. If IsDataDescriptor(targetDesc) is true and - // targetDesc.[[Writable]] is false, then - // i. If SameValue(V, targetDesc.[[Value]]) is false, throw a - // TypeError exception. - // b. If IsAccessorDescriptor(targetDesc) is true, then - // i. If targetDesc.[[Set]] is undefined, throw a TypeError - // exception. - // 12. Return true. - const trapResult = - Call(context, trap, handler, target, key, value, receiverValue); - if (ToBoolean(trapResult)) { - CheckGetSetTrapResult(target, proxy, name, value, kProxySet); - return value; - } - ThrowTypeErrorIfStrict( - SmiConstant(MessageTemplate::kProxyTrapReturnedFalsishFor), 'set', - name); + // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, + // « target, P, V, Receiver »)). + // 9. If booleanTrapResult is false, return false. + // 10. Let targetDesc be ? target.[[GetOwnProperty]](P). + // 11. If targetDesc is not undefined and targetDesc.[[Configurable]] is + // false, then + // a. If IsDataDescriptor(targetDesc) is true and + // targetDesc.[[Writable]] is false, then + // i. If SameValue(V, targetDesc.[[Value]]) is false, throw a + // TypeError exception. + // b. If IsAccessorDescriptor(targetDesc) is true, then + // i. If targetDesc.[[Set]] is undefined, throw a TypeError + // exception. + // 12. Return true. + const trapResult = + Call(context, trap, handler, target, key, value, receiverValue); + if (ToBoolean(trapResult)) { + CheckGetSetTrapResult(target, proxy, name, value, kProxySet); return value; } - label TrapUndefined(target: Object) { - // 7.a. Return ? target.[[Set]](P, V, Receiver). - SetPropertyWithReceiver(target, name, value, receiverValue); - return value; - } - label ThrowProxyHandlerRevoked deferred { - ThrowTypeError(MessageTemplate::kProxyRevoked, 'set'); - } + ThrowTypeErrorIfStrict( + SmiConstant(MessageTemplate::kProxyTrapReturnedFalsishFor), 'set', + name); + return value; + } label TrapUndefined(target: Object) { + // 7.a. Return ? target.[[Set]](P, V, Receiver). + SetPropertyWithReceiver(target, name, value, receiverValue); + return value; + } label ThrowProxyHandlerRevoked deferred { + ThrowTypeError(MessageTemplate::kProxyRevoked, 'set'); } } +} diff --git a/deps/v8/src/builtins/proxy-set-prototype-of.tq b/deps/v8/src/builtins/proxy-set-prototype-of.tq index a7a76b753551b5..ec68cef44c839d 100644 --- a/deps/v8/src/builtins/proxy-set-prototype-of.tq +++ b/deps/v8/src/builtins/proxy-set-prototype-of.tq @@ -6,73 +6,71 @@ namespace proxy { - // ES #sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v - // https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v - transitioning builtin - ProxySetPrototypeOf(implicit context: Context)( - proxy: JSProxy, proto: Null|JSReceiver, doThrow: Boolean): JSAny { - PerformStackCheck(); - const kTrapName: constexpr string = 'setPrototypeOf'; - try { - // 1. Assert: Either Type(V) is Object or Type(V) is Null. - assert(proto == Null || Is(proto)); +// ES #sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v +// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v +transitioning builtin +ProxySetPrototypeOf(implicit context: Context)( + proxy: JSProxy, proto: Null|JSReceiver, doThrow: Boolean): JSAny { + PerformStackCheck(); + const kTrapName: constexpr string = 'setPrototypeOf'; + try { + // 1. Assert: Either Type(V) is Object or Type(V) is Null. + assert(proto == Null || Is(proto)); - // 2. Let handler be O.[[ProxyHandler]]. - // 3. If handler is null, throw a TypeError exception. - // 4. Assert: Type(handler) is Object. - assert(proxy.handler == Null || Is(proxy.handler)); - const handler = - Cast(proxy.handler) otherwise ThrowProxyHandlerRevoked; + // 2. Let handler be O.[[ProxyHandler]]. + // 3. If handler is null, throw a TypeError exception. + // 4. Assert: Type(handler) is Object. + assert(proxy.handler == Null || Is(proxy.handler)); + const handler = + Cast(proxy.handler) otherwise ThrowProxyHandlerRevoked; - // 5. Let target be O.[[ProxyTarget]]. - const target = proxy.target; + // 5. Let target be O.[[ProxyTarget]]. + const target = proxy.target; - // 6. Let trap be ? GetMethod(handler, "setPrototypeOf"). - // 7. If trap is undefined, then (see 7.a below). - const trap: Callable = GetMethod(handler, kTrapName) - otherwise goto TrapUndefined(target, proto); + // 6. Let trap be ? GetMethod(handler, "setPrototypeOf"). + // 7. If trap is undefined, then (see 7.a below). + const trap: Callable = GetMethod(handler, kTrapName) + otherwise goto TrapUndefined(target, proto); - // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, V - // »)). - const trapResult = Call(context, trap, handler, target, proto); + // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, V + // »)). + const trapResult = Call(context, trap, handler, target, proto); - // 9. If booleanTrapResult is false, return false. - if (!ToBoolean(trapResult)) { - if (doThrow == True) { - ThrowTypeError( - MessageTemplate::kProxyTrapReturnedFalsishFor, kTrapName); - } - return False; + // 9. If booleanTrapResult is false, return false. + if (!ToBoolean(trapResult)) { + if (doThrow == True) { + ThrowTypeError( + MessageTemplate::kProxyTrapReturnedFalsishFor, kTrapName); } + return False; + } - // 10. Let extensibleTarget be ? IsExtensible(target). - // 11. If extensibleTarget is true, return true. - const extensibleTarget: Object = object::ObjectIsExtensibleImpl(target); - assert(extensibleTarget == True || extensibleTarget == False); - if (extensibleTarget == True) { - return True; - } + // 10. Let extensibleTarget be ? IsExtensible(target). + // 11. If extensibleTarget is true, return true. + const extensibleTarget: Object = object::ObjectIsExtensibleImpl(target); + assert(extensibleTarget == True || extensibleTarget == False); + if (extensibleTarget == True) { + return True; + } - // 12. Let targetProto be ? target.[[GetPrototypeOf]](). - const targetProto = object::ObjectGetPrototypeOfImpl(target); + // 12. Let targetProto be ? target.[[GetPrototypeOf]](). + const targetProto = object::ObjectGetPrototypeOfImpl(target); - // 13. If SameValue(V, targetProto) is false, throw a TypeError - // exception. - // 14. Return true. - if (SameValue(proto, targetProto)) { - return True; - } - ThrowTypeError(MessageTemplate::kProxySetPrototypeOfNonExtensible); + // 13. If SameValue(V, targetProto) is false, throw a TypeError + // exception. + // 14. Return true. + if (SameValue(proto, targetProto)) { + return True; } - label TrapUndefined(target: JSAny, proto: JSReceiver|Null) { - // 7.a. Return ? target.[[SetPrototypeOf]](). - if (doThrow == True) { - return object::ObjectSetPrototypeOfThrow(target, proto); - } - return object::ObjectSetPrototypeOfDontThrow(target, proto); - } - label ThrowProxyHandlerRevoked deferred { - ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName); + ThrowTypeError(MessageTemplate::kProxySetPrototypeOfNonExtensible); + } label TrapUndefined(target: JSAny, proto: JSReceiver|Null) { + // 7.a. Return ? target.[[SetPrototypeOf]](). + if (doThrow == True) { + return object::ObjectSetPrototypeOfThrow(target, proto); } + return object::ObjectSetPrototypeOfDontThrow(target, proto); + } label ThrowProxyHandlerRevoked deferred { + ThrowTypeError(MessageTemplate::kProxyRevoked, kTrapName); } } +} diff --git a/deps/v8/src/builtins/proxy.tq b/deps/v8/src/builtins/proxy.tq index 2db794e8e80c83..8f662a9f4d15ac 100644 --- a/deps/v8/src/builtins/proxy.tq +++ b/deps/v8/src/builtins/proxy.tq @@ -6,27 +6,21 @@ namespace proxy { - extern macro ProxiesCodeStubAssembler::AllocateProxy( - implicit context: Context)(JSReceiver, JSReceiver): JSProxy; +extern macro ProxiesCodeStubAssembler::AllocateProxy(implicit context: Context)( + JSReceiver, JSReceiver): JSProxy; - macro IsRevokedProxy(implicit context: Context)(o: JSReceiver): bool { - const proxy: JSProxy = Cast(o) otherwise return false; - Cast(proxy.handler) otherwise return true; - return false; - } +extern transitioning macro ProxiesCodeStubAssembler::CheckGetSetTrapResult( + implicit context: Context)( + JSReceiver, JSProxy, Name, Object, constexpr int31); - extern transitioning macro ProxiesCodeStubAssembler::CheckGetSetTrapResult( - implicit context: - Context)(JSReceiver, JSProxy, Name, Object, constexpr int31); +extern transitioning macro ProxiesCodeStubAssembler::CheckDeleteTrapResult( + implicit context: Context)(JSReceiver, JSProxy, Name); - extern transitioning macro ProxiesCodeStubAssembler::CheckDeleteTrapResult( - implicit context: Context)(JSReceiver, JSProxy, Name); +extern transitioning macro ProxiesCodeStubAssembler::CheckHasTrapResult( + implicit context: Context)(JSReceiver, JSProxy, Name); - extern transitioning macro ProxiesCodeStubAssembler::CheckHasTrapResult( - implicit context: Context)(JSReceiver, JSProxy, Name); - - const kProxyGet: constexpr int31 - generates 'JSProxy::AccessKind::kGet'; - const kProxySet: constexpr int31 - generates 'JSProxy::AccessKind::kSet'; +const kProxyGet: constexpr int31 + generates 'JSProxy::AccessKind::kGet'; +const kProxySet: constexpr int31 + generates 'JSProxy::AccessKind::kSet'; } diff --git a/deps/v8/src/builtins/reflect.tq b/deps/v8/src/builtins/reflect.tq index f1818ed32d7310..477c586403c022 100644 --- a/deps/v8/src/builtins/reflect.tq +++ b/deps/v8/src/builtins/reflect.tq @@ -3,92 +3,88 @@ // found in the LICENSE file. namespace reflect { - // ES6 section 26.1.10 Reflect.isExtensible - transitioning javascript builtin - ReflectIsExtensible(js-implicit context: NativeContext)(object: JSAny): - JSAny { - const objectJSReceiver = Cast(object) - otherwise ThrowTypeError( - MessageTemplate::kCalledOnNonObject, 'Reflect.isExtensible'); - return object::ObjectIsExtensibleImpl(objectJSReceiver); - } +// ES6 section 26.1.10 Reflect.isExtensible +transitioning javascript builtin +ReflectIsExtensible(js-implicit context: NativeContext)(object: JSAny): JSAny { + const objectJSReceiver = Cast(object) + otherwise ThrowTypeError( + MessageTemplate::kCalledOnNonObject, 'Reflect.isExtensible'); + return object::ObjectIsExtensibleImpl(objectJSReceiver); +} - // ES6 section 26.1.12 Reflect.preventExtensions - transitioning javascript builtin - ReflectPreventExtensions(js-implicit context: NativeContext)(object: JSAny): - JSAny { - const objectJSReceiver = Cast(object) - otherwise ThrowTypeError( - MessageTemplate::kCalledOnNonObject, 'Reflect.preventExtensions'); - return object::ObjectPreventExtensionsDontThrow(objectJSReceiver); - } +// ES6 section 26.1.12 Reflect.preventExtensions +transitioning javascript builtin +ReflectPreventExtensions(js-implicit context: NativeContext)(object: JSAny): + JSAny { + const objectJSReceiver = Cast(object) + otherwise ThrowTypeError( + MessageTemplate::kCalledOnNonObject, 'Reflect.preventExtensions'); + return object::ObjectPreventExtensionsDontThrow(objectJSReceiver); +} - // ES6 section 26.1.8 Reflect.getPrototypeOf - transitioning javascript builtin - ReflectGetPrototypeOf(js-implicit context: NativeContext)(object: JSAny): - JSAny { - const objectJSReceiver = Cast(object) - otherwise ThrowTypeError( - MessageTemplate::kCalledOnNonObject, 'Reflect.getPrototypeOf'); - return object::JSReceiverGetPrototypeOf(objectJSReceiver); - } +// ES6 section 26.1.8 Reflect.getPrototypeOf +transitioning javascript builtin +ReflectGetPrototypeOf(js-implicit context: NativeContext)(object: JSAny): + JSAny { + const objectJSReceiver = Cast(object) + otherwise ThrowTypeError( + MessageTemplate::kCalledOnNonObject, 'Reflect.getPrototypeOf'); + return object::JSReceiverGetPrototypeOf(objectJSReceiver); +} - // ES6 section 26.1.14 Reflect.setPrototypeOf - transitioning javascript builtin ReflectSetPrototypeOf( - js-implicit context: NativeContext)(object: JSAny, proto: JSAny): JSAny { - const objectJSReceiver = Cast(object) - otherwise ThrowTypeError( - MessageTemplate::kCalledOnNonObject, 'Reflect.setPrototypeOf'); - typeswitch (proto) { - case (proto: JSReceiver|Null): { - return object::ObjectSetPrototypeOfDontThrow(objectJSReceiver, proto); - } - case (JSAny): { - ThrowTypeError(MessageTemplate::kProtoObjectOrNull, proto); - } +// ES6 section 26.1.14 Reflect.setPrototypeOf +transitioning javascript builtin ReflectSetPrototypeOf( + js-implicit context: NativeContext)(object: JSAny, proto: JSAny): JSAny { + const objectJSReceiver = Cast(object) + otherwise ThrowTypeError( + MessageTemplate::kCalledOnNonObject, 'Reflect.setPrototypeOf'); + typeswitch (proto) { + case (proto: JSReceiver|Null): { + return object::ObjectSetPrototypeOfDontThrow(objectJSReceiver, proto); + } + case (JSAny): { + ThrowTypeError(MessageTemplate::kProtoObjectOrNull, proto); } } +} - extern transitioning builtin ToName(implicit context: Context)(JSAny): - AnyName; - type OnNonExistent constexpr 'OnNonExistent'; - const kReturnUndefined: constexpr OnNonExistent - generates 'OnNonExistent::kReturnUndefined'; - extern macro SmiConstant(constexpr OnNonExistent): Smi; - extern transitioning builtin GetPropertyWithReceiver( - implicit context: Context)(JSAny, Name, JSAny, Smi): JSAny; +extern transitioning builtin ToName(implicit context: Context)(JSAny): AnyName; +type OnNonExistent constexpr 'OnNonExistent'; +const kReturnUndefined: constexpr OnNonExistent + generates 'OnNonExistent::kReturnUndefined'; +extern macro SmiConstant(constexpr OnNonExistent): Smi; +extern transitioning builtin GetPropertyWithReceiver(implicit context: Context)( + JSAny, Name, JSAny, Smi): JSAny; - // ES6 section 26.1.6 Reflect.get - transitioning javascript builtin - ReflectGet(js-implicit context: NativeContext)(...arguments): JSAny { - const length = arguments.length; - const object: JSAny = length > 0 ? arguments[0] : Undefined; - const objectJSReceiver = Cast(object) - otherwise ThrowTypeError( - MessageTemplate::kCalledOnNonObject, 'Reflect.get'); - const propertyKey: JSAny = length > 1 ? arguments[1] : Undefined; - const name: AnyName = ToName(propertyKey); - const receiver: JSAny = length > 2 ? arguments[2] : objectJSReceiver; - return GetPropertyWithReceiver( - objectJSReceiver, name, receiver, SmiConstant(kReturnUndefined)); - } +// ES6 section 26.1.6 Reflect.get +transitioning javascript builtin +ReflectGet(js-implicit context: NativeContext)(...arguments): JSAny { + const object: JSAny = arguments[0]; + const objectJSReceiver = Cast(object) + otherwise ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'Reflect.get'); + const propertyKey: JSAny = arguments[1]; + const name: AnyName = ToName(propertyKey); + const receiver: JSAny = + arguments.length > 2 ? arguments[2] : objectJSReceiver; + return GetPropertyWithReceiver( + objectJSReceiver, name, receiver, SmiConstant(kReturnUndefined)); +} - // ES6 section 26.1.4 Reflect.deleteProperty - transitioning javascript builtin ReflectDeleteProperty( - js-implicit context: NativeContext)(object: JSAny, key: JSAny): JSAny { - const objectJSReceiver = Cast(object) - otherwise ThrowTypeError( - MessageTemplate::kCalledOnNonObject, 'Reflect.deleteProperty'); - return DeleteProperty(objectJSReceiver, key, LanguageMode::kSloppy); - } +// ES6 section 26.1.4 Reflect.deleteProperty +transitioning javascript builtin ReflectDeleteProperty( + js-implicit context: NativeContext)(object: JSAny, key: JSAny): JSAny { + const objectJSReceiver = Cast(object) + otherwise ThrowTypeError( + MessageTemplate::kCalledOnNonObject, 'Reflect.deleteProperty'); + return DeleteProperty(objectJSReceiver, key, LanguageMode::kSloppy); +} - // ES section #sec-reflect.has - transitioning javascript builtin - ReflectHas(js-implicit context: NativeContext)(object: JSAny, key: JSAny): - JSAny { - const objectJSReceiver = Cast(object) - otherwise ThrowTypeError( - MessageTemplate::kCalledOnNonObject, 'Reflect.has'); - return HasProperty(objectJSReceiver, key); - } +// ES section #sec-reflect.has +transitioning javascript builtin +ReflectHas(js-implicit context: NativeContext)( + object: JSAny, key: JSAny): JSAny { + const objectJSReceiver = Cast(object) + otherwise ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'Reflect.has'); + return HasProperty(objectJSReceiver, key); +} } // namespace reflect diff --git a/deps/v8/src/builtins/regexp-exec.tq b/deps/v8/src/builtins/regexp-exec.tq index 0b11c42fbf7f01..87b00c1fdcdfc7 100644 --- a/deps/v8/src/builtins/regexp-exec.tq +++ b/deps/v8/src/builtins/regexp-exec.tq @@ -6,40 +6,39 @@ namespace regexp { - @export - transitioning macro RegExpPrototypeExecBodyFast(implicit context: Context)( - receiver: JSReceiver, string: String): JSAny { - return RegExpPrototypeExecBody(receiver, string, true); - } - - transitioning macro RegExpPrototypeExecBodySlow(implicit context: Context)( - receiver: JSReceiver, string: String): JSAny { - return RegExpPrototypeExecBody(receiver, string, false); - } - - // Slow path stub for RegExpPrototypeExec to decrease code size. - transitioning builtin - RegExpPrototypeExecSlow(implicit context: Context)( - regexp: JSRegExp, string: String): JSAny { - return RegExpPrototypeExecBodySlow(regexp, string); - } - - extern macro RegExpBuiltinsAssembler::IsFastRegExpNoPrototype( - implicit context: Context)(Object): bool; - - // ES#sec-regexp.prototype.exec - // RegExp.prototype.exec ( string ) - transitioning javascript builtin RegExpPrototypeExec( - js-implicit context: NativeContext, - receiver: JSAny)(string: JSAny): JSAny { - // Ensure {receiver} is a JSRegExp. - const receiver = Cast(receiver) otherwise ThrowTypeError( - MessageTemplate::kIncompatibleMethodReceiver, 'RegExp.prototype.exec', - receiver); - const string = ToString_Inline(string); - - return IsFastRegExpNoPrototype(receiver) ? - RegExpPrototypeExecBodyFast(receiver, string) : - RegExpPrototypeExecSlow(receiver, string); - } +@export +transitioning macro RegExpPrototypeExecBodyFast(implicit context: Context)( + receiver: JSReceiver, string: String): JSAny { + return RegExpPrototypeExecBody(receiver, string, true); +} + +transitioning macro RegExpPrototypeExecBodySlow(implicit context: Context)( + receiver: JSReceiver, string: String): JSAny { + return RegExpPrototypeExecBody(receiver, string, false); +} + +// Slow path stub for RegExpPrototypeExec to decrease code size. +transitioning builtin +RegExpPrototypeExecSlow(implicit context: Context)( + regexp: JSRegExp, string: String): JSAny { + return RegExpPrototypeExecBodySlow(regexp, string); +} + +extern macro RegExpBuiltinsAssembler::IsFastRegExpNoPrototype( + implicit context: Context)(Object): bool; + +// ES#sec-regexp.prototype.exec +// RegExp.prototype.exec ( string ) +transitioning javascript builtin RegExpPrototypeExec( + js-implicit context: NativeContext, receiver: JSAny)(string: JSAny): JSAny { + // Ensure {receiver} is a JSRegExp. + const receiver = Cast(receiver) otherwise ThrowTypeError( + MessageTemplate::kIncompatibleMethodReceiver, 'RegExp.prototype.exec', + receiver); + const string = ToString_Inline(string); + + return IsFastRegExpNoPrototype(receiver) ? + RegExpPrototypeExecBodyFast(receiver, string) : + RegExpPrototypeExecSlow(receiver, string); +} } diff --git a/deps/v8/src/builtins/regexp-match-all.tq b/deps/v8/src/builtins/regexp-match-all.tq index 022f8bc53f101d..932972d8440632 100644 --- a/deps/v8/src/builtins/regexp-match-all.tq +++ b/deps/v8/src/builtins/regexp-match-all.tq @@ -6,247 +6,217 @@ namespace regexp { - extern transitioning macro - RegExpMatchAllAssembler::CreateRegExpStringIterator( - NativeContext, Object, String, bool, bool): JSAny; - - @export - transitioning macro RegExpPrototypeMatchAllImpl(implicit context: Context)( - nativeContext: NativeContext, receiver: JSAny, string: JSAny): JSAny { - // 1. Let R be the this value. - // 2. If Type(R) is not Object, throw a TypeError exception. - ThrowIfNotJSReceiver( - receiver, MessageTemplate::kIncompatibleMethodReceiver, - 'RegExp.prototype.@@matchAll'); - const receiver = UnsafeCast(receiver); - - // 3. Let S be ? ToString(O). - const string: String = ToString_Inline(string); - - let matcher: Object; - let global: bool; - let unicode: bool; - - // 'FastJSRegExp' uses the strict fast path check because following code - // uses the flags property. - // TODO(jgruber): Handle slow flag accesses on the fast path and make this - // permissive. - typeswitch (receiver) { - case (fastRegExp: FastJSRegExp): { - const source = fastRegExp.source; - - // 4. Let C be ? SpeciesConstructor(R, %RegExp%). - // 5. Let flags be ? ToString(? Get(R, "flags")). - // 6. Let matcher be ? Construct(C, « R, flags »). - const flags: String = FastFlagsGetter(fastRegExp); - matcher = RegExpCreate(nativeContext, source, flags); - const matcherRegExp = UnsafeCast(matcher); - assert(IsFastRegExpPermissive(matcherRegExp)); - - // 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")). - // 8. Perform ? Set(matcher, "lastIndex", lastIndex, true). - const fastRegExp = UnsafeCast(receiver); - FastStoreLastIndex(matcherRegExp, fastRegExp.lastIndex); - - // 9. If flags contains "g", let global be true. - // 10. Else, let global be false. - global = FastFlagGetter(matcherRegExp, Flag::kGlobal); - - // 11. If flags contains "u", let fullUnicode be true. - // 12. Else, let fullUnicode be false. - unicode = FastFlagGetter(matcherRegExp, Flag::kUnicode); - } - case (Object): { - // 4. Let C be ? SpeciesConstructor(R, %RegExp%). - const regexpFun = LoadRegExpFunction(nativeContext); - const speciesConstructor = - UnsafeCast(SpeciesConstructor(receiver, regexpFun)); - - // 5. Let flags be ? ToString(? Get(R, "flags")). - const flags = GetProperty(receiver, 'flags'); - const flagsString = ToString_Inline(flags); - - // 6. Let matcher be ? Construct(C, « R, flags »). - matcher = Construct(speciesConstructor, receiver, flagsString); - - // 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")). - const lastIndex: Number = ToLength_Inline(SlowLoadLastIndex(receiver)); - - // 8. Perform ? Set(matcher, "lastIndex", lastIndex, true). - SlowStoreLastIndex(UnsafeCast(matcher), lastIndex); - - // 9. If flags contains "g", let global be true. - // 10. Else, let global be false. - const globalCharString: String = StringConstant('g'); - const globalIndex: Smi = - StringIndexOf(flagsString, globalCharString, 0); - global = globalIndex != -1; - - // 11. If flags contains "u", let fullUnicode be true. - // 12. Else, let fullUnicode be false. - const unicodeCharString = StringConstant('u'); - const unicodeIndex: Smi = - StringIndexOf(flagsString, unicodeCharString, 0); - unicode = unicodeIndex != -1; - } +extern transitioning macro +RegExpMatchAllAssembler::CreateRegExpStringIterator( + NativeContext, Object, String, bool, bool): JSAny; + +@export +transitioning macro RegExpPrototypeMatchAllImpl(implicit context: Context)( + nativeContext: NativeContext, receiver: JSAny, string: JSAny): JSAny { + // 1. Let R be the this value. + // 2. If Type(R) is not Object, throw a TypeError exception. + ThrowIfNotJSReceiver( + receiver, MessageTemplate::kIncompatibleMethodReceiver, + 'RegExp.prototype.@@matchAll'); + const receiver = UnsafeCast(receiver); + + // 3. Let S be ? ToString(O). + const string: String = ToString_Inline(string); + + let matcher: Object; + let global: bool; + let unicode: bool; + + // 'FastJSRegExp' uses the strict fast path check because following code + // uses the flags property. + // TODO(jgruber): Handle slow flag accesses on the fast path and make this + // permissive. + typeswitch (receiver) { + case (fastRegExp: FastJSRegExp): { + const source = fastRegExp.source; + + // 4. Let C be ? SpeciesConstructor(R, %RegExp%). + // 5. Let flags be ? ToString(? Get(R, "flags")). + // 6. Let matcher be ? Construct(C, « R, flags »). + const flags: String = FastFlagsGetter(fastRegExp); + matcher = RegExpCreate(nativeContext, source, flags); + const matcherRegExp = UnsafeCast(matcher); + assert(IsFastRegExpPermissive(matcherRegExp)); + + // 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")). + // 8. Perform ? Set(matcher, "lastIndex", lastIndex, true). + const fastRegExp = UnsafeCast(receiver); + FastStoreLastIndex(matcherRegExp, fastRegExp.lastIndex); + + // 9. If flags contains "g", let global be true. + // 10. Else, let global be false. + global = FastFlagGetter(matcherRegExp, Flag::kGlobal); + + // 11. If flags contains "u", let fullUnicode be true. + // 12. Else, let fullUnicode be false. + unicode = FastFlagGetter(matcherRegExp, Flag::kUnicode); + } + case (Object): { + // 4. Let C be ? SpeciesConstructor(R, %RegExp%). + const regexpFun = LoadRegExpFunction(nativeContext); + const speciesConstructor = + UnsafeCast(SpeciesConstructor(receiver, regexpFun)); + + // 5. Let flags be ? ToString(? Get(R, "flags")). + const flags = GetProperty(receiver, 'flags'); + const flagsString = ToString_Inline(flags); + + // 6. Let matcher be ? Construct(C, « R, flags »). + matcher = Construct(speciesConstructor, receiver, flagsString); + + // 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")). + const lastIndex: Number = ToLength_Inline(SlowLoadLastIndex(receiver)); + + // 8. Perform ? Set(matcher, "lastIndex", lastIndex, true). + SlowStoreLastIndex(UnsafeCast(matcher), lastIndex); + + // 9. If flags contains "g", let global be true. + // 10. Else, let global be false. + const globalCharString: String = StringConstant('g'); + const globalIndex: Smi = StringIndexOf(flagsString, globalCharString, 0); + global = globalIndex != -1; + + // 11. If flags contains "u", let fullUnicode be true. + // 12. Else, let fullUnicode be false. + const unicodeCharString = StringConstant('u'); + const unicodeIndex: Smi = + StringIndexOf(flagsString, unicodeCharString, 0); + unicode = unicodeIndex != -1; } - - // 13. Return ! CreateRegExpStringIterator(matcher, S, global, fullUnicode). - return CreateRegExpStringIterator( - nativeContext, matcher, string, global, unicode); - } - - // https://tc39.github.io/proposal-string-matchall/ - // RegExp.prototype [ @@matchAll ] ( string ) - transitioning javascript builtin RegExpPrototypeMatchAll( - js-implicit context: NativeContext, - receiver: JSAny)(string: JSAny): JSAny { - return RegExpPrototypeMatchAllImpl(context, receiver, string); - } - - const kJSRegExpStringIteratorDone: - constexpr int31 generates '1 << JSRegExpStringIterator::kDoneBit'; - const kJSRegExpStringIteratorGlobal: constexpr int31 - generates '1 << JSRegExpStringIterator::kGlobalBit'; - const kJSRegExpStringIteratorUnicode: constexpr int31 - generates '1 << JSRegExpStringIterator::kUnicodeBit'; - - extern macro IsSetSmi(Smi, constexpr int31): bool; - - macro HasDoneFlag(flags: Smi): bool { - return IsSetSmi(flags, kJSRegExpStringIteratorDone); - } - - macro HasGlobalFlag(flags: Smi): bool { - return IsSetSmi(flags, kJSRegExpStringIteratorGlobal); - } - - macro HasUnicodeFlag(flags: Smi): bool { - return IsSetSmi(flags, kJSRegExpStringIteratorUnicode); } - macro SetDoneFlag(iterator: JSRegExpStringIterator, flags: Smi) { - const newFlags: Smi = flags | kJSRegExpStringIteratorDone; - iterator.flags = newFlags; - } + // 13. Return ! CreateRegExpStringIterator(matcher, S, global, fullUnicode). + return CreateRegExpStringIterator( + nativeContext, matcher, string, global, unicode); +} - // https://tc39.github.io/proposal-string-matchall/ - // %RegExpStringIteratorPrototype%.next ( ) - transitioning javascript builtin RegExpStringIteratorPrototypeNext( - js-implicit context: NativeContext, receiver: JSAny)(): JSAny { - // 1. Let O be the this value. - // 2. If Type(O) is not Object, throw a TypeError exception. - // 3. If O does not have all of the internal slots of a RegExp String - // Iterator Object Instance (see 5.3), throw a TypeError exception. - const methodName: constexpr string = - '%RegExpStringIterator%.prototype.next'; - const receiver = Cast(receiver) otherwise - ThrowTypeError( - MessageTemplate::kIncompatibleMethodReceiver, methodName, receiver); +// https://tc39.github.io/proposal-string-matchall/ +// RegExp.prototype [ @@matchAll ] ( string ) +transitioning javascript builtin RegExpPrototypeMatchAll( + js-implicit context: NativeContext, receiver: JSAny)(string: JSAny): JSAny { + return RegExpPrototypeMatchAllImpl(context, receiver, string); +} +// https://tc39.github.io/proposal-string-matchall/ +// %RegExpStringIteratorPrototype%.next ( ) +transitioning javascript builtin RegExpStringIteratorPrototypeNext( + js-implicit context: NativeContext, receiver: JSAny)(): JSAny { + // 1. Let O be the this value. + // 2. If Type(O) is not Object, throw a TypeError exception. + // 3. If O does not have all of the internal slots of a RegExp String + // Iterator Object Instance (see 5.3), throw a TypeError exception. + const methodName: constexpr string = '%RegExpStringIterator%.prototype.next'; + const receiver = Cast(receiver) otherwise + ThrowTypeError( + MessageTemplate::kIncompatibleMethodReceiver, methodName, receiver); + + try { + // 4. If O.[[Done]] is true, then + // a. Return ! CreateIterResultObject(undefined, true). + const flags: SmiTagged = receiver.flags; + if (flags.done) goto ReturnEmptyDoneResult; + + // 5. Let R be O.[[iteratingRegExp]]. + const iteratingRegExp: JSReceiver = receiver.iterating_reg_exp; + + // 6. Let S be O.[[IteratedString]]. + const iteratingString: String = receiver.iterated_string; + + // 7. Let global be O.[[Global]]. + // 8. Let fullUnicode be O.[[Unicode]]. + // 9. Let match be ? RegExpExec(R, S). + let match: Object; + let isFastRegExp: bool = false; try { - // 4. If O.[[Done]] is true, then - // a. Return ! CreateIterResultObject(undefined, true). - const flags: Smi = receiver.flags; - if (HasDoneFlag(flags)) goto ReturnEmptyDoneResult; - - // 5. Let R be O.[[iteratingRegExp]]. - const iteratingRegExp: JSReceiver = receiver.iterating_reg_exp; - - // 6. Let S be O.[[IteratedString]]. - const iteratingString: String = receiver.iterated_string; - - // 7. Let global be O.[[Global]]. - // 8. Let fullUnicode be O.[[Unicode]]. - // 9. Let match be ? RegExpExec(R, S). - let match: Object; - let isFastRegExp: bool = false; - try { - if (IsFastRegExpPermissive(iteratingRegExp)) { - const regexp = UnsafeCast(iteratingRegExp); - const lastIndex = LoadLastIndexAsLength(regexp, true); - const matchIndices: RegExpMatchInfo = - RegExpPrototypeExecBodyWithoutResultFast( - regexp, iteratingString, lastIndex) - otherwise IfNoMatch; - match = ConstructNewResultFromMatchInfo( - regexp, matchIndices, iteratingString, lastIndex); - isFastRegExp = true; - } else { - match = RegExpExec(iteratingRegExp, iteratingString); - if (match == Null) { - goto IfNoMatch; - } + if (IsFastRegExpPermissive(iteratingRegExp)) { + const regexp = UnsafeCast(iteratingRegExp); + const lastIndex = LoadLastIndexAsLength(regexp, true); + const matchIndices: RegExpMatchInfo = + RegExpPrototypeExecBodyWithoutResultFast( + regexp, iteratingString, lastIndex) + otherwise IfNoMatch; + match = ConstructNewResultFromMatchInfo( + regexp, matchIndices, iteratingString, lastIndex); + isFastRegExp = true; + } else { + match = RegExpExec(iteratingRegExp, iteratingString); + if (match == Null) { + goto IfNoMatch; } - // 11. Else, - // b. Else, handle non-global case first. - if (!HasGlobalFlag(flags)) { - // i. Set O.[[Done]] to true. - SetDoneFlag(receiver, flags); - - // ii. Return ! CreateIterResultObject(match, false). - return AllocateJSIteratorResult(UnsafeCast(match), False); - } - // a. If global is true, - assert(HasGlobalFlag(flags)); - if (isFastRegExp) { - // i. Let matchStr be ? ToString(? Get(match, "0")). - const match = UnsafeCast(match); - const resultFixedArray = UnsafeCast(match.elements); - const matchStr = UnsafeCast(resultFixedArray.objects[0]); - - // When iterating_regexp is fast, we assume it stays fast even after - // accessing the first match from the RegExp result. - assert(IsFastRegExpPermissive(iteratingRegExp)); - const iteratingRegExp = UnsafeCast(iteratingRegExp); - if (matchStr == kEmptyString) { - // 1. Let thisIndex be ? ToLength(? Get(R, "lastIndex")). - const thisIndex: Smi = FastLoadLastIndex(iteratingRegExp); - - // 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex, - // fullUnicode). - const nextIndex: Smi = AdvanceStringIndexFast( - iteratingString, thisIndex, HasUnicodeFlag(flags)); - - // 3. Perform ? Set(R, "lastIndex", nextIndex, true). - FastStoreLastIndex(iteratingRegExp, nextIndex); - } - - // iii. Return ! CreateIterResultObject(match, false). - return AllocateJSIteratorResult(match, False); - } - assert(!isFastRegExp); + } + // 11. Else, + // b. Else, handle non-global case first. + if (!flags.global) { + // i. Set O.[[Done]] to true. + receiver.flags.done = true; + + // ii. Return ! CreateIterResultObject(match, false). + return AllocateJSIteratorResult(UnsafeCast(match), False); + } + // a. If global is true, + assert(flags.global); + if (isFastRegExp) { // i. Let matchStr be ? ToString(? Get(match, "0")). - const match = UnsafeCast(match); - const matchStr = ToString_Inline(GetProperty(match, SmiConstant(0))); - + const match = UnsafeCast(match); + const resultFixedArray = UnsafeCast(match.elements); + const matchStr = UnsafeCast(resultFixedArray.objects[0]); + + // When iterating_regexp is fast, we assume it stays fast even after + // accessing the first match from the RegExp result. + assert(IsFastRegExpPermissive(iteratingRegExp)); + const iteratingRegExp = UnsafeCast(iteratingRegExp); if (matchStr == kEmptyString) { // 1. Let thisIndex be ? ToLength(? Get(R, "lastIndex")). - const lastIndex: JSAny = SlowLoadLastIndex(iteratingRegExp); - const thisIndex: Number = ToLength_Inline(lastIndex); + const thisIndex: Smi = FastLoadLastIndex(iteratingRegExp); // 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex, // fullUnicode). - const nextIndex: Number = AdvanceStringIndexSlow( - iteratingString, thisIndex, HasUnicodeFlag(flags)); + const nextIndex: Smi = + AdvanceStringIndexFast(iteratingString, thisIndex, flags.unicode); // 3. Perform ? Set(R, "lastIndex", nextIndex, true). - SlowStoreLastIndex(iteratingRegExp, nextIndex); + FastStoreLastIndex(iteratingRegExp, nextIndex); } + // iii. Return ! CreateIterResultObject(match, false). return AllocateJSIteratorResult(match, False); } - // 10. If match is null, then - label IfNoMatch { - // a. Set O.[[Done]] to true. - SetDoneFlag(receiver, flags); - - // b. Return ! CreateIterResultObject(undefined, true). - goto ReturnEmptyDoneResult; + assert(!isFastRegExp); + // i. Let matchStr be ? ToString(? Get(match, "0")). + const match = UnsafeCast(match); + const matchStr = ToString_Inline(GetProperty(match, SmiConstant(0))); + + if (matchStr == kEmptyString) { + // 1. Let thisIndex be ? ToLength(? Get(R, "lastIndex")). + const lastIndex: JSAny = SlowLoadLastIndex(iteratingRegExp); + const thisIndex: Number = ToLength_Inline(lastIndex); + + // 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex, + // fullUnicode). + const nextIndex: Number = + AdvanceStringIndexSlow(iteratingString, thisIndex, flags.unicode); + + // 3. Perform ? Set(R, "lastIndex", nextIndex, true). + SlowStoreLastIndex(iteratingRegExp, nextIndex); } + // iii. Return ! CreateIterResultObject(match, false). + return AllocateJSIteratorResult(match, False); } - label ReturnEmptyDoneResult { - return AllocateJSIteratorResult(Undefined, True); + // 10. If match is null, then + label IfNoMatch { + // a. Set O.[[Done]] to true. + receiver.flags.done = true; + + // b. Return ! CreateIterResultObject(undefined, true). + goto ReturnEmptyDoneResult; } + } label ReturnEmptyDoneResult { + return AllocateJSIteratorResult(Undefined, True); } } +} diff --git a/deps/v8/src/builtins/regexp-match.tq b/deps/v8/src/builtins/regexp-match.tq index 9b0255762c0f8a..d5581e050936f9 100644 --- a/deps/v8/src/builtins/regexp-match.tq +++ b/deps/v8/src/builtins/regexp-match.tq @@ -6,160 +6,157 @@ namespace regexp { - const kATOM: constexpr int31 - generates 'JSRegExp::ATOM'; - const kTagIndex: constexpr int31 - generates 'JSRegExp::kTagIndex'; - const kAtomPatternIndex: constexpr int31 - generates 'JSRegExp::kAtomPatternIndex'; - - extern transitioning macro RegExpBuiltinsAssembler::FlagGetter( - implicit context: Context)(Object, constexpr Flag, constexpr bool): bool; - - extern macro UnsafeLoadFixedArrayElement(RegExpMatchInfo, constexpr int31): - Object; - - transitioning macro RegExpPrototypeMatchBody(implicit context: Context)( - regexp: JSReceiver, string: String, isFastPath: constexpr bool): JSAny { - if constexpr (isFastPath) { - assert(Is(regexp)); - } +const kATOM: constexpr int31 + generates 'JSRegExp::ATOM'; +const kTagIndex: constexpr int31 + generates 'JSRegExp::kTagIndex'; +const kAtomPatternIndex: constexpr int31 + generates 'JSRegExp::kAtomPatternIndex'; + +extern transitioning macro RegExpBuiltinsAssembler::FlagGetter( + implicit context: Context)(Object, constexpr Flag, constexpr bool): bool; + +extern macro UnsafeLoadFixedArrayElement( + RegExpMatchInfo, constexpr int31): Object; + +transitioning macro RegExpPrototypeMatchBody(implicit context: Context)( + regexp: JSReceiver, string: String, isFastPath: constexpr bool): JSAny { + if constexpr (isFastPath) { + assert(Is(regexp)); + } - const isGlobal: bool = FlagGetter(regexp, Flag::kGlobal, isFastPath); + const isGlobal: bool = FlagGetter(regexp, Flag::kGlobal, isFastPath); - if (!isGlobal) { - return isFastPath ? RegExpPrototypeExecBodyFast(regexp, string) : - RegExpExec(regexp, string); - } + if (!isGlobal) { + return isFastPath ? RegExpPrototypeExecBodyFast(regexp, string) : + RegExpExec(regexp, string); + } - assert(isGlobal); - const isUnicode: bool = FlagGetter(regexp, Flag::kUnicode, isFastPath); + assert(isGlobal); + const isUnicode: bool = FlagGetter(regexp, Flag::kUnicode, isFastPath); - StoreLastIndex(regexp, 0, isFastPath); + StoreLastIndex(regexp, 0, isFastPath); - // Allocate an array to store the resulting match strings. + // Allocate an array to store the resulting match strings. - let array = growable_fixed_array::NewGrowableFixedArray(); + let array = growable_fixed_array::NewGrowableFixedArray(); - // Check if the regexp is an ATOM type. If so, then keep the literal string - // to search for so that we can avoid calling substring in the loop below. - let atom: bool = false; - let searchString: String = EmptyStringConstant(); - if constexpr (isFastPath) { - const maybeAtomRegexp = UnsafeCast(regexp); - const data = UnsafeCast(maybeAtomRegexp.data); - if (UnsafeCast(data.objects[kTagIndex]) == kATOM) { - searchString = UnsafeCast(data.objects[kAtomPatternIndex]); - atom = true; - } + // Check if the regexp is an ATOM type. If so, then keep the literal string + // to search for so that we can avoid calling substring in the loop below. + let atom: bool = false; + let searchString: String = EmptyStringConstant(); + if constexpr (isFastPath) { + const maybeAtomRegexp = UnsafeCast(regexp); + const data = UnsafeCast(maybeAtomRegexp.data); + if (UnsafeCast(data.objects[kTagIndex]) == kATOM) { + searchString = UnsafeCast(data.objects[kAtomPatternIndex]); + atom = true; } + } - while (true) { - let match: String = EmptyStringConstant(); - try { - if constexpr (isFastPath) { - // On the fast path, grab the matching string from the raw match index - // array. - const matchIndices: RegExpMatchInfo = - RegExpPrototypeExecBodyWithoutResultFast( - UnsafeCast(regexp), string) otherwise IfDidNotMatch; - if (atom) { - match = searchString; - } else { - const matchFrom = UnsafeLoadFixedArrayElement( - matchIndices, kRegExpMatchInfoFirstCaptureIndex); - const matchTo = UnsafeLoadFixedArrayElement( - matchIndices, kRegExpMatchInfoFirstCaptureIndex + 1); - match = SubString( - string, UnsafeCast(matchFrom), UnsafeCast(matchTo)); - } + while (true) { + let match: String = EmptyStringConstant(); + try { + if constexpr (isFastPath) { + // On the fast path, grab the matching string from the raw match index + // array. + const matchIndices: RegExpMatchInfo = + RegExpPrototypeExecBodyWithoutResultFast( + UnsafeCast(regexp), string) otherwise IfDidNotMatch; + if (atom) { + match = searchString; } else { - assert(!isFastPath); - const resultTemp = RegExpExec(regexp, string); - if (resultTemp == Null) { - goto IfDidNotMatch; - } - match = ToString_Inline(GetProperty(resultTemp, SmiConstant(0))); + const matchFrom = UnsafeLoadFixedArrayElement( + matchIndices, kRegExpMatchInfoFirstCaptureIndex); + const matchTo = UnsafeLoadFixedArrayElement( + matchIndices, kRegExpMatchInfoFirstCaptureIndex + 1); + match = SubString( + string, UnsafeCast(matchFrom), UnsafeCast(matchTo)); + } + } else { + assert(!isFastPath); + const resultTemp = RegExpExec(regexp, string); + if (resultTemp == Null) { + goto IfDidNotMatch; } - goto IfDidMatch; + match = ToString_Inline(GetProperty(resultTemp, SmiConstant(0))); } - label IfDidNotMatch { - return array.length == 0 ? Null : array.ToJSArray(); + goto IfDidMatch; + } label IfDidNotMatch { + return array.length == 0 ? Null : array.ToJSArray(); + } label IfDidMatch { + // Store the match, growing the fixed array if needed. + + array.Push(match); + + // Advance last index if the match is the empty string. + const matchLength: Smi = match.length_smi; + if (matchLength != 0) { + continue; + } + let lastIndex = LoadLastIndex(regexp, isFastPath); + if constexpr (isFastPath) { + assert(TaggedIsPositiveSmi(lastIndex)); + } else { + lastIndex = ToLength_Inline(lastIndex); } - label IfDidMatch { - // Store the match, growing the fixed array if needed. - - array.Push(match); - - // Advance last index if the match is the empty string. - const matchLength: Smi = match.length_smi; - if (matchLength != 0) { - continue; - } - let lastIndex = LoadLastIndex(regexp, isFastPath); - if constexpr (isFastPath) { - assert(TaggedIsPositiveSmi(lastIndex)); - } else { - lastIndex = ToLength_Inline(lastIndex); - } - - const newLastIndex: Number = AdvanceStringIndex( - string, UnsafeCast(lastIndex), isUnicode, isFastPath); - - if constexpr (isFastPath) { - // On the fast path, we can be certain that lastIndex can never be - // incremented to overflow the Smi range since the maximal string - // length is less than the maximal Smi value. - const kMaxStringLengthFitsSmi: constexpr bool = - kStringMaxLengthUintptr < kSmiMaxValue; - StaticAssert(kMaxStringLengthFitsSmi); - assert(TaggedIsPositiveSmi(newLastIndex)); - } - StoreLastIndex(regexp, newLastIndex, isFastPath); + const newLastIndex: Number = AdvanceStringIndex( + string, UnsafeCast(lastIndex), isUnicode, isFastPath); + + if constexpr (isFastPath) { + // On the fast path, we can be certain that lastIndex can never be + // incremented to overflow the Smi range since the maximal string + // length is less than the maximal Smi value. + const kMaxStringLengthFitsSmi: constexpr bool = + kStringMaxLengthUintptr < kSmiMaxValue; + StaticAssert(kMaxStringLengthFitsSmi); + assert(TaggedIsPositiveSmi(newLastIndex)); } - } - VerifiedUnreachable(); + StoreLastIndex(regexp, newLastIndex, isFastPath); + } } - transitioning macro FastRegExpPrototypeMatchBody(implicit context: Context)( - receiver: FastJSRegExp, string: String): JSAny { - return RegExpPrototypeMatchBody(receiver, string, true); - } + VerifiedUnreachable(); +} - transitioning macro SlowRegExpPrototypeMatchBody(implicit context: Context)( - receiver: JSReceiver, string: String): JSAny { - return RegExpPrototypeMatchBody(receiver, string, false); - } +transitioning macro FastRegExpPrototypeMatchBody(implicit context: Context)( + receiver: FastJSRegExp, string: String): JSAny { + return RegExpPrototypeMatchBody(receiver, string, true); +} - // Helper that skips a few initial checks. and assumes... - // 1) receiver is a "fast" RegExp - // 2) pattern is a string - transitioning builtin RegExpMatchFast(implicit context: Context)( - receiver: FastJSRegExp, string: String): JSAny { - return FastRegExpPrototypeMatchBody(receiver, string); - } +transitioning macro SlowRegExpPrototypeMatchBody(implicit context: Context)( + receiver: JSReceiver, string: String): JSAny { + return RegExpPrototypeMatchBody(receiver, string, false); +} - // ES#sec-regexp.prototype-@@match - // RegExp.prototype [ @@match ] ( string ) - transitioning javascript builtin RegExpPrototypeMatch( - js-implicit context: NativeContext, - receiver: JSAny)(string: JSAny): JSAny { - ThrowIfNotJSReceiver( - receiver, MessageTemplate::kIncompatibleMethodReceiver, - 'RegExp.prototype.@@match'); - const receiver = UnsafeCast(receiver); - const string: String = ToString_Inline(string); - - // Strict: Reads global and unicode properties. - // TODO(jgruber): Handle slow flag accesses on the fast path and make this - // permissive. - const fastRegExp = Cast(receiver) - otherwise return SlowRegExpPrototypeMatchBody(receiver, string); - - // TODO(pwong): Could be optimized to remove the overhead of calling the - // builtin (at the cost of a larger builtin). - return RegExpMatchFast(fastRegExp, string); - } +// Helper that skips a few initial checks. and assumes... +// 1) receiver is a "fast" RegExp +// 2) pattern is a string +transitioning builtin RegExpMatchFast(implicit context: Context)( + receiver: FastJSRegExp, string: String): JSAny { + return FastRegExpPrototypeMatchBody(receiver, string); +} + +// ES#sec-regexp.prototype-@@match +// RegExp.prototype [ @@match ] ( string ) +transitioning javascript builtin RegExpPrototypeMatch( + js-implicit context: NativeContext, receiver: JSAny)(string: JSAny): JSAny { + ThrowIfNotJSReceiver( + receiver, MessageTemplate::kIncompatibleMethodReceiver, + 'RegExp.prototype.@@match'); + const receiver = UnsafeCast(receiver); + const string: String = ToString_Inline(string); + + // Strict: Reads global and unicode properties. + // TODO(jgruber): Handle slow flag accesses on the fast path and make this + // permissive. + const fastRegExp = Cast(receiver) + otherwise return SlowRegExpPrototypeMatchBody(receiver, string); + + // TODO(pwong): Could be optimized to remove the overhead of calling the + // builtin (at the cost of a larger builtin). + return RegExpMatchFast(fastRegExp, string); +} } diff --git a/deps/v8/src/builtins/regexp-replace.tq b/deps/v8/src/builtins/regexp-replace.tq index fc9d13cf3c6237..c59a41b27f6653 100644 --- a/deps/v8/src/builtins/regexp-replace.tq +++ b/deps/v8/src/builtins/regexp-replace.tq @@ -6,259 +6,252 @@ namespace regexp { - extern builtin - SubString(implicit context: Context)(String, Smi, Smi): String; - - extern runtime RegExpExecMultiple(implicit context: Context)( - JSRegExp, String, RegExpMatchInfo, JSArray): Null|JSArray; - extern transitioning runtime - RegExpReplaceRT(Context, JSReceiver, String, Object): String; - extern transitioning runtime - StringBuilderConcat(implicit context: Context)(JSArray, Smi, String): String; - extern transitioning runtime - StringReplaceNonGlobalRegExpWithFunction(implicit context: Context)( - String, JSRegExp, Callable): String; - - transitioning macro RegExpReplaceCallableNoExplicitCaptures(implicit context: - Context)( - matchesElements: FixedArray, matchesLength: intptr, string: String, - replaceFn: Callable) { - let matchStart: Smi = 0; - for (let i: intptr = 0; i < matchesLength; i++) { - typeswitch (matchesElements.objects[i]) { - // Element represents a slice. - case (elSmi: Smi): { - // The slice's match start and end is either encoded as one or two - // smis. A positive smi indicates a single smi encoding (see - // ReplacementStringBuilder::AddSubjectSlice()). - if (elSmi > 0) { - // For single smi encoding, see - // StringBuilderSubstringLength::encode() and - // StringBuilderSubstringPosition::encode(). - const elInt: intptr = Convert(elSmi); - const newMatchStart: intptr = (elInt >> 11) + (elInt & 0x7FF); - matchStart = Convert(newMatchStart); - } else { - // For two smi encoding, the length is negative followed by the - // match start. - const nextEl: Smi = UnsafeCast(matchesElements.objects[++i]); - matchStart = nextEl - elSmi; - } - } - // Element represents the matched substring, which is then passed to the - // replace function. - case (elString: String): { - const replacementObj: JSAny = - Call(context, replaceFn, Undefined, elString, matchStart, string); - const replacement: String = ToString_Inline(replacementObj); - matchesElements.objects[i] = replacement; - matchStart += elString.length_smi; - } - case (Object): deferred { - unreachable; +extern builtin +SubString(implicit context: Context)(String, Smi, Smi): String; + +extern runtime RegExpExecMultiple(implicit context: Context)( + JSRegExp, String, RegExpMatchInfo, JSArray): Null|JSArray; +extern transitioning runtime +RegExpReplaceRT(Context, JSReceiver, String, Object): String; +extern transitioning runtime +StringBuilderConcat(implicit context: Context)(JSArray, Smi, String): String; +extern transitioning runtime +StringReplaceNonGlobalRegExpWithFunction(implicit context: Context)( + String, JSRegExp, Callable): String; + +transitioning macro RegExpReplaceCallableNoExplicitCaptures( + implicit context: Context)( + matchesElements: FixedArray, matchesLength: intptr, string: String, + replaceFn: Callable) { + let matchStart: Smi = 0; + for (let i: intptr = 0; i < matchesLength; i++) { + typeswitch (matchesElements.objects[i]) { + // Element represents a slice. + case (elSmi: Smi): { + // The slice's match start and end is either encoded as one or two + // smis. A positive smi indicates a single smi encoding (see + // ReplacementStringBuilder::AddSubjectSlice()). + if (elSmi > 0) { + // For single smi encoding, see + // StringBuilderSubstringLength::encode() and + // StringBuilderSubstringPosition::encode(). + const elInt: intptr = Convert(elSmi); + const newMatchStart: intptr = (elInt >> 11) + (elInt & 0x7FF); + matchStart = Convert(newMatchStart); + } else { + // For two smi encoding, the length is negative followed by the + // match start. + const nextEl: Smi = UnsafeCast(matchesElements.objects[++i]); + matchStart = nextEl - elSmi; } } + // Element represents the matched substring, which is then passed to the + // replace function. + case (elString: String): { + const replacementObj: JSAny = + Call(context, replaceFn, Undefined, elString, matchStart, string); + const replacement: String = ToString_Inline(replacementObj); + matchesElements.objects[i] = replacement; + matchStart += elString.length_smi; + } + case (Object): deferred { + unreachable; + } } } +} - transitioning macro - RegExpReplaceCallableWithExplicitCaptures(implicit context: Context)( - matchesElements: FixedArray, matchesLength: intptr, replaceFn: Callable) { - for (let i: intptr = 0; i < matchesLength; i++) { - const elArray = - Cast(matchesElements.objects[i]) otherwise continue; - - // The JSArray is expanded into the function args by Reflect.apply(). - // TODO(jgruber): Remove indirection through Call->ReflectApply. - const replacementObj: JSAny = Call( - context, GetReflectApply(), Undefined, replaceFn, Undefined, elArray); - - // Overwrite the i'th element in the results with the string - // we got back from the callback function. - matchesElements.objects[i] = ToString_Inline(replacementObj); - } +transitioning macro +RegExpReplaceCallableWithExplicitCaptures(implicit context: Context)( + matchesElements: FixedArray, matchesLength: intptr, replaceFn: Callable) { + for (let i: intptr = 0; i < matchesLength; i++) { + const elArray = + Cast(matchesElements.objects[i]) otherwise continue; + + // The JSArray is expanded into the function args by Reflect.apply(). + // TODO(jgruber): Remove indirection through Call->ReflectApply. + const replacementObj: JSAny = Call( + context, GetReflectApply(), Undefined, replaceFn, Undefined, elArray); + + // Overwrite the i'th element in the results with the string + // we got back from the callback function. + matchesElements.objects[i] = ToString_Inline(replacementObj); } +} - transitioning macro RegExpReplaceFastGlobalCallable(implicit context: - Context)( - regexp: FastJSRegExp, string: String, replaceFn: Callable): String { - regexp.lastIndex = 0; - - const kInitialCapacity: intptr = 16; - const kInitialLength: Smi = 0; - const result: Null|JSArray = RegExpExecMultiple( - regexp, string, GetRegExpLastMatchInfo(), - AllocateJSArray( - ElementsKind::PACKED_ELEMENTS, GetFastPackedElementsJSArrayMap(), - kInitialCapacity, kInitialLength)); - - regexp.lastIndex = 0; - - // If no matches, return the subject string. - if (result == Null) return string; - - const matches: JSArray = UnsafeCast(result); - const matchesLength: Smi = Cast(matches.length) otherwise unreachable; - const matchesLengthInt: intptr = Convert(matchesLength); - const matchesElements: FixedArray = - UnsafeCast(matches.elements); - - // Reload last match info since it might have changed. - const nofCaptures: Smi = GetRegExpLastMatchInfo().NumberOfCaptures(); - - // If the number of captures is two then there are no explicit captures in - // the regexp, just the implicit capture that captures the whole match. In - // this case we can simplify quite a bit and end up with something faster. - if (nofCaptures == 2) { - RegExpReplaceCallableNoExplicitCaptures( - matchesElements, matchesLengthInt, string, replaceFn); - } else { - RegExpReplaceCallableWithExplicitCaptures( - matchesElements, matchesLengthInt, replaceFn); - } - - return StringBuilderConcat(matches, matchesLength, string); +transitioning macro RegExpReplaceFastGlobalCallable(implicit context: Context)( + regexp: FastJSRegExp, string: String, replaceFn: Callable): String { + regexp.lastIndex = 0; + + const kInitialCapacity: intptr = 16; + const kInitialLength: Smi = 0; + const result: Null|JSArray = RegExpExecMultiple( + regexp, string, GetRegExpLastMatchInfo(), + AllocateJSArray( + ElementsKind::PACKED_ELEMENTS, GetFastPackedElementsJSArrayMap(), + kInitialCapacity, kInitialLength)); + + regexp.lastIndex = 0; + + // If no matches, return the subject string. + if (result == Null) return string; + + const matches: JSArray = UnsafeCast(result); + const matchesLength: Smi = Cast(matches.length) otherwise unreachable; + const matchesLengthInt: intptr = Convert(matchesLength); + const matchesElements: FixedArray = UnsafeCast(matches.elements); + + // Reload last match info since it might have changed. + const nofCaptures: Smi = GetRegExpLastMatchInfo().NumberOfCaptures(); + + // If the number of captures is two then there are no explicit captures in + // the regexp, just the implicit capture that captures the whole match. In + // this case we can simplify quite a bit and end up with something faster. + if (nofCaptures == 2) { + RegExpReplaceCallableNoExplicitCaptures( + matchesElements, matchesLengthInt, string, replaceFn); + } else { + RegExpReplaceCallableWithExplicitCaptures( + matchesElements, matchesLengthInt, replaceFn); } - transitioning macro RegExpReplaceFastString(implicit context: Context)( - regexp: JSRegExp, string: String, replaceString: String): String { - // The fast path is reached only if {receiver} is an unmodified JSRegExp - // instance, {replace_value} is non-callable, and ToString({replace_value}) - // does not contain '$', i.e. we're doing a simple string replacement. - let result: String = kEmptyString; - let lastMatchEnd: Smi = 0; - let unicode: bool = false; - const replaceLength: Smi = replaceString.length_smi; - const fastRegexp = UnsafeCast(regexp); - const global: bool = fastRegexp.global; - - if (global) { - unicode = fastRegexp.unicode; - fastRegexp.lastIndex = 0; - } - - while (true) { - const match: RegExpMatchInfo = - RegExpPrototypeExecBodyWithoutResultFast(regexp, string) - otherwise break; - const matchStart: Smi = match.GetStartOfCapture(0); - const matchEnd: Smi = match.GetEndOfCapture(0); - - // TODO(jgruber): We could skip many of the checks that using SubString - // here entails. - result = result + SubString(string, lastMatchEnd, matchStart); - lastMatchEnd = matchEnd; - - if (replaceLength != 0) result = result + replaceString; - - // Non-global case ends here after the first replacement. - if (!global) break; - - // If match is the empty string, we have to increment lastIndex. - if (matchEnd == matchStart) { - typeswitch (regexp) { - case (fastRegexp: FastJSRegExp): { - fastRegexp.lastIndex = - AdvanceStringIndexFast(string, fastRegexp.lastIndex, unicode); - } - case (Object): { - const lastIndex: JSAny = SlowLoadLastIndex(regexp); - const thisIndex: Number = ToLength_Inline(lastIndex); - const nextIndex: Number = - AdvanceStringIndexSlow(string, thisIndex, unicode); - SlowStoreLastIndex(regexp, nextIndex); - } - } - } - } + return StringBuilderConcat(matches, matchesLength, string); +} - return result + SubString(string, lastMatchEnd, string.length_smi); +transitioning macro RegExpReplaceFastString(implicit context: Context)( + regexp: JSRegExp, string: String, replaceString: String): String { + // The fast path is reached only if {receiver} is an unmodified JSRegExp + // instance, {replace_value} is non-callable, and ToString({replace_value}) + // does not contain '$', i.e. we're doing a simple string replacement. + let result: String = kEmptyString; + let lastMatchEnd: Smi = 0; + let unicode: bool = false; + const replaceLength: Smi = replaceString.length_smi; + const fastRegexp = UnsafeCast(regexp); + const global: bool = fastRegexp.global; + + if (global) { + unicode = fastRegexp.unicode; + fastRegexp.lastIndex = 0; } - transitioning builtin RegExpReplace(implicit context: Context)( - regexp: FastJSRegExp, string: String, replaceValue: JSAny): String { - // TODO(pwong): Remove assert when all callers (StringPrototypeReplace) are - // from Torque. - assert(Is(regexp)); - - // 2. Is {replace_value} callable? - typeswitch (replaceValue) { - case (replaceFn: Callable): { - return regexp.global ? - RegExpReplaceFastGlobalCallable(regexp, string, replaceFn) : - StringReplaceNonGlobalRegExpWithFunction(string, regexp, replaceFn); - } - case (JSAny): { - const stableRegexp: JSRegExp = regexp; - const replaceString: String = ToString_Inline(replaceValue); - - try { - // ToString(replaceValue) could potentially change the shape of the - // RegExp object. Recheck that we are still on the fast path and bail - // to runtime otherwise. - const fastRegexp = Cast(stableRegexp) otherwise Runtime; - if (StringIndexOf( - replaceString, SingleCharacterStringConstant('$'), 0) != -1) { - goto Runtime; - } - - return RegExpReplaceFastString(fastRegexp, string, replaceString); + while (true) { + const match: RegExpMatchInfo = + RegExpPrototypeExecBodyWithoutResultFast(regexp, string) + otherwise break; + const matchStart: Smi = match.GetStartOfCapture(0); + const matchEnd: Smi = match.GetEndOfCapture(0); + + // TODO(jgruber): We could skip many of the checks that using SubString + // here entails. + result = result + SubString(string, lastMatchEnd, matchStart); + lastMatchEnd = matchEnd; + + if (replaceLength != 0) result = result + replaceString; + + // Non-global case ends here after the first replacement. + if (!global) break; + + // If match is the empty string, we have to increment lastIndex. + if (matchEnd == matchStart) { + typeswitch (regexp) { + case (fastRegexp: FastJSRegExp): { + fastRegexp.lastIndex = + AdvanceStringIndexFast(string, fastRegexp.lastIndex, unicode); } - label Runtime deferred { - return RegExpReplaceRT(context, stableRegexp, string, replaceString); + case (Object): { + const lastIndex: JSAny = SlowLoadLastIndex(regexp); + const thisIndex: Number = ToLength_Inline(lastIndex); + const nextIndex: Number = + AdvanceStringIndexSlow(string, thisIndex, unicode); + SlowStoreLastIndex(regexp, nextIndex); } } } } - const kRegExpReplaceCalledOnSlowRegExp: constexpr int31 - generates 'v8::Isolate::kRegExpReplaceCalledOnSlowRegExp'; - - transitioning javascript builtin RegExpPrototypeReplace( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - const methodName: constexpr string = 'RegExp.prototype.@@replace'; - - // RegExpPrototypeReplace is a bit of a beast - a summary of dispatch logic: - // - // if (!IsFastRegExp(receiver)) CallRuntime(RegExpReplace) - // if (IsCallable(replace)) { - // if (IsGlobal(receiver)) { - // // Called 'fast-path' but contains several runtime calls. - // RegExpReplaceFastGlobalCallable() - // } else { - // CallRuntime(StringReplaceNonGlobalRegExpWithFunction) - // } - // } else { - // if (replace.contains("$")) { - // CallRuntime(RegExpReplace) - // } else { - // RegExpReplaceFastString() - // } - // } - - const string: JSAny = arguments[0]; - const replaceValue: JSAny = arguments[1]; - - // Let rx be the this value. - // If Type(rx) is not Object, throw a TypeError exception. - const rx = Cast(receiver) - otherwise ThrowTypeError( - MessageTemplate::kIncompatibleMethodReceiver, methodName); - - // Let S be ? ToString(string). - const s = ToString_Inline(string); - - // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? - try { - const fastRx: FastJSRegExp = Cast(rx) otherwise Runtime; - return RegExpReplace(fastRx, s, replaceValue); + return result + SubString(string, lastMatchEnd, string.length_smi); +} + +transitioning builtin RegExpReplace(implicit context: Context)( + regexp: FastJSRegExp, string: String, replaceValue: JSAny): String { + // TODO(pwong): Remove assert when all callers (StringPrototypeReplace) are + // from Torque. + assert(Is(regexp)); + + // 2. Is {replace_value} callable? + typeswitch (replaceValue) { + case (replaceFn: Callable): { + return regexp.global ? + RegExpReplaceFastGlobalCallable(regexp, string, replaceFn) : + StringReplaceNonGlobalRegExpWithFunction(string, regexp, replaceFn); } - label Runtime deferred { - IncrementUseCounter( - context, SmiConstant(kRegExpReplaceCalledOnSlowRegExp)); - return RegExpReplaceRT(context, rx, s, replaceValue); + case (JSAny): { + const stableRegexp: JSRegExp = regexp; + const replaceString: String = ToString_Inline(replaceValue); + + try { + // ToString(replaceValue) could potentially change the shape of the + // RegExp object. Recheck that we are still on the fast path and bail + // to runtime otherwise. + const fastRegexp = Cast(stableRegexp) otherwise Runtime; + if (StringIndexOf( + replaceString, SingleCharacterStringConstant('$'), 0) != -1) { + goto Runtime; + } + + return RegExpReplaceFastString(fastRegexp, string, replaceString); + } label Runtime deferred { + return RegExpReplaceRT(context, stableRegexp, string, replaceString); + } } } +} +const kRegExpReplaceCalledOnSlowRegExp: constexpr int31 + generates 'v8::Isolate::kRegExpReplaceCalledOnSlowRegExp'; + +transitioning javascript builtin RegExpPrototypeReplace( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + const methodName: constexpr string = 'RegExp.prototype.@@replace'; + + // RegExpPrototypeReplace is a bit of a beast - a summary of dispatch logic: + // + // if (!IsFastRegExp(receiver)) CallRuntime(RegExpReplace) + // if (IsCallable(replace)) { + // if (IsGlobal(receiver)) { + // // Called 'fast-path' but contains several runtime calls. + // RegExpReplaceFastGlobalCallable() + // } else { + // CallRuntime(StringReplaceNonGlobalRegExpWithFunction) + // } + // } else { + // if (replace.contains("$")) { + // CallRuntime(RegExpReplace) + // } else { + // RegExpReplaceFastString() + // } + // } + + const string: JSAny = arguments[0]; + const replaceValue: JSAny = arguments[1]; + + // Let rx be the this value. + // If Type(rx) is not Object, throw a TypeError exception. + const rx = Cast(receiver) + otherwise ThrowTypeError( + MessageTemplate::kIncompatibleMethodReceiver, methodName); + + // Let S be ? ToString(string). + const s = ToString_Inline(string); + + // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? + try { + const fastRx: FastJSRegExp = Cast(rx) otherwise Runtime; + return RegExpReplace(fastRx, s, replaceValue); + } label Runtime deferred { + IncrementUseCounter(context, SmiConstant(kRegExpReplaceCalledOnSlowRegExp)); + return RegExpReplaceRT(context, rx, s, replaceValue); + } +} } diff --git a/deps/v8/src/builtins/regexp-search.tq b/deps/v8/src/builtins/regexp-search.tq index 14fb9f9b03c202..b70d23a0dd5a0b 100644 --- a/deps/v8/src/builtins/regexp-search.tq +++ b/deps/v8/src/builtins/regexp-search.tq @@ -6,103 +6,101 @@ namespace regexp { - transitioning macro - RegExpPrototypeSearchBodyFast(implicit context: Context)( - regexp: JSRegExp, string: String): JSAny { - assert(IsFastRegExpPermissive(regexp)); - - // Grab the initial value of last index. - const previousLastIndex: Smi = FastLoadLastIndex(regexp); - - // Ensure last index is 0. - FastStoreLastIndex(regexp, 0); - - // Call exec. - try { - const matchIndices: RegExpMatchInfo = - RegExpPrototypeExecBodyWithoutResultFast( - UnsafeCast(regexp), string) - otherwise DidNotMatch; - - // Successful match. - // Reset last index. - FastStoreLastIndex(regexp, previousLastIndex); - - // Return the index of the match. - return UnsafeCast( - matchIndices.objects[kRegExpMatchInfoFirstCaptureIndex]); - } - label DidNotMatch { - // Reset last index and return -1. - FastStoreLastIndex(regexp, previousLastIndex); - return SmiConstant(-1); - } - } +transitioning macro +RegExpPrototypeSearchBodyFast(implicit context: Context)( + regexp: JSRegExp, string: String): JSAny { + assert(IsFastRegExpPermissive(regexp)); + + // Grab the initial value of last index. + const previousLastIndex: Smi = FastLoadLastIndex(regexp); + + // Ensure last index is 0. + FastStoreLastIndex(regexp, 0); + + // Call exec. + try { + const matchIndices: RegExpMatchInfo = + RegExpPrototypeExecBodyWithoutResultFast( + UnsafeCast(regexp), string) + otherwise DidNotMatch; - extern macro RegExpBuiltinsAssembler::BranchIfRegExpResult( - implicit context: Context)(Object): never labels IsUnmodified, - IsModified; + // Successful match. + // Reset last index. + FastStoreLastIndex(regexp, previousLastIndex); - macro - IsRegExpResult(implicit context: Context)(execResult: HeapObject): bool { - BranchIfRegExpResult(execResult) otherwise return true, return false; + // Return the index of the match. + return UnsafeCast( + matchIndices.objects[kRegExpMatchInfoFirstCaptureIndex]); + } label DidNotMatch { + // Reset last index and return -1. + FastStoreLastIndex(regexp, previousLastIndex); + return SmiConstant(-1); } +} - transitioning macro RegExpPrototypeSearchBodySlow(implicit context: Context)( - regexp: JSReceiver, string: String): JSAny { - // Grab the initial value of last index. - const previousLastIndex = SlowLoadLastIndex(regexp); - const smiZero: Smi = 0; +extern macro RegExpBuiltinsAssembler::BranchIfRegExpResult( + implicit context: Context)(Object): never labels IsUnmodified, + IsModified; - // Ensure last index is 0. - if (!SameValue(previousLastIndex, smiZero)) { - SlowStoreLastIndex(regexp, smiZero); - } +macro +IsRegExpResult(implicit context: Context)(execResult: HeapObject): bool { + BranchIfRegExpResult(execResult) otherwise return true, return false; +} - // Call exec. - const execResult = RegExpExec(regexp, string); +transitioning macro RegExpPrototypeSearchBodySlow(implicit context: Context)( + regexp: JSReceiver, string: String): JSAny { + // Grab the initial value of last index. + const previousLastIndex = SlowLoadLastIndex(regexp); + const smiZero: Smi = 0; - // Reset last index if necessary. - const currentLastIndex = SlowLoadLastIndex(regexp); - if (!SameValue(currentLastIndex, previousLastIndex)) { - SlowStoreLastIndex(regexp, previousLastIndex); - } + // Ensure last index is 0. + if (!SameValue(previousLastIndex, smiZero)) { + SlowStoreLastIndex(regexp, smiZero); + } - // Return -1 if no match was found. - if (execResult == Null) { - return SmiConstant(-1); - } + // Call exec. + const execResult = RegExpExec(regexp, string); - // Return the index of the match. - const fastExecResult = Cast(execResult) - otherwise return GetProperty(execResult, 'index'); - return fastExecResult.index; + // Reset last index if necessary. + const currentLastIndex = SlowLoadLastIndex(regexp); + if (!SameValue(currentLastIndex, previousLastIndex)) { + SlowStoreLastIndex(regexp, previousLastIndex); } - // Helper that skips a few initial checks. and assumes... - // 1) receiver is a "fast permissive" RegExp - // 2) pattern is a string - transitioning builtin RegExpSearchFast(implicit context: Context)( - receiver: JSRegExp, string: String): JSAny { - return RegExpPrototypeSearchBodyFast(receiver, string); + // Return -1 if no match was found. + if (execResult == Null) { + return SmiConstant(-1); } - // ES#sec-regexp.prototype-@@search - // RegExp.prototype [ @@search ] ( string ) - transitioning javascript builtin RegExpPrototypeSearch( - js-implicit context: NativeContext, - receiver: JSAny)(string: JSAny): JSAny { - ThrowIfNotJSReceiver( - receiver, MessageTemplate::kIncompatibleMethodReceiver, - 'RegExp.prototype.@@search'); - const receiver = UnsafeCast(receiver); - const string: String = ToString_Inline(string); - - if (IsFastRegExpPermissive(receiver)) { - // TODO(pwong): Could be optimized to remove the overhead of calling the - // builtin (at the cost of a larger builtin). - return RegExpSearchFast(UnsafeCast(receiver), string); - } - return RegExpPrototypeSearchBodySlow(receiver, string); + // Return the index of the match. + const fastExecResult = Cast(execResult) + otherwise return GetProperty(execResult, 'index'); + return fastExecResult.index; +} + +// Helper that skips a few initial checks. and assumes... +// 1) receiver is a "fast permissive" RegExp +// 2) pattern is a string +transitioning builtin RegExpSearchFast(implicit context: Context)( + receiver: JSRegExp, string: String): JSAny { + return RegExpPrototypeSearchBodyFast(receiver, string); +} + +// ES#sec-regexp.prototype-@@search +// RegExp.prototype [ @@search ] ( string ) +transitioning javascript builtin RegExpPrototypeSearch( + js-implicit context: NativeContext, receiver: JSAny)(string: JSAny): JSAny { + ThrowIfNotJSReceiver( + receiver, MessageTemplate::kIncompatibleMethodReceiver, + 'RegExp.prototype.@@search'); + const receiver = UnsafeCast(receiver); + const string: String = ToString_Inline(string); + + if (IsFastRegExpPermissive(receiver)) { + // TODO(pwong): Could be optimized to remove the overhead of calling the + // builtin (at the cost of a larger builtin). + return RegExpSearchFast(UnsafeCast(receiver), string); } + return RegExpPrototypeSearchBodySlow(receiver, string); +} } diff --git a/deps/v8/src/builtins/regexp-source.tq b/deps/v8/src/builtins/regexp-source.tq index 009e5181dc2190..5f9c6b22c3a973 100644 --- a/deps/v8/src/builtins/regexp-source.tq +++ b/deps/v8/src/builtins/regexp-source.tq @@ -6,22 +6,22 @@ namespace regexp { - // ES6 21.2.5.10. - // ES #sec-get-regexp.prototype.source - transitioning javascript builtin RegExpPrototypeSourceGetter( - js-implicit context: NativeContext, receiver: JSAny)(): JSAny { - typeswitch (receiver) { - case (receiver: JSRegExp): { - return receiver.source; - } - case (Object): { - } +// ES6 21.2.5.10. +// ES #sec-get-regexp.prototype.source +transitioning javascript builtin RegExpPrototypeSourceGetter( + js-implicit context: NativeContext, receiver: JSAny)(): JSAny { + typeswitch (receiver) { + case (receiver: JSRegExp): { + return receiver.source; } - if (!IsReceiverInitialRegExpPrototype(receiver)) { - const methodName: constexpr string = 'RegExp.prototype.source'; - ThrowTypeError(MessageTemplate::kRegExpNonRegExp, methodName); + case (Object): { } - IncrementUseCounter(context, SmiConstant(kRegExpPrototypeSourceGetter)); - return '(?:)'; } + if (!IsReceiverInitialRegExpPrototype(receiver)) { + const methodName: constexpr string = 'RegExp.prototype.source'; + ThrowTypeError(MessageTemplate::kRegExpNonRegExp, methodName); + } + IncrementUseCounter(context, SmiConstant(kRegExpPrototypeSourceGetter)); + return '(?:)'; +} } diff --git a/deps/v8/src/builtins/regexp-split.tq b/deps/v8/src/builtins/regexp-split.tq index e4092803eeae9d..47ff214130f142 100644 --- a/deps/v8/src/builtins/regexp-split.tq +++ b/deps/v8/src/builtins/regexp-split.tq @@ -5,70 +5,68 @@ #include 'src/builtins/builtins-regexp-gen.h' namespace runtime { - extern transitioning runtime - RegExpSplit(implicit context: Context)(JSReceiver, String, Object): JSAny; +extern transitioning runtime +RegExpSplit(implicit context: Context)(JSReceiver, String, Object): JSAny; } // namespace runtime namespace regexp { - const kMaxValueSmi: constexpr int31 - generates 'Smi::kMaxValue'; +const kMaxValueSmi: constexpr int31 + generates 'Smi::kMaxValue'; - extern transitioning macro RegExpBuiltinsAssembler::RegExpPrototypeSplitBody( - implicit context: Context)(JSRegExp, String, Smi): JSArray; +extern transitioning macro RegExpBuiltinsAssembler::RegExpPrototypeSplitBody( + implicit context: Context)(JSRegExp, String, Smi): JSArray; - // Helper that skips a few initial checks. - transitioning builtin - RegExpSplit(implicit context: Context)( - regexp: FastJSRegExp, string: String, limit: JSAny): JSAny { - let sanitizedLimit: Smi; +// Helper that skips a few initial checks. +transitioning builtin +RegExpSplit(implicit context: Context)( + regexp: FastJSRegExp, string: String, limit: JSAny): JSAny { + let sanitizedLimit: Smi; - // We need to be extra-strict and require the given limit to be either - // undefined or a positive smi. We can't call ToUint32(maybe_limit) since - // that might move us onto the slow path, resulting in ordering spec - // violations (see https://crbug.com/801171). + // We need to be extra-strict and require the given limit to be either + // undefined or a positive smi. We can't call ToUint32(maybe_limit) since + // that might move us onto the slow path, resulting in ordering spec + // violations (see https://crbug.com/801171). - if (limit == Undefined) { - // TODO(jgruber): In this case, we can probably avoid generation of limit - // checks in Generate_RegExpPrototypeSplitBody. - sanitizedLimit = SmiConstant(kMaxValueSmi); - } else if (!TaggedIsPositiveSmi(limit)) { - return runtime::RegExpSplit(regexp, string, limit); - } else { - sanitizedLimit = UnsafeCast(limit); - } - - // Due to specific shortcuts we take on the fast path (specifically, we - // don't allocate a new regexp instance as specced), we need to ensure that - // the given regexp is non-sticky to avoid invalid results. See - // crbug.com/v8/6706. + if (limit == Undefined) { + // TODO(jgruber): In this case, we can probably avoid generation of limit + // checks in Generate_RegExpPrototypeSplitBody. + sanitizedLimit = SmiConstant(kMaxValueSmi); + } else if (!TaggedIsPositiveSmi(limit)) { + return runtime::RegExpSplit(regexp, string, limit); + } else { + sanitizedLimit = UnsafeCast(limit); + } - if (FastFlagGetter(regexp, Flag::kSticky)) { - return runtime::RegExpSplit(regexp, string, sanitizedLimit); - } + // Due to specific shortcuts we take on the fast path (specifically, we + // don't allocate a new regexp instance as specced), we need to ensure that + // the given regexp is non-sticky to avoid invalid results. See + // crbug.com/v8/6706. - // We're good to go on the fast path, which is inlined here. - return RegExpPrototypeSplitBody(regexp, string, sanitizedLimit); + if (FastFlagGetter(regexp, Flag::kSticky)) { + return runtime::RegExpSplit(regexp, string, sanitizedLimit); } - // ES#sec-regexp.prototype-@@split - // RegExp.prototype [ @@split ] ( string, limit ) - transitioning javascript builtin RegExpPrototypeSplit( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - ThrowIfNotJSReceiver( - receiver, MessageTemplate::kIncompatibleMethodReceiver, - 'RegExp.prototype.@@split'); - const receiver = UnsafeCast(receiver); - const string: String = ToString_Inline(arguments[0]); - const limit = arguments[1]; + // We're good to go on the fast path, which is inlined here. + return RegExpPrototypeSplitBody(regexp, string, sanitizedLimit); +} - // Strict: Reads the flags property. - // TODO(jgruber): Handle slow flag accesses on the fast path and make this - // permissive. - const fastRegExp = Cast(receiver) - otherwise return runtime::RegExpSplit(receiver, string, limit); - return RegExpSplit(fastRegExp, string, limit); - } +// ES#sec-regexp.prototype-@@split +// RegExp.prototype [ @@split ] ( string, limit ) +transitioning javascript builtin RegExpPrototypeSplit( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + ThrowIfNotJSReceiver( + receiver, MessageTemplate::kIncompatibleMethodReceiver, + 'RegExp.prototype.@@split'); + const receiver = UnsafeCast(receiver); + const string: String = ToString_Inline(arguments[0]); + const limit = arguments[1]; + // Strict: Reads the flags property. + // TODO(jgruber): Handle slow flag accesses on the fast path and make this + // permissive. + const fastRegExp = Cast(receiver) + otherwise return runtime::RegExpSplit(receiver, string, limit); + return RegExpSplit(fastRegExp, string, limit); +} } diff --git a/deps/v8/src/builtins/regexp-test.tq b/deps/v8/src/builtins/regexp-test.tq index cd418239757185..c83afd602de85c 100644 --- a/deps/v8/src/builtins/regexp-test.tq +++ b/deps/v8/src/builtins/regexp-test.tq @@ -6,30 +6,29 @@ namespace regexp { - // ES#sec-regexp.prototype.test - // RegExp.prototype.test ( S ) - transitioning javascript builtin RegExpPrototypeTest( - js-implicit context: NativeContext, - receiver: JSAny)(string: JSAny): JSAny { - const methodName: constexpr string = 'RegExp.prototype.test'; - const receiver = Cast(receiver) - otherwise ThrowTypeError( - MessageTemplate::kIncompatibleMethodReceiver, methodName); - const str: String = ToString_Inline(string); - if (IsFastRegExpPermissive(receiver)) { - RegExpPrototypeExecBodyWithoutResultFast( - UnsafeCast(receiver), str) - otherwise return False; - return True; - } - const matchIndices = RegExpExec(receiver, str); - return SelectBooleanConstant(matchIndices != Null); - } - - transitioning builtin RegExpPrototypeTestFast(implicit context: Context)( - receiver: JSRegExp, string: String): Object { - RegExpPrototypeExecBodyWithoutResultFast(receiver, string) +// ES#sec-regexp.prototype.test +// RegExp.prototype.test ( S ) +transitioning javascript builtin RegExpPrototypeTest( + js-implicit context: NativeContext, receiver: JSAny)(string: JSAny): JSAny { + const methodName: constexpr string = 'RegExp.prototype.test'; + const receiver = Cast(receiver) + otherwise ThrowTypeError( + MessageTemplate::kIncompatibleMethodReceiver, methodName); + const str: String = ToString_Inline(string); + if (IsFastRegExpPermissive(receiver)) { + RegExpPrototypeExecBodyWithoutResultFast( + UnsafeCast(receiver), str) otherwise return False; return True; } + const matchIndices = RegExpExec(receiver, str); + return SelectBooleanConstant(matchIndices != Null); +} + +transitioning builtin RegExpPrototypeTestFast(implicit context: Context)( + receiver: JSRegExp, string: String): Object { + RegExpPrototypeExecBodyWithoutResultFast(receiver, string) + otherwise return False; + return True; +} } diff --git a/deps/v8/src/builtins/regexp.tq b/deps/v8/src/builtins/regexp.tq index a48dce3863bacb..21577b47634569 100644 --- a/deps/v8/src/builtins/regexp.tq +++ b/deps/v8/src/builtins/regexp.tq @@ -6,417 +6,415 @@ namespace regexp { - extern macro RegExpBuiltinsAssembler::BranchIfFastRegExp_Strict( - implicit context: Context)(HeapObject): never labels IsFast, - IsSlow; - macro IsFastRegExpStrict(implicit context: Context)(o: HeapObject): bool { - BranchIfFastRegExp_Strict(o) otherwise return true, return false; - } +extern macro RegExpBuiltinsAssembler::BranchIfFastRegExp_Strict( + implicit context: Context)(HeapObject): never labels IsFast, + IsSlow; +macro IsFastRegExpStrict(implicit context: Context)(o: HeapObject): bool { + BranchIfFastRegExp_Strict(o) otherwise return true, return false; +} - extern macro RegExpBuiltinsAssembler::BranchIfFastRegExp_Permissive( - implicit context: Context)(HeapObject): never labels IsFast, - IsSlow; +extern macro RegExpBuiltinsAssembler::BranchIfFastRegExp_Permissive( + implicit context: Context)(HeapObject): never labels IsFast, + IsSlow; - @export - macro IsFastRegExpPermissive(implicit context: Context)(o: HeapObject): bool { - BranchIfFastRegExp_Permissive(o) otherwise return true, return false; - } +@export +macro IsFastRegExpPermissive(implicit context: Context)(o: HeapObject): bool { + BranchIfFastRegExp_Permissive(o) otherwise return true, return false; +} - // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S ) - @export - transitioning macro RegExpExec(implicit context: Context)( - receiver: JSReceiver, string: String): JSAny { - // Take the slow path of fetching the exec property, calling it, and - // verifying its return value. - - const exec = GetProperty(receiver, 'exec'); - - // Is {exec} callable? - typeswitch (exec) { - case (execCallable: Callable): { - const result = Call(context, execCallable, receiver, string); - if (result != Null) { - ThrowIfNotJSReceiver( - result, MessageTemplate::kInvalidRegExpExecResult, ''); - } - return result; - } - case (Object): { - const regexp = Cast(receiver) otherwise ThrowTypeError( - MessageTemplate::kIncompatibleMethodReceiver, - 'RegExp.prototype.exec', receiver); - return RegExpPrototypeExecSlow(regexp, string); +// ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S ) +@export +transitioning macro RegExpExec(implicit context: Context)( + receiver: JSReceiver, string: String): JSAny { + // Take the slow path of fetching the exec property, calling it, and + // verifying its return value. + + const exec = GetProperty(receiver, 'exec'); + + // Is {exec} callable? + typeswitch (exec) { + case (execCallable: Callable): { + const result = Call(context, execCallable, receiver, string); + if (result != Null) { + ThrowIfNotJSReceiver( + result, MessageTemplate::kInvalidRegExpExecResult, ''); } + return result; } - } - - extern macro RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo( - implicit context: Context)(JSRegExp, RegExpMatchInfo, String, Number): - JSRegExpResult; - - const kGlobalOrSticky: constexpr int31 - generates 'JSRegExp::kGlobal | JSRegExp::kSticky'; - - extern macro RegExpBuiltinsAssembler::RegExpExecInternal( - implicit context: Context)(JSRegExp, String, Number, RegExpMatchInfo): - HeapObject; - - // ES#sec-regexp.prototype.exec - // RegExp.prototype.exec ( string ) - // Implements the core of RegExp.prototype.exec but without actually - // constructing the JSRegExpResult. Returns a fixed array containing match - // indices as returned by RegExpExecStub on successful match, and jumps to - // IfDidNotMatch otherwise. - transitioning macro RegExpPrototypeExecBodyWithoutResult(implicit context: - Context)( - regexp: JSRegExp, string: String, regexpLastIndex: Number, - isFastPath: constexpr bool): RegExpMatchInfo labels IfDidNotMatch { - if (isFastPath) { - assert(HasInitialRegExpMap(regexp)); - } else { - IncrementUseCounter(context, SmiConstant(kRegExpExecCalledOnSlowRegExp)); + case (Object): { + const regexp = Cast(receiver) otherwise ThrowTypeError( + MessageTemplate::kIncompatibleMethodReceiver, 'RegExp.prototype.exec', + receiver); + return RegExpPrototypeExecSlow(regexp, string); } + } +} - let lastIndex = regexpLastIndex; +extern macro RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo( + implicit context: Context)( + JSRegExp, RegExpMatchInfo, String, Number): JSRegExpResult; + +const kGlobalOrSticky: constexpr int31 + generates 'JSRegExp::kGlobal | JSRegExp::kSticky'; + +extern macro RegExpBuiltinsAssembler::RegExpExecInternal( + implicit context: Context)( + JSRegExp, String, Number, RegExpMatchInfo): HeapObject; + +// ES#sec-regexp.prototype.exec +// RegExp.prototype.exec ( string ) +// Implements the core of RegExp.prototype.exec but without actually +// constructing the JSRegExpResult. Returns a fixed array containing match +// indices as returned by RegExpExecStub on successful match, and jumps to +// IfDidNotMatch otherwise. +transitioning macro RegExpPrototypeExecBodyWithoutResult( + implicit context: Context)( + regexp: JSRegExp, string: String, regexpLastIndex: Number, + isFastPath: constexpr bool): RegExpMatchInfo labels IfDidNotMatch { + if (isFastPath) { + assert(HasInitialRegExpMap(regexp)); + } else { + IncrementUseCounter(context, SmiConstant(kRegExpExecCalledOnSlowRegExp)); + } - // Check whether the regexp is global or sticky, which determines whether we - // update last index later on. - const flags = UnsafeCast(regexp.flags); - const isGlobalOrSticky: intptr = - SmiUntag(flags) & IntPtrConstant(kGlobalOrSticky); - const shouldUpdateLastIndex: bool = isGlobalOrSticky != 0; + let lastIndex = regexpLastIndex; - // Grab and possibly update last index. - if (shouldUpdateLastIndex) { - if (!TaggedIsSmi(lastIndex) || (lastIndex > string.length_smi)) { - StoreLastIndex(regexp, SmiConstant(0), isFastPath); - goto IfDidNotMatch; - } - } else { - lastIndex = SmiConstant(0); - } + // Check whether the regexp is global or sticky, which determines whether we + // update last index later on. + const flags = UnsafeCast(regexp.flags); + const isGlobalOrSticky: intptr = + SmiUntag(flags) & IntPtrConstant(kGlobalOrSticky); + const shouldUpdateLastIndex: bool = isGlobalOrSticky != 0; - const lastMatchInfo: RegExpMatchInfo = GetRegExpLastMatchInfo(); - - const matchIndices = - RegExpExecInternal(regexp, string, lastIndex, lastMatchInfo); - - // {match_indices} is either null or the RegExpMatchInfo array. - // Return early if exec failed, possibly updating last index. - if (matchIndices != Null) { - const matchIndicesRegExpMatchInfo = - UnsafeCast(matchIndices); - if (shouldUpdateLastIndex) { - // Update the new last index from {match_indices}. - const newLastIndex: Smi = - matchIndicesRegExpMatchInfo.GetEndOfCapture(0); - StoreLastIndex(regexp, newLastIndex, isFastPath); - } - return matchIndicesRegExpMatchInfo; - } - if (shouldUpdateLastIndex) { + // Grab and possibly update last index. + if (shouldUpdateLastIndex) { + if (!TaggedIsSmi(lastIndex) || (lastIndex > string.length_smi)) { StoreLastIndex(regexp, SmiConstant(0), isFastPath); + goto IfDidNotMatch; } - goto IfDidNotMatch; + } else { + lastIndex = SmiConstant(0); } - @export - transitioning macro RegExpPrototypeExecBodyWithoutResultFast( - implicit context: Context)(regexp: JSRegExp, string: String): - RegExpMatchInfo labels IfDidNotMatch { - const lastIndex = LoadLastIndexAsLength(regexp, true); - return RegExpPrototypeExecBodyWithoutResult(regexp, string, lastIndex, true) - otherwise IfDidNotMatch; - } + const lastMatchInfo: RegExpMatchInfo = GetRegExpLastMatchInfo(); - transitioning macro RegExpPrototypeExecBodyWithoutResultFast( - implicit context: - Context)(regexp: JSRegExp, string: String, lastIndex: Number): - RegExpMatchInfo labels IfDidNotMatch { - return RegExpPrototypeExecBodyWithoutResult(regexp, string, lastIndex, true) - otherwise IfDidNotMatch; - } + const matchIndices = + RegExpExecInternal(regexp, string, lastIndex, lastMatchInfo); - // ES#sec-regexp.prototype.exec - // RegExp.prototype.exec ( string ) - transitioning macro RegExpPrototypeExecBody(implicit context: Context)( - receiver: JSReceiver, string: String, isFastPath: constexpr bool): JSAny { - let regexp: JSRegExp; - if constexpr (isFastPath) { - regexp = UnsafeCast(receiver); - } else { - regexp = Cast(receiver) otherwise ThrowTypeError( - MessageTemplate::kIncompatibleMethodReceiver, 'RegExp.prototype.exec', - receiver); + // {match_indices} is either null or the RegExpMatchInfo array. + // Return early if exec failed, possibly updating last index. + if (matchIndices != Null) { + const matchIndicesRegExpMatchInfo = + UnsafeCast(matchIndices); + if (shouldUpdateLastIndex) { + // Update the new last index from {match_indices}. + const newLastIndex: Smi = matchIndicesRegExpMatchInfo.GetEndOfCapture(0); + StoreLastIndex(regexp, newLastIndex, isFastPath); } - const lastIndex = LoadLastIndexAsLength(regexp, isFastPath); - const matchIndices: RegExpMatchInfo = RegExpPrototypeExecBodyWithoutResult( - regexp, string, lastIndex, isFastPath) otherwise return Null; - return ConstructNewResultFromMatchInfo( - regexp, matchIndices, string, lastIndex); + return matchIndicesRegExpMatchInfo; } - - macro LoadRegExpFunction(implicit context: Context)( - nativeContext: NativeContext): JSFunction { - return UnsafeCast( - nativeContext[NativeContextSlot::REGEXP_FUNCTION_INDEX]); + if (shouldUpdateLastIndex) { + StoreLastIndex(regexp, SmiConstant(0), isFastPath); } + goto IfDidNotMatch; +} - // Note this doesn't guarantee const-ness of object properties, just - // unchanged object layout. - macro HasInitialRegExpMap(implicit context: Context)(o: HeapObject): bool { - const nativeContext = LoadNativeContext(context); - const function = LoadRegExpFunction(nativeContext); - const initialMap = UnsafeCast(function.prototype_or_initial_map); - return initialMap == o.map; - } +@export +transitioning macro RegExpPrototypeExecBodyWithoutResultFast( + implicit context: Context)(regexp: JSRegExp, string: String): + RegExpMatchInfo labels IfDidNotMatch { + const lastIndex = LoadLastIndexAsLength(regexp, true); + return RegExpPrototypeExecBodyWithoutResult(regexp, string, lastIndex, true) + otherwise IfDidNotMatch; +} - macro IsReceiverInitialRegExpPrototype(implicit context: - Context)(receiver: Object): bool { - const nativeContext = LoadNativeContext(context); - const regexpFun = LoadRegExpFunction(nativeContext); - const initialMap = UnsafeCast(regexpFun.prototype_or_initial_map); - const initialPrototype: HeapObject = initialMap.prototype; - return TaggedEqual(receiver, initialPrototype); - } +transitioning macro RegExpPrototypeExecBodyWithoutResultFast( + implicit context: Context)( + regexp: JSRegExp, string: String, + lastIndex: Number): RegExpMatchInfo labels IfDidNotMatch { + return RegExpPrototypeExecBodyWithoutResult(regexp, string, lastIndex, true) + otherwise IfDidNotMatch; +} - extern enum Flag constexpr 'JSRegExp::Flag' { - kNone, - kGlobal, - kIgnoreCase, - kMultiline, - kSticky, - kUnicode, - kDotAll, - kInvalid +// ES#sec-regexp.prototype.exec +// RegExp.prototype.exec ( string ) +transitioning macro RegExpPrototypeExecBody(implicit context: Context)( + receiver: JSReceiver, string: String, isFastPath: constexpr bool): JSAny { + let regexp: JSRegExp; + if constexpr (isFastPath) { + regexp = UnsafeCast(receiver); + } else { + regexp = Cast(receiver) otherwise ThrowTypeError( + MessageTemplate::kIncompatibleMethodReceiver, 'RegExp.prototype.exec', + receiver); } + const lastIndex = LoadLastIndexAsLength(regexp, isFastPath); + const matchIndices: RegExpMatchInfo = RegExpPrototypeExecBodyWithoutResult( + regexp, string, lastIndex, isFastPath) otherwise return Null; + return ConstructNewResultFromMatchInfo( + regexp, matchIndices, string, lastIndex); +} - const kRegExpPrototypeOldFlagGetter: constexpr int31 - generates 'v8::Isolate::kRegExpPrototypeOldFlagGetter'; - const kRegExpPrototypeStickyGetter: constexpr int31 - generates 'v8::Isolate::kRegExpPrototypeStickyGetter'; - const kRegExpPrototypeUnicodeGetter: constexpr int31 - generates 'v8::Isolate::kRegExpPrototypeUnicodeGetter'; - - extern macro RegExpBuiltinsAssembler::FastFlagGetter( - JSRegExp, constexpr Flag): bool; - extern runtime IncrementUseCounter(Context, Smi): void; - - macro FlagGetter(implicit context: Context)( - receiver: Object, flag: constexpr Flag, counter: constexpr int31, - methodName: constexpr string): JSAny { - typeswitch (receiver) { - case (receiver: JSRegExp): { - return SelectBooleanConstant(FastFlagGetter(receiver, flag)); - } - case (Object): { - } - } - if (!IsReceiverInitialRegExpPrototype(receiver)) { - ThrowTypeError(MessageTemplate::kRegExpNonRegExp, methodName); +macro LoadRegExpFunction(implicit context: Context)( + nativeContext: NativeContext): JSFunction { + return UnsafeCast( + nativeContext[NativeContextSlot::REGEXP_FUNCTION_INDEX]); +} + +// Note this doesn't guarantee const-ness of object properties, just +// unchanged object layout. +macro HasInitialRegExpMap(implicit context: Context)(o: HeapObject): bool { + const nativeContext = LoadNativeContext(context); + const function = LoadRegExpFunction(nativeContext); + const initialMap = UnsafeCast(function.prototype_or_initial_map); + return initialMap == o.map; +} + +macro IsReceiverInitialRegExpPrototype(implicit context: Context)( + receiver: Object): bool { + const nativeContext = LoadNativeContext(context); + const regexpFun = LoadRegExpFunction(nativeContext); + const initialMap = UnsafeCast(regexpFun.prototype_or_initial_map); + const initialPrototype: HeapObject = initialMap.prototype; + return TaggedEqual(receiver, initialPrototype); +} + +extern enum Flag constexpr 'JSRegExp::Flag' { + kNone, + kGlobal, + kIgnoreCase, + kMultiline, + kSticky, + kUnicode, + kDotAll, + kInvalid +} + +const kRegExpPrototypeOldFlagGetter: constexpr int31 + generates 'v8::Isolate::kRegExpPrototypeOldFlagGetter'; +const kRegExpPrototypeStickyGetter: constexpr int31 + generates 'v8::Isolate::kRegExpPrototypeStickyGetter'; +const kRegExpPrototypeUnicodeGetter: constexpr int31 + generates 'v8::Isolate::kRegExpPrototypeUnicodeGetter'; + +extern macro RegExpBuiltinsAssembler::FastFlagGetter( + JSRegExp, constexpr Flag): bool; +extern runtime IncrementUseCounter(Context, Smi): void; + +macro FlagGetter(implicit context: Context)( + receiver: Object, flag: constexpr Flag, counter: constexpr int31, + methodName: constexpr string): JSAny { + typeswitch (receiver) { + case (receiver: JSRegExp): { + return SelectBooleanConstant(FastFlagGetter(receiver, flag)); } - if constexpr (counter != -1) { - IncrementUseCounter(context, SmiConstant(counter)); + case (Object): { } - return Undefined; } - - // ES6 21.2.5.4. - // ES #sec-get-regexp.prototype.global - transitioning javascript builtin RegExpPrototypeGlobalGetter( - js-implicit context: NativeContext, receiver: JSAny)(): JSAny { - return FlagGetter( - receiver, Flag::kGlobal, kRegExpPrototypeOldFlagGetter, - 'RegExp.prototype.global'); + if (!IsReceiverInitialRegExpPrototype(receiver)) { + ThrowTypeError(MessageTemplate::kRegExpNonRegExp, methodName); } - - // ES6 21.2.5.5. - // ES #sec-get-regexp.prototype.ignorecase - transitioning javascript builtin RegExpPrototypeIgnoreCaseGetter( - js-implicit context: NativeContext, receiver: JSAny)(): JSAny { - return FlagGetter( - receiver, Flag::kIgnoreCase, kRegExpPrototypeOldFlagGetter, - 'RegExp.prototype.ignoreCase'); + if constexpr (counter != -1) { + IncrementUseCounter(context, SmiConstant(counter)); } + return Undefined; +} - // ES6 21.2.5.7. - // ES #sec-get-regexp.prototype.multiline - transitioning javascript builtin RegExpPrototypeMultilineGetter( - js-implicit context: NativeContext, receiver: JSAny)(): JSAny { - return FlagGetter( - receiver, Flag::kMultiline, kRegExpPrototypeOldFlagGetter, - 'RegExp.prototype.multiline'); - } +// ES6 21.2.5.4. +// ES #sec-get-regexp.prototype.global +transitioning javascript builtin RegExpPrototypeGlobalGetter( + js-implicit context: NativeContext, receiver: JSAny)(): JSAny { + return FlagGetter( + receiver, Flag::kGlobal, kRegExpPrototypeOldFlagGetter, + 'RegExp.prototype.global'); +} - // ES #sec-get-regexp.prototype.dotAll - transitioning javascript builtin RegExpPrototypeDotAllGetter( - js-implicit context: NativeContext, receiver: JSAny)(): JSAny { - const kNoCounter: constexpr int31 = -1; - return FlagGetter( - receiver, Flag::kDotAll, kNoCounter, 'RegExp.prototype.dotAll'); - } +// ES6 21.2.5.5. +// ES #sec-get-regexp.prototype.ignorecase +transitioning javascript builtin RegExpPrototypeIgnoreCaseGetter( + js-implicit context: NativeContext, receiver: JSAny)(): JSAny { + return FlagGetter( + receiver, Flag::kIgnoreCase, kRegExpPrototypeOldFlagGetter, + 'RegExp.prototype.ignoreCase'); +} - // ES6 21.2.5.12. - // ES #sec-get-regexp.prototype.sticky - transitioning javascript builtin RegExpPrototypeStickyGetter( - js-implicit context: NativeContext, receiver: JSAny)(): JSAny { - return FlagGetter( - receiver, Flag::kSticky, kRegExpPrototypeStickyGetter, - 'RegExp.prototype.sticky'); - } +// ES6 21.2.5.7. +// ES #sec-get-regexp.prototype.multiline +transitioning javascript builtin RegExpPrototypeMultilineGetter( + js-implicit context: NativeContext, receiver: JSAny)(): JSAny { + return FlagGetter( + receiver, Flag::kMultiline, kRegExpPrototypeOldFlagGetter, + 'RegExp.prototype.multiline'); +} - // ES6 21.2.5.15. - // ES #sec-get-regexp.prototype.unicode - transitioning javascript builtin RegExpPrototypeUnicodeGetter( - js-implicit context: NativeContext, receiver: JSAny)(): JSAny { - return FlagGetter( - receiver, Flag::kUnicode, kRegExpPrototypeUnicodeGetter, - 'RegExp.prototype.unicode'); - } +// ES #sec-get-regexp.prototype.dotAll +transitioning javascript builtin RegExpPrototypeDotAllGetter( + js-implicit context: NativeContext, receiver: JSAny)(): JSAny { + const kNoCounter: constexpr int31 = -1; + return FlagGetter( + receiver, Flag::kDotAll, kNoCounter, 'RegExp.prototype.dotAll'); +} - extern transitioning macro - RegExpBuiltinsAssembler::FlagsGetter(implicit context: Context)( - Object, constexpr bool): String; +// ES6 21.2.5.12. +// ES #sec-get-regexp.prototype.sticky +transitioning javascript builtin RegExpPrototypeStickyGetter( + js-implicit context: NativeContext, receiver: JSAny)(): JSAny { + return FlagGetter( + receiver, Flag::kSticky, kRegExpPrototypeStickyGetter, + 'RegExp.prototype.sticky'); +} - transitioning macro - FastFlagsGetter(implicit context: Context)(receiver: FastJSRegExp): String { - return FlagsGetter(receiver, true); - } +// ES6 21.2.5.15. +// ES #sec-get-regexp.prototype.unicode +transitioning javascript builtin RegExpPrototypeUnicodeGetter( + js-implicit context: NativeContext, receiver: JSAny)(): JSAny { + return FlagGetter( + receiver, Flag::kUnicode, kRegExpPrototypeUnicodeGetter, + 'RegExp.prototype.unicode'); +} - transitioning macro SlowFlagsGetter(implicit context: - Context)(receiver: JSAny): String { - return FlagsGetter(receiver, false); - } +extern transitioning macro +RegExpBuiltinsAssembler::FlagsGetter(implicit context: Context)( + Object, constexpr bool): String; - // ES #sec-get-regexp.prototype.flags - // TFJ(RegExpPrototypeFlagsGetter, 0, kReceiver) \ - transitioning javascript builtin RegExpPrototypeFlagsGetter( - js-implicit context: NativeContext, receiver: JSAny)(): String { - ThrowIfNotJSReceiver( - receiver, MessageTemplate::kRegExpNonObject, 'RegExp.prototype.flags'); - - // The check is strict because the following code relies on individual flag - // getters on the regexp prototype (e.g.: global, sticky, ...). We don't - // bother to check these individually. - const fastRegexp = Cast(receiver) - otherwise return SlowFlagsGetter(receiver); - return FastFlagsGetter(fastRegexp); - } +transitioning macro +FastFlagsGetter(implicit context: Context)(receiver: FastJSRegExp): String { + return FlagsGetter(receiver, true); +} + +transitioning macro SlowFlagsGetter(implicit context: Context)(receiver: JSAny): + String { + return FlagsGetter(receiver, false); +} - extern transitioning macro RegExpBuiltinsAssembler::SlowLoadLastIndex( - implicit context: Context)(JSAny): JSAny; - extern transitioning macro RegExpBuiltinsAssembler::SlowStoreLastIndex( - implicit context: Context)(JSAny, JSAny): void; +// ES #sec-get-regexp.prototype.flags +// TFJ(RegExpPrototypeFlagsGetter, 0, kReceiver) \ +transitioning javascript builtin RegExpPrototypeFlagsGetter( + js-implicit context: NativeContext, receiver: JSAny)(): String { + ThrowIfNotJSReceiver( + receiver, MessageTemplate::kRegExpNonObject, 'RegExp.prototype.flags'); + + // The check is strict because the following code relies on individual flag + // getters on the regexp prototype (e.g.: global, sticky, ...). We don't + // bother to check these individually. + const fastRegexp = Cast(receiver) + otherwise return SlowFlagsGetter(receiver); + return FastFlagsGetter(fastRegexp); +} - extern macro RegExpBuiltinsAssembler::FastLoadLastIndex(JSRegExp): Smi; - extern macro RegExpBuiltinsAssembler::FastStoreLastIndex(JSRegExp, Smi): void; +extern transitioning macro RegExpBuiltinsAssembler::SlowLoadLastIndex( + implicit context: Context)(JSAny): JSAny; +extern transitioning macro RegExpBuiltinsAssembler::SlowStoreLastIndex( + implicit context: Context)(JSAny, JSAny): void; - @export - transitioning macro LoadLastIndex(implicit context: Context)( - regexp: JSAny, isFastPath: constexpr bool): JSAny { - return isFastPath ? FastLoadLastIndex(UnsafeCast(regexp)) : - SlowLoadLastIndex(regexp); - } +extern macro RegExpBuiltinsAssembler::FastLoadLastIndex(JSRegExp): Smi; +extern macro RegExpBuiltinsAssembler::FastStoreLastIndex(JSRegExp, Smi): void; - @export - transitioning macro LoadLastIndexAsLength(implicit context: Context)( - regexp: JSRegExp, isFastPath: constexpr bool): Number { - const lastIndex = LoadLastIndex(regexp, isFastPath); - if (isFastPath) { - // ToLength on a positive smi is a nop and can be skipped. - return UnsafeCast(lastIndex); - } else { - // Omit ToLength if last_index is a non-negative smi. - typeswitch (lastIndex) { - case (i: PositiveSmi): { - return i; - } - case (o: JSAny): { - return ToLength_Inline(o); - } +@export +transitioning macro LoadLastIndex(implicit context: Context)( + regexp: JSAny, isFastPath: constexpr bool): JSAny { + return isFastPath ? FastLoadLastIndex(UnsafeCast(regexp)) : + SlowLoadLastIndex(regexp); +} + +@export +transitioning macro LoadLastIndexAsLength(implicit context: Context)( + regexp: JSRegExp, isFastPath: constexpr bool): Number { + const lastIndex = LoadLastIndex(regexp, isFastPath); + if (isFastPath) { + // ToLength on a positive smi is a nop and can be skipped. + return UnsafeCast(lastIndex); + } else { + // Omit ToLength if last_index is a non-negative smi. + typeswitch (lastIndex) { + case (i: PositiveSmi): { + return i; + } + case (o: JSAny): { + return ToLength_Inline(o); } } } +} - @export - transitioning macro StoreLastIndex(implicit context: Context)( - regexp: JSAny, value: Number, isFastPath: constexpr bool): void { - if (isFastPath) { - FastStoreLastIndex(UnsafeCast(regexp), UnsafeCast(value)); - } else { - SlowStoreLastIndex(regexp, value); - } +@export +transitioning macro StoreLastIndex(implicit context: Context)( + regexp: JSAny, value: Number, isFastPath: constexpr bool): void { + if (isFastPath) { + FastStoreLastIndex(UnsafeCast(regexp), UnsafeCast(value)); + } else { + SlowStoreLastIndex(regexp, value); } +} - extern builtin - StringIndexOf(implicit context: Context)(String, String, Smi): Smi; - - extern macro RegExpBuiltinsAssembler::AdvanceStringIndex( - String, Number, bool, constexpr bool): Number; - extern macro - RegExpBuiltinsAssembler::AdvanceStringIndexFast(String, Smi, bool): Smi; - extern macro - RegExpBuiltinsAssembler::AdvanceStringIndexSlow(String, Number, bool): Smi; - - type UseCounterFeature extends int31 - constexpr 'v8::Isolate::UseCounterFeature'; - const kRegExpMatchIsTrueishOnNonJSRegExp: constexpr UseCounterFeature - generates 'v8::Isolate::kRegExpMatchIsTrueishOnNonJSRegExp'; - const kRegExpMatchIsFalseishOnJSRegExp: constexpr UseCounterFeature - generates 'v8::Isolate::kRegExpMatchIsFalseishOnJSRegExp'; - const kRegExpPrototypeSourceGetter: constexpr UseCounterFeature - generates 'v8::Isolate::kRegExpPrototypeSourceGetter'; - const kRegExpExecCalledOnSlowRegExp: constexpr UseCounterFeature - generates 'v8::Isolate::kRegExpExecCalledOnSlowRegExp'; - - // ES#sec-isregexp IsRegExp ( argument ) - @export - transitioning macro IsRegExp(implicit context: Context)(obj: JSAny): bool { - const receiver = Cast(obj) otherwise return false; - - // Check @match. - const value = GetProperty(receiver, MatchSymbolConstant()); - if (value == Undefined) { - return Is(receiver); - } - - assert(value != Undefined); - // The common path. Symbol.match exists, equals the RegExpPrototypeMatch - // function (and is thus trueish), and the receiver is a JSRegExp. - if (ToBoolean(value)) { - if (!Is(receiver)) { - IncrementUseCounter( - context, SmiConstant(kRegExpMatchIsTrueishOnNonJSRegExp)); - } - return true; - } +extern builtin +StringIndexOf(implicit context: Context)(String, String, Smi): Smi; + +extern macro RegExpBuiltinsAssembler::AdvanceStringIndex( + String, Number, bool, constexpr bool): Number; +extern macro +RegExpBuiltinsAssembler::AdvanceStringIndexFast(String, Smi, bool): Smi; +extern macro +RegExpBuiltinsAssembler::AdvanceStringIndexSlow(String, Number, bool): Smi; + +type UseCounterFeature extends int31 +constexpr 'v8::Isolate::UseCounterFeature'; +const kRegExpMatchIsTrueishOnNonJSRegExp: constexpr UseCounterFeature + generates 'v8::Isolate::kRegExpMatchIsTrueishOnNonJSRegExp'; +const kRegExpMatchIsFalseishOnJSRegExp: constexpr UseCounterFeature + generates 'v8::Isolate::kRegExpMatchIsFalseishOnJSRegExp'; +const kRegExpPrototypeSourceGetter: constexpr UseCounterFeature + generates 'v8::Isolate::kRegExpPrototypeSourceGetter'; +const kRegExpExecCalledOnSlowRegExp: constexpr UseCounterFeature + generates 'v8::Isolate::kRegExpExecCalledOnSlowRegExp'; + +// ES#sec-isregexp IsRegExp ( argument ) +@export +transitioning macro IsRegExp(implicit context: Context)(obj: JSAny): bool { + const receiver = Cast(obj) otherwise return false; + + // Check @match. + const value = GetProperty(receiver, MatchSymbolConstant()); + if (value == Undefined) { + return Is(receiver); + } - assert(!ToBoolean(value)); - if (Is(receiver)) { + assert(value != Undefined); + // The common path. Symbol.match exists, equals the RegExpPrototypeMatch + // function (and is thus trueish), and the receiver is a JSRegExp. + if (ToBoolean(value)) { + if (!Is(receiver)) { IncrementUseCounter( - context, SmiConstant(kRegExpMatchIsFalseishOnJSRegExp)); + context, SmiConstant(kRegExpMatchIsTrueishOnNonJSRegExp)); } - return false; + return true; } - extern runtime RegExpInitializeAndCompile(Context, JSRegExp, String, String): - JSAny; - - @export - transitioning macro RegExpCreate(implicit context: Context)( - nativeContext: NativeContext, maybeString: JSAny, flags: String): JSAny { - const regexpFun = LoadRegExpFunction(nativeContext); - const initialMap = UnsafeCast(regexpFun.prototype_or_initial_map); - return RegExpCreate(initialMap, maybeString, flags); + assert(!ToBoolean(value)); + if (Is(receiver)) { + IncrementUseCounter(context, SmiConstant(kRegExpMatchIsFalseishOnJSRegExp)); } + return false; +} - @export - transitioning macro RegExpCreate(implicit context: Context)( - initialMap: Map, maybeString: JSAny, flags: String): JSAny { - const pattern: String = - maybeString == Undefined ? kEmptyString : ToString_Inline(maybeString); - const regexp = - UnsafeCast(AllocateFastOrSlowJSObjectFromMap(initialMap)); - return RegExpInitializeAndCompile(context, regexp, pattern, flags); - } +extern runtime RegExpInitializeAndCompile( + Context, JSRegExp, String, String): JSAny; + +@export +transitioning macro RegExpCreate(implicit context: Context)( + nativeContext: NativeContext, maybeString: JSAny, flags: String): JSAny { + const regexpFun = LoadRegExpFunction(nativeContext); + const initialMap = UnsafeCast(regexpFun.prototype_or_initial_map); + return RegExpCreate(initialMap, maybeString, flags); +} + +@export +transitioning macro RegExpCreate(implicit context: Context)( + initialMap: Map, maybeString: JSAny, flags: String): JSAny { + const pattern: String = + maybeString == Undefined ? kEmptyString : ToString_Inline(maybeString); + const regexp = + UnsafeCast(AllocateFastOrSlowJSObjectFromMap(initialMap)); + return RegExpInitializeAndCompile(context, regexp, pattern, flags); +} } diff --git a/deps/v8/src/builtins/string-endswith.tq b/deps/v8/src/builtins/string-endswith.tq index 6941728b39aa87..b9615b114985fd 100644 --- a/deps/v8/src/builtins/string-endswith.tq +++ b/deps/v8/src/builtins/string-endswith.tq @@ -3,79 +3,78 @@ // found in the LICENSE file. namespace string { - macro TryFastStringCompareSequence( - string: String, searchStr: String, start: uintptr, - searchLength: uintptr): Boolean labels Slow { - const directString = Cast(string) otherwise Slow; - const directSearchStr = Cast(searchStr) otherwise Slow; +macro TryFastStringCompareSequence( + string: String, searchStr: String, start: uintptr, + searchLength: uintptr): Boolean labels Slow { + const directString = Cast(string) otherwise Slow; + const directSearchStr = Cast(searchStr) otherwise Slow; - let searchIndex: uintptr = 0; - let stringIndex: uintptr = start; + let searchIndex: uintptr = 0; + let stringIndex: uintptr = start; - while (searchIndex < searchLength) { - if (StringCharCodeAt(directSearchStr, searchIndex) != - StringCharCodeAt(directString, stringIndex)) { - return False; - } - - searchIndex++; - stringIndex++; + while (searchIndex < searchLength) { + if (StringCharCodeAt(directSearchStr, searchIndex) != + StringCharCodeAt(directString, stringIndex)) { + return False; } - return True; + + searchIndex++; + stringIndex++; } + return True; +} - // https://tc39.github.io/ecma262/#sec-string.prototype.endswith - transitioning javascript builtin StringPrototypeEndsWith( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): Boolean { - const searchString: JSAny = arguments[0]; - const endPosition: JSAny = arguments[1]; - const kBuiltinName: constexpr string = 'String.prototype.endsWith'; +// https://tc39.github.io/ecma262/#sec-string.prototype.endswith +transitioning javascript builtin StringPrototypeEndsWith( + js-implicit context: NativeContext, + receiver: JSAny)(...arguments): Boolean { + const searchString: JSAny = arguments[0]; + const endPosition: JSAny = arguments[1]; + const kBuiltinName: constexpr string = 'String.prototype.endsWith'; - // 1. Let O be ? RequireObjectCoercible(this value). - // 2. Let S be ? ToString(O). - const string: String = ToThisString(receiver, kBuiltinName); + // 1. Let O be ? RequireObjectCoercible(this value). + // 2. Let S be ? ToString(O). + const string: String = ToThisString(receiver, kBuiltinName); - // 3. Let isRegExp be ? IsRegExp(searchString). - // 4. If isRegExp is true, throw a TypeError exception. - if (regexp::IsRegExp(searchString)) { - ThrowTypeError(MessageTemplate::kFirstArgumentNotRegExp, kBuiltinName); - } + // 3. Let isRegExp be ? IsRegExp(searchString). + // 4. If isRegExp is true, throw a TypeError exception. + if (regexp::IsRegExp(searchString)) { + ThrowTypeError(MessageTemplate::kFirstArgumentNotRegExp, kBuiltinName); + } - // 5. Let searchStr be ? ToString(searchString). - const searchStr: String = ToString_Inline(searchString); + // 5. Let searchStr be ? ToString(searchString). + const searchStr: String = ToString_Inline(searchString); - // 6. Let len be the length of S. - const len: uintptr = string.length_uintptr; + // 6. Let len be the length of S. + const len: uintptr = string.length_uintptr; - // 7. If endPosition is undefined, let pos be len, - // else let pos be ? ToInteger(endPosition). - // 8. Let end be min(max(pos, 0), len). - const end: uintptr = - (endPosition != Undefined) ? ClampToIndexRange(endPosition, len) : len; + // 7. If endPosition is undefined, let pos be len, + // else let pos be ? ToInteger(endPosition). + // 8. Let end be min(max(pos, 0), len). + const end: uintptr = + (endPosition != Undefined) ? ClampToIndexRange(endPosition, len) : len; - // 9. Let searchLength be the length of searchStr. - const searchLength: uintptr = searchStr.length_uintptr; + // 9. Let searchLength be the length of searchStr. + const searchLength: uintptr = searchStr.length_uintptr; - // 10. Let start be end - searchLength. - const start: uintptr = end - searchLength; + // 10. Let start be end - searchLength. + const start: uintptr = end - searchLength; - // 11. If start is less than 0, return false. - if (Signed(start) < 0) return False; + // 11. If start is less than 0, return false. + if (Signed(start) < 0) return False; - // 12. If the sequence of code units of S starting at start of length - // searchLength is the same as the full code unit sequence of searchStr, - // return true. - // 13. Otherwise, return false. - try { - // Fast Path: If both strings are direct and relevant indices are Smis. - return TryFastStringCompareSequence( - string, searchStr, start, searchLength) otherwise Slow; - } - label Slow { - // Slow Path: If either of the string is indirect, bail into runtime. - return StringCompareSequence( - context, string, searchStr, Convert(start)); - } + // 12. If the sequence of code units of S starting at start of length + // searchLength is the same as the full code unit sequence of searchStr, + // return true. + // 13. Otherwise, return false. + try { + // Fast Path: If both strings are direct and relevant indices are Smis. + return TryFastStringCompareSequence(string, searchStr, start, searchLength) + otherwise Slow; + } label Slow { + // Slow Path: If either of the string is indirect, bail into runtime. + return StringCompareSequence( + context, string, searchStr, Convert(start)); } } +} diff --git a/deps/v8/src/builtins/string-html.tq b/deps/v8/src/builtins/string-html.tq index f12c2dd22f1e03..8b3e01342eaad1 100644 --- a/deps/v8/src/builtins/string-html.tq +++ b/deps/v8/src/builtins/string-html.tq @@ -3,127 +3,124 @@ // found in the LICENSE file. namespace string { - extern runtime StringEscapeQuotes(Context, String): String; - - // https://tc39.github.io/ecma262/#sec-createhtml - transitioning builtin CreateHTML(implicit context: Context)( - receiver: JSAny, methodName: String, tagName: String, attr: String, - attrValue: JSAny): String { - const tagContents: String = ToThisString(receiver, methodName); - let result = '<' + tagName; - if (attr != kEmptyString) { - const attrStringValue: String = - StringEscapeQuotes(context, ToString_Inline(attrValue)); - result = result + ' ' + attr + '=\"' + attrStringValue + '\"'; - } - - return result + '>' + tagContents + ''; +extern runtime StringEscapeQuotes(Context, String): String; + +// https://tc39.github.io/ecma262/#sec-createhtml +transitioning builtin CreateHTML(implicit context: Context)( + receiver: JSAny, methodName: String, tagName: String, attr: String, + attrValue: JSAny): String { + const tagContents: String = ToThisString(receiver, methodName); + let result = '<' + tagName; + if (attr != kEmptyString) { + const attrStringValue: String = + StringEscapeQuotes(context, ToString_Inline(attrValue)); + result = result + ' ' + attr + '=\"' + attrStringValue + '\"'; } - // https://tc39.github.io/ecma262/#sec-string.prototype.anchor - transitioning javascript builtin StringPrototypeAnchor( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): String { - return CreateHTML( - receiver, 'String.prototype.anchor', 'a', 'name', arguments[0]); - } + return result + '>' + tagContents + ''; +} - // https://tc39.github.io/ecma262/#sec-string.prototype.big - transitioning javascript builtin - StringPrototypeBig(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): String { - return CreateHTML( - receiver, 'String.prototype.big', 'big', kEmptyString, kEmptyString); - } +// https://tc39.github.io/ecma262/#sec-string.prototype.anchor +transitioning javascript builtin StringPrototypeAnchor( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + return CreateHTML( + receiver, 'String.prototype.anchor', 'a', 'name', arguments[0]); +} - // https://tc39.github.io/ecma262/#sec-string.prototype.blink - transitioning javascript builtin - StringPrototypeBlink(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): String { - return CreateHTML( - receiver, 'String.prototype.blink', 'blink', kEmptyString, - kEmptyString); - } +// https://tc39.github.io/ecma262/#sec-string.prototype.big +transitioning javascript builtin +StringPrototypeBig( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + return CreateHTML( + receiver, 'String.prototype.big', 'big', kEmptyString, kEmptyString); +} - // https://tc39.github.io/ecma262/#sec-string.prototype.bold - transitioning javascript builtin - StringPrototypeBold(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): String { - return CreateHTML( - receiver, 'String.prototype.bold', 'b', kEmptyString, kEmptyString); - } +// https://tc39.github.io/ecma262/#sec-string.prototype.blink +transitioning javascript builtin +StringPrototypeBlink( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + return CreateHTML( + receiver, 'String.prototype.blink', 'blink', kEmptyString, kEmptyString); +} - // https://tc39.github.io/ecma262/#sec-string.prototype.fontcolor - transitioning javascript builtin - StringPrototypeFontcolor(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): String { - return CreateHTML( - receiver, 'String.prototype.fontcolor', 'font', 'color', arguments[0]); - } +// https://tc39.github.io/ecma262/#sec-string.prototype.bold +transitioning javascript builtin +StringPrototypeBold( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + return CreateHTML( + receiver, 'String.prototype.bold', 'b', kEmptyString, kEmptyString); +} - // https://tc39.github.io/ecma262/#sec-string.prototype.fontsize - transitioning javascript builtin - StringPrototypeFontsize(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): String { - return CreateHTML( - receiver, 'String.prototype.fontsize', 'font', 'size', arguments[0]); - } +// https://tc39.github.io/ecma262/#sec-string.prototype.fontcolor +transitioning javascript builtin +StringPrototypeFontcolor( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + return CreateHTML( + receiver, 'String.prototype.fontcolor', 'font', 'color', arguments[0]); +} - // https://tc39.github.io/ecma262/#sec-string.prototype.fixed - transitioning javascript builtin - StringPrototypeFixed(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): String { - return CreateHTML( - receiver, 'String.prototype.fixed', 'tt', kEmptyString, kEmptyString); - } +// https://tc39.github.io/ecma262/#sec-string.prototype.fontsize +transitioning javascript builtin +StringPrototypeFontsize( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + return CreateHTML( + receiver, 'String.prototype.fontsize', 'font', 'size', arguments[0]); +} - // https://tc39.github.io/ecma262/#sec-string.prototype.italics - transitioning javascript builtin - StringPrototypeItalics(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): String { - return CreateHTML( - receiver, 'String.prototype.italics', 'i', kEmptyString, kEmptyString); - } +// https://tc39.github.io/ecma262/#sec-string.prototype.fixed +transitioning javascript builtin +StringPrototypeFixed( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + return CreateHTML( + receiver, 'String.prototype.fixed', 'tt', kEmptyString, kEmptyString); +} - // https://tc39.github.io/ecma262/#sec-string.prototype.link - transitioning javascript builtin - StringPrototypeLink(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): String { - return CreateHTML( - receiver, 'String.prototype.link', 'a', 'href', arguments[0]); - } +// https://tc39.github.io/ecma262/#sec-string.prototype.italics +transitioning javascript builtin +StringPrototypeItalics( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + return CreateHTML( + receiver, 'String.prototype.italics', 'i', kEmptyString, kEmptyString); +} - // https://tc39.github.io/ecma262/#sec-string.prototype.small - transitioning javascript builtin - StringPrototypeSmall(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): String { - return CreateHTML( - receiver, 'String.prototype.small', 'small', kEmptyString, - kEmptyString); - } +// https://tc39.github.io/ecma262/#sec-string.prototype.link +transitioning javascript builtin +StringPrototypeLink( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + return CreateHTML( + receiver, 'String.prototype.link', 'a', 'href', arguments[0]); +} - // https://tc39.github.io/ecma262/#sec-string.prototype.strike - transitioning javascript builtin - StringPrototypeStrike(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): String { - return CreateHTML( - receiver, 'String.prototype.strike', 'strike', kEmptyString, - kEmptyString); - } +// https://tc39.github.io/ecma262/#sec-string.prototype.small +transitioning javascript builtin +StringPrototypeSmall( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + return CreateHTML( + receiver, 'String.prototype.small', 'small', kEmptyString, kEmptyString); +} - // https://tc39.github.io/ecma262/#sec-string.prototype.sub - transitioning javascript builtin - StringPrototypeSub(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): String { - return CreateHTML( - receiver, 'String.prototype.sub', 'sub', kEmptyString, kEmptyString); - } +// https://tc39.github.io/ecma262/#sec-string.prototype.strike +transitioning javascript builtin +StringPrototypeStrike( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + return CreateHTML( + receiver, 'String.prototype.strike', 'strike', kEmptyString, + kEmptyString); +} - // https://tc39.github.io/ecma262/#sec-string.prototype.sup - transitioning javascript builtin - StringPrototypeSup(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): String { - return CreateHTML( - receiver, 'String.prototype.sup', 'sup', kEmptyString, kEmptyString); - } +// https://tc39.github.io/ecma262/#sec-string.prototype.sub +transitioning javascript builtin +StringPrototypeSub( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + return CreateHTML( + receiver, 'String.prototype.sub', 'sub', kEmptyString, kEmptyString); +} + +// https://tc39.github.io/ecma262/#sec-string.prototype.sup +transitioning javascript builtin +StringPrototypeSup( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + return CreateHTML( + receiver, 'String.prototype.sup', 'sup', kEmptyString, kEmptyString); +} } diff --git a/deps/v8/src/builtins/string-iterator.tq b/deps/v8/src/builtins/string-iterator.tq index 79032e0e28074e..eea7a621f0f8ae 100644 --- a/deps/v8/src/builtins/string-iterator.tq +++ b/deps/v8/src/builtins/string-iterator.tq @@ -4,43 +4,43 @@ namespace string { - macro NewJSStringIterator(implicit context: Context)( - string: String, nextIndex: Smi): JSStringIterator { - return new JSStringIterator{ - map: GetInitialStringIteratorMap(), - properties_or_hash: kEmptyFixedArray, - elements: kEmptyFixedArray, - string: string, - index: nextIndex - }; - } +macro NewJSStringIterator(implicit context: Context)( + string: String, nextIndex: Smi): JSStringIterator { + return new JSStringIterator{ + map: GetInitialStringIteratorMap(), + properties_or_hash: kEmptyFixedArray, + elements: kEmptyFixedArray, + string: string, + index: nextIndex + }; +} - // ES6 #sec-string.prototype-@@iterator - transitioning javascript builtin StringPrototypeIterator( - js-implicit context: NativeContext, receiver: JSAny)(): JSStringIterator { - const name: String = - ToThisString(receiver, 'String.prototype[Symbol.iterator]'); - const index: Smi = 0; - return NewJSStringIterator(name, index); - } +// ES6 #sec-string.prototype-@@iterator +transitioning javascript builtin StringPrototypeIterator( + js-implicit context: NativeContext, receiver: JSAny)(): JSStringIterator { + const name: String = + ToThisString(receiver, 'String.prototype[Symbol.iterator]'); + const index: Smi = 0; + return NewJSStringIterator(name, index); +} - // ES6 #sec-%stringiteratorprototype%.next - transitioning javascript builtin StringIteratorPrototypeNext( - js-implicit context: NativeContext, receiver: JSAny)(): JSObject { - const iterator = Cast(receiver) otherwise ThrowTypeError( - MessageTemplate::kIncompatibleMethodReceiver, - 'String Iterator.prototype.next', receiver); - const string = iterator.string; - const position: intptr = SmiUntag(iterator.index); - const length: intptr = string.length_intptr; - if (position >= length) { - return AllocateJSIteratorResult(Undefined, True); - } - // Move to next codepoint. - const encoding = UnicodeEncoding::UTF16; - const ch = string::LoadSurrogatePairAt(string, length, position, encoding); - const value: String = string::StringFromSingleUTF16EncodedCodePoint(ch); - iterator.index = SmiTag(position + value.length_intptr); - return AllocateJSIteratorResult(value, False); +// ES6 #sec-%stringiteratorprototype%.next +transitioning javascript builtin StringIteratorPrototypeNext( + js-implicit context: NativeContext, receiver: JSAny)(): JSObject { + const iterator = Cast(receiver) otherwise ThrowTypeError( + MessageTemplate::kIncompatibleMethodReceiver, + 'String Iterator.prototype.next', receiver); + const string = iterator.string; + const position: intptr = SmiUntag(iterator.index); + const length: intptr = string.length_intptr; + if (position >= length) { + return AllocateJSIteratorResult(Undefined, True); } + // Move to next codepoint. + const encoding = UnicodeEncoding::UTF16; + const ch = string::LoadSurrogatePairAt(string, length, position, encoding); + const value: String = string::StringFromSingleUTF16EncodedCodePoint(ch); + iterator.index = SmiTag(position + value.length_intptr); + return AllocateJSIteratorResult(value, False); +} } diff --git a/deps/v8/src/builtins/string-pad.tq b/deps/v8/src/builtins/string-pad.tq index 4a4c3704065ae1..b95e68628a4497 100644 --- a/deps/v8/src/builtins/string-pad.tq +++ b/deps/v8/src/builtins/string-pad.tq @@ -6,106 +6,106 @@ namespace string { - extern transitioning builtin - StringSubstring(implicit context: Context)(String, intptr, intptr): String; +extern transitioning builtin +StringSubstring(implicit context: Context)(String, intptr, intptr): String; - const kStringPadStart: constexpr int31 = 0; - const kStringPadEnd: constexpr int31 = 1; +const kStringPadStart: constexpr int31 = 0; +const kStringPadEnd: constexpr int31 = 1; - transitioning macro StringPad(implicit context: Context)( - receiver: JSAny, arguments: Arguments, methodName: constexpr string, - variant: constexpr int31): String { - const receiverString: String = ToThisString(receiver, methodName); - const stringLength: Smi = receiverString.length_smi; +transitioning macro StringPad(implicit context: Context)( + receiver: JSAny, arguments: Arguments, methodName: constexpr string, + variant: constexpr int31): String { + const receiverString: String = ToThisString(receiver, methodName); + const stringLength: Smi = receiverString.length_smi; - if (arguments.length == 0) { - return receiverString; - } - const maxLength: Number = ToLength_Inline(arguments[0]); - assert(IsNumberNormalized(maxLength)); - - typeswitch (maxLength) { - case (smiMaxLength: Smi): { - if (smiMaxLength <= stringLength) { - return receiverString; - } - } - case (Number): { - } - } + if (arguments.length == 0) { + return receiverString; + } + const maxLength: Number = ToLength_Inline(arguments[0]); + assert(IsNumberNormalized(maxLength)); - let fillString: String = ' '; - let fillLength: intptr = 1; - - if (arguments.length != 1) { - const fill = arguments[1]; - if (fill != Undefined) { - fillString = ToString_Inline(fill); - fillLength = fillString.length_intptr; - if (fillLength == 0) { - return receiverString; - } + typeswitch (maxLength) { + case (smiMaxLength: Smi): { + if (smiMaxLength <= stringLength) { + return receiverString; } } - - // Pad. - assert(fillLength > 0); - // Throw if max_length is greater than String::kMaxLength. - if (!TaggedIsSmi(maxLength)) { - ThrowInvalidStringLength(context); + case (Number): { } + } - const smiMaxLength: Smi = UnsafeCast(maxLength); - if (smiMaxLength > SmiConstant(kStringMaxLength)) { - ThrowInvalidStringLength(context); - } - assert(smiMaxLength > stringLength); - const padLength: Smi = smiMaxLength - stringLength; - - let padding: String; - if (fillLength == 1) { - // Single char fill. - // Fast path for a single character fill. No need to calculate number of - // repetitions or remainder. - padding = StringRepeat(context, fillString, padLength); - } else { - // Multi char fill. - const fillLengthWord32: int32 = TruncateIntPtrToInt32(fillLength); - const padLengthWord32: int32 = Convert(padLength); - const repetitionsWord32: int32 = padLengthWord32 / fillLengthWord32; - const remainingWord32: int32 = padLengthWord32 % fillLengthWord32; - padding = - StringRepeat(context, fillString, Convert(repetitionsWord32)); - - if (remainingWord32 != 0) { - const remainderString = - StringSubstring(fillString, 0, Convert(remainingWord32)); - padding = padding + remainderString; + let fillString: String = ' '; + let fillLength: intptr = 1; + + if (arguments.length != 1) { + const fill = arguments[1]; + if (fill != Undefined) { + fillString = ToString_Inline(fill); + fillLength = fillString.length_intptr; + if (fillLength == 0) { + return receiverString; } } + } - // Return result. - assert(padLength == padding.length_smi); - if (variant == kStringPadStart) { - return padding + receiverString; - } - assert(variant == kStringPadEnd); - return receiverString + padding; + // Pad. + assert(fillLength > 0); + // Throw if max_length is greater than String::kMaxLength. + if (!TaggedIsSmi(maxLength)) { + ThrowInvalidStringLength(context); } - // ES6 #sec-string.prototype.padstart - transitioning javascript builtin - StringPrototypePadStart(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): String { - const methodName: constexpr string = 'String.prototype.padStart'; - return StringPad(receiver, arguments, methodName, kStringPadStart); + const smiMaxLength: Smi = UnsafeCast(maxLength); + if (smiMaxLength > SmiConstant(kStringMaxLength)) { + ThrowInvalidStringLength(context); + } + assert(smiMaxLength > stringLength); + const padLength: Smi = smiMaxLength - stringLength; + + let padding: String; + if (fillLength == 1) { + // Single char fill. + // Fast path for a single character fill. No need to calculate number of + // repetitions or remainder. + padding = StringRepeat(context, fillString, padLength); + } else { + // Multi char fill. + const fillLengthWord32: int32 = TruncateIntPtrToInt32(fillLength); + const padLengthWord32: int32 = Convert(padLength); + const repetitionsWord32: int32 = padLengthWord32 / fillLengthWord32; + const remainingWord32: int32 = padLengthWord32 % fillLengthWord32; + padding = + StringRepeat(context, fillString, Convert(repetitionsWord32)); + + if (remainingWord32 != 0) { + const remainderString = + StringSubstring(fillString, 0, Convert(remainingWord32)); + padding = padding + remainderString; + } } - // ES6 #sec-string.prototype.padend - transitioning javascript builtin - StringPrototypePadEnd(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): String { - const methodName: constexpr string = 'String.prototype.padEnd'; - return StringPad(receiver, arguments, methodName, kStringPadEnd); + // Return result. + assert(padLength == padding.length_smi); + if (variant == kStringPadStart) { + return padding + receiverString; } + assert(variant == kStringPadEnd); + return receiverString + padding; +} + +// ES6 #sec-string.prototype.padstart +transitioning javascript builtin +StringPrototypePadStart( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + const methodName: constexpr string = 'String.prototype.padStart'; + return StringPad(receiver, arguments, methodName, kStringPadStart); +} + +// ES6 #sec-string.prototype.padend +transitioning javascript builtin +StringPrototypePadEnd( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + const methodName: constexpr string = 'String.prototype.padEnd'; + return StringPad(receiver, arguments, methodName, kStringPadEnd); +} } diff --git a/deps/v8/src/builtins/string-repeat.tq b/deps/v8/src/builtins/string-repeat.tq index e3e72ae7b5882a..e1e33eb53abe22 100644 --- a/deps/v8/src/builtins/string-repeat.tq +++ b/deps/v8/src/builtins/string-repeat.tq @@ -3,76 +3,72 @@ // found in the LICENSE file. namespace string { - const kBuiltinName: constexpr string = 'String.prototype.repeat'; +const kBuiltinName: constexpr string = 'String.prototype.repeat'; - builtin StringRepeat(implicit context: Context)(string: String, count: Smi): - String { - assert(count >= 0); - assert(string != kEmptyString); +builtin StringRepeat(implicit context: Context)( + string: String, count: Smi): String { + assert(count >= 0); + assert(string != kEmptyString); - let result: String = kEmptyString; - let powerOfTwoRepeats: String = string; - let n: intptr = Convert(count); + let result: String = kEmptyString; + let powerOfTwoRepeats: String = string; + let n: intptr = Convert(count); - while (true) { - if ((n & 1) == 1) result = result + powerOfTwoRepeats; + while (true) { + if ((n & 1) == 1) result = result + powerOfTwoRepeats; - n = n >> 1; - if (n == 0) break; + n = n >> 1; + if (n == 0) break; - powerOfTwoRepeats = powerOfTwoRepeats + powerOfTwoRepeats; - } - - return result; + powerOfTwoRepeats = powerOfTwoRepeats + powerOfTwoRepeats; } - // https://tc39.github.io/ecma262/#sec-string.prototype.repeat - transitioning javascript builtin StringPrototypeRepeat( - js-implicit context: NativeContext, - receiver: JSAny)(count: JSAny): String { - // 1. Let O be ? RequireObjectCoercible(this value). - // 2. Let S be ? ToString(O). - const s: String = ToThisString(receiver, kBuiltinName); + return result; +} - try { - // 3. Let n be ? ToInteger(count). - typeswitch (ToInteger_Inline(count)) { - case (n: Smi): { - // 4. If n < 0, throw a RangeError exception. - if (n < 0) goto InvalidCount; +// https://tc39.github.io/ecma262/#sec-string.prototype.repeat +transitioning javascript builtin StringPrototypeRepeat( + js-implicit context: NativeContext, receiver: JSAny)(count: JSAny): String { + // 1. Let O be ? RequireObjectCoercible(this value). + // 2. Let S be ? ToString(O). + const s: String = ToThisString(receiver, kBuiltinName); - // 6. If n is 0, return the empty String. - if (n == 0 || s.length_uint32 == 0) goto EmptyString; + try { + // 3. Let n be ? ToInteger(count). + typeswitch (ToInteger_Inline(count)) { + case (n: Smi): { + // 4. If n < 0, throw a RangeError exception. + if (n < 0) goto InvalidCount; - if (n > kStringMaxLength) goto InvalidStringLength; + // 6. If n is 0, return the empty String. + if (n == 0 || s.length_uint32 == 0) goto EmptyString; - // 7. Return the String value that is made from n copies of S appended - // together. - return StringRepeat(s, n); - } - case (heapNum: HeapNumber): deferred { - assert(IsNumberNormalized(heapNum)); - const n = LoadHeapNumberValue(heapNum); + if (n > kStringMaxLength) goto InvalidStringLength; + + // 7. Return the String value that is made from n copies of S appended + // together. + return StringRepeat(s, n); + } + case (heapNum: HeapNumber): deferred { + assert(IsNumberNormalized(heapNum)); + const n = LoadHeapNumberValue(heapNum); - // 4. If n < 0, throw a RangeError exception. - // 5. If n is +∞, throw a RangeError exception. - if (n == V8_INFINITY || n < 0.0) goto InvalidCount; + // 4. If n < 0, throw a RangeError exception. + // 5. If n is +∞, throw a RangeError exception. + if (n == V8_INFINITY || n < 0.0) goto InvalidCount; - // 6. If n is 0, return the empty String. - if (s.length_uint32 == 0) goto EmptyString; + // 6. If n is 0, return the empty String. + if (s.length_uint32 == 0) goto EmptyString; - goto InvalidStringLength; - } + goto InvalidStringLength; } } - label EmptyString { - return kEmptyString; - } - label InvalidCount deferred { - ThrowRangeError(MessageTemplate::kInvalidCountValue, count); - } - label InvalidStringLength deferred { - ThrowInvalidStringLength(context); - } + } label EmptyString { + return kEmptyString; + } label InvalidCount deferred { + ThrowRangeError(MessageTemplate::kInvalidCountValue, count); + } label InvalidStringLength deferred { + ThrowInvalidStringLength(context); } } +} diff --git a/deps/v8/src/builtins/string-replaceall.tq b/deps/v8/src/builtins/string-replaceall.tq index c7589f18a66bd4..9211304b345214 100644 --- a/deps/v8/src/builtins/string-replaceall.tq +++ b/deps/v8/src/builtins/string-replaceall.tq @@ -5,218 +5,216 @@ #include 'src/builtins/builtins-string-gen.h' namespace string { - extern macro ReplaceSymbolConstant(): Symbol; - - extern macro StringBuiltinsAssembler::GetSubstitution( - implicit context: Context)(String, Smi, Smi, String): String; - - extern builtin - StringIndexOf(implicit context: Context)(String, String, Smi): Smi; - - macro TryFastAbstractStringIndexOf(implicit context: Context)( - string: String, searchString: String, fromIndex: Smi): Smi labels Slow { - const stringLen = string.length_uintptr; - const searchLen = searchString.length_uintptr; - const directString = Cast(string) otherwise Slow; - const directSearchStr = Cast(searchString) otherwise Slow; - const fromIndexUint = Unsigned(SmiUntag(fromIndex)); - - for (let i: uintptr = fromIndexUint; i < stringLen; i++) { - let j = i; - let k: uintptr = 0; - while (j < stringLen && k < searchLen && - StringCharCodeAt(directString, j) == - StringCharCodeAt(directSearchStr, k)) { - j++; - k++; - } - if (k == searchLen) { - return SmiTag(Signed(i)); - } +extern macro ReplaceSymbolConstant(): Symbol; + +extern macro StringBuiltinsAssembler::GetSubstitution( + implicit context: Context)(String, Smi, Smi, String): String; + +extern builtin +StringIndexOf(implicit context: Context)(String, String, Smi): Smi; + +macro TryFastAbstractStringIndexOf(implicit context: Context)( + string: String, searchString: String, fromIndex: Smi): Smi labels Slow { + const stringLen = string.length_uintptr; + const searchLen = searchString.length_uintptr; + const directString = Cast(string) otherwise Slow; + const directSearchStr = Cast(searchString) otherwise Slow; + const fromIndexUint = Unsigned(SmiUntag(fromIndex)); + + for (let i: uintptr = fromIndexUint; i < stringLen; i++) { + let j = i; + let k: uintptr = 0; + while (j < stringLen && k < searchLen && + StringCharCodeAt(directString, j) == + StringCharCodeAt(directSearchStr, k)) { + j++; + k++; + } + if (k == searchLen) { + return SmiTag(Signed(i)); } - return -1; } + return -1; +} - macro AbstractStringIndexOf(implicit context: Context)( - string: String, searchString: String, fromIndex: Smi): Smi { - // Special case the empty string. - const searchStringLength = searchString.length_intptr; - const stringLength = string.length_intptr; - if (searchStringLength == 0 && SmiUntag(fromIndex) <= stringLength) { - return fromIndex; - } +macro AbstractStringIndexOf(implicit context: Context)( + string: String, searchString: String, fromIndex: Smi): Smi { + // Special case the empty string. + const searchStringLength = searchString.length_intptr; + const stringLength = string.length_intptr; + if (searchStringLength == 0 && SmiUntag(fromIndex) <= stringLength) { + return fromIndex; + } - // Don't bother to search if the searchString would go past the end - // of the string. This is actually necessary because of runtime - // checks. - if (SmiUntag(fromIndex) + searchStringLength > stringLength) { - return -1; - } + // Don't bother to search if the searchString would go past the end + // of the string. This is actually necessary because of runtime + // checks. + if (SmiUntag(fromIndex) + searchStringLength > stringLength) { + return -1; + } - try { - return TryFastAbstractStringIndexOf(string, searchString, fromIndex) - otherwise Slow; - } - label Slow { - for (let i: intptr = SmiUntag(fromIndex); - i + searchStringLength <= stringLength; i++) { - if (StringCompareSequence( - context, string, searchString, Convert(SmiTag(i))) == - True) { - return SmiTag(i); - } + try { + return TryFastAbstractStringIndexOf(string, searchString, fromIndex) + otherwise Slow; + } label Slow { + for (let i: intptr = SmiUntag(fromIndex); + i + searchStringLength <= stringLength; i++) { + if (StringCompareSequence( + context, string, searchString, Convert(SmiTag(i))) == + True) { + return SmiTag(i); } - return -1; } + return -1; } +} - transitioning macro - ThrowIfNotGlobal(implicit context: Context)(searchValue: JSAny): void { - let shouldThrow: bool; - typeswitch (searchValue) { - case (fastRegExp: FastJSRegExp): { - shouldThrow = !fastRegExp.global; - } - case (Object): { - const flags = GetProperty(searchValue, 'flags'); - RequireObjectCoercible(flags, 'String.prototype.replaceAll'); - shouldThrow = - StringIndexOf(ToString_Inline(flags), StringConstant('g'), 0) == -1; - } +transitioning macro +ThrowIfNotGlobal(implicit context: Context)(searchValue: JSAny): void { + let shouldThrow: bool; + typeswitch (searchValue) { + case (fastRegExp: FastJSRegExp): { + shouldThrow = !fastRegExp.global; } - if (shouldThrow) { - ThrowTypeError( - MessageTemplate::kRegExpGlobalInvokedOnNonGlobal, - 'String.prototype.replaceAll'); + case (Object): { + const flags = GetProperty(searchValue, 'flags'); + RequireObjectCoercible(flags, 'String.prototype.replaceAll'); + shouldThrow = + StringIndexOf(ToString_Inline(flags), StringConstant('g'), 0) == -1; } } + if (shouldThrow) { + ThrowTypeError( + MessageTemplate::kRegExpGlobalInvokedOnNonGlobal, + 'String.prototype.replaceAll'); + } +} - // https://tc39.es/ecma262/#sec-string.prototype.replaceall - transitioning javascript builtin StringPrototypeReplaceAll( - js-implicit context: NativeContext, - receiver: JSAny)(searchValue: JSAny, replaceValue: JSAny): JSAny { - // 1. Let O be ? RequireObjectCoercible(this value). - RequireObjectCoercible(receiver, 'String.prototype.replaceAll'); - - // 2. If searchValue is neither undefined nor null, then - if (searchValue != Undefined && searchValue != Null) { - // a. Let isRegExp be ? IsRegExp(searchString). - // b. If isRegExp is true, then - // i. Let flags be ? Get(searchValue, "flags"). - // ii. Perform ? RequireObjectCoercible(flags). - // iii. If ? ToString(flags) does not contain "g", throw a - // TypeError exception. - if (regexp::IsRegExp(searchValue)) { - ThrowIfNotGlobal(searchValue); - } - - // TODO(joshualitt): We could easily add fast paths for string - // searchValues and potential FastRegExps. - // c. Let replacer be ? GetMethod(searchValue, @@replace). - // d. If replacer is not undefined, then - // i. Return ? Call(replacer, searchValue, « O, replaceValue »). - try { - const replacer = GetMethod(searchValue, ReplaceSymbolConstant()) - otherwise ReplaceSymbolIsNullOrUndefined; - return Call(context, replacer, searchValue, receiver, replaceValue); - } - label ReplaceSymbolIsNullOrUndefined {} +// https://tc39.es/ecma262/#sec-string.prototype.replaceall +transitioning javascript builtin StringPrototypeReplaceAll( + js-implicit context: NativeContext, receiver: JSAny)( + searchValue: JSAny, replaceValue: JSAny): JSAny { + // 1. Let O be ? RequireObjectCoercible(this value). + RequireObjectCoercible(receiver, 'String.prototype.replaceAll'); + + // 2. If searchValue is neither undefined nor null, then + if (searchValue != Undefined && searchValue != Null) { + // a. Let isRegExp be ? IsRegExp(searchString). + // b. If isRegExp is true, then + // i. Let flags be ? Get(searchValue, "flags"). + // ii. Perform ? RequireObjectCoercible(flags). + // iii. If ? ToString(flags) does not contain "g", throw a + // TypeError exception. + if (regexp::IsRegExp(searchValue)) { + ThrowIfNotGlobal(searchValue); } - // 3. Let string be ? ToString(O). - const string = ToString_Inline(receiver); + // TODO(joshualitt): We could easily add fast paths for string + // searchValues and potential FastRegExps. + // c. Let replacer be ? GetMethod(searchValue, @@replace). + // d. If replacer is not undefined, then + // i. Return ? Call(replacer, searchValue, « O, replaceValue »). + try { + const replacer = GetMethod(searchValue, ReplaceSymbolConstant()) + otherwise ReplaceSymbolIsNullOrUndefined; + return Call(context, replacer, searchValue, receiver, replaceValue); + } label ReplaceSymbolIsNullOrUndefined {} + } - // 4. Let searchString be ? ToString(searchValue). - const searchString = ToString_Inline(searchValue); + // 3. Let string be ? ToString(O). + const string = ToString_Inline(receiver); - // 5. Let functionalReplace be IsCallable(replaceValue). - let replaceValueArg = replaceValue; - const functionalReplace = TaggedIsCallable(replaceValue); + // 4. Let searchString be ? ToString(searchValue). + const searchString = ToString_Inline(searchValue); - // 6. If functionalReplace is false, then - if (!functionalReplace) { - // a. Let replaceValue be ? ToString(replaceValue). - replaceValueArg = ToString_Inline(replaceValue); - } + // 5. Let functionalReplace be IsCallable(replaceValue). + let replaceValueArg = replaceValue; + const functionalReplace = Is(replaceValue); - // 7. Let searchLength be the length of searchString. - const searchLength = searchString.length_smi; - - // 8. Let advanceBy be max(1, searchLength). - const advanceBy = SmiMax(1, searchLength); - - // We combine the two loops from the spec into one to avoid - // needing a growable array. - // - // 9. Let matchPositions be a new empty List. - // 10. Let position be ! StringIndexOf(string, searchString, 0). - // 11. Repeat, while position is not -1 - // a. Append position to the end of matchPositions. - // b. Let position be ! StringIndexOf(string, searchString, - // position + advanceBy). - // 12. Let endOfLastMatch be 0. - // 13. Let result be the empty string value. - // 14. For each position in matchPositions, do - let endOfLastMatch: Smi = 0; - let result: String = kEmptyString; - let position = AbstractStringIndexOf(string, searchString, 0); - while (position != -1) { - // a. If functionalReplace is true, then - // b. Else, - let replacement: String; - if (functionalReplace) { - // i. Let replacement be ? ToString(? Call(replaceValue, undefined, - // « searchString, position, - // string »). - replacement = ToString_Inline(Call( - context, UnsafeCast(replaceValueArg), Undefined, - searchString, position, string)); - } else { - // i. Assert: Type(replaceValue) is String. - const replaceValueString = UnsafeCast(replaceValueArg); - - // ii. Let captures be a new empty List. - // iii. Let replacement be GetSubstitution(searchString, - // string, position, captures, - // undefined, replaceValue). - // Note: Instead we just call a simpler GetSubstitution primitive. - const matchEndPosition = position + searchLength; - replacement = GetSubstitution( - string, position, matchEndPosition, replaceValueString); - } + // 6. If functionalReplace is false, then + if (!functionalReplace) { + // a. Let replaceValue be ? ToString(replaceValue). + replaceValueArg = ToString_Inline(replaceValue); + } - // c. Let stringSlice be the substring of string consisting of the code - // units from endOfLastMatch (inclusive) up through position - // (exclusive). - const stringSlice = string::SubString( - string, Unsigned(SmiUntag(endOfLastMatch)), - Unsigned(SmiUntag(position))); + // 7. Let searchLength be the length of searchString. + const searchLength = searchString.length_smi; + + // 8. Let advanceBy be max(1, searchLength). + const advanceBy = SmiMax(1, searchLength); + + // We combine the two loops from the spec into one to avoid + // needing a growable array. + // + // 9. Let matchPositions be a new empty List. + // 10. Let position be ! StringIndexOf(string, searchString, 0). + // 11. Repeat, while position is not -1 + // a. Append position to the end of matchPositions. + // b. Let position be ! StringIndexOf(string, searchString, + // position + advanceBy). + // 12. Let endOfLastMatch be 0. + // 13. Let result be the empty string value. + // 14. For each position in matchPositions, do + let endOfLastMatch: Smi = 0; + let result: String = kEmptyString; + let position = AbstractStringIndexOf(string, searchString, 0); + while (position != -1) { + // a. If functionalReplace is true, then + // b. Else, + let replacement: String; + if (functionalReplace) { + // i. Let replacement be ? ToString(? Call(replaceValue, undefined, + // « searchString, position, + // string »). + replacement = ToString_Inline(Call( + context, UnsafeCast(replaceValueArg), Undefined, + searchString, position, string)); + } else { + // i. Assert: Type(replaceValue) is String. + const replaceValueString = UnsafeCast(replaceValueArg); + + // ii. Let captures be a new empty List. + // iii. Let replacement be GetSubstitution(searchString, + // string, position, captures, + // undefined, replaceValue). + // Note: Instead we just call a simpler GetSubstitution primitive. + const matchEndPosition = position + searchLength; + replacement = GetSubstitution( + string, position, matchEndPosition, replaceValueString); + } - // d. Let result be the string-concatenation of result, stringSlice, - // and replacement. - // TODO(joshualitt): This leaves a completely degenerate ConsString tree. - // We could be smarter here. - result = result + stringSlice + replacement; + // c. Let stringSlice be the substring of string consisting of the code + // units from endOfLastMatch (inclusive) up through position + // (exclusive). + const stringSlice = string::SubString( + string, Unsigned(SmiUntag(endOfLastMatch)), + Unsigned(SmiUntag(position))); - // e. Let endOfLastMatch be position + searchLength. - endOfLastMatch = position + searchLength; + // d. Let result be the string-concatenation of result, stringSlice, + // and replacement. + // TODO(joshualitt): This leaves a completely degenerate ConsString tree. + // We could be smarter here. + result = result + stringSlice + replacement; - position = - AbstractStringIndexOf(string, searchString, position + advanceBy); - } + // e. Let endOfLastMatch be position + searchLength. + endOfLastMatch = position + searchLength; - // 15. If endOfLastMatch < the length of string, then - if (endOfLastMatch < string.length_smi) { - // a. Let result be the string-concatenation of result and the substring - // of string consisting of the code units from endOfLastMatch - // (inclusive) up through the final code unit of string (inclusive). - result = result + - string::SubString( - string, Unsigned(SmiUntag(endOfLastMatch)), - Unsigned(string.length_intptr)); - } + position = + AbstractStringIndexOf(string, searchString, position + advanceBy); + } - // 16. Return result. - return result; + // 15. If endOfLastMatch < the length of string, then + if (endOfLastMatch < string.length_smi) { + // a. Let result be the string-concatenation of result and the substring + // of string consisting of the code units from endOfLastMatch + // (inclusive) up through the final code unit of string (inclusive). + result = result + + string::SubString( + string, Unsigned(SmiUntag(endOfLastMatch)), + Unsigned(string.length_intptr)); } + + // 16. Return result. + return result; +} } diff --git a/deps/v8/src/builtins/string-slice.tq b/deps/v8/src/builtins/string-slice.tq index ea95d44a82381a..71442a28faaf2a 100644 --- a/deps/v8/src/builtins/string-slice.tq +++ b/deps/v8/src/builtins/string-slice.tq @@ -3,33 +3,32 @@ // found in the LICENSE file. namespace string { - // ES6 #sec-string.prototype.slice ( start, end ) - // https://tc39.github.io/ecma262/#sec-string.prototype.slice - transitioning javascript builtin StringPrototypeSlice( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): String { - // 1. Let O be ? RequireObjectCoercible(this value). - // 2. Let S be ? ToString(O). - const string: String = ToThisString(receiver, 'String.prototype.slice'); +// ES6 #sec-string.prototype.slice ( start, end ) +// https://tc39.github.io/ecma262/#sec-string.prototype.slice +transitioning javascript builtin StringPrototypeSlice( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + // 1. Let O be ? RequireObjectCoercible(this value). + // 2. Let S be ? ToString(O). + const string: String = ToThisString(receiver, 'String.prototype.slice'); - // 3. Let len be the number of elements in S. - const length: uintptr = string.length_uintptr; + // 3. Let len be the number of elements in S. + const length: uintptr = string.length_uintptr; - // Convert {start} to a relative index. - const arg0 = arguments[0]; - const start: uintptr = - arg0 != Undefined ? ConvertToRelativeIndex(arg0, length) : 0; + // Convert {start} to a relative index. + const arg0 = arguments[0]; + const start: uintptr = + arg0 != Undefined ? ConvertToRelativeIndex(arg0, length) : 0; - // 5. If end is undefined, let intEnd be len; - // else Convert {end} to a relative index. - const arg1 = arguments[1]; - const end: uintptr = - arg1 != Undefined ? ConvertToRelativeIndex(arg1, length) : length; + // 5. If end is undefined, let intEnd be len; + // else Convert {end} to a relative index. + const arg1 = arguments[1]; + const end: uintptr = + arg1 != Undefined ? ConvertToRelativeIndex(arg1, length) : length; - if (end <= start) { - return kEmptyString; - } - - return SubString(string, start, end); + if (end <= start) { + return kEmptyString; } + + return SubString(string, start, end); +} } diff --git a/deps/v8/src/builtins/string-startswith.tq b/deps/v8/src/builtins/string-startswith.tq index 045722dd824daa..a1f99df17b70bc 100644 --- a/deps/v8/src/builtins/string-startswith.tq +++ b/deps/v8/src/builtins/string-startswith.tq @@ -5,57 +5,56 @@ #include 'src/builtins/builtins-regexp-gen.h' namespace string { - // https://tc39.github.io/ecma262/#sec-string.prototype.startswith - transitioning javascript builtin StringPrototypeStartsWith( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): Boolean { - const searchString: JSAny = arguments[0]; - const position: JSAny = arguments[1]; - const kBuiltinName: constexpr string = 'String.prototype.startsWith'; - - // 1. Let O be ? RequireObjectCoercible(this value). - // 2. Let S be ? ToString(O). - const string: String = ToThisString(receiver, kBuiltinName); - - // 3. Let isRegExp be ? IsRegExp(searchString). - // 4. If isRegExp is true, throw a TypeError exception. - if (regexp::IsRegExp(searchString)) { - ThrowTypeError(MessageTemplate::kFirstArgumentNotRegExp, kBuiltinName); - } - - // 5. Let searchStr be ? ToString(searchString). - const searchStr: String = ToString_Inline(searchString); - - // 8. Let len be the length of S. - const len: uintptr = string.length_uintptr; - - // 6. Let pos be ? ToInteger(position). - // 7. Assert: If position is undefined, then pos is 0. - // 9. Let start be min(max(pos, 0), len). - const start: uintptr = - (position != Undefined) ? ClampToIndexRange(position, len) : 0; - - // 10. Let searchLength be the length of searchStr. - const searchLength: uintptr = searchStr.length_uintptr; - - // 11. If searchLength + start is greater than len, return false. - // The comparison is rephrased to be overflow-friendly with unsigned - // indices. - if (searchLength > len - start) return False; - - // 12. If the sequence of code units of S starting at start of length - // searchLength is the same as the full code unit sequence of searchStr, - // return true. - // 13. Otherwise, return false. - try { - // Fast Path: If both strings are direct and relevant indices are Smis. - return TryFastStringCompareSequence( - string, searchStr, start, searchLength) otherwise Slow; - } - label Slow { - // Slow Path: If either of the string is indirect, bail into runtime. - return StringCompareSequence( - context, string, searchStr, Convert(start)); - } +// https://tc39.github.io/ecma262/#sec-string.prototype.startswith +transitioning javascript builtin StringPrototypeStartsWith( + js-implicit context: NativeContext, + receiver: JSAny)(...arguments): Boolean { + const searchString: JSAny = arguments[0]; + const position: JSAny = arguments[1]; + const kBuiltinName: constexpr string = 'String.prototype.startsWith'; + + // 1. Let O be ? RequireObjectCoercible(this value). + // 2. Let S be ? ToString(O). + const string: String = ToThisString(receiver, kBuiltinName); + + // 3. Let isRegExp be ? IsRegExp(searchString). + // 4. If isRegExp is true, throw a TypeError exception. + if (regexp::IsRegExp(searchString)) { + ThrowTypeError(MessageTemplate::kFirstArgumentNotRegExp, kBuiltinName); } + + // 5. Let searchStr be ? ToString(searchString). + const searchStr: String = ToString_Inline(searchString); + + // 8. Let len be the length of S. + const len: uintptr = string.length_uintptr; + + // 6. Let pos be ? ToInteger(position). + // 7. Assert: If position is undefined, then pos is 0. + // 9. Let start be min(max(pos, 0), len). + const start: uintptr = + (position != Undefined) ? ClampToIndexRange(position, len) : 0; + + // 10. Let searchLength be the length of searchStr. + const searchLength: uintptr = searchStr.length_uintptr; + + // 11. If searchLength + start is greater than len, return false. + // The comparison is rephrased to be overflow-friendly with unsigned + // indices. + if (searchLength > len - start) return False; + + // 12. If the sequence of code units of S starting at start of length + // searchLength is the same as the full code unit sequence of searchStr, + // return true. + // 13. Otherwise, return false. + try { + // Fast Path: If both strings are direct and relevant indices are Smis. + return TryFastStringCompareSequence(string, searchStr, start, searchLength) + otherwise Slow; + } label Slow { + // Slow Path: If either of the string is indirect, bail into runtime. + return StringCompareSequence( + context, string, searchStr, Convert(start)); + } +} } diff --git a/deps/v8/src/builtins/string-substr.tq b/deps/v8/src/builtins/string-substr.tq index 917bee691e0623..068c4437ca60dd 100644 --- a/deps/v8/src/builtins/string-substr.tq +++ b/deps/v8/src/builtins/string-substr.tq @@ -4,40 +4,39 @@ namespace string { - // String.prototype.substr ( start, length ) - // ES6 #sec-string.prototype.substr - transitioning javascript builtin StringPrototypeSubstr( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): String { - const methodName: constexpr string = 'String.prototype.substr'; - // 1. Let O be ? RequireObjectCoercible(this value). - // 2. Let S be ? ToString(O). - const string: String = ToThisString(receiver, methodName); +// String.prototype.substr ( start, length ) +// ES6 #sec-string.prototype.substr +transitioning javascript builtin StringPrototypeSubstr( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + const methodName: constexpr string = 'String.prototype.substr'; + // 1. Let O be ? RequireObjectCoercible(this value). + // 2. Let S be ? ToString(O). + const string: String = ToThisString(receiver, methodName); - // 5. Let size be the number of code units in S. - const size: uintptr = string.length_uintptr; + // 5. Let size be the number of code units in S. + const size: uintptr = string.length_uintptr; - // 3. Let intStart be ? ToInteger(start). - // 6. If intStart < 0, set intStart to max(size + intStart, 0). - const start = arguments[0]; - const initStart: uintptr = - start != Undefined ? ConvertToRelativeIndex(start, size) : 0; + // 3. Let intStart be ? ToInteger(start). + // 6. If intStart < 0, set intStart to max(size + intStart, 0). + const start = arguments[0]; + const initStart: uintptr = + start != Undefined ? ConvertToRelativeIndex(start, size) : 0; - // 4. If length is undefined, - // let end be +∞; otherwise let end be ? ToInteger(length). - // 7. Let resultLength be min(max(end, 0), size - intStart). - const length = arguments[1]; - const lengthLimit = size - initStart; - assert(lengthLimit <= size); - const resultLength: uintptr = length != Undefined ? - ClampToIndexRange(length, lengthLimit) : - lengthLimit; + // 4. If length is undefined, + // let end be +∞; otherwise let end be ? ToInteger(length). + // 7. Let resultLength be min(max(end, 0), size - intStart). + const length = arguments[1]; + const lengthLimit = size - initStart; + assert(lengthLimit <= size); + const resultLength: uintptr = length != Undefined ? + ClampToIndexRange(length, lengthLimit) : + lengthLimit; - // 8. If resultLength ≤ 0, return the empty String "". - if (resultLength == 0) return EmptyStringConstant(); + // 8. If resultLength ≤ 0, return the empty String "". + if (resultLength == 0) return EmptyStringConstant(); - // 9. Return the String value containing resultLength consecutive code units - // from S beginning with the code unit at index intStart. - return SubString(string, initStart, initStart + resultLength); - } + // 9. Return the String value containing resultLength consecutive code units + // from S beginning with the code unit at index intStart. + return SubString(string, initStart, initStart + resultLength); +} } diff --git a/deps/v8/src/builtins/string-substring.tq b/deps/v8/src/builtins/string-substring.tq index e4e7d700003721..099a28b5057c44 100644 --- a/deps/v8/src/builtins/string-substring.tq +++ b/deps/v8/src/builtins/string-substring.tq @@ -4,28 +4,26 @@ namespace string { - // ES6 #sec-string.prototype.substring - transitioning javascript builtin StringPrototypeSubstring( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): String { - // Check that {receiver} is coercible to Object and convert it to a String. - const string: String = ToThisString(receiver, 'String.prototype.substring'); - const length: uintptr = string.length_uintptr; +// ES6 #sec-string.prototype.substring +transitioning javascript builtin StringPrototypeSubstring( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): String { + // Check that {receiver} is coercible to Object and convert it to a String. + const string: String = ToThisString(receiver, 'String.prototype.substring'); + const length: uintptr = string.length_uintptr; - // Conversion and bounds-checks for {start}. - const arg0 = arguments[0]; - let start: uintptr = - arg0 != Undefined ? ClampToIndexRange(arg0, length) : 0; + // Conversion and bounds-checks for {start}. + const arg0 = arguments[0]; + let start: uintptr = arg0 != Undefined ? ClampToIndexRange(arg0, length) : 0; - // Conversion and bounds-checks for {end}. - const arg1 = arguments[1]; - let end: uintptr = - arg1 != Undefined ? ClampToIndexRange(arg1, length) : length; - if (end < start) { - const tmp: uintptr = end; - end = start; - start = tmp; - } - return SubString(string, start, end); + // Conversion and bounds-checks for {end}. + const arg1 = arguments[1]; + let end: uintptr = + arg1 != Undefined ? ClampToIndexRange(arg1, length) : length; + if (end < start) { + const tmp: uintptr = end; + end = start; + start = tmp; } + return SubString(string, start, end); +} } diff --git a/deps/v8/src/builtins/symbol.tq b/deps/v8/src/builtins/symbol.tq index cda344471fbc45..18bdebd380e62a 100644 --- a/deps/v8/src/builtins/symbol.tq +++ b/deps/v8/src/builtins/symbol.tq @@ -3,47 +3,45 @@ // found in the LICENSE file. namespace symbol { - extern runtime SymbolDescriptiveString(implicit context: Context)(Symbol): - String; +extern runtime SymbolDescriptiveString(implicit context: Context)(Symbol): + String; - transitioning macro ThisSymbolValue(implicit context: Context)( - receiver: JSAny, method: constexpr string): Symbol { - return UnsafeCast( - ToThisValue(receiver, PrimitiveType::kSymbol, method)); - } +transitioning macro ThisSymbolValue(implicit context: Context)( + receiver: JSAny, method: constexpr string): Symbol { + return UnsafeCast( + ToThisValue(receiver, PrimitiveType::kSymbol, method)); +} - // ES #sec-symbol.prototype.description - transitioning javascript builtin SymbolPrototypeDescriptionGetter( - js-implicit context: NativeContext, receiver: JSAny)(): String|Undefined { - // 1. Let s be the this value. - // 2. Let sym be ? thisSymbolValue(s). - const sym: Symbol = - ThisSymbolValue(receiver, 'Symbol.prototype.description'); - // 3. Return sym.[[Description]]. - return sym.description; - } +// ES #sec-symbol.prototype.description +transitioning javascript builtin SymbolPrototypeDescriptionGetter( + js-implicit context: NativeContext, receiver: JSAny)(): String|Undefined { + // 1. Let s be the this value. + // 2. Let sym be ? thisSymbolValue(s). + const sym: Symbol = ThisSymbolValue(receiver, 'Symbol.prototype.description'); + // 3. Return sym.[[Description]]. + return sym.description; +} - // ES6 #sec-symbol.prototype-@@toprimitive - transitioning javascript builtin SymbolPrototypeToPrimitive( - js-implicit context: NativeContext, - receiver: JSAny)(_hint: JSAny): JSAny { - // 1. Return ? thisSymbolValue(this value). - return ThisSymbolValue(receiver, 'Symbol.prototype [ @@toPrimitive ]'); - } +// ES6 #sec-symbol.prototype-@@toprimitive +transitioning javascript builtin SymbolPrototypeToPrimitive( + js-implicit context: NativeContext, receiver: JSAny)(_hint: JSAny): JSAny { + // 1. Return ? thisSymbolValue(this value). + return ThisSymbolValue(receiver, 'Symbol.prototype [ @@toPrimitive ]'); +} - // ES6 #sec-symbol.prototype.tostring - transitioning javascript builtin SymbolPrototypeToString( - js-implicit context: NativeContext, receiver: JSAny)(): JSAny { - // 1. Let sym be ? thisSymbolValue(this value). - const sym: Symbol = ThisSymbolValue(receiver, 'Symbol.prototype.toString'); - // 2. Return SymbolDescriptiveString(sym). - return SymbolDescriptiveString(sym); - } +// ES6 #sec-symbol.prototype.tostring +transitioning javascript builtin SymbolPrototypeToString( + js-implicit context: NativeContext, receiver: JSAny)(): JSAny { + // 1. Let sym be ? thisSymbolValue(this value). + const sym: Symbol = ThisSymbolValue(receiver, 'Symbol.prototype.toString'); + // 2. Return SymbolDescriptiveString(sym). + return SymbolDescriptiveString(sym); +} - // ES6 #sec-symbol.prototype.valueof - transitioning javascript builtin SymbolPrototypeValueOf( - js-implicit context: NativeContext, receiver: JSAny)(): JSAny { - // 1. Return ? thisSymbolValue(this value). - return ThisSymbolValue(receiver, 'Symbol.prototype.valueOf'); - } +// ES6 #sec-symbol.prototype.valueof +transitioning javascript builtin SymbolPrototypeValueOf( + js-implicit context: NativeContext, receiver: JSAny)(): JSAny { + // 1. Return ? thisSymbolValue(this value). + return ThisSymbolValue(receiver, 'Symbol.prototype.valueOf'); +} } diff --git a/deps/v8/src/builtins/torque-internal.tq b/deps/v8/src/builtins/torque-internal.tq index 85c43342cf1b0e..d2b107f932dcad 100644 --- a/deps/v8/src/builtins/torque-internal.tq +++ b/deps/v8/src/builtins/torque-internal.tq @@ -3,188 +3,189 @@ // found in the LICENSE file. namespace torque_internal { - // Unsafe is a marker that we require to be passed when calling internal APIs - // that might lead to unsoundness when used incorrectly. Unsafe markers should - // therefore not be instantiated anywhere outside of this namespace. - struct Unsafe {} - - // Size of a type in memory (on the heap). For class types, this is the size - // of the pointer, not of the instance. - intrinsic %SizeOf(): constexpr int31; - - struct Reference { - const object: HeapObject; - const offset: intptr; - unsafeMarker: Unsafe; - } - type ConstReference extends Reference; - type MutableReference extends ConstReference; - - macro UnsafeNewReference(object: HeapObject, offset: intptr):&T { - return %RawDownCast<&T>( - Reference{object: object, offset: offset, unsafeMarker: Unsafe {}}); - } - - struct Slice { - macro TryAtIndex(index: intptr):&T labels OutOfBounds { - if (Convert(index) < Convert(this.length)) { - return UnsafeNewReference( - this.object, this.offset + index * %SizeOf()); - } else { - goto OutOfBounds; - } - } +// Unsafe is a marker that we require to be passed when calling internal APIs +// that might lead to unsoundness when used incorrectly. Unsafe markers should +// therefore not be instantiated anywhere outside of this namespace. +struct Unsafe {} + +// Size of a type in memory (on the heap). For class types, this is the size +// of the pointer, not of the instance. +intrinsic %SizeOf(): constexpr int31; + +struct Reference { + const object: HeapObject; + const offset: intptr; + unsafeMarker: Unsafe; +} +type ConstReference extends Reference; +type MutableReference extends ConstReference; - macro AtIndex(index: intptr):&T { - return this.TryAtIndex(index) otherwise unreachable; - } +namespace unsafe { +macro NewReference(object: HeapObject, offset: intptr):&T { + return %RawDownCast<&T>( + Reference{object: object, offset: offset, unsafeMarker: Unsafe {}}); +} +} // namespace unsafe - macro AtIndex(index: uintptr):&T { - return this.TryAtIndex(Convert(index)) otherwise unreachable; +struct Slice { + macro TryAtIndex(index: intptr):&T labels OutOfBounds { + if (Convert(index) < Convert(this.length)) { + return unsafe::NewReference( + this.object, this.offset + index * %SizeOf()); + } else { + goto OutOfBounds; } + } - macro AtIndex(index: constexpr int31):&T { - const i: intptr = Convert(index); - return this.TryAtIndex(i) otherwise unreachable; - } + macro AtIndex(index: intptr):&T { + return this.TryAtIndex(index) otherwise unreachable; + } - macro AtIndex(index: Smi):&T { - const i: intptr = Convert(index); - return this.TryAtIndex(i) otherwise unreachable; - } + macro AtIndex(index: uintptr):&T { + return this.TryAtIndex(Convert(index)) otherwise unreachable; + } - macro Iterator(): SliceIterator { - const end = this.offset + this.length * %SizeOf(); - return SliceIterator{ - object: this.object, - start: this.offset, - end: end, - unsafeMarker: Unsafe {} - }; - } - macro Iterator(startIndex: intptr, endIndex: intptr): SliceIterator { - check( - Convert(endIndex) <= Convert(this.length) && - Convert(startIndex) <= Convert(endIndex)); - const start = this.offset + startIndex * %SizeOf(); - const end = this.offset + endIndex * %SizeOf(); - return SliceIterator{ - object: this.object, - start, - end, - unsafeMarker: Unsafe {} - }; - } + macro AtIndex(index: constexpr int31):&T { + const i: intptr = Convert(index); + return this.TryAtIndex(i) otherwise unreachable; + } - const object: HeapObject; - const offset: intptr; - const length: intptr; - unsafeMarker: Unsafe; + macro AtIndex(index: Smi):&T { + const i: intptr = Convert(index); + return this.TryAtIndex(i) otherwise unreachable; } - macro UnsafeNewSlice( - object: HeapObject, offset: intptr, length: intptr): Slice { - return Slice{ - object: object, - offset: offset, - length: length, + macro Iterator(): SliceIterator { + const end = this.offset + this.length * %SizeOf(); + return SliceIterator{ + object: this.object, + start: this.offset, + end: end, + unsafeMarker: Unsafe {} + }; + } + macro Iterator(startIndex: intptr, endIndex: intptr): SliceIterator { + check( + Convert(endIndex) <= Convert(this.length) && + Convert(startIndex) <= Convert(endIndex)); + const start = this.offset + startIndex * %SizeOf(); + const end = this.offset + endIndex * %SizeOf(); + return SliceIterator{ + object: this.object, + start, + end, unsafeMarker: Unsafe {} }; } - struct SliceIterator { - macro Empty(): bool { - return this.start == this.end; - } - - macro Next(): T labels NoMore { - return * this.NextReference() otherwise NoMore; - } + const object: HeapObject; + const offset: intptr; + const length: intptr; + unsafeMarker: Unsafe; +} - macro NextReference():&T labels NoMore { - if (this.Empty()) { - goto NoMore; - } else { - const result = UnsafeNewReference(this.object, this.start); - this.start += %SizeOf(); - return result; - } - } +macro UnsafeNewSlice( + object: HeapObject, offset: intptr, length: intptr): Slice { + return Slice{ + object: object, + offset: offset, + length: length, + unsafeMarker: Unsafe {} + }; +} - object: HeapObject; - start: intptr; - end: intptr; - unsafeMarker: Unsafe; +struct SliceIterator { + macro Empty(): bool { + return this.start == this.end; } - macro AddIndexedFieldSizeToObjectSize( - baseSize: intptr, arrayLength: intptr, - fieldSize: constexpr int32): intptr { - const arrayLength = Convert(arrayLength); - const byteLength = TryInt32Mul(arrayLength, fieldSize) - otherwise unreachable; - return TryIntPtrAdd(baseSize, Convert(byteLength)) - otherwise unreachable; + macro Next(): T labels NoMore { + return * this.NextReference() otherwise NoMore; } - macro AlignTagged(x: intptr): intptr { - // Round up to a multiple of kTaggedSize. - return (x + kObjectAlignmentMask) & ~kObjectAlignmentMask; + macro NextReference():&T labels NoMore { + if (this.Empty()) { + goto NoMore; + } else { + const result = unsafe::NewReference(this.object, this.start); + this.start += %SizeOf(); + return result; + } } - macro IsTaggedAligned(x: intptr): bool { - return (x & kObjectAlignmentMask) == 0; - } + object: HeapObject; + start: intptr; + end: intptr; + unsafeMarker: Unsafe; +} - macro ValidAllocationSize(sizeInBytes: intptr, map: Map): bool { - if (sizeInBytes <= 0) return false; - if (!IsTaggedAligned(sizeInBytes)) return false; - const instanceSizeInWords = Convert(map.instance_size_in_words); - return instanceSizeInWords == kVariableSizeSentinel || - instanceSizeInWords * kTaggedSize == sizeInBytes; - } +macro AddIndexedFieldSizeToObjectSize( + baseSize: intptr, arrayLength: intptr, fieldSize: constexpr int32): intptr { + const arrayLength = Convert(arrayLength); + const byteLength = TryInt32Mul(arrayLength, fieldSize) + otherwise unreachable; + return TryIntPtrAdd(baseSize, Convert(byteLength)) + otherwise unreachable; +} - type UninitializedHeapObject extends HeapObject; +macro AlignTagged(x: intptr): intptr { + // Round up to a multiple of kTaggedSize. + return (x + kObjectAlignmentMask) & ~kObjectAlignmentMask; +} - extern macro AllocateAllowLOS(intptr): UninitializedHeapObject; - extern macro GetInstanceTypeMap(constexpr InstanceType): Map; +macro IsTaggedAligned(x: intptr): bool { + return (x & kObjectAlignmentMask) == 0; +} - macro Allocate(sizeInBytes: intptr, map: Map): UninitializedHeapObject { - assert(ValidAllocationSize(sizeInBytes, map)); - return AllocateAllowLOS(sizeInBytes); - } +macro ValidAllocationSize(sizeInBytes: intptr, map: Map): bool { + if (sizeInBytes <= 0) return false; + if (!IsTaggedAligned(sizeInBytes)) return false; + const instanceSizeInWords = Convert(map.instance_size_in_words); + return instanceSizeInWords == kVariableSizeSentinel || + instanceSizeInWords * kTaggedSize == sizeInBytes; +} - macro InitializeFieldsFromIterator( - target: Slice, originIterator: Iterator) { - let targetIterator = target.Iterator(); - let originIterator = originIterator; - while (true) { - const ref:&T = targetIterator.NextReference() otherwise break; - * ref = originIterator.Next() otherwise unreachable; - } - } - // Dummy implementations: do not initialize for UninitializedIterator. - InitializeFieldsFromIterator( - _target: Slice, _originIterator: UninitializedIterator) {} - InitializeFieldsFromIterator( - _target: Slice, _originIterator: UninitializedIterator) {} - - extern macro IsDoubleHole(HeapObject, intptr): bool; - extern macro StoreDoubleHole(HeapObject, intptr); - - macro LoadFloat64OrHole(r:&float64_or_hole): float64_or_hole { - return float64_or_hole{ - is_hole: IsDoubleHole(r.object, r.offset - kHeapObjectTag), - value: * UnsafeNewReference(r.object, r.offset) - }; +type UninitializedHeapObject extends HeapObject; + +extern macro AllocateAllowLOS(intptr): UninitializedHeapObject; +extern macro GetInstanceTypeMap(constexpr InstanceType): Map; + +macro Allocate(sizeInBytes: intptr, map: Map): UninitializedHeapObject { + assert(ValidAllocationSize(sizeInBytes, map)); + return AllocateAllowLOS(sizeInBytes); +} + +macro InitializeFieldsFromIterator( + target: Slice, originIterator: Iterator) { + let targetIterator = target.Iterator(); + let originIterator = originIterator; + while (true) { + const ref:&T = targetIterator.NextReference() otherwise break; + * ref = originIterator.Next() otherwise unreachable; } - macro StoreFloat64OrHole(r:&float64_or_hole, value: float64_or_hole) { - if (value.is_hole) { - StoreDoubleHole(r.object, r.offset - kHeapObjectTag); - } else { - * UnsafeNewReference(r.object, r.offset) = value.value; - } +} +// Dummy implementations: do not initialize for UninitializedIterator. +InitializeFieldsFromIterator( + _target: Slice, _originIterator: UninitializedIterator) {} +InitializeFieldsFromIterator( + _target: Slice, _originIterator: UninitializedIterator) {} + +extern macro IsDoubleHole(HeapObject, intptr): bool; +extern macro StoreDoubleHole(HeapObject, intptr); + +macro LoadFloat64OrHole(r:&float64_or_hole): float64_or_hole { + return float64_or_hole{ + is_hole: IsDoubleHole(r.object, r.offset - kHeapObjectTag), + value: * unsafe::NewReference(r.object, r.offset) + }; +} +macro StoreFloat64OrHole(r:&float64_or_hole, value: float64_or_hole) { + if (value.is_hole) { + StoreDoubleHole(r.object, r.offset - kHeapObjectTag); + } else { + * unsafe::NewReference(r.object, r.offset) = value.value; } +} } // namespace torque_internal // Indicates that an array-field should not be initialized. diff --git a/deps/v8/src/builtins/typed-array-createtypedarray.tq b/deps/v8/src/builtins/typed-array-createtypedarray.tq index e5398fc50ac08b..ec51efc00abfdd 100644 --- a/deps/v8/src/builtins/typed-array-createtypedarray.tq +++ b/deps/v8/src/builtins/typed-array-createtypedarray.tq @@ -5,452 +5,431 @@ #include 'src/builtins/builtins-constructor-gen.h' namespace typed_array { - extern builtin IterableToListMayPreserveHoles(Context, Object, Callable): - JSArray; - - extern macro TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer( - implicit context: Context)(uintptr): JSArrayBuffer; - extern macro CodeStubAssembler::AllocateByteArray(uintptr): ByteArray; - extern macro TypedArrayBuiltinsAssembler::GetDefaultConstructor( - implicit context: Context)(JSTypedArray): JSFunction; - extern macro TypedArrayBuiltinsAssembler::SetupTypedArrayEmbedderFields( - JSTypedArray): void; - - extern runtime ThrowInvalidTypedArrayAlignment(implicit context: Context)( - Map, String): never; - - transitioning macro AllocateTypedArray(implicit context: Context)( - isOnHeap: constexpr bool, map: Map, buffer: JSArrayBuffer, - byteOffset: uintptr, byteLength: uintptr, length: uintptr): JSTypedArray { - let elements: ByteArray; - if constexpr (isOnHeap) { - elements = AllocateByteArray(byteLength); - } else { - elements = kEmptyByteArray; - - // The max byteOffset is 8 * MaxSmi on the particular platform. 32 bit - // platforms are self-limiting, because we can't allocate an array bigger - // than our 32-bit arithmetic range anyway. 64 bit platforms could - // theoretically have an offset up to 2^35 - 1. - const backingStore: uintptr = Convert(buffer.backing_store); - - // Assert no overflow has occurred. Only assert if the mock array buffer - // allocator is NOT used. When the mock array buffer is used, impossibly - // large allocations are allowed that would erroneously cause an overflow - // and this assertion to fail. - assert( - IsMockArrayBufferAllocatorFlag() || - (backingStore + byteOffset) >= backingStore); - } - - // We can't just build the new object with "new JSTypedArray" here because - // Torque doesn't know its full size including embedder fields, so use CSA - // for the allocation step. - const typedArray = - UnsafeCast(AllocateFastOrSlowJSObjectFromMap(map)); - typedArray.elements = elements; - typedArray.buffer = buffer; - typedArray.byte_offset = byteOffset; - typedArray.byte_length = byteLength; - typedArray.length = length; - if constexpr (isOnHeap) { - typed_array::SetJSTypedArrayOnHeapDataPtr( - typedArray, elements, byteOffset); - } else { - typed_array::SetJSTypedArrayOffHeapDataPtr( - typedArray, buffer.backing_store, byteOffset); - assert( - typedArray.data_ptr == - (buffer.backing_store + Convert(byteOffset))); - } - SetupTypedArrayEmbedderFields(typedArray); - return typedArray; +extern builtin IterableToListMayPreserveHoles( + Context, Object, Callable): JSArray; + +extern macro TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer( + implicit context: Context)(uintptr): JSArrayBuffer; +extern macro CodeStubAssembler::AllocateByteArray(uintptr): ByteArray; +extern macro TypedArrayBuiltinsAssembler::GetDefaultConstructor( + implicit context: Context)(JSTypedArray): JSFunction; +extern macro TypedArrayBuiltinsAssembler::SetupTypedArrayEmbedderFields( + JSTypedArray): void; + +extern runtime ThrowInvalidTypedArrayAlignment(implicit context: Context)( + Map, String): never; + +transitioning macro AllocateTypedArray(implicit context: Context)( + isOnHeap: constexpr bool, map: Map, buffer: JSArrayBuffer, + byteOffset: uintptr, byteLength: uintptr, length: uintptr): JSTypedArray { + let elements: ByteArray; + if constexpr (isOnHeap) { + elements = AllocateByteArray(byteLength); + } else { + elements = kEmptyByteArray; + + // The max byteOffset is 8 * MaxSmi on the particular platform. 32 bit + // platforms are self-limiting, because we can't allocate an array bigger + // than our 32-bit arithmetic range anyway. 64 bit platforms could + // theoretically have an offset up to 2^35 - 1. + const backingStore: uintptr = Convert(buffer.backing_store_ptr); + + // Assert no overflow has occurred. Only assert if the mock array buffer + // allocator is NOT used. When the mock array buffer is used, impossibly + // large allocations are allowed that would erroneously cause an overflow + // and this assertion to fail. + assert( + IsMockArrayBufferAllocatorFlag() || + (backingStore + byteOffset) >= backingStore); } - transitioning macro TypedArrayInitialize(implicit context: Context)( - initialize: constexpr bool, map: Map, length: uintptr, - elementsInfo: typed_array::TypedArrayElementsInfo, - bufferConstructor: JSReceiver): JSTypedArray labels IfRangeError { - const byteLength = elementsInfo.CalculateByteLength(length) - otherwise IfRangeError; - const byteLengthNum = Convert(byteLength); - const defaultConstructor = GetArrayBufferFunction(); - const byteOffset: uintptr = 0; - - try { - if (bufferConstructor != defaultConstructor) { - goto AttachOffHeapBuffer(ConstructWithTarget( - defaultConstructor, bufferConstructor, byteLengthNum)); - } + // We can't just build the new object with "new JSTypedArray" here because + // Torque doesn't know its full size including embedder fields, so use CSA + // for the allocation step. + const typedArray = + UnsafeCast(AllocateFastOrSlowJSObjectFromMap(map)); + typedArray.elements = elements; + typedArray.buffer = buffer; + typedArray.byte_offset = byteOffset; + typedArray.byte_length = byteLength; + typedArray.length = length; + if constexpr (isOnHeap) { + typed_array::SetJSTypedArrayOnHeapDataPtr(typedArray, elements, byteOffset); + } else { + typed_array::SetJSTypedArrayOffHeapDataPtr( + typedArray, buffer.backing_store_ptr, byteOffset); + assert( + typedArray.data_ptr == + (buffer.backing_store_ptr + Convert(byteOffset))); + } + SetupTypedArrayEmbedderFields(typedArray); + return typedArray; +} - if (byteLength > kMaxTypedArrayInHeap) goto AllocateOffHeap; +transitioning macro TypedArrayInitialize(implicit context: Context)( + initialize: constexpr bool, map: Map, length: uintptr, + elementsInfo: typed_array::TypedArrayElementsInfo, + bufferConstructor: JSReceiver): JSTypedArray labels IfRangeError { + const byteLength = elementsInfo.CalculateByteLength(length) + otherwise IfRangeError; + const byteLengthNum = Convert(byteLength); + const defaultConstructor = GetArrayBufferFunction(); + const byteOffset: uintptr = 0; + + try { + if (bufferConstructor != defaultConstructor) { + goto AttachOffHeapBuffer(ConstructWithTarget( + defaultConstructor, bufferConstructor, byteLengthNum)); + } - const buffer = AllocateEmptyOnHeapBuffer(byteLength); + if (byteLength > kMaxTypedArrayInHeap) goto AllocateOffHeap; - const isOnHeap: constexpr bool = true; - const typedArray = AllocateTypedArray( - isOnHeap, map, buffer, byteOffset, byteLength, length); + const buffer = AllocateEmptyOnHeapBuffer(byteLength); - if constexpr (initialize) { - const backingStore = typedArray.data_ptr; - typed_array::CallCMemset(backingStore, 0, byteLength); - } + const isOnHeap: constexpr bool = true; + const typedArray = AllocateTypedArray( + isOnHeap, map, buffer, byteOffset, byteLength, length); - return typedArray; + if constexpr (initialize) { + const backingStore = typedArray.data_ptr; + typed_array::CallCMemset(backingStore, 0, byteLength); } - label AllocateOffHeap { - if constexpr (initialize) { - goto AttachOffHeapBuffer(Construct(defaultConstructor, byteLengthNum)); - } else { - goto AttachOffHeapBuffer(Call( - context, GetArrayBufferNoInitFunction(), Undefined, byteLengthNum)); - } - } - label AttachOffHeapBuffer(bufferObj: Object) { - const buffer = Cast(bufferObj) otherwise unreachable; - const isOnHeap: constexpr bool = false; - return AllocateTypedArray( - isOnHeap, map, buffer, byteOffset, byteLength, length); + + return typedArray; + } label AllocateOffHeap { + if constexpr (initialize) { + goto AttachOffHeapBuffer(Construct(defaultConstructor, byteLengthNum)); + } else { + goto AttachOffHeapBuffer(Call( + context, GetArrayBufferNoInitFunction(), Undefined, byteLengthNum)); } + } label AttachOffHeapBuffer(bufferObj: Object) { + const buffer = Cast(bufferObj) otherwise unreachable; + const isOnHeap: constexpr bool = false; + return AllocateTypedArray( + isOnHeap, map, buffer, byteOffset, byteLength, length); } +} - // 22.2.4.2 TypedArray ( length ) - // ES #sec-typedarray-length - transitioning macro ConstructByLength(implicit context: Context)( - map: Map, lengthObj: JSAny, - elementsInfo: typed_array::TypedArrayElementsInfo): JSTypedArray { - try { - const length: uintptr = ToIndex(lengthObj) otherwise RangeError; - const defaultConstructor: Constructor = GetArrayBufferFunction(); - const initialize: constexpr bool = true; - return TypedArrayInitialize( - initialize, map, length, elementsInfo, defaultConstructor) - otherwise RangeError; - } - label RangeError deferred { - ThrowRangeError(MessageTemplate::kInvalidTypedArrayLength, lengthObj); - } +// 22.2.4.2 TypedArray ( length ) +// ES #sec-typedarray-length +transitioning macro ConstructByLength(implicit context: Context)( + map: Map, lengthObj: JSAny, + elementsInfo: typed_array::TypedArrayElementsInfo): JSTypedArray { + try { + const length: uintptr = ToIndex(lengthObj) otherwise RangeError; + const defaultConstructor: Constructor = GetArrayBufferFunction(); + const initialize: constexpr bool = true; + return TypedArrayInitialize( + initialize, map, length, elementsInfo, defaultConstructor) + otherwise RangeError; + } label RangeError deferred { + ThrowRangeError(MessageTemplate::kInvalidTypedArrayLength, lengthObj); } +} + +// 22.2.4.4 TypedArray ( object ) +// ES #sec-typedarray-object +transitioning macro ConstructByArrayLike(implicit context: Context)( + map: Map, arrayLike: HeapObject, length: uintptr, + elementsInfo: typed_array::TypedArrayElementsInfo, + bufferConstructor: JSReceiver): JSTypedArray { + try { + const initialize: constexpr bool = false; + const typedArray = TypedArrayInitialize( + initialize, map, length, elementsInfo, bufferConstructor) + otherwise RangeError; - // 22.2.4.4 TypedArray ( object ) - // ES #sec-typedarray-object - transitioning macro ConstructByArrayLike(implicit context: Context)( - map: Map, arrayLike: HeapObject, length: uintptr, - elementsInfo: typed_array::TypedArrayElementsInfo, - bufferConstructor: JSReceiver): JSTypedArray { try { - const initialize: constexpr bool = false; - const typedArray = TypedArrayInitialize( - initialize, map, length, elementsInfo, bufferConstructor) - otherwise RangeError; - - try { - const src: JSTypedArray = - Cast(arrayLike) otherwise IfSlow; - - if (IsDetachedBuffer(src.buffer)) { - ThrowTypeError(MessageTemplate::kDetachedOperation, 'Construct'); - - } else if (src.elements_kind != elementsInfo.kind) { - goto IfSlow; - - } else if (length > 0) { - const byteLength = typedArray.byte_length; - assert(byteLength <= kArrayBufferMaxByteLength); - typed_array::CallCMemcpy( - typedArray.data_ptr, src.data_ptr, byteLength); - } + const src: JSTypedArray = Cast(arrayLike) otherwise IfSlow; + + if (IsDetachedBuffer(src.buffer)) { + ThrowTypeError(MessageTemplate::kDetachedOperation, 'Construct'); + + } else if (src.elements_kind != elementsInfo.kind) { + goto IfSlow; + + } else if (length > 0) { + const byteLength = typedArray.byte_length; + assert(byteLength <= kArrayBufferMaxByteLength); + typed_array::CallCMemcpy(typedArray.data_ptr, src.data_ptr, byteLength); } - label IfSlow deferred { - if (length > 0) { - TypedArrayCopyElements( - context, typedArray, arrayLike, Convert(length)); - } + } label IfSlow deferred { + if (length > 0) { + TypedArrayCopyElements( + context, typedArray, arrayLike, Convert(length)); } - return typedArray; - } - label RangeError deferred { - ThrowRangeError( - MessageTemplate::kInvalidTypedArrayLength, Convert(length)); } + return typedArray; + } label RangeError deferred { + ThrowRangeError( + MessageTemplate::kInvalidTypedArrayLength, Convert(length)); } +} - // 22.2.4.4 TypedArray ( object ) - // ES #sec-typedarray-object - transitioning macro ConstructByIterable(implicit context: Context)( - iterable: JSReceiver, iteratorFn: Callable): never - labels IfConstructByArrayLike(JSArray, uintptr, JSReceiver) { - const array: JSArray = - IterableToListMayPreserveHoles(context, iterable, iteratorFn); - // Max JSArray length is a valid JSTypedArray length so we just use it. - goto IfConstructByArrayLike( - array, array.length_uintptr, GetArrayBufferFunction()); - } +// 22.2.4.4 TypedArray ( object ) +// ES #sec-typedarray-object +transitioning macro ConstructByIterable(implicit context: Context)( + iterable: JSReceiver, iteratorFn: Callable): never + labels IfConstructByArrayLike(JSArray, uintptr, JSReceiver) { + const array: JSArray = + IterableToListMayPreserveHoles(context, iterable, iteratorFn); + // Max JSArray length is a valid JSTypedArray length so we just use it. + goto IfConstructByArrayLike( + array, array.length_uintptr, GetArrayBufferFunction()); +} - // 22.2.4.3 TypedArray ( typedArray ) - // ES #sec-typedarray-typedarray - transitioning macro ConstructByTypedArray(implicit context: Context)( - srcTypedArray: JSTypedArray): never - labels IfConstructByArrayLike(JSTypedArray, uintptr, JSReceiver) { - let bufferConstructor: JSReceiver = GetArrayBufferFunction(); - const srcBuffer: JSArrayBuffer = srcTypedArray.buffer; +// 22.2.4.3 TypedArray ( typedArray ) +// ES #sec-typedarray-typedarray +transitioning macro ConstructByTypedArray(implicit context: Context)( + srcTypedArray: JSTypedArray): never + labels IfConstructByArrayLike(JSTypedArray, uintptr, JSReceiver) { + let bufferConstructor: JSReceiver = GetArrayBufferFunction(); + const srcBuffer: JSArrayBuffer = srcTypedArray.buffer; + // TODO(petermarshall): Throw on detached typedArray. + let length: uintptr = IsDetachedBuffer(srcBuffer) ? 0 : srcTypedArray.length; + + // The spec requires that constructing a typed array using a SAB-backed + // typed array use the ArrayBuffer constructor, not the species constructor. + // See https://tc39.github.io/ecma262/#sec-typedarray-typedarray. + if (!IsSharedArrayBuffer(srcBuffer)) { + bufferConstructor = SpeciesConstructor(srcBuffer, bufferConstructor); // TODO(petermarshall): Throw on detached typedArray. - let length: uintptr = - IsDetachedBuffer(srcBuffer) ? 0 : srcTypedArray.length; - - // The spec requires that constructing a typed array using a SAB-backed - // typed array use the ArrayBuffer constructor, not the species constructor. - // See https://tc39.github.io/ecma262/#sec-typedarray-typedarray. - if (!IsSharedArrayBuffer(srcBuffer)) { - bufferConstructor = SpeciesConstructor(srcBuffer, bufferConstructor); - // TODO(petermarshall): Throw on detached typedArray. - if (IsDetachedBuffer(srcBuffer)) length = 0; - } - goto IfConstructByArrayLike(srcTypedArray, length, bufferConstructor); + if (IsDetachedBuffer(srcBuffer)) length = 0; } + goto IfConstructByArrayLike(srcTypedArray, length, bufferConstructor); +} - // 22.2.4.5 TypedArray ( buffer, byteOffset, length ) - // ES #sec-typedarray-buffer-byteoffset-length - transitioning macro ConstructByArrayBuffer(implicit context: Context)( - map: Map, buffer: JSArrayBuffer, byteOffset: JSAny, length: JSAny, - elementsInfo: typed_array::TypedArrayElementsInfo): JSTypedArray { - try { - // 6. Let offset be ? ToIndex(byteOffset). - const offset: uintptr = ToIndex(byteOffset) otherwise IfInvalidOffset; +// 22.2.4.5 TypedArray ( buffer, byteOffset, length ) +// ES #sec-typedarray-buffer-byteoffset-length +transitioning macro ConstructByArrayBuffer(implicit context: Context)( + map: Map, buffer: JSArrayBuffer, byteOffset: JSAny, length: JSAny, + elementsInfo: typed_array::TypedArrayElementsInfo): JSTypedArray { + try { + // 6. Let offset be ? ToIndex(byteOffset). + const offset: uintptr = ToIndex(byteOffset) otherwise IfInvalidOffset; + + // 7. If offset modulo elementSize ≠ 0, throw a RangeError exception. + if (elementsInfo.IsUnaligned(offset)) { + goto IfInvalidAlignment('start offset'); + } - // 7. If offset modulo elementSize ≠ 0, throw a RangeError exception. - if (elementsInfo.IsUnaligned(offset)) { - goto IfInvalidAlignment('start offset'); - } + // 8. If length is present and length is not undefined, then + // a. Let newLength be ? ToIndex(length). + let newLength: uintptr = ToIndex(length) otherwise IfInvalidLength; + let newByteLength: uintptr; - // 8. If length is present and length is not undefined, then - // a. Let newLength be ? ToIndex(length). - let newLength: uintptr = ToIndex(length) otherwise IfInvalidLength; - let newByteLength: uintptr; + // 9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(buffer)) { + ThrowTypeError(MessageTemplate::kDetachedOperation, 'Construct'); + } - // 9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. - if (IsDetachedBuffer(buffer)) { - ThrowTypeError(MessageTemplate::kDetachedOperation, 'Construct'); - } + // 10. Let bufferByteLength be buffer.[[ArrayBufferByteLength]]. + const bufferByteLength: uintptr = buffer.byte_length; - // 10. Let bufferByteLength be buffer.[[ArrayBufferByteLength]]. - const bufferByteLength: uintptr = buffer.byte_length; - - // 11. If length is either not present or undefined, then - if (length == Undefined) { - // a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError - // exception. - if (elementsInfo.IsUnaligned(bufferByteLength)) { - goto IfInvalidAlignment('byte length'); - } - - // b. Let newByteLength be bufferByteLength - offset. - // c. If newByteLength < 0, throw a RangeError exception. - if (bufferByteLength < offset) goto IfInvalidOffset; - - // Spec step 16 length calculated here to avoid recalculating the length - // in the step 12 branch. - newByteLength = bufferByteLength - offset; - newLength = elementsInfo.CalculateLength(newByteLength) - otherwise IfInvalidOffset; - - // 12. Else, - } else { - // a. Let newByteLength be newLength × elementSize. - newByteLength = elementsInfo.CalculateByteLength(newLength) - otherwise IfInvalidLength; - - // b. If offset + newByteLength > bufferByteLength, throw a RangeError - // exception. - if ((bufferByteLength < newByteLength) || - (offset > bufferByteLength - newByteLength)) - goto IfInvalidLength; + // 11. If length is either not present or undefined, then + if (length == Undefined) { + // a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError + // exception. + if (elementsInfo.IsUnaligned(bufferByteLength)) { + goto IfInvalidAlignment('byte length'); } - const isOnHeap: constexpr bool = false; - return AllocateTypedArray( - isOnHeap, map, buffer, offset, newByteLength, newLength); - } - label IfInvalidAlignment(problemString: String) deferred { - ThrowInvalidTypedArrayAlignment(map, problemString); - } - label IfInvalidLength deferred { - ThrowRangeError(MessageTemplate::kInvalidTypedArrayLength, length); - } - label IfInvalidOffset deferred { - ThrowRangeError(MessageTemplate::kInvalidOffset, byteOffset); - } - } + // b. Let newByteLength be bufferByteLength - offset. + // c. If newByteLength < 0, throw a RangeError exception. + if (bufferByteLength < offset) goto IfInvalidOffset; - // 22.2.4.6 TypedArrayCreate ( constructor, argumentList ) - // ES #typedarray-create - @export - transitioning macro TypedArrayCreateByLength(implicit context: Context)( - constructor: Constructor, length: Number, methodName: constexpr string): - JSTypedArray { - assert(IsSafeInteger(length)); + // Spec step 16 length calculated here to avoid recalculating the length + // in the step 12 branch. + newByteLength = bufferByteLength - offset; + newLength = elementsInfo.CalculateLength(newByteLength) + otherwise IfInvalidOffset; - // 1. Let newTypedArray be ? Construct(constructor, argumentList). - const newTypedArrayObj = Construct(constructor, length); + // 12. Else, + } else { + // a. Let newByteLength be newLength × elementSize. + newByteLength = elementsInfo.CalculateByteLength(newLength) + otherwise IfInvalidLength; + + // b. If offset + newByteLength > bufferByteLength, throw a RangeError + // exception. + if ((bufferByteLength < newByteLength) || + (offset > bufferByteLength - newByteLength)) + goto IfInvalidLength; + } - // 2. Perform ? ValidateTypedArray(newTypedArray). - // ValidateTypedArray currently returns the array, not the ViewBuffer. - const newTypedArray: JSTypedArray = - ValidateTypedArray(context, newTypedArrayObj, methodName); + const isOnHeap: constexpr bool = false; + return AllocateTypedArray( + isOnHeap, map, buffer, offset, newByteLength, newLength); + } label IfInvalidAlignment(problemString: String) deferred { + ThrowInvalidTypedArrayAlignment(map, problemString); + } label IfInvalidLength deferred { + ThrowRangeError(MessageTemplate::kInvalidTypedArrayLength, length); + } label IfInvalidOffset deferred { + ThrowRangeError(MessageTemplate::kInvalidOffset, byteOffset); + } +} - if (IsDetachedBuffer(newTypedArray.buffer)) deferred { - ThrowTypeError(MessageTemplate::kDetachedOperation, methodName); - } +// 22.2.4.6 TypedArrayCreate ( constructor, argumentList ) +// ES #typedarray-create +@export +transitioning macro TypedArrayCreateByLength(implicit context: Context)( + constructor: Constructor, length: Number, methodName: constexpr string): + JSTypedArray { + assert(IsSafeInteger(length)); - // 3. If argumentList is a List of a single Number, then - // a. If newTypedArray.[[ArrayLength]] < argumentList[0], throw a - // TypeError exception. - if (newTypedArray.length < Convert(length)) deferred { - ThrowTypeError(MessageTemplate::kTypedArrayTooShort); - } + // 1. Let newTypedArray be ? Construct(constructor, argumentList). + const newTypedArrayObj = Construct(constructor, length); - // 4. Return newTypedArray. - return newTypedArray; - } + // 2. Perform ? ValidateTypedArray(newTypedArray). + // ValidateTypedArray currently returns the array, not the ViewBuffer. + const newTypedArray: JSTypedArray = + ValidateTypedArray(context, newTypedArrayObj, methodName); - transitioning macro ConstructByJSReceiver(implicit context: - Context)(obj: JSReceiver): never - labels IfConstructByArrayLike(JSReceiver, uintptr, JSReceiver) { - try { - // TODO(v8:8906): Use iterator::GetIteratorMethod() once it supports - // labels. - const iteratorMethod = GetMethod(obj, IteratorSymbolConstant()) - otherwise IfIteratorUndefined, IfIteratorNotCallable; - ConstructByIterable(obj, iteratorMethod) - otherwise IfConstructByArrayLike; - } - label IfIteratorUndefined { - const lengthObj: JSAny = GetProperty(obj, kLengthString); - const lengthNumber: Number = ToLength_Inline(lengthObj); - // Throw RangeError here if the length does not fit in uintptr because - // such a length will not pass bounds checks in ConstructByArrayLike() - // anyway. - const length: uintptr = ChangeSafeIntegerNumberToUintPtr(lengthNumber) - otherwise goto IfInvalidLength(lengthNumber); - goto IfConstructByArrayLike(obj, length, GetArrayBufferFunction()); - } - label IfInvalidLength(length: Number) { - ThrowRangeError(MessageTemplate::kInvalidTypedArrayLength, length); + if (IsDetachedBuffer(newTypedArray.buffer)) deferred { + ThrowTypeError(MessageTemplate::kDetachedOperation, methodName); } - label IfIteratorNotCallable(_value: JSAny) deferred { - ThrowTypeError(MessageTemplate::kIteratorSymbolNonCallable); + + // 3. If argumentList is a List of a single Number, then + // a. If newTypedArray.[[ArrayLength]] < argumentList[0], throw a + // TypeError exception. + if (newTypedArray.length < Convert(length)) deferred { + ThrowTypeError(MessageTemplate::kTypedArrayTooShort); } - } - // 22.2.4 The TypedArray Constructors - // ES #sec-typedarray-constructors - transitioning builtin CreateTypedArray( - context: Context, target: JSFunction, newTarget: JSReceiver, arg1: JSAny, - arg2: JSAny, arg3: JSAny): JSTypedArray { - assert(IsConstructor(target)); - // 4. Let O be ? AllocateTypedArray(constructorName, NewTarget, - // "%TypedArrayPrototype%"). - const map = GetDerivedMap(target, newTarget); + // 4. Return newTypedArray. + return newTypedArray; +} - // 5. Let elementSize be the Number value of the Element Size value in Table - // 56 for constructorName. - const elementsInfo = GetTypedArrayElementsInfo(map); +transitioning macro ConstructByJSReceiver(implicit context: Context)( + obj: JSReceiver): never + labels IfConstructByArrayLike(JSReceiver, uintptr, JSReceiver) { + try { + // TODO(v8:8906): Use iterator::GetIteratorMethod() once it supports + // labels. + const iteratorMethod = GetMethod(obj, IteratorSymbolConstant()) + otherwise IfIteratorUndefined, IfIteratorNotCallable; + ConstructByIterable(obj, iteratorMethod) + otherwise IfConstructByArrayLike; + } label IfIteratorUndefined { + const lengthObj: JSAny = GetProperty(obj, kLengthString); + const lengthNumber: Number = ToLength_Inline(lengthObj); + // Throw RangeError here if the length does not fit in uintptr because + // such a length will not pass bounds checks in ConstructByArrayLike() + // anyway. + const length: uintptr = ChangeSafeIntegerNumberToUintPtr(lengthNumber) + otherwise goto IfInvalidLength(lengthNumber); + goto IfConstructByArrayLike(obj, length, GetArrayBufferFunction()); + } label IfInvalidLength(length: Number) { + ThrowRangeError(MessageTemplate::kInvalidTypedArrayLength, length); + } label IfIteratorNotCallable(_value: JSAny) deferred { + ThrowTypeError(MessageTemplate::kIteratorSymbolNonCallable); + } +} - try { - typeswitch (arg1) { - case (length: Smi): { - goto IfConstructByLength(length); - } - case (buffer: JSArrayBuffer): { - return ConstructByArrayBuffer(map, buffer, arg2, arg3, elementsInfo); - } - case (typedArray: JSTypedArray): { - ConstructByTypedArray(typedArray) otherwise IfConstructByArrayLike; - } - case (obj: JSReceiver): { - ConstructByJSReceiver(obj) otherwise IfConstructByArrayLike; - } - // The first argument was a number or fell through and is treated as - // a number. https://tc39.github.io/ecma262/#sec-typedarray-length - case (lengthObj: JSAny): { - goto IfConstructByLength(lengthObj); - } +// 22.2.4 The TypedArray Constructors +// ES #sec-typedarray-constructors +transitioning builtin CreateTypedArray( + context: Context, target: JSFunction, newTarget: JSReceiver, arg1: JSAny, + arg2: JSAny, arg3: JSAny): JSTypedArray { + assert(IsConstructor(target)); + // 4. Let O be ? AllocateTypedArray(constructorName, NewTarget, + // "%TypedArrayPrototype%"). + const map = GetDerivedMap(target, newTarget); + + // 5. Let elementSize be the Number value of the Element Size value in Table + // 56 for constructorName. + const elementsInfo = GetTypedArrayElementsInfo(map); + + try { + typeswitch (arg1) { + case (length: Smi): { + goto IfConstructByLength(length); + } + case (buffer: JSArrayBuffer): { + return ConstructByArrayBuffer(map, buffer, arg2, arg3, elementsInfo); + } + case (typedArray: JSTypedArray): { + ConstructByTypedArray(typedArray) otherwise IfConstructByArrayLike; + } + case (obj: JSReceiver): { + ConstructByJSReceiver(obj) otherwise IfConstructByArrayLike; + } + // The first argument was a number or fell through and is treated as + // a number. https://tc39.github.io/ecma262/#sec-typedarray-length + case (lengthObj: JSAny): { + goto IfConstructByLength(lengthObj); } } - label IfConstructByLength(length: JSAny) { - return ConstructByLength(map, length, elementsInfo); - } - label IfConstructByArrayLike( - arrayLike: JSReceiver, length: uintptr, bufferConstructor: JSReceiver) { - return ConstructByArrayLike( - map, arrayLike, length, elementsInfo, bufferConstructor); - } + } label IfConstructByLength(length: JSAny) { + return ConstructByLength(map, length, elementsInfo); + } label IfConstructByArrayLike( + arrayLike: JSReceiver, length: uintptr, bufferConstructor: JSReceiver) { + return ConstructByArrayLike( + map, arrayLike, length, elementsInfo, bufferConstructor); } +} - transitioning macro TypedArraySpeciesCreate(implicit context: Context)( - methodName: constexpr string, numArgs: constexpr int31, - exemplar: JSTypedArray, arg0: JSAny, arg1: JSAny, - arg2: JSAny): JSTypedArray { - const defaultConstructor = GetDefaultConstructor(exemplar); +transitioning macro TypedArraySpeciesCreate(implicit context: Context)( + methodName: constexpr string, numArgs: constexpr int31, + exemplar: JSTypedArray, arg0: JSAny, arg1: JSAny, + arg2: JSAny): JSTypedArray { + const defaultConstructor = GetDefaultConstructor(exemplar); - try { - if (!IsPrototypeTypedArrayPrototype(exemplar.map)) goto IfSlow; - if (IsTypedArraySpeciesProtectorCellInvalid()) goto IfSlow; + try { + if (!IsPrototypeTypedArrayPrototype(exemplar.map)) goto IfSlow; + if (IsTypedArraySpeciesProtectorCellInvalid()) goto IfSlow; - const typedArray = CreateTypedArray( - context, defaultConstructor, defaultConstructor, arg0, arg1, arg2); + const typedArray = CreateTypedArray( + context, defaultConstructor, defaultConstructor, arg0, arg1, arg2); - // It is assumed that the CreateTypedArray builtin does not produce a - // typed array that fails ValidateTypedArray - assert(!IsDetachedBuffer(typedArray.buffer)); + // It is assumed that the CreateTypedArray builtin does not produce a + // typed array that fails ValidateTypedArray + assert(!IsDetachedBuffer(typedArray.buffer)); - return typedArray; + return typedArray; + } label IfSlow deferred { + const constructor = + Cast(SpeciesConstructor(exemplar, defaultConstructor)) + otherwise unreachable; + + // TODO(pwong): Simplify and remove numArgs when varargs are supported in + // macros. + let newObj: JSAny = Undefined; + if constexpr (numArgs == 1) { + newObj = Construct(constructor, arg0); + } else { + assert(numArgs == 3); + newObj = Construct(constructor, arg0, arg1, arg2); } - label IfSlow deferred { - const constructor = - Cast(SpeciesConstructor(exemplar, defaultConstructor)) - otherwise unreachable; - - // TODO(pwong): Simplify and remove numArgs when varargs are supported in - // macros. - let newObj: JSAny = Undefined; - if constexpr (numArgs == 1) { - newObj = Construct(constructor, arg0); - } else { - assert(numArgs == 3); - newObj = Construct(constructor, arg0, arg1, arg2); - } - return ValidateTypedArray(context, newObj, methodName); - } + return ValidateTypedArray(context, newObj, methodName); } +} - @export - transitioning macro TypedArraySpeciesCreateByLength(implicit context: - Context)( - methodName: constexpr string, exemplar: JSTypedArray, length: uintptr): - JSTypedArray { - const numArgs: constexpr int31 = 1; - // TODO(v8:4153): pass length further as uintptr. - const typedArray: JSTypedArray = TypedArraySpeciesCreate( - methodName, numArgs, exemplar, Convert(length), Undefined, - Undefined); - if (typedArray.length < length) deferred { - ThrowTypeError(MessageTemplate::kTypedArrayTooShort); - } - return typedArray; - } +@export +transitioning macro TypedArraySpeciesCreateByLength(implicit context: Context)( + methodName: constexpr string, exemplar: JSTypedArray, length: uintptr): + JSTypedArray { + const numArgs: constexpr int31 = 1; + // TODO(v8:4153): pass length further as uintptr. + const typedArray: JSTypedArray = TypedArraySpeciesCreate( + methodName, numArgs, exemplar, Convert(length), Undefined, + Undefined); + if (typedArray.length < length) deferred { + ThrowTypeError(MessageTemplate::kTypedArrayTooShort); + } + return typedArray; +} - transitioning macro TypedArraySpeciesCreateByBuffer(implicit context: - Context)( - methodName: constexpr string, exemplar: JSTypedArray, - buffer: JSArrayBuffer, beginByteOffset: uintptr, - newLength: uintptr): JSTypedArray { - const numArgs: constexpr int31 = 3; - // TODO(v8:4153): pass length further as uintptr. - const typedArray: JSTypedArray = TypedArraySpeciesCreate( - methodName, numArgs, exemplar, buffer, Convert(beginByteOffset), - Convert(newLength)); - return typedArray; - } +transitioning macro TypedArraySpeciesCreateByBuffer(implicit context: Context)( + methodName: constexpr string, exemplar: JSTypedArray, buffer: JSArrayBuffer, + beginByteOffset: uintptr, newLength: uintptr): JSTypedArray { + const numArgs: constexpr int31 = 3; + // TODO(v8:4153): pass length further as uintptr. + const typedArray: JSTypedArray = TypedArraySpeciesCreate( + methodName, numArgs, exemplar, buffer, Convert(beginByteOffset), + Convert(newLength)); + return typedArray; +} } diff --git a/deps/v8/src/builtins/typed-array-every.tq b/deps/v8/src/builtins/typed-array-every.tq index cba688244b59ea..fdd4961dee64ab 100644 --- a/deps/v8/src/builtins/typed-array-every.tq +++ b/deps/v8/src/builtins/typed-array-every.tq @@ -5,52 +5,49 @@ #include 'src/builtins/builtins-typed-array-gen.h' namespace typed_array { - const kBuiltinNameEvery: constexpr string = '%TypedArray%.prototype.every'; +const kBuiltinNameEvery: constexpr string = '%TypedArray%.prototype.every'; - transitioning macro EveryAllElements(implicit context: Context)( - array: typed_array::AttachedJSTypedArray, callbackfn: Callable, - thisArg: JSAny): Boolean { - let witness = typed_array::NewAttachedJSTypedArrayWitness(array); - const length: uintptr = witness.Get().length; - for (let k: uintptr = 0; k < length; k++) { - // BUG(4895): We should throw on detached buffers rather than simply exit. - witness.Recheck() otherwise break; - const value: JSAny = witness.Load(k); - // TODO(v8:4153): Consider versioning this loop for Smi and non-Smi - // indices to optimize Convert(k) for the most common case. - const result = Call( - context, callbackfn, thisArg, value, Convert(k), - witness.GetStable()); - if (!ToBoolean(result)) { - return False; - } +transitioning macro EveryAllElements(implicit context: Context)( + array: typed_array::AttachedJSTypedArray, callbackfn: Callable, + thisArg: JSAny): Boolean { + let witness = typed_array::NewAttachedJSTypedArrayWitness(array); + const length: uintptr = witness.Get().length; + for (let k: uintptr = 0; k < length; k++) { + // BUG(4895): We should throw on detached buffers rather than simply exit. + witness.Recheck() otherwise break; + const value: JSAny = witness.Load(k); + // TODO(v8:4153): Consider versioning this loop for Smi and non-Smi + // indices to optimize Convert(k) for the most common case. + const result = Call( + context, callbackfn, thisArg, value, Convert(k), + witness.GetStable()); + if (!ToBoolean(result)) { + return False; } - return True; } + return True; +} - // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.every - transitioning javascript builtin - TypedArrayPrototypeEvery(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): JSAny { - // arguments[0] = callback - // arguments[1] = thisArg - try { - const array: JSTypedArray = Cast(receiver) - otherwise NotTypedArray; - const uarray = typed_array::EnsureAttached(array) otherwise IsDetached; +// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.every +transitioning javascript builtin +TypedArrayPrototypeEvery( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + // arguments[0] = callback + // arguments[1] = thisArg + try { + const array: JSTypedArray = Cast(receiver) + otherwise NotTypedArray; + const uarray = typed_array::EnsureAttached(array) otherwise IsDetached; - const callbackfn = Cast(arguments[0]) otherwise NotCallable; - const thisArg = arguments[1]; - return EveryAllElements(uarray, callbackfn, thisArg); - } - label NotCallable deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); - } - label NotTypedArray deferred { - ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameEvery); - } - label IsDetached deferred { - ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameEvery); - } + const callbackfn = Cast(arguments[0]) otherwise NotCallable; + const thisArg = arguments[1]; + return EveryAllElements(uarray, callbackfn, thisArg); + } label NotCallable deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); + } label NotTypedArray deferred { + ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameEvery); + } label IsDetached deferred { + ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameEvery); } } +} diff --git a/deps/v8/src/builtins/typed-array-filter.tq b/deps/v8/src/builtins/typed-array-filter.tq index dd3b4900dc70bf..15d40f92ebe53b 100644 --- a/deps/v8/src/builtins/typed-array-filter.tq +++ b/deps/v8/src/builtins/typed-array-filter.tq @@ -3,85 +3,81 @@ // found in the LICENSE file. namespace typed_array { - const kBuiltinNameFilter: constexpr string = '%TypedArray%.prototype.filter'; +const kBuiltinNameFilter: constexpr string = '%TypedArray%.prototype.filter'; - // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.filter - transitioning javascript builtin TypedArrayPrototypeFilter( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - // arguments[0] = callback - // arguments[1] = thisArg - try { - // 1. Let O be the this value. - // 2. Perform ? ValidateTypedArray(O). - const array: JSTypedArray = Cast(receiver) - otherwise ThrowTypeError( - MessageTemplate::kNotTypedArray, kBuiltinNameFilter); - const src = typed_array::EnsureAttached(array) otherwise IsDetached; +// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.filter +transitioning javascript builtin TypedArrayPrototypeFilter( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + // arguments[0] = callback + // arguments[1] = thisArg + try { + // 1. Let O be the this value. + // 2. Perform ? ValidateTypedArray(O). + const array: JSTypedArray = Cast(receiver) + otherwise ThrowTypeError( + MessageTemplate::kNotTypedArray, kBuiltinNameFilter); + const src = typed_array::EnsureAttached(array) otherwise IsDetached; - // 3. Let len be O.[[ArrayLength]]. - const len: uintptr = src.length; + // 3. Let len be O.[[ArrayLength]]. + const len: uintptr = src.length; - // 4. If IsCallable(callbackfn) is false, throw a TypeError exception. - const callbackfn = Cast(arguments[0]) - otherwise ThrowTypeError( - MessageTemplate::kCalledNonCallable, arguments[0]); + // 4. If IsCallable(callbackfn) is false, throw a TypeError exception. + const callbackfn = Cast(arguments[0]) + otherwise ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); - // 5. If thisArg is present, let T be thisArg; else let T be undefined. - const thisArg: JSAny = arguments[1]; + // 5. If thisArg is present, let T be thisArg; else let T be undefined. + const thisArg: JSAny = arguments[1]; - // 6. Let kept be a new empty List. - // TODO(v8:4153): Support huge TypedArrays here. (growable fixed arrays - // can't be longer than kMaxSmiValue). - let kept = growable_fixed_array::NewGrowableFixedArray(); - let witness = typed_array::NewAttachedJSTypedArrayWitness(src); + // 6. Let kept be a new empty List. + // TODO(v8:4153): Support huge TypedArrays here. (growable fixed arrays + // can't be longer than kMaxSmiValue). + let kept = growable_fixed_array::NewGrowableFixedArray(); + let witness = typed_array::NewAttachedJSTypedArrayWitness(src); - // 7. Let k be 0. - // 8. Let captured be 0. - // 9. Repeat, while k < len - for (let k: uintptr = 0; k < len; k++) { - witness.Recheck() otherwise IsDetached; + // 7. Let k be 0. + // 8. Let captured be 0. + // 9. Repeat, while k < len + for (let k: uintptr = 0; k < len; k++) { + witness.Recheck() otherwise IsDetached; - // a. Let Pk be ! ToString(k). - // b. Let kValue be ? Get(O, Pk). - const value: JSAny = witness.Load(k); + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(O, Pk). + const value: JSAny = witness.Load(k); - // c. Let selected be ToBoolean(? Call(callbackfn, T, « kValue, k, O - // »)). - // TODO(v8:4153): Consider versioning this loop for Smi and non-Smi - // indices to optimize Convert(k) for the most common case. - const selected: JSAny = Call( - context, callbackfn, thisArg, value, Convert(k), - witness.GetStable()); + // c. Let selected be ToBoolean(? Call(callbackfn, T, « kValue, k, O + // »)). + // TODO(v8:4153): Consider versioning this loop for Smi and non-Smi + // indices to optimize Convert(k) for the most common case. + const selected: JSAny = Call( + context, callbackfn, thisArg, value, Convert(k), + witness.GetStable()); - // d. If selected is true, then - // i. Append kValue to the end of kept. - // ii. Increase captured by 1. - if (ToBoolean(selected)) kept.Push(value); + // d. If selected is true, then + // i. Append kValue to the end of kept. + // ii. Increase captured by 1. + if (ToBoolean(selected)) kept.Push(value); - // e.Increase k by 1. - } + // e.Increase k by 1. + } - // 10. Let A be ? TypedArraySpeciesCreate(O, captured). - const typedArray: JSTypedArray = TypedArraySpeciesCreateByLength( - kBuiltinNameFilter, array, Unsigned(kept.length)); + // 10. Let A be ? TypedArraySpeciesCreate(O, captured). + const typedArray: JSTypedArray = TypedArraySpeciesCreateByLength( + kBuiltinNameFilter, array, Unsigned(kept.length)); - // 11. Let n be 0. - // 12. For each element e of kept, do - // a. Perform ! Set(A, ! ToString(n), e, true). - // b. Increment n by 1. - // TODO(v8:4153): Consider passing growable typed array directly to - // TypedArrayCopyElements() to avoid JSArray materialization. Or collect - // indices instead of values the loop above. - const lengthNumber = Convert(Unsigned(kept.length)); - TypedArrayCopyElements( - context, typedArray, kept.ToJSArray(), lengthNumber); + // 11. Let n be 0. + // 12. For each element e of kept, do + // a. Perform ! Set(A, ! ToString(n), e, true). + // b. Increment n by 1. + // TODO(v8:4153): Consider passing growable typed array directly to + // TypedArrayCopyElements() to avoid JSArray materialization. Or collect + // indices instead of values the loop above. + const lengthNumber = Convert(Unsigned(kept.length)); + TypedArrayCopyElements(context, typedArray, kept.ToJSArray(), lengthNumber); - // 13. Return A. - return typedArray; - } - label IsDetached deferred { - ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameFilter); - } + // 13. Return A. + return typedArray; + } label IsDetached deferred { + ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameFilter); } } +} diff --git a/deps/v8/src/builtins/typed-array-find.tq b/deps/v8/src/builtins/typed-array-find.tq index ef4954274b2c63..24a13dbc23eea1 100644 --- a/deps/v8/src/builtins/typed-array-find.tq +++ b/deps/v8/src/builtins/typed-array-find.tq @@ -5,52 +5,49 @@ #include 'src/builtins/builtins-typed-array-gen.h' namespace typed_array { - const kBuiltinNameFind: constexpr string = '%TypedArray%.prototype.find'; +const kBuiltinNameFind: constexpr string = '%TypedArray%.prototype.find'; - transitioning macro FindAllElements(implicit context: Context)( - array: typed_array::AttachedJSTypedArray, callbackfn: Callable, - thisArg: JSAny): JSAny { - let witness = typed_array::NewAttachedJSTypedArrayWitness(array); - const length: uintptr = witness.Get().length; - for (let k: uintptr = 0; k < length; k++) { - // BUG(4895): We should throw on detached buffers rather than simply exit. - witness.Recheck() otherwise break; - const value: JSAny = witness.Load(k); - // TODO(v8:4153): Consider versioning this loop for Smi and non-Smi - // indices to optimize Convert(k) for the most common case. - const result = Call( - context, callbackfn, thisArg, value, Convert(k), - witness.GetStable()); - if (ToBoolean(result)) { - return value; - } +transitioning macro FindAllElements(implicit context: Context)( + array: typed_array::AttachedJSTypedArray, callbackfn: Callable, + thisArg: JSAny): JSAny { + let witness = typed_array::NewAttachedJSTypedArrayWitness(array); + const length: uintptr = witness.Get().length; + for (let k: uintptr = 0; k < length; k++) { + // BUG(4895): We should throw on detached buffers rather than simply exit. + witness.Recheck() otherwise break; + const value: JSAny = witness.Load(k); + // TODO(v8:4153): Consider versioning this loop for Smi and non-Smi + // indices to optimize Convert(k) for the most common case. + const result = Call( + context, callbackfn, thisArg, value, Convert(k), + witness.GetStable()); + if (ToBoolean(result)) { + return value; } - return Undefined; } + return Undefined; +} - // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.find - transitioning javascript builtin - TypedArrayPrototypeFind(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): JSAny { - // arguments[0] = callback - // arguments[1] = thisArg - try { - const array: JSTypedArray = Cast(receiver) - otherwise NotTypedArray; - const uarray = typed_array::EnsureAttached(array) otherwise IsDetached; +// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.find +transitioning javascript builtin +TypedArrayPrototypeFind( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + // arguments[0] = callback + // arguments[1] = thisArg + try { + const array: JSTypedArray = Cast(receiver) + otherwise NotTypedArray; + const uarray = typed_array::EnsureAttached(array) otherwise IsDetached; - const callbackfn = Cast(arguments[0]) otherwise NotCallable; - const thisArg = arguments[1]; - return FindAllElements(uarray, callbackfn, thisArg); - } - label NotCallable deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); - } - label NotTypedArray deferred { - ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameFind); - } - label IsDetached deferred { - ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameFind); - } + const callbackfn = Cast(arguments[0]) otherwise NotCallable; + const thisArg = arguments[1]; + return FindAllElements(uarray, callbackfn, thisArg); + } label NotCallable deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); + } label NotTypedArray deferred { + ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameFind); + } label IsDetached deferred { + ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameFind); } } +} diff --git a/deps/v8/src/builtins/typed-array-findindex.tq b/deps/v8/src/builtins/typed-array-findindex.tq index 5af9aede2968a1..7bb01151f351f2 100644 --- a/deps/v8/src/builtins/typed-array-findindex.tq +++ b/deps/v8/src/builtins/typed-array-findindex.tq @@ -5,56 +5,50 @@ #include 'src/builtins/builtins-typed-array-gen.h' namespace typed_array { - const kBuiltinNameFindIndex: constexpr string = - '%TypedArray%.prototype.findIndex'; +const kBuiltinNameFindIndex: constexpr string = + '%TypedArray%.prototype.findIndex'; - transitioning macro FindIndexAllElements(implicit context: Context)( - array: typed_array::AttachedJSTypedArray, callbackfn: Callable, - thisArg: JSAny): Number { - let witness = typed_array::NewAttachedJSTypedArrayWitness(array); - const length: uintptr = witness.Get().length; - for (let k: uintptr = 0; k < length; k++) { - // BUG(4895): We should throw on detached buffers rather than simply exit. - witness.Recheck() otherwise break; - const value: JSAny = witness.Load(k); - // TODO(v8:4153): Consider versioning this loop for Smi and non-Smi - // indices to optimize Convert(k) for the most common case. - const indexNumber: Number = Convert(k); - const result = Call( - context, callbackfn, thisArg, value, indexNumber, - witness.GetStable()); - if (ToBoolean(result)) { - return indexNumber; - } +transitioning macro FindIndexAllElements(implicit context: Context)( + array: typed_array::AttachedJSTypedArray, callbackfn: Callable, + thisArg: JSAny): Number { + let witness = typed_array::NewAttachedJSTypedArrayWitness(array); + const length: uintptr = witness.Get().length; + for (let k: uintptr = 0; k < length; k++) { + // BUG(4895): We should throw on detached buffers rather than simply exit. + witness.Recheck() otherwise break; + const value: JSAny = witness.Load(k); + // TODO(v8:4153): Consider versioning this loop for Smi and non-Smi + // indices to optimize Convert(k) for the most common case. + const indexNumber: Number = Convert(k); + const result = Call( + context, callbackfn, thisArg, value, indexNumber, witness.GetStable()); + if (ToBoolean(result)) { + return indexNumber; } - return -1; } + return -1; +} - // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.findIndex - transitioning javascript builtin - TypedArrayPrototypeFindIndex( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - // arguments[0] = callback - // arguments[1] = thisArg. - try { - const array: JSTypedArray = Cast(receiver) - otherwise NotTypedArray; - const uarray = typed_array::EnsureAttached(array) otherwise IsDetached; +// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.findIndex +transitioning javascript builtin +TypedArrayPrototypeFindIndex( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + // arguments[0] = callback + // arguments[1] = thisArg. + try { + const array: JSTypedArray = Cast(receiver) + otherwise NotTypedArray; + const uarray = typed_array::EnsureAttached(array) otherwise IsDetached; - const callbackfn = Cast(arguments[0]) otherwise NotCallable; - const thisArg = arguments[1]; - return FindIndexAllElements(uarray, callbackfn, thisArg); - } - label NotCallable deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); - } - label NotTypedArray deferred { - ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameFindIndex); - } - label IsDetached deferred { - ThrowTypeError( - MessageTemplate::kDetachedOperation, kBuiltinNameFindIndex); - } + const callbackfn = Cast(arguments[0]) otherwise NotCallable; + const thisArg = arguments[1]; + return FindIndexAllElements(uarray, callbackfn, thisArg); + } label NotCallable deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); + } label NotTypedArray deferred { + ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameFindIndex); + } label IsDetached deferred { + ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameFindIndex); } } +} diff --git a/deps/v8/src/builtins/typed-array-foreach.tq b/deps/v8/src/builtins/typed-array-foreach.tq index dd134174568916..d696d9c8dd8447 100644 --- a/deps/v8/src/builtins/typed-array-foreach.tq +++ b/deps/v8/src/builtins/typed-array-foreach.tq @@ -5,52 +5,47 @@ #include 'src/builtins/builtins-typed-array-gen.h' namespace typed_array { - const kBuiltinNameForEach: constexpr string = - '%TypedArray%.prototype.forEach'; +const kBuiltinNameForEach: constexpr string = '%TypedArray%.prototype.forEach'; - transitioning macro ForEachAllElements(implicit context: Context)( - array: typed_array::AttachedJSTypedArray, callbackfn: Callable, - thisArg: JSAny): Undefined { - let witness = typed_array::NewAttachedJSTypedArrayWitness(array); - const length: uintptr = witness.Get().length; - for (let k: uintptr = 0; k < length; k++) { - // BUG(4895): We should throw on detached buffers rather than simply exit. - witness.Recheck() otherwise break; - const value: JSAny = witness.Load(k); - // TODO(v8:4153): Consider versioning this loop for Smi and non-Smi - // indices to optimize Convert(k) for the most common case. - Call( - context, callbackfn, thisArg, value, Convert(k), - witness.GetStable()); - } - return Undefined; +transitioning macro ForEachAllElements(implicit context: Context)( + array: typed_array::AttachedJSTypedArray, callbackfn: Callable, + thisArg: JSAny): Undefined { + let witness = typed_array::NewAttachedJSTypedArrayWitness(array); + const length: uintptr = witness.Get().length; + for (let k: uintptr = 0; k < length; k++) { + // BUG(4895): We should throw on detached buffers rather than simply exit. + witness.Recheck() otherwise break; + const value: JSAny = witness.Load(k); + // TODO(v8:4153): Consider versioning this loop for Smi and non-Smi + // indices to optimize Convert(k) for the most common case. + Call( + context, callbackfn, thisArg, value, Convert(k), + witness.GetStable()); } + return Undefined; +} - // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.every - transitioning javascript builtin - TypedArrayPrototypeForEach( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): Undefined { - // arguments[0] = callback - // arguments[1] = this_arg. +// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.every +transitioning javascript builtin +TypedArrayPrototypeForEach(js-implicit context: NativeContext, receiver: JSAny)( + ...arguments): Undefined { + // arguments[0] = callback + // arguments[1] = this_arg. - try { - const array: JSTypedArray = Cast(receiver) - otherwise NotTypedArray; - const uarray = typed_array::EnsureAttached(array) otherwise IsDetached; + try { + const array: JSTypedArray = Cast(receiver) + otherwise NotTypedArray; + const uarray = typed_array::EnsureAttached(array) otherwise IsDetached; - const callbackfn = Cast(arguments[0]) otherwise NotCallable; - const thisArg = arguments[1]; - return ForEachAllElements(uarray, callbackfn, thisArg); - } - label NotCallable deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); - } - label NotTypedArray deferred { - ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameForEach); - } - label IsDetached deferred { - ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameForEach); - } + const callbackfn = Cast(arguments[0]) otherwise NotCallable; + const thisArg = arguments[1]; + return ForEachAllElements(uarray, callbackfn, thisArg); + } label NotCallable deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); + } label NotTypedArray deferred { + ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameForEach); + } label IsDetached deferred { + ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameForEach); } } +} diff --git a/deps/v8/src/builtins/typed-array-from.tq b/deps/v8/src/builtins/typed-array-from.tq index a0c2fa72a86314..56d4d1d6cf75d3 100644 --- a/deps/v8/src/builtins/typed-array-from.tq +++ b/deps/v8/src/builtins/typed-array-from.tq @@ -5,190 +5,179 @@ #include 'src/builtins/builtins-typed-array-gen.h' namespace typed_array { - const kBuiltinNameFrom: constexpr string = '%TypedArray%.from'; +const kBuiltinNameFrom: constexpr string = '%TypedArray%.from'; + +type BuiltinsName extends int31 constexpr 'Builtins::Name'; +const kTypedArrayPrototypeValues: constexpr BuiltinsName + generates 'Builtins::kTypedArrayPrototypeValues'; + +extern builtin IterableToList(implicit context: Context)(JSAny, JSAny): JSArray; + +// %TypedArray%.from ( source [ , mapfn [ , thisArg ] ] ) +// https://tc39.github.io/ecma262/#sec-%typedarray%.from +transitioning javascript builtin +TypedArrayFrom(js-implicit context: NativeContext, receiver: JSAny)( + ...arguments): JSTypedArray { + try { + const source: JSAny = arguments[0]; + const mapfnObj: JSAny = arguments[1]; + const thisArg = arguments[2]; + + // 1. Let C be the this value. + // 2. If IsConstructor(C) is false, throw a TypeError exception. + const constructor = Cast(receiver) otherwise NotConstructor; + + // 3. If mapfn is undefined, then let mapping be false. + // 4. Else, + // a. If IsCallable(mapfn) is false, throw a TypeError exception. + // b. Let mapping be true. + const mapping: bool = mapfnObj != Undefined; + if (mapping && !Is(mapfnObj)) deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, mapfnObj); + } - type BuiltinsName extends int31 constexpr 'Builtins::Name'; - const kTypedArrayPrototypeValues: constexpr BuiltinsName - generates 'Builtins::kTypedArrayPrototypeValues'; + // We split up this builtin differently to the way it is written in the + // spec. We already have great code in the elements accessor for copying + // from a JSArray into a TypedArray, so we use that when possible. We only + // avoid calling into the elements accessor when we have a mapping + // function, because we can't handle that. Here, presence of a mapping + // function is the slow path. We also combine the two different loops in + // the specification (starting at 7.e and 13) because they are essentially + // identical. We also save on code-size this way. - extern builtin IterableToList(implicit context: Context)(JSAny, JSAny): - JSArray; + let finalLength: uintptr; + let finalSource: JSAny; - // %TypedArray%.from ( source [ , mapfn [ , thisArg ] ] ) - // https://tc39.github.io/ecma262/#sec-%typedarray%.from - transitioning javascript builtin - TypedArrayFrom(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): JSTypedArray { try { - const source: JSAny = arguments[0]; - - // 1. Let C be the this value. - // 2. If IsConstructor(C) is false, throw a TypeError exception. - const constructor = Cast(receiver) otherwise NotConstructor; - - // 3. If mapfn is present and mapfn is not undefined, then - // a. If IsCallable(mapfn) is false, throw a TypeError exception. - // b. Let mapping be true. - // 4. Else, let mapping be false. - const mapping: bool = arguments.length > 1; - const mapfnObj: JSAny = mapping ? arguments[1] : Undefined; - if (mapping && !TaggedIsCallable(mapfnObj)) deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, mapfnObj); - } - - // 5. If thisArg is present, let T be thisArg; else let T be undefined. - const thisArg = arguments.length > 2 ? arguments[2] : Undefined; - - // We split up this builtin differently to the way it is written in the - // spec. We already have great code in the elements accessor for copying - // from a JSArray into a TypedArray, so we use that when possible. We only - // avoid calling into the elements accessor when we have a mapping - // function, because we can't handle that. Here, presence of a mapping - // function is the slow path. We also combine the two different loops in - // the specification (starting at 7.e and 13) because they are essentially - // identical. We also save on code-size this way. - - let finalLength: uintptr; - let finalSource: JSAny; + // 5. Let usingIterator be ? GetMethod(source, @@iterator). + // TODO(v8:8906): Use iterator::GetIteratorMethod() once it supports + // labels. + const usingIterator = GetMethod(source, IteratorSymbolConstant()) + otherwise IteratorIsUndefined, IteratorNotCallable; try { - // 6. Let usingIterator be ? GetMethod(source, @@iterator). - // TODO(v8:8906): Use iterator::GetIteratorMethod() once it supports - // labels. - const usingIterator = GetMethod(source, IteratorSymbolConstant()) - otherwise IteratorIsUndefined, IteratorNotCallable; - - try { - // TypedArrays have iterators, so normally we would go through the - // IterableToList case below, which would convert the TypedArray to a - // JSArray (boxing the values if they won't fit in a Smi). - // - // However, if we can guarantee that the source object has the - // built-in iterator and that the %ArrayIteratorPrototype%.next method - // has not been overridden, then we know the behavior of the iterator: - // returning the values in the TypedArray sequentially from index 0 to - // length-1. - // - // In this case, we can avoid creating the intermediate array and the - // associated HeapNumbers, and use the fast path in - // TypedArrayCopyElements which uses the same ordering as the default - // iterator. - // - // Drop through to the default check_iterator behavior if any of these - // checks fail. - const sourceTypedArray = - Cast(source) otherwise UseUserProvidedIterator; - const sourceBuffer = sourceTypedArray.buffer; - if (IsDetachedBuffer(sourceBuffer)) goto UseUserProvidedIterator; - - // Check that the iterator function is exactly - // Builtins::kTypedArrayPrototypeValues. - const iteratorFn = - Cast(usingIterator) otherwise UseUserProvidedIterator; - if (!TaggedEqual( - iteratorFn.shared_function_info.function_data, - SmiConstant(kTypedArrayPrototypeValues))) - goto UseUserProvidedIterator; - - // Check that the ArrayIterator prototype's "next" method hasn't been - // overridden. - if (IsArrayIteratorProtectorCellInvalid()) - goto UseUserProvidedIterator; - - // Source is a TypedArray with unmodified iterator behavior. Use the - // source object directly, taking advantage of the special-case code - // in TypedArrayCopyElements - finalLength = sourceTypedArray.length; - finalSource = source; - } - label UseUserProvidedIterator { - // 7. If usingIterator is not undefined, then - // a. Let values be ? IterableToList(source, usingIterator). - // b. Let len be the number of elements in values. - const values: JSArray = IterableToList(source, usingIterator); - - finalLength = Convert(values.length); - finalSource = values; - } - } - label IteratorIsUndefined { - // 8. NOTE: source is not an Iterable so assume it is already an - // array-like object. - - // 9. Let arrayLike be ! ToObject(source). - const arrayLike: JSReceiver = ToObject_Inline(context, source); - - // 10. Let len be ? ToLength(? Get(arrayLike, "length")). - const length = GetLengthProperty(arrayLike); - - try { - finalLength = ChangeSafeIntegerNumberToUintPtr(length) - otherwise IfInvalidLength; - finalSource = arrayLike; - } - label IfInvalidLength deferred { - ThrowRangeError(MessageTemplate::kInvalidTypedArrayLength, length); - } - } - label IteratorNotCallable(_value: JSAny) deferred { - ThrowTypeError(MessageTemplate::kIteratorSymbolNonCallable); + // TypedArrays have iterators, so normally we would go through the + // IterableToList case below, which would convert the TypedArray to a + // JSArray (boxing the values if they won't fit in a Smi). + // + // However, if we can guarantee that the source object has the + // built-in iterator and that the %ArrayIteratorPrototype%.next method + // has not been overridden, then we know the behavior of the iterator: + // returning the values in the TypedArray sequentially from index 0 to + // length-1. + // + // In this case, we can avoid creating the intermediate array and the + // associated HeapNumbers, and use the fast path in + // TypedArrayCopyElements which uses the same ordering as the default + // iterator. + // + // Drop through to the default check_iterator behavior if any of these + // checks fail. + const sourceTypedArray = + Cast(source) otherwise UseUserProvidedIterator; + const sourceBuffer = sourceTypedArray.buffer; + if (IsDetachedBuffer(sourceBuffer)) goto UseUserProvidedIterator; + + // Check that the iterator function is exactly + // Builtins::kTypedArrayPrototypeValues. + const iteratorFn = + Cast(usingIterator) otherwise UseUserProvidedIterator; + if (!TaggedEqual( + iteratorFn.shared_function_info.function_data, + SmiConstant(kTypedArrayPrototypeValues))) + goto UseUserProvidedIterator; + + // Check that the ArrayIterator prototype's "next" method hasn't been + // overridden. + if (IsArrayIteratorProtectorCellInvalid()) goto UseUserProvidedIterator; + + // Source is a TypedArray with unmodified iterator behavior. Use the + // source object directly, taking advantage of the special-case code + // in TypedArrayCopyElements + finalLength = sourceTypedArray.length; + finalSource = source; + } label UseUserProvidedIterator { + // 6. If usingIterator is not undefined, then + // a. Let values be ? IterableToList(source, usingIterator). + // b. Let len be the number of elements in values. + const values: JSArray = IterableToList(source, usingIterator); + + finalLength = Convert(values.length); + finalSource = values; } + } label IteratorIsUndefined { + // 7. NOTE: source is not an Iterable so assume it is already an + // array-like object. - const finalLengthNum = Convert(finalLength); + // 8. Let arrayLike be ! ToObject(source). + const arrayLike: JSReceiver = ToObject_Inline(context, source); - // 7c/11. Let targetObj be ? TypedArrayCreate(C, «len»). - const targetObj = TypedArrayCreateByLength( - constructor, finalLengthNum, kBuiltinNameFrom); + // 9. Let len be ? LengthOfArrayLike(arrayLike). + const length = GetLengthProperty(arrayLike); - if (!mapping) { - // Fast path. - if (finalLength != 0) { - // Call runtime. - TypedArrayCopyElements( - context, targetObj, finalSource, finalLengthNum); - } - return targetObj; + try { + finalLength = ChangeSafeIntegerNumberToUintPtr(length) + otherwise IfInvalidLength; + finalSource = arrayLike; + } label IfInvalidLength deferred { + ThrowRangeError(MessageTemplate::kInvalidTypedArrayLength, length); } - // Slow path. - - const mapfn: Callable = Cast(mapfnObj) otherwise unreachable; - const accessor: TypedArrayAccessor = - GetTypedArrayAccessor(targetObj.elements_kind); - - // 7d-7e and 12-13. - // 12. Let k be 0. - // 13. Repeat, while k < len - for (let k: uintptr = 0; k < finalLength; k++) { - // 13a. Let Pk be ! ToString(k). - const kNum = Convert(k); - - // 13b. Let kValue be ? Get(arrayLike, Pk). - const kValue: JSAny = GetProperty(finalSource, kNum); - - let mappedValue: JSAny; - // 13c. If mapping is true, then - if (mapping) { - // i. Let mappedValue be ? Call(mapfn, T, « kValue, k »). - mappedValue = Call(context, mapfn, thisArg, kValue, kNum); - } else { - // 13d. Else, let mappedValue be kValue. - mappedValue = kValue; - } - - // 13e. Perform ? Set(targetObj, Pk, mappedValue, true). - // Buffer may be detached during executing ToNumber/ToBigInt. - accessor.StoreJSAny(context, targetObj, k, mappedValue) - otherwise IfDetached; - - // 13f. Set k to k + 1. (done by the loop). + } label IteratorNotCallable(_value: JSAny) deferred { + ThrowTypeError(MessageTemplate::kIteratorSymbolNonCallable); + } + + const finalLengthNum = Convert(finalLength); + + // 6c/10. Let targetObj be ? TypedArrayCreate(C, «len»). + const targetObj = + TypedArrayCreateByLength(constructor, finalLengthNum, kBuiltinNameFrom); + + if (!mapping) { + // Fast path. + if (finalLength != 0) { + // Call runtime. + TypedArrayCopyElements(context, targetObj, finalSource, finalLengthNum); } return targetObj; } - label NotConstructor deferred { - ThrowTypeError(MessageTemplate::kNotConstructor, receiver); - } - label IfDetached deferred { - ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameFrom); + // Slow path. + + const mapfn: Callable = Cast(mapfnObj) otherwise unreachable; + const accessor: TypedArrayAccessor = + GetTypedArrayAccessor(targetObj.elements_kind); + + // 6d-6e and 11-12. + // 11. Let k be 0. + // 12. Repeat, while k < len + for (let k: uintptr = 0; k < finalLength; k++) { + // 12a. Let Pk be ! ToString(k). + const kNum = Convert(k); + + // 12b. Let kValue be ? Get(arrayLike, Pk). + const kValue: JSAny = GetProperty(finalSource, kNum); + + let mappedValue: JSAny; + // 12c. If mapping is true, then + if (mapping) { + // i. Let mappedValue be ? Call(mapfn, T, « kValue, k »). + mappedValue = Call(context, mapfn, thisArg, kValue, kNum); + } else { + // 12d. Else, let mappedValue be kValue. + mappedValue = kValue; + } + + // 12e. Perform ? Set(targetObj, Pk, mappedValue, true). + // Buffer may be detached during executing ToNumber/ToBigInt. + accessor.StoreJSAny(context, targetObj, k, mappedValue) + otherwise IfDetached; + + // 12f. Set k to k + 1. (done by the loop). } + return targetObj; + } label NotConstructor deferred { + ThrowTypeError(MessageTemplate::kNotConstructor, receiver); + } label IfDetached deferred { + ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameFrom); } } +} diff --git a/deps/v8/src/builtins/typed-array-of.tq b/deps/v8/src/builtins/typed-array-of.tq index c65deeeb150aac..b5d42ef9a2f58e 100644 --- a/deps/v8/src/builtins/typed-array-of.tq +++ b/deps/v8/src/builtins/typed-array-of.tq @@ -5,52 +5,50 @@ #include 'src/builtins/builtins-typed-array-gen.h' namespace typed_array { - const kBuiltinNameOf: constexpr string = '%TypedArray%.of'; - - // %TypedArray%.of ( ...items ) - // https://tc39.github.io/ecma262/#sec-%typedarray%.of - transitioning javascript builtin - TypedArrayOf(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): JSTypedArray { - try { - // 1. Let len be the actual number of arguments passed to this function. - const len: uintptr = Unsigned(arguments.length); - - // 2. Let items be the List of arguments passed to this function. - - // 3. Let C be the this value. - // 4. If IsConstructor(C) is false, throw a TypeError exception. - const constructor = Cast(receiver) otherwise NotConstructor; - - // 5. Let newObj be ? TypedArrayCreate(C, len). - const newObj = TypedArrayCreateByLength( - constructor, Convert(len), kBuiltinNameOf); - - const accessor: TypedArrayAccessor = - GetTypedArrayAccessor(newObj.elements_kind); - - // 6. Let k be 0. - // 7. Repeat, while k < len - for (let k: uintptr = 0; k < len; k++) { - // 7a. Let kValue be items[k]. - const kValue: JSAny = arguments[Signed(k)]; - - // 7b. Let Pk be ! ToString(k). - // 7c. Perform ? Set(newObj, Pk, kValue, true). - // Buffer may be detached during executing ToNumber/ToBigInt. - accessor.StoreJSAny(context, newObj, k, kValue) otherwise IfDetached; - - // 7d. Increase k by 1. (done by the loop). - } - - // 8. Return newObj. - return newObj; - } - label NotConstructor deferred { - ThrowTypeError(MessageTemplate::kNotConstructor, receiver); - } - label IfDetached deferred { - ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameOf); +const kBuiltinNameOf: constexpr string = '%TypedArray%.of'; + +// %TypedArray%.of ( ...items ) +// https://tc39.github.io/ecma262/#sec-%typedarray%.of +transitioning javascript builtin +TypedArrayOf(js-implicit context: NativeContext, receiver: JSAny)(...arguments): + JSTypedArray { + try { + // 1. Let len be the actual number of arguments passed to this function. + const len: uintptr = Unsigned(arguments.length); + + // 2. Let items be the List of arguments passed to this function. + + // 3. Let C be the this value. + // 4. If IsConstructor(C) is false, throw a TypeError exception. + const constructor = Cast(receiver) otherwise NotConstructor; + + // 5. Let newObj be ? TypedArrayCreate(C, len). + const newObj = TypedArrayCreateByLength( + constructor, Convert(len), kBuiltinNameOf); + + const accessor: TypedArrayAccessor = + GetTypedArrayAccessor(newObj.elements_kind); + + // 6. Let k be 0. + // 7. Repeat, while k < len + for (let k: uintptr = 0; k < len; k++) { + // 7a. Let kValue be items[k]. + const kValue: JSAny = arguments[Signed(k)]; + + // 7b. Let Pk be ! ToString(k). + // 7c. Perform ? Set(newObj, Pk, kValue, true). + // Buffer may be detached during executing ToNumber/ToBigInt. + accessor.StoreJSAny(context, newObj, k, kValue) otherwise IfDetached; + + // 7d. Increase k by 1. (done by the loop). } + + // 8. Return newObj. + return newObj; + } label NotConstructor deferred { + ThrowTypeError(MessageTemplate::kNotConstructor, receiver); + } label IfDetached deferred { + ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameOf); } } +} diff --git a/deps/v8/src/builtins/typed-array-reduce.tq b/deps/v8/src/builtins/typed-array-reduce.tq index a2ff53f65c4f6c..a54ed1040e92f7 100644 --- a/deps/v8/src/builtins/typed-array-reduce.tq +++ b/deps/v8/src/builtins/typed-array-reduce.tq @@ -5,65 +5,61 @@ #include 'src/builtins/builtins-typed-array-gen.h' namespace typed_array { - const kBuiltinNameReduce: constexpr string = '%TypedArray%.prototype.reduce'; +const kBuiltinNameReduce: constexpr string = '%TypedArray%.prototype.reduce'; - transitioning macro ReduceAllElements(implicit context: Context)( - array: typed_array::AttachedJSTypedArray, callbackfn: Callable, - initialValue: JSAny|TheHole): JSAny { - let witness = typed_array::NewAttachedJSTypedArrayWitness(array); - const length: uintptr = witness.Get().length; - let accumulator = initialValue; - for (let k: uintptr = 0; k < length; k++) { - // BUG(4895): We should throw on detached buffers rather than simply exit. - witness.Recheck() otherwise break; - const value: JSAny = witness.Load(k); - typeswitch (accumulator) { - case (TheHole): { - accumulator = value; - } - case (accumulatorNotHole: JSAny): { - // TODO(v8:4153): Consider versioning this loop for Smi and non-Smi - // indices to optimize Convert(k) for the most common case. - accumulator = Call( - context, callbackfn, Undefined, accumulatorNotHole, value, - Convert(k), witness.GetStable()); - } - } - } +transitioning macro ReduceAllElements(implicit context: Context)( + array: typed_array::AttachedJSTypedArray, callbackfn: Callable, + initialValue: JSAny|TheHole): JSAny { + let witness = typed_array::NewAttachedJSTypedArrayWitness(array); + const length: uintptr = witness.Get().length; + let accumulator = initialValue; + for (let k: uintptr = 0; k < length; k++) { + // BUG(4895): We should throw on detached buffers rather than simply exit. + witness.Recheck() otherwise break; + const value: JSAny = witness.Load(k); typeswitch (accumulator) { case (TheHole): { - ThrowTypeError(MessageTemplate::kReduceNoInitial, kBuiltinNameReduce); + accumulator = value; } - case (accumulator: JSAny): { - return accumulator; + case (accumulatorNotHole: JSAny): { + // TODO(v8:4153): Consider versioning this loop for Smi and non-Smi + // indices to optimize Convert(k) for the most common case. + accumulator = Call( + context, callbackfn, Undefined, accumulatorNotHole, value, + Convert(k), witness.GetStable()); } } } - - // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.reduce - transitioning javascript builtin - TypedArrayPrototypeReduce( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - // arguments[0] = callback - // arguments[1] = initialValue. - try { - const array: JSTypedArray = Cast(receiver) - otherwise NotTypedArray; - const uarray = typed_array::EnsureAttached(array) otherwise IsDetached; - - const callbackfn = Cast(arguments[0]) otherwise NotCallable; - const initialValue = arguments.length >= 2 ? arguments[1] : TheHole; - return ReduceAllElements(uarray, callbackfn, initialValue); - } - label NotCallable deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); - } - label NotTypedArray deferred { - ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameReduce); + typeswitch (accumulator) { + case (TheHole): { + ThrowTypeError(MessageTemplate::kReduceNoInitial, kBuiltinNameReduce); } - label IsDetached deferred { - ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameReduce); + case (accumulator: JSAny): { + return accumulator; } } } + +// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.reduce +transitioning javascript builtin +TypedArrayPrototypeReduce( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + // arguments[0] = callback + // arguments[1] = initialValue. + try { + const array: JSTypedArray = Cast(receiver) + otherwise NotTypedArray; + const uarray = typed_array::EnsureAttached(array) otherwise IsDetached; + + const callbackfn = Cast(arguments[0]) otherwise NotCallable; + const initialValue = arguments.length >= 2 ? arguments[1] : TheHole; + return ReduceAllElements(uarray, callbackfn, initialValue); + } label NotCallable deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); + } label NotTypedArray deferred { + ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameReduce); + } label IsDetached deferred { + ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameReduce); + } +} +} diff --git a/deps/v8/src/builtins/typed-array-reduceright.tq b/deps/v8/src/builtins/typed-array-reduceright.tq index ab334a1b8667e9..9ba2f70de4e886 100644 --- a/deps/v8/src/builtins/typed-array-reduceright.tq +++ b/deps/v8/src/builtins/typed-array-reduceright.tq @@ -5,69 +5,65 @@ #include 'src/builtins/builtins-typed-array-gen.h' namespace typed_array { - const kBuiltinNameReduceRight: constexpr string = - '%TypedArray%.prototype.reduceRight'; +const kBuiltinNameReduceRight: constexpr string = + '%TypedArray%.prototype.reduceRight'; - transitioning macro ReduceRightAllElements(implicit context: Context)( - array: typed_array::AttachedJSTypedArray, callbackfn: Callable, - initialValue: JSAny|TheHole): JSAny { - let witness = typed_array::NewAttachedJSTypedArrayWitness(array); - const length: uintptr = witness.Get().length; - let accumulator = initialValue; - for (let k: uintptr = length; k-- > 0;) { - // BUG(4895): We should throw on detached buffers rather than simply exit. - witness.Recheck() otherwise break; - const value: JSAny = witness.Load(k); - typeswitch (accumulator) { - case (TheHole): { - accumulator = value; - } - case (accumulatorNotHole: JSAny): { - // TODO(v8:4153): Consider versioning this loop for Smi and non-Smi - // indices to optimize Convert(k) for the most common case. - accumulator = Call( - context, callbackfn, Undefined, accumulatorNotHole, value, - Convert(k), witness.GetStable()); - } - } - } +transitioning macro ReduceRightAllElements(implicit context: Context)( + array: typed_array::AttachedJSTypedArray, callbackfn: Callable, + initialValue: JSAny|TheHole): JSAny { + let witness = typed_array::NewAttachedJSTypedArrayWitness(array); + const length: uintptr = witness.Get().length; + let accumulator = initialValue; + for (let k: uintptr = length; k-- > 0;) { + // BUG(4895): We should throw on detached buffers rather than simply exit. + witness.Recheck() otherwise break; + const value: JSAny = witness.Load(k); typeswitch (accumulator) { case (TheHole): { - ThrowTypeError( - MessageTemplate::kReduceNoInitial, kBuiltinNameReduceRight); + accumulator = value; } - case (accumulator: JSAny): { - return accumulator; + case (accumulatorNotHole: JSAny): { + // TODO(v8:4153): Consider versioning this loop for Smi and non-Smi + // indices to optimize Convert(k) for the most common case. + accumulator = Call( + context, callbackfn, Undefined, accumulatorNotHole, value, + Convert(k), witness.GetStable()); } } } + typeswitch (accumulator) { + case (TheHole): { + ThrowTypeError( + MessageTemplate::kReduceNoInitial, kBuiltinNameReduceRight); + } + case (accumulator: JSAny): { + return accumulator; + } + } +} - // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.reduceright - transitioning javascript builtin - TypedArrayPrototypeReduceRight( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - // arguments[0] = callback - // arguments[1] = initialValue. - try { - const array: JSTypedArray = Cast(receiver) - otherwise NotTypedArray; - const uarray = typed_array::EnsureAttached(array) otherwise IsDetached; +// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.reduceright +transitioning javascript builtin +TypedArrayPrototypeReduceRight( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + // arguments[0] = callback + // arguments[1] = initialValue. + try { + const array: JSTypedArray = Cast(receiver) + otherwise NotTypedArray; + const uarray = typed_array::EnsureAttached(array) otherwise IsDetached; - const callbackfn = Cast(arguments[0]) otherwise NotCallable; - const initialValue = arguments.length >= 2 ? arguments[1] : TheHole; + const callbackfn = Cast(arguments[0]) otherwise NotCallable; + const initialValue = arguments.length >= 2 ? arguments[1] : TheHole; - return ReduceRightAllElements(uarray, callbackfn, initialValue); - } - label NotCallable deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); - } - label NotTypedArray deferred { - ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameReduceRight); - } - label IsDetached deferred { - ThrowTypeError( - MessageTemplate::kDetachedOperation, kBuiltinNameReduceRight); - } + return ReduceRightAllElements(uarray, callbackfn, initialValue); + } label NotCallable deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); + } label NotTypedArray deferred { + ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameReduceRight); + } label IsDetached deferred { + ThrowTypeError( + MessageTemplate::kDetachedOperation, kBuiltinNameReduceRight); } } +} diff --git a/deps/v8/src/builtins/typed-array-set.tq b/deps/v8/src/builtins/typed-array-set.tq index bb70692e766b92..b5c9dcb261df65 100644 --- a/deps/v8/src/builtins/typed-array-set.tq +++ b/deps/v8/src/builtins/typed-array-set.tq @@ -5,315 +5,306 @@ #include 'src/builtins/builtins-typed-array-gen.h' namespace typed_array { - const kBuiltinNameSet: constexpr string = '%TypedArray%.prototype.set'; - - extern runtime TypedArraySet(Context, JSTypedArray, Object, Number, Number): - void; - - extern macro - TypedArrayBuiltinsAssembler::CallCCopyFastNumberJSArrayElementsToTypedArray( - Context, - FastJSArray, // source - AttachedJSTypedArray, // dest - uintptr, // sourceLength - uintptr // destOffset - ): void; - - extern macro - TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray( - AttachedJSTypedArray, // source - AttachedJSTypedArray, // dest - uintptr, // sourceLength - uintptr // destOffset - ): void; - - // %TypedArray%.prototype.set ( overloaded [ , offset ] ) - // https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-overloaded-offset - transitioning javascript builtin - TypedArrayPrototypeSet(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): JSAny { - // Steps 2-8 are the same for - // %TypedArray%.prototype.set ( array [ , offset ] ) and - // %TypedArray%.prototype.set ( typedArray [ , offset ] ) overloads. - - let target: JSTypedArray; - try { - // 2. Let target be the this value. - // 3. Perform ? RequireInternalSlot(target, [[TypedArrayName]]). - // 4. Assert: target has a [[ViewedArrayBuffer]] internal slot. - target = Cast(receiver) otherwise NotTypedArray; - } - label NotTypedArray deferred { - ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameSet); - } - - try { - // 5. Let targetOffset be ? ToInteger(offset). - // 6. If targetOffset < 0, throw a RangeError exception. - let targetOffsetOverflowed: bool = false; - let targetOffset: uintptr = 0; - if (arguments.length > 1) { - const offsetArg = arguments[1]; - try { - targetOffset = ToUintPtr(offsetArg) - // On values less than zero throw RangeError immediately. - otherwise OffsetOutOfBounds, - // On UintPtr or SafeInteger range overflow throw RangeError after - // performing observable steps to follow the spec. - OffsetOverflow, OffsetOverflow; - } - label OffsetOverflow { - targetOffsetOverflowed = true; - } - } else { - // If the offset argument is not provided then the targetOffset is 0. - } - - // 7. Let targetBuffer be target.[[ViewedArrayBuffer]]. - // 8. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError - // exception. - const utarget = typed_array::EnsureAttached(target) otherwise IsDetached; +const kBuiltinNameSet: constexpr string = '%TypedArray%.prototype.set'; + +extern runtime TypedArraySet( + Context, JSTypedArray, Object, Number, Number): void; + +extern macro +TypedArrayBuiltinsAssembler::CallCCopyFastNumberJSArrayElementsToTypedArray( + Context, + FastJSArray, // source + AttachedJSTypedArray, // dest + uintptr, // sourceLength + uintptr // destOffset + ): void; + +extern macro +TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray( + AttachedJSTypedArray, // source + AttachedJSTypedArray, // dest + uintptr, // sourceLength + uintptr // destOffset + ): void; + +// %TypedArray%.prototype.set ( overloaded [ , offset ] ) +// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-overloaded-offset +transitioning javascript builtin +TypedArrayPrototypeSet( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + // Steps 2-8 are the same for + // %TypedArray%.prototype.set ( array [ , offset ] ) and + // %TypedArray%.prototype.set ( typedArray [ , offset ] ) overloads. + + let target: JSTypedArray; + try { + // 2. Let target be the this value. + // 3. Perform ? RequireInternalSlot(target, [[TypedArrayName]]). + // 4. Assert: target has a [[ViewedArrayBuffer]] internal slot. + target = Cast(receiver) otherwise NotTypedArray; + } label NotTypedArray deferred { + ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameSet); + } - const overloadedArg = arguments[0]; + try { + // 5. Let targetOffset be ? ToInteger(offset). + // 6. If targetOffset < 0, throw a RangeError exception. + let targetOffsetOverflowed: bool = false; + let targetOffset: uintptr = 0; + if (arguments.length > 1) { + const offsetArg = arguments[1]; try { - // 1. Choose 22.2.3.23.2 or 22.2.3.23.1 depending on whether the - // overloadedArg has a [[TypedArrayName]] internal slot. - // If it does, the definition in 22.2.3.23.2 applies. - // If it does not, the definition in 22.2.3.23.1 applies. - const typedArray = - Cast(overloadedArg) otherwise NotTypedArray; - - // Step 9 is not observable, do it later. - - // 10. Let srcBuffer be typedArray.[[ViewedArrayBuffer]]. - // 11. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError - // exception. - const utypedArray = - typed_array::EnsureAttached(typedArray) otherwise IsDetached; - - TypedArrayPrototypeSetTypedArray( - utarget, utypedArray, targetOffset, targetOffsetOverflowed) - otherwise OffsetOutOfBounds; - return Undefined; - } - label NotTypedArray deferred { - TypedArrayPrototypeSetArray( - utarget, overloadedArg, targetOffset, targetOffsetOverflowed) - otherwise OffsetOutOfBounds, IsDetached; - return Undefined; + targetOffset = ToUintPtr(offsetArg) + // On values less than zero throw RangeError immediately. + otherwise OffsetOutOfBounds, + // On UintPtr or SafeInteger range overflow throw RangeError after + // performing observable steps to follow the spec. + OffsetOverflow, OffsetOverflow; + } label OffsetOverflow { + targetOffsetOverflowed = true; } + } else { + // If the offset argument is not provided then the targetOffset is 0. } - label OffsetOutOfBounds deferred { - ThrowRangeError(MessageTemplate::kTypedArraySetOffsetOutOfBounds); - } - label IsDetached deferred { - ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSet); - } - } - // %TypedArray%.prototype.set ( array [ , offset ] ) - // https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-array-offset - transitioning macro - TypedArrayPrototypeSetArray(implicit context: Context, receiver: JSAny)( - target: JSTypedArray, arrayArg: JSAny, targetOffset: uintptr, - targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds, - IfDetached { - // Steps 9-13 are not observable, do them later. - - // TODO(v8:8906): This ported behaviour is an observable spec violation and - // the comment below seems to be outdated. Consider removing this code. + // 7. Let targetBuffer be target.[[ViewedArrayBuffer]]. + // 8. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError + // exception. + const utarget = typed_array::EnsureAttached(target) otherwise IsDetached; + + const overloadedArg = arguments[0]; try { - const _arrayArgNum = Cast(arrayArg) otherwise NotNumber; - // For number as a first argument, throw TypeError instead of silently - // ignoring the call, so that users know they did something wrong. - // (Consistent with Firefox and Blink/WebKit) - ThrowTypeError(MessageTemplate::kInvalidArgument); - } - label NotNumber { - // Proceed to step 14. - } + // 1. Choose 22.2.3.23.2 or 22.2.3.23.1 depending on whether the + // overloadedArg has a [[TypedArrayName]] internal slot. + // If it does, the definition in 22.2.3.23.2 applies. + // If it does not, the definition in 22.2.3.23.1 applies. + const typedArray = + Cast(overloadedArg) otherwise NotTypedArray; - // 14. Let src be ? ToObject(array). - const src: JSReceiver = ToObject_Inline(context, arrayArg); + // Step 9 is not observable, do it later. - // 15. Let srcLength be ? LengthOfArrayLike(src). - const srcLengthNum: Number = GetLengthProperty(src); + // 10. Let srcBuffer be typedArray.[[ViewedArrayBuffer]]. + // 11. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError + // exception. + const utypedArray = + typed_array::EnsureAttached(typedArray) otherwise IsDetached; + + TypedArrayPrototypeSetTypedArray( + utarget, utypedArray, targetOffset, targetOffsetOverflowed) + otherwise OffsetOutOfBounds; + return Undefined; + } label NotTypedArray deferred { + TypedArrayPrototypeSetArray( + utarget, overloadedArg, targetOffset, targetOffsetOverflowed) + otherwise OffsetOutOfBounds, IsDetached; + return Undefined; + } + } label OffsetOutOfBounds deferred { + ThrowRangeError(MessageTemplate::kTypedArraySetOffsetOutOfBounds); + } label IsDetached deferred { + ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSet); + } +} - if (targetOffsetOverflowed) goto IfOffsetOutOfBounds; +// %TypedArray%.prototype.set ( array [ , offset ] ) +// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-array-offset +transitioning macro +TypedArrayPrototypeSetArray(implicit context: Context, receiver: JSAny)( + target: JSTypedArray, arrayArg: JSAny, targetOffset: uintptr, + targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds, + IfDetached { + // Steps 9-13 are not observable, do them later. + + // TODO(v8:8906): This ported behaviour is an observable spec violation and + // the comment below seems to be outdated. Consider removing this code. + try { + const _arrayArgNum = Cast(arrayArg) otherwise NotNumber; + // For number as a first argument, throw TypeError instead of silently + // ignoring the call, so that users know they did something wrong. + // (Consistent with Firefox and Blink/WebKit) + ThrowTypeError(MessageTemplate::kInvalidArgument); + } label NotNumber { + // Proceed to step 14. + } - // 9. Let targetLength be target.[[ArrayLength]]. - const targetLength = target.length; + // 14. Let src be ? ToObject(array). + const src: JSReceiver = ToObject_Inline(context, arrayArg); + + // 15. Let srcLength be ? LengthOfArrayLike(src). + const srcLengthNum: Number = GetLengthProperty(src); + + if (targetOffsetOverflowed) goto IfOffsetOutOfBounds; + + // 9. Let targetLength be target.[[ArrayLength]]. + const targetLength = target.length; + + // 16. If srcLength + targetOffset > targetLength, throw a RangeError + // exception. + const srcLength = ChangeSafeIntegerNumberToUintPtr(srcLengthNum) + otherwise IfOffsetOutOfBounds; + CheckIntegerIndexAdditionOverflow(srcLength, targetOffset, targetLength) + otherwise IfOffsetOutOfBounds; + + // All the obvervable side effects are executed, so there's nothing else + // to do with the empty source array. + if (srcLength == 0) return; + + // 10. Let targetName be the String value of target.[[TypedArrayName]]. + // 11. Let targetElementSize be the Element Size value specified in + // Table 62 for targetName. + // 12. Let targetType be the Element Type value in Table 62 for + // targetName. + + try { + // BigInt typed arrays are not handled by + // CopyFastNumberJSArrayElementsToTypedArray. + if (IsBigInt64ElementsKind(target.elements_kind)) goto IfSlow; + + const fastSrc: FastJSArray = Cast(src) otherwise goto IfSlow; + const srcKind: ElementsKind = fastSrc.map.elements_kind; + + // CopyFastNumberJSArrayElementsToTypedArray() can be used only with the + // following elements kinds: + // PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS, + // HOLEY_DOUBLE_ELEMENTS. + if (IsElementsKindInRange( + srcKind, ElementsKind::PACKED_SMI_ELEMENTS, + ElementsKind::HOLEY_SMI_ELEMENTS) || + IsElementsKindInRange( + srcKind, ElementsKind::PACKED_DOUBLE_ELEMENTS, + ElementsKind::HOLEY_DOUBLE_ELEMENTS)) { + const utarget = typed_array::EnsureAttached(target) otherwise IfDetached; + CallCCopyFastNumberJSArrayElementsToTypedArray( + context, fastSrc, utarget, srcLength, targetOffset); + + } else { + goto IfSlow; + } + } label IfSlow deferred { + TypedArraySet( + context, target, src, srcLengthNum, Convert(targetOffset)); + } +} - // 16. If srcLength + targetOffset > targetLength, throw a RangeError - // exception. - const srcLength = ChangeSafeIntegerNumberToUintPtr(srcLengthNum) - otherwise IfOffsetOutOfBounds; - CheckIntegerIndexAdditionOverflow(srcLength, targetOffset, targetLength) - otherwise IfOffsetOutOfBounds; +// %TypedArray%.prototype.set ( typedArray [ , offset ] ) +// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-typedarray-offset +transitioning macro +TypedArrayPrototypeSetTypedArray(implicit context: Context, receiver: JSAny)( + target: AttachedJSTypedArray, typedArray: AttachedJSTypedArray, + targetOffset: uintptr, + targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds { + // Steps 12-20 are not observable, so we can handle offset overflow + // at step 21 here. + if (targetOffsetOverflowed) goto IfOffsetOutOfBounds; + + // 9. Let targetLength be target.[[ArrayLength]]. + const targetLength = target.length; + + // 19. Let srcLength be typedArray.[[ArrayLength]]. + const srcLength: uintptr = typedArray.length; + + // Steps 12-20 are not observable, so we can do step 21 here. + + // 21. If srcLength + targetOffset > targetLength, throw a RangeError + // exception. + CheckIntegerIndexAdditionOverflow(srcLength, targetOffset, targetLength) + otherwise IfOffsetOutOfBounds; + + // 12. Let targetName be the String value of target.[[TypedArrayName]]. + // 13. Let targetType be the Element Type value in Table 62 for + // targetName. + // 14. Let targetElementSize be the Element Size value specified in + // Table 62 for targetName. + const targetElementsInfo = GetTypedArrayElementsInfo(target); + + // 16. Let srcName be the String value of typedArray.[[TypedArrayName]]. + // 17. Let srcType be the Element Type value in Table 62 for srcName. + // 18. Let srcElementSize be the Element Size value specified in + // Table 62 for srcName. + const srcKind: ElementsKind = typedArray.elements_kind; + // const srcElementsInfo = GetTypedArrayElementsInfo(typedArray); + + // We skip steps 23-25 because both memmove and + // CopyTypedArrayElementsToTypedArray() properly handle overlapping + // regions. + + // 23. If both IsSharedArrayBuffer(srcBuffer) and + // IsSharedArrayBuffer(targetBuffer) are true, then + // 23a. If srcBuffer.[[ArrayBufferData]] and + // targetBuffer.[[ArrayBufferData]] are the same Shared Data Block + // values, let same be true; else let same be false. + // 24. Else, let same be SameValue(srcBuffer, targetBuffer). + // 25. If same is true, then + // a. Let srcByteLength be typedArray.[[ByteLength]]. + // b. Set srcBuffer to ? CloneArrayBuffer(srcBuffer, srcByteOffset, + // srcByteLength, %ArrayBuffer%). + // c. NOTE: %ArrayBuffer% is used to clone srcBuffer because is it known + // to not have any observable side-effects. + // d. Let srcByteIndex be 0. + + try { + // Use memmove if possible. + if (srcKind != targetElementsInfo.kind) { + // Uint8/Uint8Clamped elements could still be copied with memmove. + if (!IsUint8ElementsKind(srcKind) || + !IsUint8ElementsKind(targetElementsInfo.kind)) { + goto IfSlow; + } + } // All the obvervable side effects are executed, so there's nothing else // to do with the empty source array. if (srcLength == 0) return; - // 10. Let targetName be the String value of target.[[TypedArrayName]]. - // 11. Let targetElementSize be the Element Size value specified in - // Table 62 for targetName. - // 12. Let targetType be the Element Type value in Table 62 for - // targetName. - - try { - // BigInt typed arrays are not handled by - // CopyFastNumberJSArrayElementsToTypedArray. - if (IsBigInt64ElementsKind(target.elements_kind)) goto IfSlow; - - const fastSrc: FastJSArray = Cast(src) otherwise goto IfSlow; - const srcKind: ElementsKind = fastSrc.map.elements_kind; - - // CopyFastNumberJSArrayElementsToTypedArray() can be used only with the - // following elements kinds: - // PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS, - // HOLEY_DOUBLE_ELEMENTS. - if (IsElementsKindInRange( - srcKind, ElementsKind::PACKED_SMI_ELEMENTS, - ElementsKind::HOLEY_SMI_ELEMENTS) || - IsElementsKindInRange( - srcKind, ElementsKind::PACKED_DOUBLE_ELEMENTS, - ElementsKind::HOLEY_DOUBLE_ELEMENTS)) { - const utarget = - typed_array::EnsureAttached(target) otherwise IfDetached; - CallCCopyFastNumberJSArrayElementsToTypedArray( - context, fastSrc, utarget, srcLength, targetOffset); - - } else { - goto IfSlow; + // Source and destination typed arrays have same elements kinds (modulo + // Uint8-Uint8Clamped difference) so we can use targetElementsInfo for + // calculations. + const countBytes: uintptr = + targetElementsInfo.CalculateByteLength(srcLength) + otherwise unreachable; + const startOffset: uintptr = + targetElementsInfo.CalculateByteLength(targetOffset) + otherwise unreachable; + const dstPtr: RawPtr = target.data_ptr + Convert(startOffset); + + assert(countBytes <= target.byte_length - startOffset); + assert(countBytes <= typedArray.byte_length); + + // 29. If srcType is the same as targetType, then + // a. NOTE: If srcType and targetType are the same, the transfer must + // be performed in a manner that preserves the bit-level encoding of + // the source data. + // b. Repeat, while targetByteIndex < limit + // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, Uint8, + // true, Unordered). + // ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, Uint8, + // value, true, Unordered). + // iii. Set srcByteIndex to srcByteIndex + 1. + // iv. Set targetByteIndex to targetByteIndex + 1. + CallCMemmove(dstPtr, typedArray.data_ptr, countBytes); + } label IfSlow deferred { + // 22. If target.[[ContentType]] is not equal to + // typedArray.[[ContentType]], throw a TypeError exception. + if (IsBigInt64ElementsKind(srcKind) != + IsBigInt64ElementsKind(targetElementsInfo.kind)) + deferred { + ThrowTypeError(MessageTemplate::kBigIntMixedTypes); } - } - label IfSlow deferred { - TypedArraySet( - context, target, src, srcLengthNum, Convert(targetOffset)); - } - } - - // %TypedArray%.prototype.set ( typedArray [ , offset ] ) - // https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-typedarray-offset - transitioning macro - TypedArrayPrototypeSetTypedArray(implicit context: Context, receiver: JSAny)( - target: AttachedJSTypedArray, typedArray: AttachedJSTypedArray, - targetOffset: uintptr, - targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds { - // Steps 12-20 are not observable, so we can handle offset overflow - // at step 21 here. - if (targetOffsetOverflowed) goto IfOffsetOutOfBounds; - - // 9. Let targetLength be target.[[ArrayLength]]. - const targetLength = target.length; - // 19. Let srcLength be typedArray.[[ArrayLength]]. - const srcLength: uintptr = typedArray.length; - - // Steps 12-20 are not observable, so we can do step 21 here. - - // 21. If srcLength + targetOffset > targetLength, throw a RangeError - // exception. - CheckIntegerIndexAdditionOverflow(srcLength, targetOffset, targetLength) - otherwise IfOffsetOutOfBounds; - - // 12. Let targetName be the String value of target.[[TypedArrayName]]. - // 13. Let targetType be the Element Type value in Table 62 for - // targetName. - // 14. Let targetElementSize be the Element Size value specified in - // Table 62 for targetName. - const targetElementsInfo = GetTypedArrayElementsInfo(target); - - // 16. Let srcName be the String value of typedArray.[[TypedArrayName]]. - // 17. Let srcType be the Element Type value in Table 62 for srcName. - // 18. Let srcElementSize be the Element Size value specified in - // Table 62 for srcName. - const srcKind: ElementsKind = typedArray.elements_kind; - // const srcElementsInfo = GetTypedArrayElementsInfo(typedArray); - - // We skip steps 23-25 because both memmove and - // CopyTypedArrayElementsToTypedArray() properly handle overlapping - // regions. - - // 23. If both IsSharedArrayBuffer(srcBuffer) and - // IsSharedArrayBuffer(targetBuffer) are true, then - // 23a. If srcBuffer.[[ArrayBufferData]] and - // targetBuffer.[[ArrayBufferData]] are the same Shared Data Block - // values, let same be true; else let same be false. - // 24. Else, let same be SameValue(srcBuffer, targetBuffer). - // 25. If same is true, then - // a. Let srcByteLength be typedArray.[[ByteLength]]. - // b. Set srcBuffer to ? CloneArrayBuffer(srcBuffer, srcByteOffset, - // srcByteLength, %ArrayBuffer%). - // c. NOTE: %ArrayBuffer% is used to clone srcBuffer because is it known - // to not have any observable side-effects. - // d. Let srcByteIndex be 0. - - try { - // Use memmove if possible. - if (srcKind != targetElementsInfo.kind) { - // Uint8/Uint8Clamped elements could still be copied with memmove. - if (!IsUint8ElementsKind(srcKind) || - !IsUint8ElementsKind(targetElementsInfo.kind)) { - goto IfSlow; - } - } + // All the obvervable side effects are executed, so there's nothing else + // to do with the empty source array. + if (srcLength == 0) return; - // All the obvervable side effects are executed, so there's nothing else - // to do with the empty source array. - if (srcLength == 0) return; - - // Source and destination typed arrays have same elements kinds (modulo - // Uint8-Uint8Clamped difference) so we can use targetElementsInfo for - // calculations. - const countBytes: uintptr = - targetElementsInfo.CalculateByteLength(srcLength) - otherwise unreachable; - const startOffset: uintptr = - targetElementsInfo.CalculateByteLength(targetOffset) - otherwise unreachable; - const dstPtr: RawPtr = target.data_ptr + Convert(startOffset); - - assert(countBytes <= target.byte_length - startOffset); - assert(countBytes <= typedArray.byte_length); - - // 29. If srcType is the same as targetType, then - // a. NOTE: If srcType and targetType are the same, the transfer must - // be performed in a manner that preserves the bit-level encoding of - // the source data. - // b. Repeat, while targetByteIndex < limit - // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, Uint8, - // true, Unordered). - // ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, Uint8, - // value, true, Unordered). - // iii. Set srcByteIndex to srcByteIndex + 1. - // iv. Set targetByteIndex to targetByteIndex + 1. - CallCMemmove(dstPtr, typedArray.data_ptr, countBytes); - } - label IfSlow deferred { - // 22. If target.[[ContentType]] is not equal to - // typedArray.[[ContentType]], throw a TypeError exception. - if (IsBigInt64ElementsKind(srcKind) != - IsBigInt64ElementsKind(targetElementsInfo.kind)) - deferred { - ThrowTypeError(MessageTemplate::kBigIntMixedTypes); - } - - // All the obvervable side effects are executed, so there's nothing else - // to do with the empty source array. - if (srcLength == 0) return; - - // 30. Else, - // a. Repeat, while targetByteIndex < limit - // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, - // srcType, true, Unordered). - // ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, - // targetType, value, true, Unordered). - // iii. Set srcByteIndex to srcByteIndex + srcElementSize. - // iv. Set targetByteIndex to targetByteIndex + targetElementSize. - CallCCopyTypedArrayElementsToTypedArray( - typedArray, target, srcLength, targetOffset); - } + // 30. Else, + // a. Repeat, while targetByteIndex < limit + // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, + // srcType, true, Unordered). + // ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, + // targetType, value, true, Unordered). + // iii. Set srcByteIndex to srcByteIndex + srcElementSize. + // iv. Set targetByteIndex to targetByteIndex + targetElementSize. + CallCCopyTypedArrayElementsToTypedArray( + typedArray, target, srcLength, targetOffset); } } +} diff --git a/deps/v8/src/builtins/typed-array-slice.tq b/deps/v8/src/builtins/typed-array-slice.tq index 578c2d017ada71..60604c548fc272 100644 --- a/deps/v8/src/builtins/typed-array-slice.tq +++ b/deps/v8/src/builtins/typed-array-slice.tq @@ -5,102 +5,99 @@ #include 'src/builtins/builtins-typed-array-gen.h' namespace typed_array { - const kBuiltinNameSlice: constexpr string = '%TypedArray%.prototype.slice'; - - extern macro TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsSlice( - JSTypedArray, JSTypedArray, uintptr, uintptr): void; - - macro FastCopy( - src: typed_array::AttachedJSTypedArray, dest: JSTypedArray, k: uintptr, - count: uintptr) labels IfSlow { - if (IsForceSlowPath()) goto IfSlow; - - const srcKind: ElementsKind = src.elements_kind; - const destInfo = typed_array::GetTypedArrayElementsInfo(dest); - - // dest could be a different type from src or share the same buffer - // with the src because of custom species constructor. If the types - // of src and result array are the same and they are not sharing the - // same buffer, use memmove. - if (srcKind != destInfo.kind) goto IfSlow; - if (dest.buffer == src.buffer) { - goto IfSlow; - } +const kBuiltinNameSlice: constexpr string = '%TypedArray%.prototype.slice'; + +extern macro TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsSlice( + JSTypedArray, JSTypedArray, uintptr, uintptr): void; + +macro FastCopy( + src: typed_array::AttachedJSTypedArray, dest: JSTypedArray, k: uintptr, + count: uintptr) labels IfSlow { + if (IsForceSlowPath()) goto IfSlow; + + const srcKind: ElementsKind = src.elements_kind; + const destInfo = typed_array::GetTypedArrayElementsInfo(dest); + + // dest could be a different type from src or share the same buffer + // with the src because of custom species constructor. If the types + // of src and result array are the same and they are not sharing the + // same buffer, use memmove. + if (srcKind != destInfo.kind) goto IfSlow; + if (dest.buffer == src.buffer) { + goto IfSlow; + } - const countBytes: uintptr = destInfo.CalculateByteLength(count) - otherwise unreachable; - const startOffset: uintptr = destInfo.CalculateByteLength(k) - otherwise unreachable; - const srcPtr: RawPtr = src.data_ptr + Convert(startOffset); + const countBytes: uintptr = destInfo.CalculateByteLength(count) + otherwise unreachable; + const startOffset: uintptr = destInfo.CalculateByteLength(k) + otherwise unreachable; + const srcPtr: RawPtr = src.data_ptr + Convert(startOffset); - assert(countBytes <= dest.byte_length); - assert(countBytes <= src.byte_length - startOffset); + assert(countBytes <= dest.byte_length); + assert(countBytes <= src.byte_length - startOffset); - typed_array::CallCMemmove(dest.data_ptr, srcPtr, countBytes); - } + typed_array::CallCMemmove(dest.data_ptr, srcPtr, countBytes); +} - macro SlowCopy(implicit context: Context)( - src: JSTypedArray, dest: JSTypedArray, k: uintptr, final: uintptr) { - if (typed_array::IsBigInt64ElementsKind(src.elements_kind) != - typed_array::IsBigInt64ElementsKind(dest.elements_kind)) - deferred { - ThrowTypeError(MessageTemplate::kBigIntMixedTypes); - } +macro SlowCopy(implicit context: Context)( + src: JSTypedArray, dest: JSTypedArray, k: uintptr, final: uintptr) { + if (typed_array::IsBigInt64ElementsKind(src.elements_kind) != + typed_array::IsBigInt64ElementsKind(dest.elements_kind)) + deferred { + ThrowTypeError(MessageTemplate::kBigIntMixedTypes); + } - CallCCopyTypedArrayElementsSlice(src, dest, k, final); - } + CallCCopyTypedArrayElementsSlice(src, dest, k, final); +} - // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.slice - transitioning javascript builtin TypedArrayPrototypeSlice( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSAny { - // arguments[0] = start - // arguments[1] = end - - // 1. Let O be the this value. - // 2. Perform ? ValidateTypedArray(O). - const src: JSTypedArray = - ValidateTypedArray(context, receiver, kBuiltinNameSlice); - - // 3. Let len be O.[[ArrayLength]]. - const len: uintptr = src.length; - - // 4. Let relativeStart be ? ToInteger(start). - // 5. If relativeStart < 0, let k be max((len + relativeStart), 0); - // else let k be min(relativeStart, len). - const start = arguments[0]; - const k: uintptr = - start != Undefined ? ConvertToRelativeIndex(start, len) : 0; - - // 6. If end is undefined, let relativeEnd be len; - // else let relativeEnd be ? ToInteger(end). - // 7. If relativeEnd < 0, let final be max((len + relativeEnd), 0); - // else let final be min(relativeEnd, len). - const end = arguments[1]; - const final: uintptr = - end != Undefined ? ConvertToRelativeIndex(end, len) : len; - - // 8. Let count be max(final - k, 0). - const count: uintptr = Unsigned(IntPtrMax(Signed(final - k), 0)); - - // 9. Let A be ? TypedArraySpeciesCreate(O, « count »). - const dest: JSTypedArray = - TypedArraySpeciesCreateByLength(kBuiltinNameSlice, src, count); - - if (count > 0) { - try { - const srcAttached = typed_array::EnsureAttached(src) - otherwise IfDetached; - FastCopy(srcAttached, dest, k, count) otherwise IfSlow; - } - label IfDetached deferred { - ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSlice); - } - label IfSlow deferred { - SlowCopy(src, dest, k, final); - } +// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.slice +transitioning javascript builtin TypedArrayPrototypeSlice( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + // arguments[0] = start + // arguments[1] = end + + // 1. Let O be the this value. + // 2. Perform ? ValidateTypedArray(O). + const src: JSTypedArray = + ValidateTypedArray(context, receiver, kBuiltinNameSlice); + + // 3. Let len be O.[[ArrayLength]]. + const len: uintptr = src.length; + + // 4. Let relativeStart be ? ToInteger(start). + // 5. If relativeStart < 0, let k be max((len + relativeStart), 0); + // else let k be min(relativeStart, len). + const start = arguments[0]; + const k: uintptr = + start != Undefined ? ConvertToRelativeIndex(start, len) : 0; + + // 6. If end is undefined, let relativeEnd be len; + // else let relativeEnd be ? ToInteger(end). + // 7. If relativeEnd < 0, let final be max((len + relativeEnd), 0); + // else let final be min(relativeEnd, len). + const end = arguments[1]; + const final: uintptr = + end != Undefined ? ConvertToRelativeIndex(end, len) : len; + + // 8. Let count be max(final - k, 0). + const count: uintptr = Unsigned(IntPtrMax(Signed(final - k), 0)); + + // 9. Let A be ? TypedArraySpeciesCreate(O, « count »). + const dest: JSTypedArray = + TypedArraySpeciesCreateByLength(kBuiltinNameSlice, src, count); + + if (count > 0) { + try { + const srcAttached = typed_array::EnsureAttached(src) + otherwise IfDetached; + FastCopy(srcAttached, dest, k, count) otherwise IfSlow; + } label IfDetached deferred { + ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSlice); + } label IfSlow deferred { + SlowCopy(src, dest, k, final); } - - return dest; } + + return dest; +} } diff --git a/deps/v8/src/builtins/typed-array-some.tq b/deps/v8/src/builtins/typed-array-some.tq index a09ce964d58a0d..ecdfae1e8a58da 100644 --- a/deps/v8/src/builtins/typed-array-some.tq +++ b/deps/v8/src/builtins/typed-array-some.tq @@ -5,52 +5,49 @@ #include 'src/builtins/builtins-typed-array-gen.h' namespace typed_array { - const kBuiltinNameSome: constexpr string = '%TypedArray%.prototype.some'; +const kBuiltinNameSome: constexpr string = '%TypedArray%.prototype.some'; - transitioning macro SomeAllElements(implicit context: Context)( - array: typed_array::AttachedJSTypedArray, callbackfn: Callable, - thisArg: JSAny): Boolean { - let witness = typed_array::NewAttachedJSTypedArrayWitness(array); - const length: uintptr = witness.Get().length; - for (let k: uintptr = 0; k < length; k++) { - // BUG(4895): We should throw on detached buffers rather than simply exit. - witness.Recheck() otherwise break; - const value: JSAny = witness.Load(k); - // TODO(v8:4153): Consider versioning this loop for Smi and non-Smi - // indices to optimize Convert(k) for the most common case. - const result = Call( - context, callbackfn, thisArg, value, Convert(k), - witness.GetStable()); - if (ToBoolean(result)) { - return True; - } +transitioning macro SomeAllElements(implicit context: Context)( + array: typed_array::AttachedJSTypedArray, callbackfn: Callable, + thisArg: JSAny): Boolean { + let witness = typed_array::NewAttachedJSTypedArrayWitness(array); + const length: uintptr = witness.Get().length; + for (let k: uintptr = 0; k < length; k++) { + // BUG(4895): We should throw on detached buffers rather than simply exit. + witness.Recheck() otherwise break; + const value: JSAny = witness.Load(k); + // TODO(v8:4153): Consider versioning this loop for Smi and non-Smi + // indices to optimize Convert(k) for the most common case. + const result = Call( + context, callbackfn, thisArg, value, Convert(k), + witness.GetStable()); + if (ToBoolean(result)) { + return True; } - return False; } + return False; +} - // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.some - transitioning javascript builtin - TypedArrayPrototypeSome(js-implicit context: NativeContext, receiver: JSAny)( - ...arguments): JSAny { - // arguments[0] = callback - // arguments[1] = thisArg. - try { - const array: JSTypedArray = Cast(receiver) - otherwise NotTypedArray; - const uarray = typed_array::EnsureAttached(array) otherwise IsDetached; +// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.some +transitioning javascript builtin +TypedArrayPrototypeSome( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { + // arguments[0] = callback + // arguments[1] = thisArg. + try { + const array: JSTypedArray = Cast(receiver) + otherwise NotTypedArray; + const uarray = typed_array::EnsureAttached(array) otherwise IsDetached; - const callbackfn = Cast(arguments[0]) otherwise NotCallable; - const thisArg = arguments[1]; - return SomeAllElements(uarray, callbackfn, thisArg); - } - label NotCallable deferred { - ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); - } - label NotTypedArray deferred { - ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameSome); - } - label IsDetached deferred { - ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSome); - } + const callbackfn = Cast(arguments[0]) otherwise NotCallable; + const thisArg = arguments[1]; + return SomeAllElements(uarray, callbackfn, thisArg); + } label NotCallable deferred { + ThrowTypeError(MessageTemplate::kCalledNonCallable, arguments[0]); + } label NotTypedArray deferred { + ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameSome); + } label IsDetached deferred { + ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSome); } } +} diff --git a/deps/v8/src/builtins/typed-array-sort.tq b/deps/v8/src/builtins/typed-array-sort.tq index 171068761d6747..c32808038d0562 100644 --- a/deps/v8/src/builtins/typed-array-sort.tq +++ b/deps/v8/src/builtins/typed-array-sort.tq @@ -5,141 +5,140 @@ #include 'src/builtins/builtins-typed-array-gen.h' namespace typed_array { - const kBuiltinNameSort: constexpr string = '%TypedArray%.prototype.sort'; +const kBuiltinNameSort: constexpr string = '%TypedArray%.prototype.sort'; - extern runtime TypedArraySortFast(Context, JSAny): JSTypedArray; +extern runtime TypedArraySortFast(Context, JSAny): JSTypedArray; - transitioning macro CallCompare( - implicit context: Context, array: JSTypedArray, - comparefn: Callable)(a: JSAny, b: JSAny): Number { - // a. Let v be ? ToNumber(? Call(comparefn, undefined, x, y)). - const v: Number = - ToNumber_Inline(Call(context, comparefn, Undefined, a, b)); +transitioning macro CallCompare( + implicit context: Context, array: JSTypedArray, comparefn: Callable)( + a: JSAny, b: JSAny): Number { + // a. Let v be ? ToNumber(? Call(comparefn, undefined, x, y)). + const v: Number = ToNumber_Inline(Call(context, comparefn, Undefined, a, b)); - // b. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. - if (IsDetachedBuffer(array.buffer)) { - ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSort); - } + // b. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (IsDetachedBuffer(array.buffer)) { + ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSort); + } - // c. If v is NaN, return +0. - if (NumberIsNaN(v)) return 0; + // c. If v is NaN, return +0. + if (NumberIsNaN(v)) return 0; - // d. return v. - return v; - } + // d. return v. + return v; +} - // Merges two sorted runs [from, middle) and [middle, to) - // from "source" into "target". - transitioning macro - TypedArrayMerge( - implicit context: Context, array: JSTypedArray, comparefn: Callable)( - source: FixedArray, from: uintptr, middle: uintptr, to: uintptr, - target: FixedArray) { - let left: uintptr = from; - let right: uintptr = middle; - - for (let targetIndex: uintptr = from; targetIndex < to; ++targetIndex) { - if (left < middle && right >= to) { - // If the left run has elements, but the right does not, we take - // from the left. - target.objects[targetIndex] = source.objects[left++]; - } else if (left < middle) { - // If both have elements, we need to compare. - const leftElement = UnsafeCast(source.objects[left]); - const rightElement = UnsafeCast(source.objects[right]); - if (CallCompare(leftElement, rightElement) <= 0) { - target.objects[targetIndex] = leftElement; - left++; - } else { - target.objects[targetIndex] = rightElement; - right++; - } +// Merges two sorted runs [from, middle) and [middle, to) +// from "source" into "target". +transitioning macro +TypedArrayMerge( + implicit context: Context, array: JSTypedArray, comparefn: Callable)( + source: FixedArray, from: uintptr, middle: uintptr, to: uintptr, + target: FixedArray) { + let left: uintptr = from; + let right: uintptr = middle; + + for (let targetIndex: uintptr = from; targetIndex < to; ++targetIndex) { + if (left < middle && right >= to) { + // If the left run has elements, but the right does not, we take + // from the left. + target.objects[targetIndex] = source.objects[left++]; + } else if (left < middle) { + // If both have elements, we need to compare. + const leftElement = UnsafeCast(source.objects[left]); + const rightElement = UnsafeCast(source.objects[right]); + if (CallCompare(leftElement, rightElement) <= 0) { + target.objects[targetIndex] = leftElement; + left++; } else { - // No elements on the left, but the right does, so we take - // from the right. - assert(left == middle); - target.objects[targetIndex] = source.objects[right++]; + target.objects[targetIndex] = rightElement; + right++; } + } else { + // No elements on the left, but the right does, so we take + // from the right. + assert(left == middle); + target.objects[targetIndex] = source.objects[right++]; } } +} - transitioning builtin - TypedArrayMergeSort(implicit context: Context)( - source: FixedArray, from: uintptr, to: uintptr, target: FixedArray, - array: JSTypedArray, comparefn: Callable): JSAny { - assert(to - from > 1); - const middle: uintptr = from + ((to - from) >>> 1); - - // On the next recursion step source becomes target and vice versa. - // This saves the copy of the relevant range from the original - // array into a work array on each recursion step. - if (middle - from > 1) { - TypedArrayMergeSort(target, from, middle, source, array, comparefn); - } - if (to - middle > 1) { - TypedArrayMergeSort(target, middle, to, source, array, comparefn); - } - - TypedArrayMerge(source, from, middle, to, target); - - return Undefined; +transitioning builtin +TypedArrayMergeSort(implicit context: Context)( + source: FixedArray, from: uintptr, to: uintptr, target: FixedArray, + array: JSTypedArray, comparefn: Callable): JSAny { + assert(to - from > 1); + const middle: uintptr = from + ((to - from) >>> 1); + + // On the next recursion step source becomes target and vice versa. + // This saves the copy of the relevant range from the original + // array into a work array on each recursion step. + if (middle - from > 1) { + TypedArrayMergeSort(target, from, middle, source, array, comparefn); + } + if (to - middle > 1) { + TypedArrayMergeSort(target, middle, to, source, array, comparefn); } - // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.sort - transitioning javascript builtin TypedArrayPrototypeSort( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSTypedArray { - // 1. If comparefn is not undefined and IsCallable(comparefn) is false, - // throw a TypeError exception. - const comparefnObj: JSAny = arguments.length > 0 ? arguments[0] : Undefined; - if (comparefnObj != Undefined && !TaggedIsCallable(comparefnObj)) { - ThrowTypeError(MessageTemplate::kBadSortComparisonFunction, comparefnObj); - } + TypedArrayMerge(source, from, middle, to, target); - // 2. Let obj be the this value. - const obj: JSAny = receiver; + return Undefined; +} - // 3. Let buffer be ? ValidateTypedArray(obj). - // ValidateTypedArray currently returns the array, not the ViewBuffer. - const array: JSTypedArray = - ValidateTypedArray(context, obj, kBuiltinNameSort); +// https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.sort +transitioning javascript builtin TypedArrayPrototypeSort( + js-implicit context: NativeContext, + receiver: JSAny)(...arguments): JSTypedArray { + // 1. If comparefn is not undefined and IsCallable(comparefn) is false, + // throw a TypeError exception. + const comparefnObj: JSAny = arguments[0]; + if (comparefnObj != Undefined && !Is(comparefnObj)) { + ThrowTypeError(MessageTemplate::kBadSortComparisonFunction, comparefnObj); + } - // 4. Let len be obj.[[ArrayLength]]. - const len: uintptr = array.length; + // 2. Let obj be the this value. + const obj: JSAny = receiver; - // Arrays of length 1 or less are considered sorted. - if (len < 2) return array; + // 3. Let buffer be ? ValidateTypedArray(obj). + // ValidateTypedArray currently returns the array, not the ViewBuffer. + const array: JSTypedArray = + ValidateTypedArray(context, obj, kBuiltinNameSort); - // Default sorting is done in C++ using std::sort - if (comparefnObj == Undefined) { - return TypedArraySortFast(context, obj); - } + // 4. Let len be obj.[[ArrayLength]]. + const len: uintptr = array.length; - const comparefn: Callable = - Cast(comparefnObj) otherwise unreachable; - const accessor: TypedArrayAccessor = - GetTypedArrayAccessor(array.elements_kind); - - // Prepare the two work arrays. All numbers are converted to tagged - // objects first, and merge sorted between the two FixedArrays. - // The result is then written back into the JSTypedArray. - const work1: FixedArray = AllocateZeroedFixedArray(Convert(len)); - const work2: FixedArray = AllocateZeroedFixedArray(Convert(len)); - - for (let i: uintptr = 0; i < len; ++i) { - const element: Numeric = accessor.LoadNumeric(context, array, i); - work1.objects[i] = element; - work2.objects[i] = element; - } + // Arrays of length 1 or less are considered sorted. + if (len < 2) return array; + + // Default sorting is done in C++ using std::sort + if (comparefnObj == Undefined) { + return TypedArraySortFast(context, obj); + } - TypedArrayMergeSort(work2, 0, len, work1, array, comparefn); + const comparefn: Callable = + Cast(comparefnObj) otherwise unreachable; + const accessor: TypedArrayAccessor = + GetTypedArrayAccessor(array.elements_kind); + + // Prepare the two work arrays. All numbers are converted to tagged + // objects first, and merge sorted between the two FixedArrays. + // The result is then written back into the JSTypedArray. + const work1: FixedArray = AllocateZeroedFixedArray(Convert(len)); + const work2: FixedArray = AllocateZeroedFixedArray(Convert(len)); + + for (let i: uintptr = 0; i < len; ++i) { + const element: Numeric = accessor.LoadNumeric(array, i); + work1.objects[i] = element; + work2.objects[i] = element; + } - // work1 contains the sorted numbers. Write them back. - for (let i: uintptr = 0; i < len; ++i) { - accessor.StoreNumeric( - context, array, i, UnsafeCast(work1.objects[i])); - } + TypedArrayMergeSort(work2, 0, len, work1, array, comparefn); - return array; + // work1 contains the sorted numbers. Write them back. + for (let i: uintptr = 0; i < len; ++i) { + accessor.StoreNumeric( + context, array, i, UnsafeCast(work1.objects[i])); } + + return array; +} } diff --git a/deps/v8/src/builtins/typed-array-subarray.tq b/deps/v8/src/builtins/typed-array-subarray.tq index c0adc5f19e3959..73d9e80c619500 100644 --- a/deps/v8/src/builtins/typed-array-subarray.tq +++ b/deps/v8/src/builtins/typed-array-subarray.tq @@ -3,60 +3,60 @@ // found in the LICENSE file. namespace typed_array { - // ES %TypedArray%.prototype.subarray - transitioning javascript builtin TypedArrayPrototypeSubArray( - js-implicit context: NativeContext, - receiver: JSAny)(...arguments): JSTypedArray { - const methodName: constexpr string = '%TypedArray%.prototype.subarray'; - - // 1. Let O be the this value. - // 3. If O does not have a [[TypedArrayName]] internal slot, throw a - // TypeError exception. - const source = Cast(receiver) - otherwise ThrowTypeError( - MessageTemplate::kIncompatibleMethodReceiver, methodName); - - // 5. Let buffer be O.[[ViewedArrayBuffer]]. - const buffer = typed_array::GetBuffer(source); - - // 6. Let srcLength be O.[[ArrayLength]]. - const srcLength: uintptr = source.length; - - // 7. Let relativeBegin be ? ToInteger(begin). - // 8. If relativeBegin < 0, let beginIndex be max((srcLength + - // relativeBegin), 0); else let beginIndex be min(relativeBegin, - // srcLength). - const arg0 = arguments[0]; - const begin: uintptr = - arg0 != Undefined ? ConvertToRelativeIndex(arg0, srcLength) : 0; - - // 9. If end is undefined, let relativeEnd be srcLength; - // else, let relativeEnd be ? ToInteger(end). - // 10. If relativeEnd < 0, let endIndex be max((srcLength + relativeEnd), - // 0); else let endIndex be min(relativeEnd, srcLength). - const arg1 = arguments[1]; - const end: uintptr = - arg1 != Undefined ? ConvertToRelativeIndex(arg1, srcLength) : srcLength; - - // 11. Let newLength be max(endIndex - beginIndex, 0). - const newLength: uintptr = Unsigned(IntPtrMax(Signed(end - begin), 0)); - - // 12. Let constructorName be the String value of O.[[TypedArrayName]]. - // 13. Let elementSize be the Number value of the Element Size value - // specified in Table 52 for constructorName. - const elementsInfo = typed_array::GetTypedArrayElementsInfo(source); - - // 14. Let srcByteOffset be O.[[ByteOffset]]. - const srcByteOffset: uintptr = source.byte_offset; - - // 15. Let beginByteOffset be srcByteOffset + beginIndex × elementSize. - const beginByteOffset = - srcByteOffset + elementsInfo.CalculateByteLength(begin) - otherwise ThrowRangeError(MessageTemplate::kInvalidArrayBufferLength); - - // 16. Let argumentsList be « buffer, beginByteOffset, newLength ». - // 17. Return ? TypedArraySpeciesCreate(O, argumentsList). - return TypedArraySpeciesCreateByBuffer( - methodName, source, buffer, beginByteOffset, newLength); - } +// ES %TypedArray%.prototype.subarray +transitioning javascript builtin TypedArrayPrototypeSubArray( + js-implicit context: NativeContext, + receiver: JSAny)(...arguments): JSTypedArray { + const methodName: constexpr string = '%TypedArray%.prototype.subarray'; + + // 1. Let O be the this value. + // 3. If O does not have a [[TypedArrayName]] internal slot, throw a + // TypeError exception. + const source = Cast(receiver) + otherwise ThrowTypeError( + MessageTemplate::kIncompatibleMethodReceiver, methodName); + + // 5. Let buffer be O.[[ViewedArrayBuffer]]. + const buffer = typed_array::GetBuffer(source); + + // 6. Let srcLength be O.[[ArrayLength]]. + const srcLength: uintptr = source.length; + + // 7. Let relativeBegin be ? ToInteger(begin). + // 8. If relativeBegin < 0, let beginIndex be max((srcLength + + // relativeBegin), 0); else let beginIndex be min(relativeBegin, + // srcLength). + const arg0 = arguments[0]; + const begin: uintptr = + arg0 != Undefined ? ConvertToRelativeIndex(arg0, srcLength) : 0; + + // 9. If end is undefined, let relativeEnd be srcLength; + // else, let relativeEnd be ? ToInteger(end). + // 10. If relativeEnd < 0, let endIndex be max((srcLength + relativeEnd), + // 0); else let endIndex be min(relativeEnd, srcLength). + const arg1 = arguments[1]; + const end: uintptr = + arg1 != Undefined ? ConvertToRelativeIndex(arg1, srcLength) : srcLength; + + // 11. Let newLength be max(endIndex - beginIndex, 0). + const newLength: uintptr = Unsigned(IntPtrMax(Signed(end - begin), 0)); + + // 12. Let constructorName be the String value of O.[[TypedArrayName]]. + // 13. Let elementSize be the Number value of the Element Size value + // specified in Table 52 for constructorName. + const elementsInfo = typed_array::GetTypedArrayElementsInfo(source); + + // 14. Let srcByteOffset be O.[[ByteOffset]]. + const srcByteOffset: uintptr = source.byte_offset; + + // 15. Let beginByteOffset be srcByteOffset + beginIndex × elementSize. + const beginByteOffset = + srcByteOffset + elementsInfo.CalculateByteLength(begin) + otherwise ThrowRangeError(MessageTemplate::kInvalidArrayBufferLength); + + // 16. Let argumentsList be « buffer, beginByteOffset, newLength ». + // 17. Return ? TypedArraySpeciesCreate(O, argumentsList). + return TypedArraySpeciesCreateByBuffer( + methodName, source, buffer, beginByteOffset, newLength); +} } diff --git a/deps/v8/src/builtins/typed-array.tq b/deps/v8/src/builtins/typed-array.tq index 1b23d3f57297d1..033de32a1d0314 100644 --- a/deps/v8/src/builtins/typed-array.tq +++ b/deps/v8/src/builtins/typed-array.tq @@ -5,271 +5,267 @@ #include 'src/builtins/builtins-typed-array-gen.h' namespace typed_array { - // Naming convention from elements.cc. We have a similar intent but implement - // fastpaths using generics instead of using a class hierarchy for elements - // kinds specific implementations. - type Uint8Elements extends ElementsKind; - type Int8Elements extends ElementsKind; - type Uint16Elements extends ElementsKind; - type Int16Elements extends ElementsKind; - type Uint32Elements extends ElementsKind; - type Int32Elements extends ElementsKind; - type Float32Elements extends ElementsKind; - type Float64Elements extends ElementsKind; - type Uint8ClampedElements extends ElementsKind; - type BigUint64Elements extends ElementsKind; - type BigInt64Elements extends ElementsKind; +// Naming convention from elements.cc. We have a similar intent but implement +// fastpaths using generics instead of using a class hierarchy for elements +// kinds specific implementations. +type Uint8Elements extends ElementsKind; +type Int8Elements extends ElementsKind; +type Uint16Elements extends ElementsKind; +type Int16Elements extends ElementsKind; +type Uint32Elements extends ElementsKind; +type Int32Elements extends ElementsKind; +type Float32Elements extends ElementsKind; +type Float64Elements extends ElementsKind; +type Uint8ClampedElements extends ElementsKind; +type BigUint64Elements extends ElementsKind; +type BigInt64Elements extends ElementsKind; - @export - struct TypedArrayElementsInfo { - // Calculates the number of bytes required for specified number of elements. - macro CalculateByteLength(length: uintptr): uintptr labels IfInvalid { - if (length > kTypedArrayMaxLength) goto IfInvalid; - const maxArrayLength = kArrayBufferMaxByteLength >>> this.sizeLog2; - if (length > maxArrayLength) goto IfInvalid; - const byteLength = length << this.sizeLog2; - return byteLength; - } - - // Calculates the maximum number of elements supported by a specified number - // of bytes. - macro CalculateLength(byteLength: uintptr): uintptr labels IfInvalid { - const length = byteLength >>> this.sizeLog2; - if (length > kTypedArrayMaxLength) goto IfInvalid; - return length; - } - - // Determines if `bytes` (byte offset or length) cannot be evenly divided by - // element size. - macro IsUnaligned(bytes: uintptr): bool { - // Exploits the fact the element size is a power of 2. Determining whether - // there is remainder (not aligned) can be achieved efficiently with bit - // masking. Shift is safe as sizeLog2 can be 3 at most (see - // ElementsKindToShiftSize). - return (bytes & ((1 << this.sizeLog2) - 1)) != 0; - } - - sizeLog2: uintptr; - kind: ElementsKind; +@export +struct TypedArrayElementsInfo { + // Calculates the number of bytes required for specified number of elements. + macro CalculateByteLength(length: uintptr): uintptr labels IfInvalid { + if (length > kTypedArrayMaxLength) goto IfInvalid; + const maxArrayLength = kArrayBufferMaxByteLength >>> this.sizeLog2; + if (length > maxArrayLength) goto IfInvalid; + const byteLength = length << this.sizeLog2; + return byteLength; } - extern runtime TypedArrayCopyElements(Context, JSTypedArray, Object, Number): - void; - extern macro TypedArrayBuiltinsAssembler::ValidateTypedArray( - Context, JSAny, constexpr string): JSTypedArray; - extern macro TypedArrayBuiltinsAssembler::CallCMemcpy( - RawPtr, RawPtr, uintptr): void; - extern macro TypedArrayBuiltinsAssembler::CallCMemmove( - RawPtr, RawPtr, uintptr): void; - extern macro TypedArrayBuiltinsAssembler::CallCMemset( - RawPtr, intptr, uintptr): void; - extern macro TypedArrayBuiltinsAssembler::GetBuffer( - implicit context: Context)(JSTypedArray): JSArrayBuffer; - extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo( - JSTypedArray): TypedArrayElementsInfo; - extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(Map): - TypedArrayElementsInfo; - extern macro TypedArrayBuiltinsAssembler::IsUint8ElementsKind(ElementsKind): - bool; - extern macro TypedArrayBuiltinsAssembler::IsBigInt64ElementsKind( - ElementsKind): bool; - extern macro LoadFixedTypedArrayElementAsTagged( - RawPtr, uintptr, constexpr ElementsKind): Numeric; - extern macro TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromNumeric( - Context, JSTypedArray, uintptr, Numeric, constexpr ElementsKind); - extern macro TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromTagged( - Context, JSTypedArray, uintptr, JSAny, - constexpr ElementsKind) labels IfDetached; + // Calculates the maximum number of elements supported by a specified number + // of bytes. + macro CalculateLength(byteLength: uintptr): uintptr labels IfInvalid { + const length = byteLength >>> this.sizeLog2; + if (length > kTypedArrayMaxLength) goto IfInvalid; + return length; + } - type LoadNumericFn = builtin(Context, JSTypedArray, uintptr) => Numeric; - type StoreNumericFn = builtin(Context, JSTypedArray, uintptr, Numeric) => Smi; - type StoreJSAnyFn = builtin(Context, JSTypedArray, uintptr, JSAny) => Smi; + // Determines if `bytes` (byte offset or length) cannot be evenly divided by + // element size. + macro IsUnaligned(bytes: uintptr): bool { + // Exploits the fact the element size is a power of 2. Determining whether + // there is remainder (not aligned) can be achieved efficiently with bit + // masking. Shift is safe as sizeLog2 can be 3 at most (see + // ElementsKindToShiftSize). + return (bytes & ((1 << this.sizeLog2) - 1)) != 0; + } - // The result codes returned by StoreNumericFn and StoreJSAnyFn builtins. - const kStoreSucceded: Smi = 0; - const kStoreFailureArrayDetached: Smi = 1; + sizeLog2: uintptr; + kind: ElementsKind; +} +extern runtime TypedArrayCopyElements( + Context, JSTypedArray, Object, Number): void; +extern macro TypedArrayBuiltinsAssembler::ValidateTypedArray( + Context, JSAny, constexpr string): JSTypedArray; - struct TypedArrayAccessor { - macro LoadNumeric( - context: Context, array: JSTypedArray, index: uintptr): Numeric { - const loadfn: LoadNumericFn = this.loadNumericFn; - return loadfn(context, array, index); - } +extern macro TypedArrayBuiltinsAssembler::CallCMemcpy( + RawPtr, RawPtr, uintptr): void; +extern macro TypedArrayBuiltinsAssembler::CallCMemmove( + RawPtr, RawPtr, uintptr): void; +extern macro TypedArrayBuiltinsAssembler::CallCMemset( + RawPtr, intptr, uintptr): void; +extern macro TypedArrayBuiltinsAssembler::GetBuffer(implicit context: Context)( + JSTypedArray): JSArrayBuffer; +extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo( + JSTypedArray): TypedArrayElementsInfo; +extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(Map): + TypedArrayElementsInfo; +extern macro TypedArrayBuiltinsAssembler::IsUint8ElementsKind(ElementsKind): + bool; +extern macro TypedArrayBuiltinsAssembler::IsBigInt64ElementsKind(ElementsKind): + bool; +extern macro LoadFixedTypedArrayElementAsTagged( + RawPtr, uintptr, constexpr ElementsKind): Numeric; +extern macro TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromNumeric( + Context, JSTypedArray, uintptr, Numeric, constexpr ElementsKind); +extern macro TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromTagged( + Context, JSTypedArray, uintptr, JSAny, constexpr ElementsKind) + labels IfDetached; - macro StoreNumeric( - context: Context, array: JSTypedArray, index: uintptr, value: Numeric) { - const storefn: StoreNumericFn = this.storeNumericFn; - const result = storefn(context, array, index, value); - assert(result == kStoreSucceded); - } +type LoadNumericFn = builtin(JSTypedArray, uintptr) => Numeric; +type StoreNumericFn = builtin(Context, JSTypedArray, uintptr, Numeric) => Smi; +type StoreJSAnyFn = builtin(Context, JSTypedArray, uintptr, JSAny) => Smi; - macro StoreJSAny( - context: Context, array: JSTypedArray, index: uintptr, value: JSAny) - labels IfDetached { - const storefn: StoreJSAnyFn = this.storeJSAnyFn; - const result = storefn(context, array, index, value); - if (result == kStoreFailureArrayDetached) { - goto IfDetached; - } - assert(result == kStoreSucceded); - } +// The result codes returned by StoreNumericFn and StoreJSAnyFn builtins. +const kStoreSucceded: Smi = 0; +const kStoreFailureArrayDetached: Smi = 1; - loadNumericFn: LoadNumericFn; - storeNumericFn: StoreNumericFn; - storeJSAnyFn: StoreJSAnyFn; +struct TypedArrayAccessor { + macro LoadNumeric(array: JSTypedArray, index: uintptr): Numeric { + const loadfn: LoadNumericFn = this.loadNumericFn; + return loadfn(array, index); } - macro GetTypedArrayAccessor(): - TypedArrayAccessor { - const loadNumericFn = LoadTypedElement; - const storeNumericFn = StoreTypedElementNumeric; - const storeJSAnyFn = StoreTypedElementJSAny; - return TypedArrayAccessor{loadNumericFn, storeNumericFn, storeJSAnyFn}; + macro StoreNumeric( + context: Context, array: JSTypedArray, index: uintptr, value: Numeric) { + const storefn: StoreNumericFn = this.storeNumericFn; + const result = storefn(context, array, index, value); + assert(result == kStoreSucceded); } - macro GetTypedArrayAccessor(elementsKind: ElementsKind): TypedArrayAccessor { - if (IsElementsKindGreaterThan( - elementsKind, ElementsKind::UINT32_ELEMENTS)) { - if (elementsKind == ElementsKind::INT32_ELEMENTS) { - return GetTypedArrayAccessor(); - } else if (elementsKind == ElementsKind::FLOAT32_ELEMENTS) { - return GetTypedArrayAccessor(); - } else if (elementsKind == ElementsKind::FLOAT64_ELEMENTS) { - return GetTypedArrayAccessor(); - } else if (elementsKind == ElementsKind::UINT8_CLAMPED_ELEMENTS) { - return GetTypedArrayAccessor(); - } else if (elementsKind == ElementsKind::BIGUINT64_ELEMENTS) { - return GetTypedArrayAccessor(); - } else if (elementsKind == ElementsKind::BIGINT64_ELEMENTS) { - return GetTypedArrayAccessor(); - } - } else { - if (elementsKind == ElementsKind::UINT8_ELEMENTS) { - return GetTypedArrayAccessor(); - } else if (elementsKind == ElementsKind::INT8_ELEMENTS) { - return GetTypedArrayAccessor(); - } else if (elementsKind == ElementsKind::UINT16_ELEMENTS) { - return GetTypedArrayAccessor(); - } else if (elementsKind == ElementsKind::INT16_ELEMENTS) { - return GetTypedArrayAccessor(); - } else if (elementsKind == ElementsKind::UINT32_ELEMENTS) { - return GetTypedArrayAccessor(); - } + macro StoreJSAny( + context: Context, array: JSTypedArray, index: uintptr, value: JSAny) + labels IfDetached { + const storefn: StoreJSAnyFn = this.storeJSAnyFn; + const result = storefn(context, array, index, value); + if (result == kStoreFailureArrayDetached) { + goto IfDetached; } - unreachable; + assert(result == kStoreSucceded); } - extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOnHeapDataPtr( - JSTypedArray, ByteArray, uintptr): void; - extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOffHeapDataPtr( - JSTypedArray, RawPtr, uintptr): void; - - // AttachedJSTypedArray guards that the array's buffer is not detached. - transient type AttachedJSTypedArray extends JSTypedArray; + loadNumericFn: LoadNumericFn; + storeNumericFn: StoreNumericFn; + storeJSAnyFn: StoreJSAnyFn; +} - macro EnsureAttached(array: JSTypedArray): AttachedJSTypedArray - labels Detached { - if (IsDetachedBuffer(array.buffer)) goto Detached; - return %RawDownCast(array); - } +macro GetTypedArrayAccessor(): + TypedArrayAccessor { + const loadNumericFn = LoadTypedElement; + const storeNumericFn = StoreTypedElementNumeric; + const storeJSAnyFn = StoreTypedElementJSAny; + return TypedArrayAccessor{loadNumericFn, storeNumericFn, storeJSAnyFn}; +} - struct AttachedJSTypedArrayWitness { - macro Get(): AttachedJSTypedArray { - return this.unstable; +macro GetTypedArrayAccessor(elementsKind: ElementsKind): TypedArrayAccessor { + if (IsElementsKindGreaterThan(elementsKind, ElementsKind::UINT32_ELEMENTS)) { + if (elementsKind == ElementsKind::INT32_ELEMENTS) { + return GetTypedArrayAccessor(); + } else if (elementsKind == ElementsKind::FLOAT32_ELEMENTS) { + return GetTypedArrayAccessor(); + } else if (elementsKind == ElementsKind::FLOAT64_ELEMENTS) { + return GetTypedArrayAccessor(); + } else if (elementsKind == ElementsKind::UINT8_CLAMPED_ELEMENTS) { + return GetTypedArrayAccessor(); + } else if (elementsKind == ElementsKind::BIGUINT64_ELEMENTS) { + return GetTypedArrayAccessor(); + } else if (elementsKind == ElementsKind::BIGINT64_ELEMENTS) { + return GetTypedArrayAccessor(); } - - macro GetStable(): JSTypedArray { - return this.stable; + } else { + if (elementsKind == ElementsKind::UINT8_ELEMENTS) { + return GetTypedArrayAccessor(); + } else if (elementsKind == ElementsKind::INT8_ELEMENTS) { + return GetTypedArrayAccessor(); + } else if (elementsKind == ElementsKind::UINT16_ELEMENTS) { + return GetTypedArrayAccessor(); + } else if (elementsKind == ElementsKind::INT16_ELEMENTS) { + return GetTypedArrayAccessor(); + } else if (elementsKind == ElementsKind::UINT32_ELEMENTS) { + return GetTypedArrayAccessor(); } + } + unreachable; +} - macro Recheck() labels Detached { - if (IsDetachedBuffer(this.stable.buffer)) goto Detached; - this.unstable = %RawDownCast(this.stable); - } +extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOnHeapDataPtr( + JSTypedArray, ByteArray, uintptr): void; +extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOffHeapDataPtr( + JSTypedArray, RawPtr, uintptr): void; - macro Load(implicit context: Context)(k: uintptr): JSAny { - const lf: LoadNumericFn = this.loadfn; - return lf(context, this.unstable, k); - } +// AttachedJSTypedArray guards that the array's buffer is not detached. +transient type AttachedJSTypedArray extends JSTypedArray; - stable: JSTypedArray; - unstable: AttachedJSTypedArray; - loadfn: LoadNumericFn; - } +macro EnsureAttached(array: JSTypedArray): AttachedJSTypedArray + labels Detached { + if (IsDetachedBuffer(array.buffer)) goto Detached; + return %RawDownCast(array); +} - macro NewAttachedJSTypedArrayWitness(array: AttachedJSTypedArray): - AttachedJSTypedArrayWitness { - const kind = array.elements_kind; - const accessor: TypedArrayAccessor = GetTypedArrayAccessor(kind); - return AttachedJSTypedArrayWitness{ - stable: array, - unstable: array, - loadfn: accessor.loadNumericFn - }; +struct AttachedJSTypedArrayWitness { + macro Get(): AttachedJSTypedArray { + return this.unstable; } - macro KindForArrayType(): - constexpr ElementsKind; - KindForArrayType(): constexpr ElementsKind { - return ElementsKind::UINT8_ELEMENTS; - } - KindForArrayType(): constexpr ElementsKind { - return ElementsKind::INT8_ELEMENTS; - } - KindForArrayType(): constexpr ElementsKind { - return ElementsKind::UINT16_ELEMENTS; - } - KindForArrayType(): constexpr ElementsKind { - return ElementsKind::INT16_ELEMENTS; - } - KindForArrayType(): constexpr ElementsKind { - return ElementsKind::UINT32_ELEMENTS; - } - KindForArrayType(): constexpr ElementsKind { - return ElementsKind::INT32_ELEMENTS; - } - KindForArrayType(): constexpr ElementsKind { - return ElementsKind::FLOAT32_ELEMENTS; - } - KindForArrayType(): constexpr ElementsKind { - return ElementsKind::FLOAT64_ELEMENTS; - } - KindForArrayType(): constexpr ElementsKind { - return ElementsKind::UINT8_CLAMPED_ELEMENTS; - } - KindForArrayType(): constexpr ElementsKind { - return ElementsKind::BIGUINT64_ELEMENTS; - } - KindForArrayType(): constexpr ElementsKind { - return ElementsKind::BIGINT64_ELEMENTS; + macro GetStable(): JSTypedArray { + return this.stable; } - builtin LoadTypedElement( - _context: Context, array: JSTypedArray, index: uintptr): Numeric { - return LoadFixedTypedArrayElementAsTagged( - array.data_ptr, index, KindForArrayType()); + macro Recheck() labels Detached { + if (IsDetachedBuffer(this.stable.buffer)) goto Detached; + this.unstable = %RawDownCast(this.stable); } - builtin StoreTypedElementNumeric( - context: Context, typedArray: JSTypedArray, index: uintptr, - value: Numeric): Smi { - StoreJSTypedArrayElementFromNumeric( - context, typedArray, index, value, KindForArrayType()); - return kStoreSucceded; + macro Load(implicit context: Context)(k: uintptr): JSAny { + const lf: LoadNumericFn = this.loadfn; + return lf(this.unstable, k); } - // Returns True on sucess or False if the typedArrays was detached. - builtin StoreTypedElementJSAny( - context: Context, typedArray: JSTypedArray, index: uintptr, - value: JSAny): Smi { - try { - StoreJSTypedArrayElementFromTagged( - context, typedArray, index, value, KindForArrayType()) - otherwise IfDetached; - } - label IfDetached { - return kStoreFailureArrayDetached; - } - return kStoreSucceded; + stable: JSTypedArray; + unstable: AttachedJSTypedArray; + loadfn: LoadNumericFn; +} + +macro NewAttachedJSTypedArrayWitness(array: AttachedJSTypedArray): + AttachedJSTypedArrayWitness { + const kind = array.elements_kind; + const accessor: TypedArrayAccessor = GetTypedArrayAccessor(kind); + return AttachedJSTypedArrayWitness{ + stable: array, + unstable: array, + loadfn: accessor.loadNumericFn + }; +} + +macro KindForArrayType(): constexpr ElementsKind; +KindForArrayType(): constexpr ElementsKind { + return ElementsKind::UINT8_ELEMENTS; +} +KindForArrayType(): constexpr ElementsKind { + return ElementsKind::INT8_ELEMENTS; +} +KindForArrayType(): constexpr ElementsKind { + return ElementsKind::UINT16_ELEMENTS; +} +KindForArrayType(): constexpr ElementsKind { + return ElementsKind::INT16_ELEMENTS; +} +KindForArrayType(): constexpr ElementsKind { + return ElementsKind::UINT32_ELEMENTS; +} +KindForArrayType(): constexpr ElementsKind { + return ElementsKind::INT32_ELEMENTS; +} +KindForArrayType(): constexpr ElementsKind { + return ElementsKind::FLOAT32_ELEMENTS; +} +KindForArrayType(): constexpr ElementsKind { + return ElementsKind::FLOAT64_ELEMENTS; +} +KindForArrayType(): constexpr ElementsKind { + return ElementsKind::UINT8_CLAMPED_ELEMENTS; +} +KindForArrayType(): constexpr ElementsKind { + return ElementsKind::BIGUINT64_ELEMENTS; +} +KindForArrayType(): constexpr ElementsKind { + return ElementsKind::BIGINT64_ELEMENTS; +} + +builtin LoadTypedElement( + array: JSTypedArray, index: uintptr): Numeric { + return LoadFixedTypedArrayElementAsTagged( + array.data_ptr, index, KindForArrayType()); +} + +builtin StoreTypedElementNumeric( + context: Context, typedArray: JSTypedArray, index: uintptr, + value: Numeric): Smi { + StoreJSTypedArrayElementFromNumeric( + context, typedArray, index, value, KindForArrayType()); + return kStoreSucceded; +} + +// Returns True on sucess or False if the typedArrays was detached. +builtin StoreTypedElementJSAny( + context: Context, typedArray: JSTypedArray, index: uintptr, + value: JSAny): Smi { + try { + StoreJSTypedArrayElementFromTagged( + context, typedArray, index, value, KindForArrayType()) + otherwise IfDetached; + } label IfDetached { + return kStoreFailureArrayDetached; } + return kStoreSucceded; +} } diff --git a/deps/v8/src/builtins/wasm.tq b/deps/v8/src/builtins/wasm.tq new file mode 100644 index 00000000000000..097e39d430e7d3 --- /dev/null +++ b/deps/v8/src/builtins/wasm.tq @@ -0,0 +1,273 @@ +// Copyright 2020 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-wasm-gen.h' + +namespace runtime { +extern runtime WasmMemoryGrow(Context, WasmInstanceObject, Smi): Smi; +extern runtime WasmRefFunc(Context, WasmInstanceObject, Smi): JSAny; +extern runtime WasmFunctionTableGet( + Context, WasmInstanceObject, Smi, Smi): JSAny; +extern runtime WasmFunctionTableSet( + Context, WasmInstanceObject, Smi, Smi, Object): JSAny; +extern runtime ThrowWasmError(Context, Smi): JSAny; +extern runtime Throw(Context, Object): JSAny; +extern runtime ReThrow(Context, Object): JSAny; +extern runtime WasmStackGuard(Context): JSAny; +extern runtime ThrowWasmStackOverflow(Context): JSAny; +extern runtime WasmTraceMemory(Context, Smi): JSAny; +} + +namespace wasm { +const kFuncTableType: + constexpr int31 generates 'wasm::ValueType::Kind::kFuncRef'; + +extern macro WasmBuiltinsAssembler::LoadInstanceFromFrame(): WasmInstanceObject; + +// WasmInstanceObject has a field layout that Torque can't handle yet. +// TODO(bbudge) Eliminate these functions when Torque is ready. +extern macro WasmBuiltinsAssembler::LoadContextFromInstance(WasmInstanceObject): + NativeContext; +extern macro WasmBuiltinsAssembler::LoadTablesFromInstance(WasmInstanceObject): + FixedArray; +extern macro WasmBuiltinsAssembler::LoadExternalFunctionsFromInstance( + WasmInstanceObject): FixedArray; + +macro LoadContextFromFrame(): NativeContext { + return LoadContextFromInstance(LoadInstanceFromFrame()); +} + +builtin WasmInt32ToHeapNumber(val: int32): HeapNumber { + return AllocateHeapNumberWithValue(Convert(val)); +} + +builtin WasmTaggedNonSmiToInt32(implicit context: Context)(val: JSAnyNotSmi): + int32 { + return ChangeTaggedNonSmiToInt32(val); +} + +builtin WasmTaggedToFloat64(implicit context: Context)(val: JSAny): float64 { + return ChangeTaggedToFloat64(val); +} + +builtin WasmMemoryGrow(numPages: int32): int32 { + if (!IsValidPositiveSmi(ChangeInt32ToIntPtr(numPages))) + return Int32Constant(-1); + const instance: WasmInstanceObject = LoadInstanceFromFrame(); + const context: NativeContext = LoadContextFromInstance(instance); + const result: Smi = + runtime::WasmMemoryGrow(context, instance, SmiFromInt32(numPages)); + return SmiToInt32(result); +} + +builtin WasmTableGet(tableIndex: intptr, index: int32): Object { + const instance: WasmInstanceObject = LoadInstanceFromFrame(); + const entryIndex: intptr = ChangeInt32ToIntPtr(index); + try { + assert(IsValidPositiveSmi(tableIndex)); + if (!IsValidPositiveSmi(entryIndex)) goto IndexOutOfRange; + + const tables: FixedArray = LoadTablesFromInstance(instance); + const table: WasmTableObject = %RawDownCast( + LoadFixedArrayElement(tables, tableIndex)); + const entriesCount: intptr = Convert(table.current_length); + if (entryIndex >= entriesCount) goto IndexOutOfRange; + + const entries: FixedArray = table.entries; + const entry: Object = LoadFixedArrayElement(entries, entryIndex); + + try { + const entryObject: HeapObject = + TaggedToHeapObject(entry) otherwise ReturnEntry; + if (IsTuple2Map(entryObject.map)) goto CallRuntime; + goto ReturnEntry; + } label ReturnEntry { + return entry; + } + } label CallRuntime deferred { + tail runtime::WasmFunctionTableGet( + LoadContextFromInstance(instance), instance, SmiFromIntPtr(tableIndex), + SmiFromIntPtr(entryIndex)); + } label IndexOutOfRange deferred { + tail ThrowWasmTrapTableOutOfBounds(); + } +} + +builtin WasmTableSet(tableIndex: intptr, index: int32, value: Object): Object { + const instance: WasmInstanceObject = LoadInstanceFromFrame(); + const entryIndex: intptr = ChangeInt32ToIntPtr(index); + try { + assert(IsValidPositiveSmi(tableIndex)); + if (!IsValidPositiveSmi(entryIndex)) goto IndexOutOfRange; + + const tables: FixedArray = LoadTablesFromInstance(instance); + const table: WasmTableObject = %RawDownCast( + LoadFixedArrayElement(tables, tableIndex)); + + // Fall back to the runtime to set funcrefs, since we have to update + // function dispatch tables. + const tableType: Smi = table.raw_type; + if (tableType == SmiConstant(kFuncTableType)) goto CallRuntime; + + const entriesCount: intptr = Convert(table.current_length); + if (entryIndex >= entriesCount) goto IndexOutOfRange; + + const entries: FixedArray = table.entries; + StoreFixedArrayElement(entries, entryIndex, value); + return Undefined; + } label CallRuntime deferred { + tail runtime::WasmFunctionTableSet( + LoadContextFromInstance(instance), instance, SmiFromIntPtr(tableIndex), + SmiFromIntPtr(entryIndex), value); + } label IndexOutOfRange deferred { + tail ThrowWasmTrapTableOutOfBounds(); + } +} + +builtin WasmRefFunc(index: uint32): Object { + const instance: WasmInstanceObject = LoadInstanceFromFrame(); + try { + const table: FixedArray = LoadExternalFunctionsFromInstance(instance); + if (table == Undefined) goto CallRuntime; + const functionIndex: intptr = Signed(ChangeUint32ToWord(index)); + const result: Object = LoadFixedArrayElement(table, functionIndex); + if (result == Undefined) goto CallRuntime; + return result; + } label CallRuntime deferred { + tail runtime::WasmRefFunc( + LoadContextFromInstance(instance), instance, SmiFromUint32(index)); + } +} + +builtin WasmThrow(exception: Object): JSAny { + tail runtime::Throw(LoadContextFromFrame(), exception); +} + +builtin WasmRethrow(exception: Object): JSAny { + if (exception == Null) tail ThrowWasmTrapRethrowNullRef(); + tail runtime::ReThrow(LoadContextFromFrame(), exception); +} + +builtin WasmStackGuard(): JSAny { + tail runtime::WasmStackGuard(LoadContextFromFrame()); +} + +builtin WasmStackOverflow(): JSAny { + tail runtime::ThrowWasmStackOverflow(LoadContextFromFrame()); +} + +builtin WasmTraceMemory(info: Smi): JSAny { + tail runtime::WasmTraceMemory(LoadContextFromFrame(), info); +} + +builtin WasmAllocateJSArray(implicit context: Context)(size: Smi): JSArray { + const map: Map = GetFastPackedElementsJSArrayMap(); + return AllocateJSArray(ElementsKind::PACKED_ELEMENTS, map, size, size); +} + +extern macro TryHasOwnProperty(HeapObject, Map, InstanceType, Name): never + labels Found, NotFound, Bailout; +type OnNonExistent constexpr 'OnNonExistent'; +const kReturnUndefined: constexpr OnNonExistent + generates 'OnNonExistent::kReturnUndefined'; +extern macro SmiConstant(constexpr OnNonExistent): Smi; +extern transitioning builtin GetPropertyWithReceiver(implicit context: Context)( + JSAny, Name, JSAny, Smi): JSAny; + +transitioning builtin WasmGetOwnProperty(implicit context: Context)( + object: Object, uniqueName: Name): JSAny { + try { + const heapObject: HeapObject = + TaggedToHeapObject(object) otherwise NotFound; + const receiver: JSReceiver = + Cast(heapObject) otherwise NotFound; + try { + TryHasOwnProperty( + receiver, receiver.map, receiver.instanceType, uniqueName) + otherwise Found, NotFound, Bailout; + } label Found { + tail GetPropertyWithReceiver( + receiver, uniqueName, receiver, SmiConstant(kReturnUndefined)); + } + } label NotFound deferred { + return Undefined; + } label Bailout deferred { + unreachable; + } +} + +// Trap builtins. + +builtin WasmTrap(error: Smi): JSAny { + tail runtime::ThrowWasmError(LoadContextFromFrame(), error); +} + +builtin ThrowWasmTrapUnreachable(): JSAny { + tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapUnreachable)); +} + +builtin ThrowWasmTrapMemOutOfBounds(): JSAny { + tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapMemOutOfBounds)); +} + +builtin ThrowWasmTrapUnalignedAccess(): JSAny { + tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapUnalignedAccess)); +} + +builtin ThrowWasmTrapDivByZero(): JSAny { + tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapDivByZero)); +} + +builtin ThrowWasmTrapDivUnrepresentable(): JSAny { + tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapDivUnrepresentable)); +} + +builtin ThrowWasmTrapRemByZero(): JSAny { + tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapRemByZero)); +} + +builtin ThrowWasmTrapFloatUnrepresentable(): JSAny { + tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapFloatUnrepresentable)); +} + +builtin ThrowWasmTrapFuncInvalid(): JSAny { + tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapFuncInvalid)); +} + +builtin ThrowWasmTrapFuncSigMismatch(): JSAny { + tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapFuncSigMismatch)); +} + +builtin ThrowWasmTrapDataSegmentDropped(): JSAny { + tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapDataSegmentDropped)); +} + +builtin ThrowWasmTrapElemSegmentDropped(): JSAny { + tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapElemSegmentDropped)); +} + +builtin ThrowWasmTrapTableOutOfBounds(): JSAny { + tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapTableOutOfBounds)); +} + +builtin ThrowWasmTrapBrOnExnNullRef(): JSAny { + tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapBrOnExnNullRef)); +} + +builtin ThrowWasmTrapRethrowNullRef(): JSAny { + tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapRethrowNullRef)); +} + +builtin ThrowWasmTrapNullDereference(): JSAny { + tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapNullDereference)); +} + +builtin ThrowWasmTrapIllegalCast(): JSAny { + tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapIllegalCast)); +} + +builtin ThrowWasmTrapArrayOutOfBounds(): JSAny { + tail WasmTrap(SmiConstant(MessageTemplate::kWasmTrapArrayOutOfBounds)); +} +} diff --git a/deps/v8/src/builtins/x64/builtins-x64.cc b/deps/v8/src/builtins/x64/builtins-x64.cc index 8d028c88f0086e..bfabe26292b8d3 100644 --- a/deps/v8/src/builtins/x64/builtins-x64.cc +++ b/deps/v8/src/builtins/x64/builtins-x64.cc @@ -1769,7 +1769,6 @@ void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) { } // static -// TODO(victor): merge steps 1, 2 and 3 when V8_REVERSE_JSARGS is set. void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { // Stack Layout: // rsp[0] : Return address @@ -1781,19 +1780,40 @@ void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { // NOTE: The order of args are reversed if V8_REVERSE_JSARGS // rax contains the number of arguments, n, not counting the receiver. - // 1. Make sure we have at least one argument. +#ifdef V8_REVERSE_JSARGS + // 1. Get the callable to call (passed as receiver) from the stack. + { + StackArgumentsAccessor args(rax); + __ movq(rdi, args.GetReceiverOperand()); + } + + // 2. Save the return address and drop the callable. + __ PopReturnAddressTo(rbx); + __ Pop(kScratchRegister); + + // 3. Make sure we have at least one argument. { Label done; __ testq(rax, rax); __ j(not_zero, &done, Label::kNear); - __ PopReturnAddressTo(rbx); -#ifdef V8_REVERSE_JSARGS - __ Pop(kScratchRegister); // Pop the receiver. __ PushRoot(RootIndex::kUndefinedValue); - __ Push(kScratchRegister); + __ incq(rax); + __ bind(&done); + } + + // 4. Push back the return address one slot down on the stack (overwriting the + // original callable), making the original first argument the new receiver. + __ PushReturnAddressFrom(rbx); + __ decq(rax); // One fewer argument (first argument is new receiver). + #else + // 1. Make sure we have at least one argument. + { + Label done; + __ testq(rax, rax); + __ j(not_zero, &done, Label::kNear); + __ PopReturnAddressTo(rbx); __ PushRoot(RootIndex::kUndefinedValue); -#endif __ PushReturnAddressFrom(rbx); __ incq(rax); __ bind(&done); @@ -1805,14 +1825,6 @@ void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { __ movq(rdi, args.GetReceiverOperand()); } -#ifdef V8_REVERSE_JSARGS - // 3. Shift return address one slot down on the stack (overwriting the - // original receiver), making the original first argument the new receiver. - { - __ DropUnderReturnAddress(1, rbx); // Drop one slot under return address. - __ decq(rax); // One fewer argument (first argument is new receiver). - } -#else // 3. Shift arguments and return address one slot down on the stack // (overwriting the original receiver). Adjust argument count to make // the original first argument the new receiver. @@ -2205,10 +2217,8 @@ void Builtins::Generate_CallOrConstructVarargs(MacroAssembler* masm, Label copy, check; Register src = r8, dest = rsp, num = r9, current = r11; __ movq(src, rsp); - __ movq(kScratchRegister, rcx); - __ negq(kScratchRegister); - __ leaq(rsp, Operand(rsp, kScratchRegister, times_system_pointer_size, - 0)); // Update stack pointer. + __ leaq(kScratchRegister, Operand(rcx, times_system_pointer_size, 0)); + __ AllocateStackSpace(kScratchRegister); __ leaq(num, Operand(rax, 2)); // Number of words to copy. // +2 for receiver and return address. __ Set(current, 0); @@ -3500,8 +3510,9 @@ void Builtins::Generate_CallApiGetter(MacroAssembler* masm) { DCHECK(api_function_address != name_arg); __ LoadTaggedPointerField( scratch, FieldOperand(callback, AccessorInfo::kJsGetterOffset)); - __ movq(api_function_address, - FieldOperand(scratch, Foreign::kForeignAddressOffset)); + __ LoadExternalPointerField( + api_function_address, + FieldOperand(scratch, Foreign::kForeignAddressOffset)); // +3 is to skip prolog, return address and name handle. Operand return_value_operand( diff --git a/deps/v8/src/codegen/arm/constants-arm.h b/deps/v8/src/codegen/arm/constants-arm.h index 171de9e1d7d135..a7726a8f25e361 100644 --- a/deps/v8/src/codegen/arm/constants-arm.h +++ b/deps/v8/src/codegen/arm/constants-arm.h @@ -459,23 +459,6 @@ class Instruction { return InstructionBits() & (((2 << (hi - lo)) - 1) << lo); } - // Static support. - - // Extract a single bit from the instruction bits and return it as bit 0 in - // the result. - static inline int Bit(Instr instr, int nr) { return (instr >> nr) & 1; } - - // Extract a bit field from the instruction bits and return it in the - // least-significant bits of the result. - static inline int Bits(Instr instr, int hi, int lo) { - return (instr >> lo) & ((2 << (hi - lo)) - 1); - } - - // Read a bit field , leaving its position unchanged in the result. - static inline int BitField(Instr instr, int hi, int lo) { - return instr & (((2 << (hi - lo)) - 1) << lo); - } - // Accessors for the different named fields used in the ARM encoding. // The naming of these accessor corresponds to figure A3-1. // diff --git a/deps/v8/src/codegen/arm/cpu-arm.cc b/deps/v8/src/codegen/arm/cpu-arm.cc index 9113de705d2052..47fe4bdb7404ce 100644 --- a/deps/v8/src/codegen/arm/cpu-arm.cc +++ b/deps/v8/src/codegen/arm/cpu-arm.cc @@ -37,18 +37,6 @@ V8_NOINLINE void CpuFeatures::FlushICache(void* start, size_t size) { register uint32_t end asm("r1") = beg + size; register uint32_t flg asm("r2") = 0; -#ifdef __clang__ - // This variant of the asm avoids a constant pool entry, which can be - // problematic when LTO'ing. It is also slightly shorter. - register uint32_t scno asm("r7") = __ARM_NR_cacheflush; - - asm volatile("svc 0\n" - : - : "r"(beg), "r"(end), "r"(flg), "r"(scno) - : "memory"); -#else - // Use a different variant of the asm with GCC because some versions doesn't - // support r7 as an asm input. asm volatile( // This assembly works for both ARM and Thumb targets. @@ -66,7 +54,6 @@ V8_NOINLINE void CpuFeatures::FlushICache(void* start, size_t size) { : "r"(beg), "r"(end), "r"(flg), [scno] "i"(__ARM_NR_cacheflush) : "memory"); #endif -#endif #endif // !USE_SIMULATOR } diff --git a/deps/v8/src/codegen/arm/interface-descriptors-arm.cc b/deps/v8/src/codegen/arm/interface-descriptors-arm.cc index 575fd2780572b4..5a4e08dc77c0bd 100644 --- a/deps/v8/src/codegen/arm/interface-descriptors-arm.cc +++ b/deps/v8/src/codegen/arm/interface-descriptors-arm.cc @@ -283,6 +283,30 @@ void RunMicrotasksEntryDescriptor::InitializePlatformSpecific( data->InitializePlatformSpecific(arraysize(registers), registers); } +void BinaryOp_WithFeedbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // TODO(v8:8888): Implement on this platform. + DefaultInitializePlatformSpecific(data, 4); +} + +void CallTrampoline_WithFeedbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // TODO(v8:8888): Implement on this platform. + DefaultInitializePlatformSpecific(data, 4); +} + +void Compare_WithFeedbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // TODO(v8:8888): Implement on this platform. + DefaultInitializePlatformSpecific(data, 4); +} + +void UnaryOp_WithFeedbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // TODO(v8:8888): Implement on this platform. + DefaultInitializePlatformSpecific(data, 3); +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/codegen/arm/macro-assembler-arm.cc b/deps/v8/src/codegen/arm/macro-assembler-arm.cc index 349c8dc29e7e97..7e5fa8cef1c1c2 100644 --- a/deps/v8/src/codegen/arm/macro-assembler-arm.cc +++ b/deps/v8/src/codegen/arm/macro-assembler-arm.cc @@ -17,7 +17,7 @@ #include "src/codegen/register-configuration.h" #include "src/debug/debug.h" #include "src/execution/frames-inl.h" -#include "src/heap/heap-inl.h" // For MemoryChunk. +#include "src/heap/memory-chunk.h" #include "src/init/bootstrapper.h" #include "src/logging/counters.h" #include "src/numbers/double.h" @@ -427,6 +427,35 @@ void TurboAssembler::Push(Smi smi) { push(scratch); } +void TurboAssembler::PushArray(Register array, Register size, Register scratch, + PushArrayOrder order) { + UseScratchRegisterScope temps(this); + Register counter = scratch; + Register tmp = temps.Acquire(); + DCHECK(!AreAliased(array, size, counter, tmp)); + Label loop, entry; + if (order == PushArrayOrder::kReverse) { + mov(counter, Operand(0)); + b(&entry); + bind(&loop); + ldr(tmp, MemOperand(array, counter, LSL, kSystemPointerSizeLog2)); + push(tmp); + add(counter, counter, Operand(1)); + bind(&entry); + cmp(counter, size); + b(lt, &loop); + } else { + mov(counter, size); + b(&entry); + bind(&loop); + ldr(tmp, MemOperand(array, counter, LSL, kSystemPointerSizeLog2)); + push(tmp); + bind(&entry); + sub(counter, counter, Operand(1), SetCC); + b(ge, &loop); + } +} + void TurboAssembler::Move(Register dst, Smi smi) { mov(dst, Operand(smi)); } void TurboAssembler::Move(Register dst, Handle value) { @@ -1556,7 +1585,7 @@ void MacroAssembler::CallDebugOnFunctionCall(Register fun, Register new_target, Register expected_parameter_count, Register actual_parameter_count) { // Load receiver to pass it later to DebugOnFunctionCall hook. - ldr(r4, MemOperand(sp, actual_parameter_count, LSL, kPointerSizeLog2)); + ldr(r4, ReceiverOperand(actual_parameter_count)); FrameScope frame(this, has_frame() ? StackFrame::NONE : StackFrame::INTERNAL); SmiTag(expected_parameter_count); diff --git a/deps/v8/src/codegen/arm/macro-assembler-arm.h b/deps/v8/src/codegen/arm/macro-assembler-arm.h index 9ec1bafb5803aa..a7dc5498b8b8ec 100644 --- a/deps/v8/src/codegen/arm/macro-assembler-arm.h +++ b/deps/v8/src/codegen/arm/macro-assembler-arm.h @@ -156,6 +156,12 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { } } + enum class PushArrayOrder { kNormal, kReverse }; + // `array` points to the first element (the lowest address). + // `array` and `size` are not modified. + void PushArray(Register array, Register size, Register scratch, + PushArrayOrder order = PushArrayOrder::kNormal); + void Pop(Register dst) { pop(dst); } // Pop two registers. Pops rightmost register first (from lower address). @@ -720,6 +726,18 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler { void JumpIfIsInRange(Register value, unsigned lower_limit, unsigned higher_limit, Label* on_in_range); + // It assumes that the arguments are located below the stack pointer. + // argc is the number of arguments not including the receiver. + // TODO(victorgomes): Remove this function once we stick with the reversed + // arguments order. + MemOperand ReceiverOperand(Register argc) { +#ifdef V8_REVERSE_JSARGS + return MemOperand(sp, 0); +#else + return MemOperand(sp, argc, LSL, kSystemPointerSizeLog2); +#endif + } + // --------------------------------------------------------------------------- // Runtime calls diff --git a/deps/v8/src/codegen/arm/register-arm.h b/deps/v8/src/codegen/arm/register-arm.h index 0d453ec03ed81b..77ae14f98cc834 100644 --- a/deps/v8/src/codegen/arm/register-arm.h +++ b/deps/v8/src/codegen/arm/register-arm.h @@ -351,6 +351,8 @@ constexpr Register kWasmCompileLazyFuncIndexRegister = r4; constexpr Register cp = r7; // JavaScript context pointer. constexpr Register kRootRegister = r10; // Roots array pointer. +constexpr DoubleRegister kFPReturnRegister0 = d0; + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/codegen/arm64/assembler-arm64-inl.h b/deps/v8/src/codegen/arm64/assembler-arm64-inl.h index f3c3e559753c10..c47f8f1aa54baf 100644 --- a/deps/v8/src/codegen/arm64/assembler-arm64-inl.h +++ b/deps/v8/src/codegen/arm64/assembler-arm64-inl.h @@ -660,6 +660,7 @@ Address RelocInfo::constant_pool_entry_address() { HeapObject RelocInfo::target_object() { DCHECK(IsCodeTarget(rmode_) || IsEmbeddedObjectMode(rmode_)); if (IsCompressedEmbeddedObject(rmode_)) { + CHECK(!host_.is_null()); return HeapObject::cast(Object(DecompressTaggedAny( host_.address(), Assembler::target_compressed_address_at(pc_, constant_pool_)))); diff --git a/deps/v8/src/codegen/arm64/assembler-arm64.cc b/deps/v8/src/codegen/arm64/assembler-arm64.cc index d5a0295934d8eb..97a57d6f3c6d5a 100644 --- a/deps/v8/src/codegen/arm64/assembler-arm64.cc +++ b/deps/v8/src/codegen/arm64/assembler-arm64.cc @@ -140,7 +140,9 @@ bool RelocInfo::IsCodedSpecially() { bool RelocInfo::IsInConstantPool() { Instruction* instr = reinterpret_cast(pc_); - return instr->IsLdrLiteralX(); + DCHECK_IMPLIES(instr->IsLdrLiteralW(), COMPRESS_POINTERS_BOOL); + return instr->IsLdrLiteralX() || + (COMPRESS_POINTERS_BOOL && instr->IsLdrLiteralW()); } uint32_t RelocInfo::wasm_call_tag() const { diff --git a/deps/v8/src/codegen/arm64/instructions-arm64.cc b/deps/v8/src/codegen/arm64/instructions-arm64.cc index c2224ffe34aa0e..7d986f286d92de 100644 --- a/deps/v8/src/codegen/arm64/instructions-arm64.cc +++ b/deps/v8/src/codegen/arm64/instructions-arm64.cc @@ -343,6 +343,10 @@ void NEONFormatDecoder::SetFormatMaps(const NEONFormatMap* format0, formats_[0] = format0; formats_[1] = (format1 == nullptr) ? formats_[0] : format1; formats_[2] = (format2 == nullptr) ? formats_[1] : format2; + // Support four parameters form (e.i. ld4r) + // to avoid using positional arguments in DisassemblingDecoder. + // See: https://crbug.com/v8/10365 + formats_[3] = formats_[2]; } void NEONFormatDecoder::SetFormatMap(unsigned index, @@ -353,15 +357,18 @@ void NEONFormatDecoder::SetFormatMap(unsigned index, } const char* NEONFormatDecoder::SubstitutePlaceholders(const char* string) { - return Substitute(string, kPlaceholder, kPlaceholder, kPlaceholder); + return Substitute(string, kPlaceholder, kPlaceholder, kPlaceholder, + kPlaceholder); } const char* NEONFormatDecoder::Substitute(const char* string, SubstitutionMode mode0, SubstitutionMode mode1, - SubstitutionMode mode2) { + SubstitutionMode mode2, + SubstitutionMode mode3) { snprintf(form_buffer_, sizeof(form_buffer_), string, GetSubstitute(0, mode0), - GetSubstitute(1, mode1), GetSubstitute(2, mode2)); + GetSubstitute(1, mode1), GetSubstitute(2, mode2), + GetSubstitute(3, mode3)); return form_buffer_; } diff --git a/deps/v8/src/codegen/arm64/instructions-arm64.h b/deps/v8/src/codegen/arm64/instructions-arm64.h index d2341b972fbcb7..c115fb692469f2 100644 --- a/deps/v8/src/codegen/arm64/instructions-arm64.h +++ b/deps/v8/src/codegen/arm64/instructions-arm64.h @@ -619,7 +619,8 @@ class NEONFormatDecoder { // substitution mode. const char* Substitute(const char* string, SubstitutionMode mode0 = kFormat, SubstitutionMode mode1 = kFormat, - SubstitutionMode mode2 = kFormat); + SubstitutionMode mode2 = kFormat, + SubstitutionMode mode3 = kFormat); // Append a "2" to a mnemonic string based of the state of the Q bit. const char* Mnemonic(const char* mnemonic); @@ -738,7 +739,7 @@ class NEONFormatDecoder { uint8_t PickBits(const uint8_t bits[]); Instr instrbits_; - const NEONFormatMap* formats_[3]; + const NEONFormatMap* formats_[4]; char form_buffer_[64]; char mne_buffer_[16]; }; diff --git a/deps/v8/src/codegen/arm64/interface-descriptors-arm64.cc b/deps/v8/src/codegen/arm64/interface-descriptors-arm64.cc index 2d86ace4bc28c0..9f0592244491be 100644 --- a/deps/v8/src/codegen/arm64/interface-descriptors-arm64.cc +++ b/deps/v8/src/codegen/arm64/interface-descriptors-arm64.cc @@ -287,6 +287,30 @@ void RunMicrotasksEntryDescriptor::InitializePlatformSpecific( data->InitializePlatformSpecific(arraysize(registers), registers); } +void BinaryOp_WithFeedbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // TODO(v8:8888): Implement on this platform. + DefaultInitializePlatformSpecific(data, 4); +} + +void CallTrampoline_WithFeedbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // TODO(v8:8888): Implement on this platform. + DefaultInitializePlatformSpecific(data, 4); +} + +void Compare_WithFeedbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // TODO(v8:8888): Implement on this platform. + DefaultInitializePlatformSpecific(data, 4); +} + +void UnaryOp_WithFeedbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // TODO(v8:8888): Implement on this platform. + DefaultInitializePlatformSpecific(data, 3); +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/codegen/arm64/macro-assembler-arm64-inl.h b/deps/v8/src/codegen/arm64/macro-assembler-arm64-inl.h index 809838bcf9d247..93b8136d9a997a 100644 --- a/deps/v8/src/codegen/arm64/macro-assembler-arm64-inl.h +++ b/deps/v8/src/codegen/arm64/macro-assembler-arm64-inl.h @@ -713,7 +713,7 @@ void TurboAssembler::Fmov(VRegister vd, float imm) { } else { UseScratchRegisterScope temps(this); Register tmp = temps.AcquireW(); - Mov(tmp, bit_cast(imm)); + Mov(tmp, bits); Fmov(vd, tmp); } } else { diff --git a/deps/v8/src/codegen/arm64/macro-assembler-arm64.cc b/deps/v8/src/codegen/arm64/macro-assembler-arm64.cc index 1273904c9c2bd0..c157df29966975 100644 --- a/deps/v8/src/codegen/arm64/macro-assembler-arm64.cc +++ b/deps/v8/src/codegen/arm64/macro-assembler-arm64.cc @@ -16,7 +16,7 @@ #include "src/deoptimizer/deoptimizer.h" #include "src/execution/frame-constants.h" #include "src/execution/frames-inl.h" -#include "src/heap/heap-inl.h" // For MemoryChunk. +#include "src/heap/memory-chunk.h" #include "src/init/bootstrapper.h" #include "src/logging/counters.h" #include "src/runtime/runtime.h" @@ -1306,7 +1306,14 @@ void TurboAssembler::CopyDoubleWords(Register dst, Register src, Register count, static_assert(kSystemPointerSize == kDRegSize, "pointers must be the same size as doubles"); - int direction = (mode == kDstLessThanSrc) ? 1 : -1; + if (mode == kDstLessThanSrcAndReverse) { + Add(src, src, Operand(count, LSL, kSystemPointerSizeLog2)); + Sub(src, src, kSystemPointerSize); + } + + int src_direction = (mode == kDstLessThanSrc) ? 1 : -1; + int dst_direction = (mode == kSrcLessThanDst) ? -1 : 1; + UseScratchRegisterScope scope(this); VRegister temp0 = scope.AcquireD(); VRegister temp1 = scope.AcquireD(); @@ -1314,23 +1321,30 @@ void TurboAssembler::CopyDoubleWords(Register dst, Register src, Register count, Label pairs, loop, done; Tbz(count, 0, &pairs); - Ldr(temp0, MemOperand(src, direction * kSystemPointerSize, PostIndex)); + Ldr(temp0, MemOperand(src, src_direction * kSystemPointerSize, PostIndex)); Sub(count, count, 1); - Str(temp0, MemOperand(dst, direction * kSystemPointerSize, PostIndex)); + Str(temp0, MemOperand(dst, dst_direction * kSystemPointerSize, PostIndex)); Bind(&pairs); if (mode == kSrcLessThanDst) { // Adjust pointers for post-index ldp/stp with negative offset: Sub(dst, dst, kSystemPointerSize); Sub(src, src, kSystemPointerSize); + } else if (mode == kDstLessThanSrcAndReverse) { + Sub(src, src, kSystemPointerSize); } Bind(&loop); Cbz(count, &done); Ldp(temp0, temp1, - MemOperand(src, 2 * direction * kSystemPointerSize, PostIndex)); + MemOperand(src, 2 * src_direction * kSystemPointerSize, PostIndex)); Sub(count, count, 2); - Stp(temp0, temp1, - MemOperand(dst, 2 * direction * kSystemPointerSize, PostIndex)); + if (mode == kDstLessThanSrcAndReverse) { + Stp(temp1, temp0, + MemOperand(dst, 2 * dst_direction * kSystemPointerSize, PostIndex)); + } else { + Stp(temp0, temp1, + MemOperand(dst, 2 * dst_direction * kSystemPointerSize, PostIndex)); + } B(&loop); // TODO(all): large copies may benefit from using temporary Q registers @@ -2093,7 +2107,7 @@ void MacroAssembler::CallDebugOnFunctionCall(Register fun, Register new_target, Register expected_parameter_count, Register actual_parameter_count) { // Load receiver to pass it later to DebugOnFunctionCall hook. - Ldr(x4, MemOperand(sp, actual_parameter_count, LSL, kSystemPointerSizeLog2)); + Peek(x4, ReceiverOperand(actual_parameter_count)); FrameScope frame(this, has_frame() ? StackFrame::NONE : StackFrame::INTERNAL); if (!new_target.is_valid()) new_target = padreg; @@ -2165,6 +2179,14 @@ void MacroAssembler::InvokeFunctionCode(Register function, Register new_target, Bind(&done); } +Operand MacroAssembler::ReceiverOperand(Register arg_count) { +#ifdef V8_REVERSE_JSARGS + return Operand(0); +#else + return Operand(arg_count, LSL, kXRegSizeLog2); +#endif +} + void MacroAssembler::InvokeFunctionWithNewTarget( Register function, Register new_target, Register actual_parameter_count, InvokeFlag flag) { @@ -2297,7 +2319,7 @@ void TurboAssembler::EnterFrame(StackFrame::Type type) { // sp[2] : fp // sp[1] : type // sp[0] : for alignment - } else if (type == StackFrame::WASM_COMPILED || + } else if (type == StackFrame::WASM || type == StackFrame::WASM_COMPILE_LAZY || type == StackFrame::WASM_EXIT) { Register type_reg = temps.AcquireX(); @@ -2966,7 +2988,9 @@ void TurboAssembler::PrintfNoPreserve(const char* format, // Copies of the printf vararg registers that we can pop from. CPURegList pcs_varargs = kPCSVarargs; +#ifndef V8_OS_WIN CPURegList pcs_varargs_fp = kPCSVarargsFP; +#endif // Place the arguments. There are lots of clever tricks and optimizations we // could use here, but Printf is a debug tool so instead we just try to keep @@ -2981,7 +3005,14 @@ void TurboAssembler::PrintfNoPreserve(const char* format, if (args[i].Is32Bits()) pcs[i] = pcs[i].W(); } else if (args[i].IsVRegister()) { // In C, floats are always cast to doubles for varargs calls. +#ifdef V8_OS_WIN + // In case of variadic functions SIMD and Floating-point registers + // aren't used. The general x0-x7 should be used instead. + // https://docs.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions + pcs[i] = pcs_varargs.PopLowestIndex().X(); +#else pcs[i] = pcs_varargs_fp.PopLowestIndex().D(); +#endif } else { DCHECK(args[i].IsNone()); arg_count = i; @@ -3012,6 +3043,22 @@ void TurboAssembler::PrintfNoPreserve(const char* format, // Do a second pass to move values into their final positions and perform any // conversions that may be required. for (int i = 0; i < arg_count; i++) { +#ifdef V8_OS_WIN + if (args[i].IsVRegister()) { + if (pcs[i].SizeInBytes() != args[i].SizeInBytes()) { + // If the argument is half- or single-precision + // converts to double-precision before that is + // moved into the one of X scratch register. + VRegister temp0 = temps.AcquireD(); + Fcvt(temp0.VReg(), args[i].VReg()); + Fmov(pcs[i].Reg(), temp0); + } else { + Fmov(pcs[i].Reg(), args[i].VReg()); + } + } else { + Mov(pcs[i].Reg(), args[i].Reg(), kDiscardForSameWReg); + } +#else DCHECK(pcs[i].type() == args[i].type()); if (pcs[i].IsRegister()) { Mov(pcs[i].Reg(), args[i].Reg(), kDiscardForSameWReg); @@ -3023,6 +3070,7 @@ void TurboAssembler::PrintfNoPreserve(const char* format, Fcvt(pcs[i].VReg(), args[i].VReg()); } } +#endif } // Load the format string into x0, as per the procedure-call standard. diff --git a/deps/v8/src/codegen/arm64/macro-assembler-arm64.h b/deps/v8/src/codegen/arm64/macro-assembler-arm64.h index 7b1fb69e954231..109e73c3c229d4 100644 --- a/deps/v8/src/codegen/arm64/macro-assembler-arm64.h +++ b/deps/v8/src/codegen/arm64/macro-assembler-arm64.h @@ -703,7 +703,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { void CopySlots(Register dst, Register src, Register slot_count); // Copy count double words from the address in register src to the address - // in register dst. There are two modes for this function: + // in register dst. There are three modes for this function: // 1) Address dst must be less than src, or the gap between them must be // greater than or equal to count double words, otherwise the result is // unpredictable. This is the default mode. @@ -711,10 +711,15 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { // greater than or equal to count double words, otherwise the result is // undpredictable. In this mode, src and dst specify the last (highest) // address of the regions to copy from and to. + // 3) The same as mode 1, but the words are copied in the reversed order. // The case where src == dst is not supported. // The function may corrupt its register arguments. The registers must not // alias each other. - enum CopyDoubleWordsMode { kDstLessThanSrc, kSrcLessThanDst }; + enum CopyDoubleWordsMode { + kDstLessThanSrc, + kSrcLessThanDst, + kDstLessThanSrcAndReverse + }; void CopyDoubleWords(Register dst, Register src, Register count, CopyDoubleWordsMode mode = kDstLessThanSrc); @@ -1762,6 +1767,10 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler { DecodeField(reg, reg); } + // TODO(victorgomes): inline this function once we remove V8_REVERSE_JSARGS + // flag. + Operand ReceiverOperand(const Register arg_count); + // ---- SMI and Number Utilities ---- inline void SmiTag(Register dst, Register src); diff --git a/deps/v8/src/codegen/arm64/register-arm64.h b/deps/v8/src/codegen/arm64/register-arm64.h index 9571aa5ab57155..c98b0f6162f9a2 100644 --- a/deps/v8/src/codegen/arm64/register-arm64.h +++ b/deps/v8/src/codegen/arm64/register-arm64.h @@ -470,8 +470,9 @@ ALIAS_REGISTER(Register, padreg, x31); // Keeps the 0 double value. ALIAS_REGISTER(VRegister, fp_zero, d15); // MacroAssembler fixed V Registers. -ALIAS_REGISTER(VRegister, fp_fixed1, d28); -ALIAS_REGISTER(VRegister, fp_fixed2, d29); +// d29 is not part of ALLOCATABLE_DOUBLE_REGISTERS, so use 27 and 28. +ALIAS_REGISTER(VRegister, fp_fixed1, d27); +ALIAS_REGISTER(VRegister, fp_fixed2, d28); // MacroAssembler scratch V registers. ALIAS_REGISTER(VRegister, fp_scratch, d30); @@ -522,8 +523,6 @@ using Simd128Register = VRegister; // Lists of registers. class V8_EXPORT_PRIVATE CPURegList { public: - CPURegList() = default; - template explicit CPURegList(CPURegister reg0, CPURegisters... regs) : list_(CPURegister::ListOf(reg0, regs...)), @@ -696,6 +695,8 @@ constexpr Register kRuntimeCallArgvRegister = x11; constexpr Register kWasmInstanceRegister = x7; constexpr Register kWasmCompileLazyFuncIndexRegister = x8; +constexpr DoubleRegister kFPReturnRegister0 = d0; + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/codegen/assembler.cc b/deps/v8/src/codegen/assembler.cc index 4bda1260a90e1d..3b27bf5db9eb52 100644 --- a/deps/v8/src/codegen/assembler.cc +++ b/deps/v8/src/codegen/assembler.cc @@ -41,7 +41,6 @@ #include "src/execution/isolate.h" #include "src/heap/heap-inl.h" // For MemoryAllocator. TODO(jkummerow): Drop. #include "src/snapshot/embedded/embedded-data.h" -#include "src/snapshot/serializer-common.h" #include "src/snapshot/snapshot.h" #include "src/utils/ostreams.h" #include "src/utils/vector.h" diff --git a/deps/v8/src/codegen/code-stub-assembler.cc b/deps/v8/src/codegen/code-stub-assembler.cc index 0464faea3b7da9..901ce0c7b49410 100644 --- a/deps/v8/src/codegen/code-stub-assembler.cc +++ b/deps/v8/src/codegen/code-stub-assembler.cc @@ -12,13 +12,15 @@ #include "src/execution/frames-inl.h" #include "src/execution/frames.h" #include "src/execution/protectors.h" -#include "src/heap/heap-inl.h" // For Page/MemoryChunk. TODO(jkummerow): Drop. +#include "src/heap/heap-inl.h" // For MemoryChunk. TODO(jkummerow): Drop. +#include "src/heap/memory-chunk.h" #include "src/logging/counters.h" #include "src/objects/api-callbacks.h" #include "src/objects/cell.h" #include "src/objects/descriptor-array.h" #include "src/objects/function-kind.h" #include "src/objects/heap-number.h" +#include "src/objects/js-aggregate-error.h" #include "src/objects/js-generator.h" #include "src/objects/oddball.h" #include "src/objects/ordered-hash-table-inl.h" @@ -144,164 +146,6 @@ TNode CodeStubAssembler::IntPtrToParameter( return value; } -void CodeStubAssembler::CollectCallableFeedback( - TNode maybe_target, TNode context, - TNode feedback_vector, TNode slot_id, - CallableFeedbackMode mode) { - Label extra_checks(this, Label::kDeferred), done(this); - - // Check if we have monomorphic {target} feedback already. - TNode feedback = - LoadFeedbackVectorSlot(feedback_vector, slot_id); - Comment("check if monomorphic"); - TNode is_monomorphic = IsWeakReferenceToObject(feedback, maybe_target); - GotoIf(is_monomorphic, &done); - - // Check if it is a megamorphic {target}. - Comment("check if megamorphic"); - TNode is_megamorphic = TaggedEqual( - feedback, HeapConstant(FeedbackVector::MegamorphicSentinel(isolate()))); - Branch(is_megamorphic, &done, &extra_checks); - - BIND(&extra_checks); - { - Label initialize(this), mark_megamorphic(this); - - Comment("check if weak reference"); - TNode is_uninitialized = TaggedEqual( - feedback, - HeapConstant(FeedbackVector::UninitializedSentinel(isolate()))); - GotoIf(is_uninitialized, &initialize); - CSA_ASSERT(this, IsWeakOrCleared(feedback)); - - // If the weak reference is cleared, we have a new chance to become - // monomorphic. - Comment("check if weak reference is cleared"); - GotoIf(IsCleared(feedback), &initialize); - GotoIf(TaggedIsSmi(maybe_target), &mark_megamorphic); - - if (mode == CallableFeedbackMode::kDontCollectFeedbackCell) { - Goto(&mark_megamorphic); - } else { - Label try_transition_to_feedback_cell(this); - - // Check if {target} is a JSFunction. - Comment("check if target is a JSFunction"); - TNode target = CAST(maybe_target); - GotoIfNot(IsJSFunction(target), &mark_megamorphic); - - // Check if {target}s feedback vector cell matches the {feedback_value}. - TNode feedback_value = GetHeapObjectAssumeWeak(feedback); - TNode target_feedback_cell = - LoadObjectField(target, JSFunction::kFeedbackCellOffset); - Branch(TaggedEqual(feedback_value, target_feedback_cell), &done, - &try_transition_to_feedback_cell); - - BIND(&try_transition_to_feedback_cell); - { - // Check if {target} and {feedback_value} are both JSFunctions with - // the same feedback vector cell, and that those functions were - // actually compiled already. - GotoIfNot(IsJSFunction(feedback_value), &mark_megamorphic); - TNode feedback_cell = CAST( - LoadObjectField(feedback_value, JSFunction::kFeedbackCellOffset)); - GotoIfNot(TaggedEqual(feedback_cell, target_feedback_cell), - &mark_megamorphic); - GotoIfNot(IsFeedbackCell(feedback_cell), &mark_megamorphic); - - // Record the feedback vector cell. - Comment("transition to polymorphic"); - StoreWeakReferenceInFeedbackVector(feedback_vector, slot_id, - feedback_cell); - ReportFeedbackUpdate(feedback_vector, slot_id, - "Call:FeedbackVectorCell"); - Goto(&done); - } - } - - BIND(&initialize); - { - Comment("check if function in same native context"); - GotoIf(TaggedIsSmi(maybe_target), &mark_megamorphic); - TNode target = CAST(maybe_target); - // Check if the {target} is a JSFunction or JSBoundFunction - // in the current native context. - TVARIABLE(HeapObject, var_current, target); - Label loop(this, &var_current), done_loop(this); - Goto(&loop); - BIND(&loop); - { - Label if_boundfunction(this), if_function(this); - TNode current = var_current.value(); - TNode current_instance_type = LoadInstanceType(current); - GotoIf(InstanceTypeEqual(current_instance_type, JS_BOUND_FUNCTION_TYPE), - &if_boundfunction); - Branch(InstanceTypeEqual(current_instance_type, JS_FUNCTION_TYPE), - &if_function, &mark_megamorphic); - - BIND(&if_function); - { - // Check that the JSFunction {current} is in the current native - // context. - TNode current_context = - CAST(LoadObjectField(current, JSFunction::kContextOffset)); - TNode current_native_context = - LoadNativeContext(current_context); - Branch( - TaggedEqual(LoadNativeContext(context), current_native_context), - &done_loop, &mark_megamorphic); - } - BIND(&if_boundfunction); - { - // Continue with the [[BoundTargetFunction]] of {target}. - var_current = LoadObjectField( - current, JSBoundFunction::kBoundTargetFunctionOffset); - Goto(&loop); - } - } - BIND(&done_loop); - StoreWeakReferenceInFeedbackVector(feedback_vector, slot_id, target); - ReportFeedbackUpdate(feedback_vector, slot_id, "Call:Initialize"); - Goto(&done); - } - - BIND(&mark_megamorphic); - { - // MegamorphicSentinel is an immortal immovable object so - // write-barrier is not needed. - Comment("transition to megamorphic"); - DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kmegamorphic_symbol)); - StoreFeedbackVectorSlot( - feedback_vector, slot_id, - HeapConstant(FeedbackVector::MegamorphicSentinel(isolate())), - SKIP_WRITE_BARRIER); - ReportFeedbackUpdate(feedback_vector, slot_id, - "Call:TransitionMegamorphic"); - Goto(&done); - } - } - - BIND(&done); -} - -void CodeStubAssembler::CollectCallFeedback( - TNode maybe_target, TNode context, - TNode maybe_feedback_vector, TNode slot_id) { - Label feedback_done(this); - // If feedback_vector is not valid, then nothing to do. - GotoIf(IsUndefined(maybe_feedback_vector), &feedback_done); - - // Increment the call count. - TNode feedback_vector = CAST(maybe_feedback_vector); - IncrementCallCount(feedback_vector, slot_id); - - // Collect the callable {target} feedback. - CollectCallableFeedback(maybe_target, context, feedback_vector, slot_id, - CallableFeedbackMode::kCollectFeedbackCell); - Goto(&feedback_done); - - BIND(&feedback_done); -} void CodeStubAssembler::IncrementCallCount( TNode feedback_vector, TNode slot_id) { @@ -373,7 +217,7 @@ TNode CodeStubAssembler::SelectSmiConstant(SloppyTNode condition, SmiConstant(false_value)); } -TNode CodeStubAssembler::NoContextConstant() { +TNode CodeStubAssembler::NoContextConstant() { return SmiConstant(Context::kNoContext); } @@ -762,8 +606,8 @@ TNode CodeStubAssembler::IsValidSmiIndex(TNode smi) { TNode CodeStubAssembler::TaggedIndexToIntPtr( TNode value) { - return Signed(WordSar(BitcastTaggedToWordForTagAndSmiBits(value), - IntPtrConstant(kSmiTagSize))); + return Signed(WordSarShiftOutZeros(BitcastTaggedToWordForTagAndSmiBits(value), + IntPtrConstant(kSmiTagSize))); } TNode CodeStubAssembler::IntPtrToTaggedIndex( @@ -858,16 +702,17 @@ TNode CodeStubAssembler::SmiUntag(SloppyTNode value) { if (ToIntPtrConstant(value, &constant_value)) { return IntPtrConstant(constant_value >> (kSmiShiftSize + kSmiTagSize)); } + TNode raw_bits = BitcastTaggedToWordForTagAndSmiBits(value); if (COMPRESS_POINTERS_BOOL) { - return ChangeInt32ToIntPtr(SmiToInt32(value)); + // Clear the upper half using sign-extension. + raw_bits = ChangeInt32ToIntPtr(TruncateIntPtrToInt32(raw_bits)); } - return Signed(WordSar(BitcastTaggedToWordForTagAndSmiBits(value), - SmiShiftBitsConstant())); + return Signed(WordSarShiftOutZeros(raw_bits, SmiShiftBitsConstant())); } TNode CodeStubAssembler::SmiToInt32(SloppyTNode value) { if (COMPRESS_POINTERS_BOOL) { - return Signed(Word32Sar( + return Signed(Word32SarShiftOutZeros( TruncateIntPtrToInt32(BitcastTaggedToWordForTagAndSmiBits(value)), SmiShiftBitsConstant32())); } @@ -2226,9 +2071,10 @@ TNode CodeStubAssembler::LoadPropertyArrayLength( TNode CodeStubAssembler::LoadJSTypedArrayDataPtr( TNode typed_array) { - // Data pointer = external_pointer + static_cast(base_pointer). - TNode external_pointer = LoadObjectField( - typed_array, JSTypedArray::kExternalPointerOffset); + // Data pointer = DecodeExternalPointer(external_pointer) + + // static_cast(base_pointer). + TNode external_pointer = + DecodeExternalPointer(LoadJSTypedArrayExternalPointer(typed_array)); TNode base_pointer; if (COMPRESS_POINTERS_BOOL) { @@ -3687,6 +3533,17 @@ void CodeStubAssembler::StoreFieldsNoWriteBarrier(TNode start_address, kTaggedSize, IndexAdvanceMode::kPost); } +void CodeStubAssembler::MakeFixedArrayCOW(TNode array) { + CSA_ASSERT(this, IsFixedArrayMap(LoadMap(array))); + Label done(this); + // The empty fixed array is not modifiable anyway. And we shouldn't change its + // Map. + GotoIf(TaggedEqual(array, EmptyFixedArrayConstant()), &done); + StoreMap(array, FixedCOWArrayMapConstant()); + Goto(&done); + BIND(&done); +} + TNode CodeStubAssembler::IsValidFastJSArrayCapacity( TNode capacity) { return UintPtrLessThanOrEqual(capacity, @@ -4900,6 +4757,12 @@ void CodeStubAssembler::CopyFixedArrayElements( Comment("] CopyFixedArrayElements"); } +TNode CodeStubAssembler::HeapObjectToJSAggregateError( + TNode heap_object, Label* fail) { + GotoIfNot(IsJSAggregateError(heap_object), fail); + return UncheckedCast(heap_object); +} + TNode CodeStubAssembler::HeapObjectToFixedArray( TNode base, Label* cast_fail) { Label fixed_array(this); @@ -5235,6 +5098,22 @@ void CodeStubAssembler::TaggedToWord32OrBigIntImpl( } } +TNode CodeStubAssembler::TruncateNumberToWord32(TNode number) { + TVARIABLE(Int32T, var_result); + Label done(this), if_heapnumber(this); + GotoIfNot(TaggedIsSmi(number), &if_heapnumber); + var_result = SmiToInt32(CAST(number)); + Goto(&done); + + BIND(&if_heapnumber); + TNode value = LoadHeapNumberValue(CAST(number)); + var_result = Signed(TruncateFloat64ToWord32(value)); + Goto(&done); + + BIND(&done); + return var_result.value(); +} + TNode CodeStubAssembler::TruncateHeapNumberValueToWord32( TNode object) { TNode value = LoadHeapNumberValue(object); @@ -5248,6 +5127,38 @@ void CodeStubAssembler::TryHeapNumberToSmi(TNode number, TryFloat64ToSmi(value, var_result_smi, if_smi); } +void CodeStubAssembler::TryFloat32ToSmi(TNode value, + TVariable* var_result_smi, + Label* if_smi) { + TNode ivalue = TruncateFloat32ToInt32(value); + TNode fvalue = RoundInt32ToFloat32(ivalue); + + Label if_int32(this), if_heap_number(this); + + GotoIfNot(Float32Equal(value, fvalue), &if_heap_number); + GotoIfNot(Word32Equal(ivalue, Int32Constant(0)), &if_int32); + Branch(Int32LessThan(UncheckedCast(BitcastFloat32ToInt32(value)), + Int32Constant(0)), + &if_heap_number, &if_int32); + + TVARIABLE(Number, var_result); + BIND(&if_int32); + { + if (SmiValuesAre32Bits()) { + *var_result_smi = SmiTag(ChangeInt32ToIntPtr(ivalue)); + } else { + DCHECK(SmiValuesAre31Bits()); + TNode> pair = Int32AddWithOverflow(ivalue, ivalue); + TNode overflow = Projection<1>(pair); + GotoIf(overflow, &if_heap_number); + *var_result_smi = + BitcastWordToTaggedSigned(ChangeInt32ToIntPtr(Projection<0>(pair))); + } + Goto(if_smi); + } + BIND(&if_heap_number); +} + void CodeStubAssembler::TryFloat64ToSmi(TNode value, TVariable* var_result_smi, Label* if_smi) { @@ -5280,6 +5191,24 @@ void CodeStubAssembler::TryFloat64ToSmi(TNode value, BIND(&if_heap_number); } +TNode CodeStubAssembler::ChangeFloat32ToTagged(TNode value) { + Label if_smi(this), done(this); + TVARIABLE(Smi, var_smi_result); + TVARIABLE(Number, var_result); + TryFloat32ToSmi(value, &var_smi_result, &if_smi); + + var_result = AllocateHeapNumberWithValue(ChangeFloat32ToFloat64(value)); + Goto(&done); + + BIND(&if_smi); + { + var_result = var_smi_result.value(); + Goto(&done); + } + BIND(&done); + return var_result.value(); +} + TNode CodeStubAssembler::ChangeFloat64ToTagged( SloppyTNode value) { Label if_smi(this), done(this); @@ -5464,6 +5393,42 @@ TNode CodeStubAssembler::ChangeNumberToFloat64(TNode value) { return result.value(); } +TNode CodeStubAssembler::ChangeTaggedNonSmiToInt32( + TNode context, TNode input) { + return Select( + IsHeapNumber(input), + [=] { + return Signed(TruncateFloat64ToWord32(LoadHeapNumberValue(input))); + }, + [=] { + return TruncateNumberToWord32( + CAST(CallBuiltin(Builtins::kNonNumberToNumber, context, input))); + }); +} + +TNode CodeStubAssembler::ChangeTaggedToFloat64(TNode context, + TNode input) { + TVARIABLE(Float64T, var_result); + Label end(this), not_smi(this); + + GotoIfNot(TaggedIsSmi(input), ¬_smi); + var_result = SmiToFloat64(CAST(input)); + Goto(&end); + + BIND(¬_smi); + var_result = Select( + IsHeapNumber(CAST(input)), + [=] { return LoadHeapNumberValue(CAST(input)); }, + [=] { + return ChangeNumberToFloat64( + CAST(CallBuiltin(Builtins::kNonNumberToNumber, context, input))); + }); + Goto(&end); + + BIND(&end); + return var_result.value(); +} + TNode CodeStubAssembler::TimesSystemPointerSize( SloppyTNode value) { return WordShl(value, kSystemPointerSizeLog2); @@ -5759,12 +5724,10 @@ TNode CodeStubAssembler::IsTypedArraySpeciesProtectorCellInvalid() { return TaggedEqual(cell_value, invalid); } -TNode CodeStubAssembler::IsRegExpSpeciesProtectorCellInvalid( - TNode native_context) { - TNode cell = CAST(LoadContextElement( - native_context, Context::REGEXP_SPECIES_PROTECTOR_INDEX)); - TNode cell_value = LoadObjectField(cell, PropertyCell::kValueOffset); +TNode CodeStubAssembler::IsRegExpSpeciesProtectorCellInvalid() { TNode invalid = SmiConstant(Protectors::kProtectorInvalid); + TNode cell = RegExpSpeciesProtectorConstant(); + TNode cell_value = LoadObjectField(cell, PropertyCell::kValueOffset); return TaggedEqual(cell_value, invalid); } @@ -5988,6 +5951,16 @@ TNode CodeStubAssembler::IsJSObject(SloppyTNode object) { return IsJSObjectMap(LoadMap(object)); } +TNode CodeStubAssembler::IsJSFinalizationRegistryMap(TNode map) { + return InstanceTypeEqual(LoadMapInstanceType(map), + JS_FINALIZATION_REGISTRY_TYPE); +} + +TNode CodeStubAssembler::IsJSFinalizationRegistry( + TNode object) { + return IsJSFinalizationRegistryMap(LoadMap(object)); +} + TNode CodeStubAssembler::IsJSPromiseMap(SloppyTNode map) { CSA_ASSERT(this, IsMap(map)); return InstanceTypeEqual(LoadMapInstanceType(map), JS_PROMISE_TYPE); @@ -6029,6 +6002,10 @@ TNode CodeStubAssembler::IsJSPrimitiveWrapperMap(SloppyTNode map) { return IsJSPrimitiveWrapperInstanceType(LoadMapInstanceType(map)); } +TNode CodeStubAssembler::IsJSAggregateError(TNode object) { + return HasInstanceType(object, JS_AGGREGATE_ERROR_TYPE); +} + TNode CodeStubAssembler::IsJSArrayInstanceType( SloppyTNode instance_type) { return InstanceTypeEqual(instance_type, JS_ARRAY_TYPE); @@ -6746,12 +6723,13 @@ TNode ToDirectStringAssembler::TryToSequential( { STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize); - TNode result = BitcastTaggedToWord(var_string_.value()); + TNode result = + ReinterpretCast(BitcastTaggedToWord(var_string_.value())); if (ptr_kind == PTR_TO_DATA) { - result = IntPtrAdd(result, IntPtrConstant(SeqOneByteString::kHeaderSize - + result = RawPtrAdd(result, IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); } - var_result = ReinterpretCast(result); + var_result = result; Goto(&out); } @@ -6761,13 +6739,13 @@ TNode ToDirectStringAssembler::TryToSequential( if_bailout); TNode string = var_string_.value(); - TNode result = - LoadObjectField(string, ExternalString::kResourceDataOffset); + TNode result = + DecodeExternalPointer(LoadExternalStringResourceData(CAST(string))); if (ptr_kind == PTR_TO_STRING) { - result = IntPtrSub(result, IntPtrConstant(SeqOneByteString::kHeaderSize - + result = RawPtrSub(result, IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); } - var_result = ReinterpretCast(result); + var_result = result; Goto(&out); } @@ -6889,8 +6867,8 @@ TNode CodeStubAssembler::NumberToString(TNode input) { BIND(&runtime); { // No cache entry, go to the runtime. - result = - CAST(CallRuntime(Runtime::kNumberToString, NoContextConstant(), input)); + result = CAST( + CallRuntime(Runtime::kNumberToStringSlow, NoContextConstant(), input)); Goto(&done); } BIND(&done); @@ -7327,7 +7305,7 @@ TNode CodeStubAssembler::DecodeWord32(SloppyTNode word32, } TNode CodeStubAssembler::DecodeWord(SloppyTNode word, - uint32_t shift, uint32_t mask) { + uint32_t shift, uintptr_t mask) { DCHECK_EQ((mask >> shift) << shift, mask); return Unsigned(WordAnd(WordShr(word, static_cast(shift)), IntPtrConstant(mask >> shift))); @@ -7335,25 +7313,39 @@ TNode CodeStubAssembler::DecodeWord(SloppyTNode word, TNode CodeStubAssembler::UpdateWord32(TNode word, TNode value, - uint32_t shift, uint32_t mask) { + uint32_t shift, uint32_t mask, + bool starts_as_zero) { DCHECK_EQ((mask >> shift) << shift, mask); // Ensure the {value} fits fully in the mask. CSA_ASSERT(this, Uint32LessThanOrEqual(value, Uint32Constant(mask >> shift))); TNode encoded_value = Word32Shl(value, Int32Constant(shift)); - TNode inverted_mask = Int32Constant(~mask); - return Word32Or(Word32And(word, inverted_mask), encoded_value); + TNode masked_word; + if (starts_as_zero) { + CSA_ASSERT(this, Word32Equal(Word32And(word, Int32Constant(~mask)), word)); + masked_word = word; + } else { + masked_word = Word32And(word, Int32Constant(~mask)); + } + return Word32Or(masked_word, encoded_value); } TNode CodeStubAssembler::UpdateWord(TNode word, TNode value, - uint32_t shift, uint32_t mask) { + uint32_t shift, uintptr_t mask, + bool starts_as_zero) { DCHECK_EQ((mask >> shift) << shift, mask); // Ensure the {value} fits fully in the mask. CSA_ASSERT(this, UintPtrLessThanOrEqual(value, UintPtrConstant(mask >> shift))); TNode encoded_value = WordShl(value, static_cast(shift)); - TNode inverted_mask = IntPtrConstant(~static_cast(mask)); - return WordOr(WordAnd(word, inverted_mask), encoded_value); + TNode masked_word; + if (starts_as_zero) { + CSA_ASSERT(this, WordEqual(WordAnd(word, UintPtrConstant(~mask)), word)); + masked_word = word; + } else { + masked_word = WordAnd(word, UintPtrConstant(~mask)); + } + return WordOr(masked_word, encoded_value); } void CodeStubAssembler::SetCounter(StatsCounter* counter, int value) { @@ -12487,15 +12479,9 @@ void CodeStubAssembler::ThrowIfArrayBufferViewBufferIsDetached( ThrowIfArrayBufferIsDetached(context, buffer, method_name); } -TNode CodeStubAssembler::LoadJSArrayBufferBitField( +TNode CodeStubAssembler::LoadJSArrayBufferBackingStorePtr( TNode array_buffer) { - return LoadObjectField(array_buffer, JSArrayBuffer::kBitFieldOffset); -} - -TNode CodeStubAssembler::LoadJSArrayBufferBackingStore( - TNode array_buffer) { - return LoadObjectField(array_buffer, - JSArrayBuffer::kBackingStoreOffset); + return DecodeExternalPointer(LoadJSArrayBufferBackingStore(array_buffer)); } TNode CodeStubAssembler::LoadJSArrayBufferViewBuffer( @@ -12672,6 +12658,18 @@ TNode CodeStubAssembler::IsFastElementsKind( Int32Constant(LAST_FAST_ELEMENTS_KIND)); } +TNode CodeStubAssembler::IsFastOrNonExtensibleOrSealedElementsKind( + TNode elements_kind) { + STATIC_ASSERT(FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND); + STATIC_ASSERT(LAST_FAST_ELEMENTS_KIND + 1 == PACKED_NONEXTENSIBLE_ELEMENTS); + STATIC_ASSERT(PACKED_NONEXTENSIBLE_ELEMENTS + 1 == + HOLEY_NONEXTENSIBLE_ELEMENTS); + STATIC_ASSERT(HOLEY_NONEXTENSIBLE_ELEMENTS + 1 == PACKED_SEALED_ELEMENTS); + STATIC_ASSERT(PACKED_SEALED_ELEMENTS + 1 == HOLEY_SEALED_ELEMENTS); + return Uint32LessThanOrEqual(elements_kind, + Int32Constant(HOLEY_SEALED_ELEMENTS)); +} + TNode CodeStubAssembler::IsDoubleElementsKind( TNode elements_kind) { STATIC_ASSERT(FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND); @@ -13240,6 +13238,21 @@ TNode CodeStubAssembler::TaggedToDirectString(TNode value, return CAST(value); } +void CodeStubAssembler::RemoveFinalizationRegistryCellFromUnregisterTokenMap( + TNode finalization_registry, + TNode weak_cell) { + const TNode remove_cell = ExternalConstant( + ExternalReference:: + js_finalization_registry_remove_cell_from_unregister_token_map()); + const TNode isolate_ptr = + ExternalConstant(ExternalReference::isolate_address(isolate())); + + CallCFunction(remove_cell, MachineType::Pointer(), + std::make_pair(MachineType::Pointer(), isolate_ptr), + std::make_pair(MachineType::AnyTagged(), finalization_registry), + std::make_pair(MachineType::AnyTagged(), weak_cell)); +} + PrototypeCheckAssembler::PrototypeCheckAssembler( compiler::CodeAssemblerState* state, Flags flags, TNode native_context, TNode initial_prototype_map, diff --git a/deps/v8/src/codegen/code-stub-assembler.h b/deps/v8/src/codegen/code-stub-assembler.h index 618481ff47a4eb..b01729c73db8d4 100644 --- a/deps/v8/src/codegen/code-stub-assembler.h +++ b/deps/v8/src/codegen/code-stub-assembler.h @@ -9,6 +9,7 @@ #include "src/base/macros.h" #include "src/codegen/bailout-reason.h" +#include "src/common/external-pointer.h" #include "src/common/globals.h" #include "src/common/message-template.h" #include "src/compiler/code-assembler.h" @@ -20,7 +21,6 @@ #include "src/objects/smi.h" #include "src/objects/tagged-index.h" #include "src/roots/roots.h" - #include "torque-generated/exported-macros-assembler-tq.h" namespace v8 { @@ -34,28 +34,79 @@ class StubCache; enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol }; -#define HEAP_MUTABLE_IMMOVABLE_OBJECT_LIST(V) \ - V(ArrayIteratorProtector, array_iterator_protector, ArrayIteratorProtector) \ - V(ArraySpeciesProtector, array_species_protector, ArraySpeciesProtector) \ - V(MapIteratorProtector, map_iterator_protector, MapIteratorProtector) \ - V(NoElementsProtector, no_elements_protector, NoElementsProtector) \ - V(NumberStringCache, number_string_cache, NumberStringCache) \ - V(PromiseResolveProtector, promise_resolve_protector, \ - PromiseResolveProtector) \ - V(PromiseSpeciesProtector, promise_species_protector, \ - PromiseSpeciesProtector) \ - V(PromiseThenProtector, promise_then_protector, PromiseThenProtector) \ - V(SetIteratorProtector, set_iterator_protector, SetIteratorProtector) \ - V(SingleCharacterStringCache, single_character_string_cache, \ - SingleCharacterStringCache) \ - V(StringIteratorProtector, string_iterator_protector, \ - StringIteratorProtector) \ - V(TypedArraySpeciesProtector, typed_array_species_protector, \ +#define HEAP_MUTABLE_IMMOVABLE_OBJECT_LIST(V) \ + V(ArrayIteratorProtector, array_iterator_protector, ArrayIteratorProtector) \ + V(ArraySpeciesProtector, array_species_protector, ArraySpeciesProtector) \ + V(AsyncFunctionAwaitRejectSharedFun, async_function_await_reject_shared_fun, \ + AsyncFunctionAwaitRejectSharedFun) \ + V(AsyncFunctionAwaitResolveSharedFun, \ + async_function_await_resolve_shared_fun, \ + AsyncFunctionAwaitResolveSharedFun) \ + V(AsyncGeneratorAwaitRejectSharedFun, \ + async_generator_await_reject_shared_fun, \ + AsyncGeneratorAwaitRejectSharedFun) \ + V(AsyncGeneratorAwaitResolveSharedFun, \ + async_generator_await_resolve_shared_fun, \ + AsyncGeneratorAwaitResolveSharedFun) \ + V(AsyncGeneratorReturnClosedRejectSharedFun, \ + async_generator_return_closed_reject_shared_fun, \ + AsyncGeneratorReturnClosedRejectSharedFun) \ + V(AsyncGeneratorReturnClosedResolveSharedFun, \ + async_generator_return_closed_resolve_shared_fun, \ + AsyncGeneratorReturnClosedResolveSharedFun) \ + V(AsyncGeneratorReturnResolveSharedFun, \ + async_generator_return_resolve_shared_fun, \ + AsyncGeneratorReturnResolveSharedFun) \ + V(AsyncGeneratorYieldResolveSharedFun, \ + async_generator_yield_resolve_shared_fun, \ + AsyncGeneratorYieldResolveSharedFun) \ + V(AsyncIteratorValueUnwrapSharedFun, async_iterator_value_unwrap_shared_fun, \ + AsyncIteratorValueUnwrapSharedFun) \ + V(MapIteratorProtector, map_iterator_protector, MapIteratorProtector) \ + V(NoElementsProtector, no_elements_protector, NoElementsProtector) \ + V(NumberStringCache, number_string_cache, NumberStringCache) \ + V(PromiseAllResolveElementSharedFun, promise_all_resolve_element_shared_fun, \ + PromiseAllResolveElementSharedFun) \ + V(PromiseAllSettledRejectElementSharedFun, \ + promise_all_settled_reject_element_shared_fun, \ + PromiseAllSettledRejectElementSharedFun) \ + V(PromiseAllSettledResolveElementSharedFun, \ + promise_all_settled_resolve_element_shared_fun, \ + PromiseAllSettledResolveElementSharedFun) \ + V(PromiseAnyRejectElementSharedFun, promise_any_reject_element_shared_fun, \ + PromiseAnyRejectElementSharedFun) \ + V(PromiseCapabilityDefaultRejectSharedFun, \ + promise_capability_default_reject_shared_fun, \ + PromiseCapabilityDefaultRejectSharedFun) \ + V(PromiseCapabilityDefaultResolveSharedFun, \ + promise_capability_default_resolve_shared_fun, \ + PromiseCapabilityDefaultResolveSharedFun) \ + V(PromiseCatchFinallySharedFun, promise_catch_finally_shared_fun, \ + PromiseCatchFinallySharedFun) \ + V(PromiseGetCapabilitiesExecutorSharedFun, \ + promise_get_capabilities_executor_shared_fun, \ + PromiseGetCapabilitiesExecutorSharedFun) \ + V(PromiseResolveProtector, promise_resolve_protector, \ + PromiseResolveProtector) \ + V(PromiseSpeciesProtector, promise_species_protector, \ + PromiseSpeciesProtector) \ + V(PromiseThenFinallySharedFun, promise_then_finally_shared_fun, \ + PromiseThenFinallySharedFun) \ + V(PromiseThenProtector, promise_then_protector, PromiseThenProtector) \ + V(PromiseThrowerFinallySharedFun, promise_thrower_finally_shared_fun, \ + PromiseThrowerFinallySharedFun) \ + V(PromiseValueThunkFinallySharedFun, promise_value_thunk_finally_shared_fun, \ + PromiseValueThunkFinallySharedFun) \ + V(ProxyRevokeSharedFun, proxy_revoke_shared_fun, ProxyRevokeSharedFun) \ + V(RegExpSpeciesProtector, regexp_species_protector, RegExpSpeciesProtector) \ + V(SetIteratorProtector, set_iterator_protector, SetIteratorProtector) \ + V(SingleCharacterStringCache, single_character_string_cache, \ + SingleCharacterStringCache) \ + V(StringIteratorProtector, string_iterator_protector, \ + StringIteratorProtector) \ + V(TypedArraySpeciesProtector, typed_array_species_protector, \ TypedArraySpeciesProtector) -#define TORQUE_INTERNAL_CLASS_LIST_CSA_ADAPTER(V, NAME, Name, name) \ - V(Name##Map, name##_map, Name##Map) - #define HEAP_IMMUTABLE_IMMOVABLE_OBJECT_LIST(V) \ V(AccessorInfoMap, accessor_info_map, AccessorInfoMap) \ V(AccessorPairMap, accessor_pair_map, AccessorPairMap) \ @@ -106,6 +157,7 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol }; V(ManyClosuresCellMap, many_closures_cell_map, ManyClosuresCellMap) \ V(match_symbol, match_symbol, MatchSymbol) \ V(megamorphic_symbol, megamorphic_symbol, MegamorphicSymbol) \ + V(message_string, message_string, MessageString) \ V(MetaMap, meta_map, MetaMap) \ V(minus_Infinity_string, minus_Infinity_string, MinusInfinityString) \ V(MinusZeroValue, minus_zero_value, MinusZero) \ @@ -178,8 +230,7 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol }; V(uninitialized_symbol, uninitialized_symbol, UninitializedSymbol) \ V(WeakFixedArrayMap, weak_fixed_array_map, WeakFixedArrayMap) \ V(zero_string, zero_string, ZeroString) \ - TORQUE_INTERNAL_CLASS_LIST_GENERATOR(TORQUE_INTERNAL_CLASS_LIST_CSA_ADAPTER, \ - V) + TORQUE_INTERNAL_MAP_CSA_LIST(V) #define HEAP_IMMOVABLE_OBJECT_LIST(V) \ HEAP_MUTABLE_IMMOVABLE_OBJECT_LIST(V) \ @@ -430,6 +481,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler return UncheckedCast(value); } + TNode HeapObjectToJSAggregateError( + TNode heap_object, Label* fail); + TNode HeapObjectToJSArray(TNode heap_object, Label* fail) { GotoIfNot(IsJSArray(heap_object), fail); @@ -576,7 +630,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler return Word32BinaryNot(TaggedEqual(a, b)); } - TNode NoContextConstant(); + TNode NoContextConstant(); #define HEAP_CONSTANT_ACCESSOR(rootIndexName, rootAccessorName, name) \ TNode DecodeExternalPointer( + TNode encoded_pointer) { + STATIC_ASSERT(kExternalPointerSize == kSystemPointerSize); + TNode value = ReinterpretCast(encoded_pointer); + if (V8_HEAP_SANDBOX_BOOL) { + value = UncheckedCast( + WordXor(value, UintPtrConstant(kExternalPointerSalt))); + } + return value; + } + + // Convert external pointer value to on-V8-heap representation. + // This should eventually become a call to a non-allocating runtime function. + TNode EncodeExternalPointer(TNode pointer) { + STATIC_ASSERT(kExternalPointerSize == kSystemPointerSize); + TNode encoded_pointer = pointer; + if (V8_HEAP_SANDBOX_BOOL) { + encoded_pointer = UncheckedCast( + WordXor(encoded_pointer, UintPtrConstant(kExternalPointerSalt))); + } + return ReinterpretCast(encoded_pointer); + } + // Load value from current parent frame by given offset in bytes. TNode LoadFromParentFrame(int offset); @@ -1781,6 +1860,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler TNode end_address, TNode value); + // Marks the FixedArray copy-on-write without moving it. + void MakeFixedArrayCOW(TNode array); + TNode AllocateCellWithValue( TNode value, WriteBarrierMode mode = UPDATE_WRITE_BARRIER); TNode AllocateSmiCell(int value = 0) { @@ -2047,35 +2129,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler enum class DestroySource { kNo, kYes }; - // Collect the callable |maybe_target| feedback for either a CALL_IC or - // an INSTANCEOF_IC in the |feedback_vector| at |slot_id|. There are - // two modes for feedback collection: - // - // kCollectFeedbackCell - collect JSFunctions, but devolve to the - // FeedbackCell as long as all JSFunctions - // seen share the same one. - // kDontCollectFeedbackCell - collect JSFunctions without devolving - // to the FeedbackCell in case a - // different JSFunction appears. Go directly - // to the Megamorphic sentinel value in this - // case. - enum class CallableFeedbackMode { - kCollectFeedbackCell, - kDontCollectFeedbackCell - }; - void CollectCallableFeedback(TNode maybe_target, - TNode context, - TNode feedback_vector, - TNode slot_id, - CallableFeedbackMode mode); - - // Collect CALL_IC feedback for |maybe_target| function in the - // |feedback_vector| at |slot_id|, and the call counts in - // the |feedback_vector| at |slot_id+1|. - void CollectCallFeedback(TNode maybe_target, TNode context, - TNode maybe_feedback_vector, - TNode slot_id); - // Increment the call count for a CALL_IC or construct call. // The call count is located at feedback_vector[slot_id + 1]. void IncrementCallCount(TNode feedback_vector, @@ -2415,14 +2468,18 @@ class V8_EXPORT_PRIVATE CodeStubAssembler TVariable* var_maybe_bigint, TVariable* var_feedback); + TNode TruncateNumberToWord32(TNode value); // Truncate the floating point value of a HeapNumber to an Int32. TNode TruncateHeapNumberValueToWord32(TNode object); // Conversions. void TryHeapNumberToSmi(TNode number, TVariable* output, Label* if_smi); + void TryFloat32ToSmi(TNode number, TVariable* output, + Label* if_smi); void TryFloat64ToSmi(TNode number, TVariable* output, Label* if_smi); + TNode ChangeFloat32ToTagged(TNode value); TNode ChangeFloat64ToTagged(SloppyTNode value); TNode ChangeInt32ToTagged(SloppyTNode value); TNode ChangeUint32ToTagged(SloppyTNode value); @@ -2430,6 +2487,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler TNode ChangeNumberToUint32(TNode value); TNode ChangeNumberToFloat64(TNode value); + TNode ChangeTaggedNonSmiToInt32(TNode context, + TNode input); + TNode ChangeTaggedToFloat64(TNode context, + TNode input); + void TaggedToNumeric(TNode context, TNode value, TVariable* var_numeric); void TaggedToNumericWithFeedback(TNode context, TNode value, @@ -2546,6 +2608,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler TNode IsOddball(SloppyTNode object); TNode IsOddballInstanceType(SloppyTNode instance_type); TNode IsIndirectStringInstanceType(SloppyTNode instance_type); + TNode IsJSAggregateError(TNode object); TNode IsJSArrayBuffer(SloppyTNode object); TNode IsJSDataView(TNode object); TNode IsJSArrayInstanceType(SloppyTNode instance_type); @@ -2565,6 +2628,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler TNode IsJSObjectInstanceType(SloppyTNode instance_type); TNode IsJSObjectMap(SloppyTNode map); TNode IsJSObject(SloppyTNode object); + TNode IsJSFinalizationRegistryMap(TNode map); + TNode IsJSFinalizationRegistry(TNode object); TNode IsJSPromiseMap(SloppyTNode map); TNode IsJSPromise(SloppyTNode object); TNode IsJSProxy(SloppyTNode object); @@ -2643,8 +2708,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler TNode IsPromiseThenProtectorCellInvalid(); TNode IsArraySpeciesProtectorCellInvalid(); TNode IsTypedArraySpeciesProtectorCellInvalid(); - TNode IsRegExpSpeciesProtectorCellInvalid( - TNode native_context); + TNode IsRegExpSpeciesProtectorCellInvalid(); TNode IsPromiseSpeciesProtectorCellInvalid(); TNode IsMockArrayBufferAllocatorFlag() { @@ -2698,6 +2762,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler bool IsFastElementsKind(ElementsKind kind) { return v8::internal::IsFastElementsKind(kind); } + TNode IsFastOrNonExtensibleOrSealedElementsKind( + TNode elements_kind); + TNode IsDictionaryElementsKind(TNode elements_kind) { return ElementsKindEqual(elements_kind, Int32Constant(DICTIONARY_ELEMENTS)); } @@ -2812,43 +2879,52 @@ class V8_EXPORT_PRIVATE CodeStubAssembler // Decodes an unsigned (!) value from |word| to a word-size node. TNode DecodeWord(SloppyTNode word, uint32_t shift, - uint32_t mask); + uintptr_t mask); // Returns a node that contains the updated values of a |BitField|. template - TNode UpdateWord32(TNode word, TNode value) { - return UpdateWord32(word, value, BitField::kShift, BitField::kMask); + TNode UpdateWord32(TNode word, TNode value, + bool starts_as_zero = false) { + return UpdateWord32(word, value, BitField::kShift, BitField::kMask, + starts_as_zero); } // Returns a node that contains the updated values of a |BitField|. template - TNode UpdateWord(TNode word, TNode value) { - return UpdateWord(word, value, BitField::kShift, BitField::kMask); + TNode UpdateWord(TNode word, TNode value, + bool starts_as_zero = false) { + return UpdateWord(word, value, BitField::kShift, BitField::kMask, + starts_as_zero); } // Returns a node that contains the updated values of a |BitField|. template - TNode UpdateWordInWord32(TNode word, - TNode value) { - return UncheckedCast(TruncateIntPtrToInt32( - Signed(UpdateWord(ChangeUint32ToWord(word), value)))); + TNode UpdateWordInWord32(TNode word, TNode value, + bool starts_as_zero = false) { + return UncheckedCast( + TruncateIntPtrToInt32(Signed(UpdateWord( + ChangeUint32ToWord(word), value, starts_as_zero)))); } // Returns a node that contains the updated values of a |BitField|. template - TNode UpdateWord32InWord(TNode word, TNode value) { - return UpdateWord(word, ChangeUint32ToWord(value)); + TNode UpdateWord32InWord(TNode word, TNode value, + bool starts_as_zero = false) { + return UpdateWord(word, ChangeUint32ToWord(value), + starts_as_zero); } // Returns a node that contains the updated {value} inside {word} starting // at {shift} and fitting in {mask}. TNode UpdateWord32(TNode word, TNode value, - uint32_t shift, uint32_t mask); + uint32_t shift, uint32_t mask, + bool starts_as_zero = false); // Returns a node that contains the updated {value} inside {word} starting // at {shift} and fitting in {mask}. TNode UpdateWord(TNode word, TNode value, - uint32_t shift, uint32_t mask); + uint32_t shift, uintptr_t mask, + bool starts_as_zero = false); // Returns true if any of the |T|'s bits in given |word32| are set. template @@ -3593,8 +3669,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler TNode IsDebugActive(); // JSArrayBuffer helpers - TNode LoadJSArrayBufferBitField(TNode array_buffer); - TNode LoadJSArrayBufferBackingStore( + TNode LoadJSArrayBufferBackingStorePtr( TNode array_buffer); void ThrowIfArrayBufferIsDetached(SloppyTNode context, TNode array_buffer, @@ -3671,10 +3746,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler TNode MakeTypeError(MessageTemplate message, TNode context, TArgs... args) { STATIC_ASSERT(sizeof...(TArgs) <= 3); - const TNode make_type_error = LoadContextElement( - LoadNativeContext(context), Context::MAKE_TYPE_ERROR_INDEX); - return CAST(Call(context, make_type_error, UndefinedConstant(), - SmiConstant(message), args...)); + return CAST(CallRuntime(Runtime::kNewTypeError, context, + SmiConstant(message), args...)); } void Abort(AbortReason reason) { @@ -3844,6 +3917,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler TNode RefillMathRandom(TNode native_context); + void RemoveFinalizationRegistryCellFromUnregisterTokenMap( + TNode finalization_registry, + TNode weak_cell); + private: friend class CodeStubArguments; diff --git a/deps/v8/src/codegen/compiler.cc b/deps/v8/src/codegen/compiler.cc index 595e59f55148c4..c436c57407cdca 100644 --- a/deps/v8/src/codegen/compiler.cc +++ b/deps/v8/src/codegen/compiler.cc @@ -30,6 +30,7 @@ #include "src/execution/frames-inl.h" #include "src/execution/isolate-inl.h" #include "src/execution/isolate.h" +#include "src/execution/off-thread-isolate.h" #include "src/execution/runtime-profiler.h" #include "src/execution/vm-state-inl.h" #include "src/handles/maybe-handles.h" @@ -42,10 +43,11 @@ #include "src/objects/map.h" #include "src/objects/object-list-macros.h" #include "src/objects/shared-function-info.h" +#include "src/objects/string.h" #include "src/parsing/parse-info.h" #include "src/parsing/parser.h" #include "src/parsing/parsing.h" -#include "src/parsing/rewriter.h" +#include "src/parsing/pending-compilation-error-handler.h" #include "src/parsing/scanner-character-streams.h" #include "src/snapshot/code-serializer.h" #include "src/utils/ostreams.h" @@ -179,13 +181,15 @@ CompilationJob::Status UnoptimizedCompilationJob::FinalizeJob( return UpdateState(FinalizeJobImpl(shared_info, isolate), State::kSucceeded); } -void UnoptimizedCompilationJob::RecordCompilationStats(Isolate* isolate) const { +namespace { + +void RecordUnoptimizedCompilationStats(Isolate* isolate, + Handle shared_info) { int code_size; - if (compilation_info()->has_bytecode_array()) { - code_size = compilation_info()->bytecode_array()->SizeIncludingMetadata(); + if (shared_info->HasBytecodeArray()) { + code_size = shared_info->GetBytecodeArray().SizeIncludingMetadata(); } else { - DCHECK(compilation_info()->has_asm_wasm_data()); - code_size = compilation_info()->asm_wasm_data()->Size(); + code_size = shared_info->asm_wasm_data().Size(); } Counters* counters = isolate->counters(); @@ -197,27 +201,30 @@ void UnoptimizedCompilationJob::RecordCompilationStats(Isolate* isolate) const { // Also add total time (there's now already timer_ on the base class). } -void UnoptimizedCompilationJob::RecordFunctionCompilation( - CodeEventListener::LogEventsAndTags tag, Handle shared, - Isolate* isolate) const { +void RecordUnoptimizedFunctionCompilation( + Isolate* isolate, CodeEventListener::LogEventsAndTags tag, + Handle shared, base::TimeDelta time_taken_to_execute, + base::TimeDelta time_taken_to_finalize) { Handle abstract_code; - if (compilation_info()->has_bytecode_array()) { + if (shared->HasBytecodeArray()) { abstract_code = - Handle::cast(compilation_info()->bytecode_array()); + handle(AbstractCode::cast(shared->GetBytecodeArray()), isolate); } else { - DCHECK(compilation_info()->has_asm_wasm_data()); + DCHECK(shared->HasAsmWasmData()); abstract_code = Handle::cast(BUILTIN_CODE(isolate, InstantiateAsmJs)); } - double time_taken_ms = time_taken_to_execute_.InMillisecondsF() + - time_taken_to_finalize_.InMillisecondsF(); + double time_taken_ms = time_taken_to_execute.InMillisecondsF() + + time_taken_to_finalize.InMillisecondsF(); Handle