diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-12-08 10:22:59 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-12-08 12:17:14 +0000 |
commit | 69b8f9169ffd66fdeca1ac60a4bc06b91d106186 (patch) | |
tree | c8b7f735583d0b4e0c0b61a014a7f4b3b26e85ab /chromium/v8 | |
parent | daa093eea7c773db06799a13bd7e4e2e2a9f8f14 (diff) | |
download | qtwebengine-chromium-69b8f9169ffd66fdeca1ac60a4bc06b91d106186.tar.gz |
BASELINE: Update Chromium to 63.0.3239.87
Change-Id: Iac27464730121b4fac76869d87d622504642e016
Reviewed-by: Peter Varga <pvarga@inf.u-szeged.hu>
Diffstat (limited to 'chromium/v8')
123 files changed, 2500 insertions, 1467 deletions
diff --git a/chromium/v8/AUTHORS b/chromium/v8/AUTHORS index 6ac0491aba0..be50e6e4996 100644 --- a/chromium/v8/AUTHORS +++ b/chromium/v8/AUTHORS @@ -43,7 +43,7 @@ Alexis Campailla <alexis@janeasystems.com> Andreas Anyuru <andreas.anyuru@gmail.com> Andrew Paprocki <andrew@ishiboo.com> Andrei Kashcha <anvaka@gmail.com> -Anna Henningsen <addaleax@gmail.com> +Anna Henningsen <anna@addaleax.net> Bangfu Tao <bangfu.tao@samsung.com> Ben Noordhuis <info@bnoordhuis.nl> Benjamin Tan <demoneaux@gmail.com> diff --git a/chromium/v8/BUILD.gn b/chromium/v8/BUILD.gn index cf706d6a0bd..daed449c0a5 100644 --- a/chromium/v8/BUILD.gn +++ b/chromium/v8/BUILD.gn @@ -56,7 +56,7 @@ declare_args() { v8_enable_vtunejit = false # Sets -dENABLE_HANDLE_ZAPPING. - v8_enable_handle_zapping = true + v8_enable_handle_zapping = is_debug # Enable slow dchecks. v8_enable_slow_dchecks = false @@ -84,7 +84,7 @@ declare_args() { v8_enable_trace_ignition = false # Sets -dV8_CONCURRENT_MARKING - v8_enable_concurrent_marking = true + v8_enable_concurrent_marking = false # Sets -dV8_CSA_WRITE_BARRIER v8_enable_csa_write_barrier = true @@ -834,7 +834,6 @@ action("v8_dump_build_config") { is_gcov_coverage = v8_code_coverage && !is_clang args = [ rebase_path("$root_out_dir/v8_build_config.json", root_build_dir), - "current_cpu=\"$current_cpu\"", "dcheck_always_on=$dcheck_always_on", "is_asan=$is_asan", "is_cfi=$is_cfi", @@ -845,7 +844,6 @@ action("v8_dump_build_config") { "is_tsan=$is_tsan", "is_ubsan_vptr=$is_ubsan_vptr", "target_cpu=\"$target_cpu\"", - "v8_current_cpu=\"$v8_current_cpu\"", "v8_enable_i18n_support=$v8_enable_i18n_support", "v8_target_cpu=\"$v8_target_cpu\"", "v8_use_snapshot=$v8_use_snapshot", diff --git a/chromium/v8/DEPS b/chromium/v8/DEPS index 2fb83e97861..b675dd830ef 100644 --- a/chromium/v8/DEPS +++ b/chromium/v8/DEPS @@ -12,7 +12,7 @@ deps = { 'v8/tools/gyp': Var('chromium_url') + '/external/gyp.git' + '@' + 'd61a9397e668fa9843c4aa7da9e79460fe590bfb', 'v8/third_party/icu': - Var('chromium_url') + '/chromium/deps/icu.git' + '@' + '08cb956852a5ccdba7f9c941728bb833529ba3c6', + Var('chromium_url') + '/chromium/deps/icu.git' + '@' + '21d33b1a09a77f033478ea4ffffb61e6970f83bd', 'v8/third_party/instrumented_libraries': Var('chromium_url') + '/chromium/src/third_party/instrumented_libraries.git' + '@' + '644afd349826cb68204226a16c38bde13abe9c3c', 'v8/buildtools': diff --git a/chromium/v8/Makefile b/chromium/v8/Makefile index eb146ac2447..167ebf8c082 100644 --- a/chromium/v8/Makefile +++ b/chromium/v8/Makefile @@ -338,32 +338,32 @@ $(ANDROID_BUILDS): $(GYPFILES) $(ENVFILE) Makefile.android # Test targets. check: all - @tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \ + @gypfiles/run-tests-legacy.py $(TESTJOBS) --outdir=$(OUTDIR) \ --arch=$(shell echo $(DEFAULT_ARCHES) | sed -e 's/ /,/g') \ $(TESTFLAGS) $(addsuffix .check,$(MODES)): $$(basename $$@) - @tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \ + @gypfiles/run-tests-legacy.py $(TESTJOBS) --outdir=$(OUTDIR) \ --mode=$(basename $@) $(TESTFLAGS) $(addsuffix .check,$(ARCHES)): $$(basename $$@) - @tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \ + @gypfiles/run-tests-legacy.py $(TESTJOBS) --outdir=$(OUTDIR) \ --arch=$(basename $@) $(TESTFLAGS) $(CHECKS): $$(basename $$@) - @tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \ + @gypfiles/run-tests-legacy.py $(TESTJOBS) --outdir=$(OUTDIR) \ --arch-and-mode=$(basename $@) $(TESTFLAGS) $(addsuffix .quickcheck,$(MODES)): $$(basename $$@) - @tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \ + @gypfiles/run-tests-legacy.py $(TESTJOBS) --outdir=$(OUTDIR) \ --mode=$(basename $@) $(TESTFLAGS) --quickcheck $(addsuffix .quickcheck,$(ARCHES)): $$(basename $$@) - @tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \ + @gypfiles/run-tests-legacy.py $(TESTJOBS) --outdir=$(OUTDIR) \ --arch=$(basename $@) $(TESTFLAGS) --quickcheck $(QUICKCHECKS): $$(basename $$@) - @tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \ + @gypfiles/run-tests-legacy.py $(TESTJOBS) --outdir=$(OUTDIR) \ --arch-and-mode=$(basename $@) $(TESTFLAGS) --quickcheck $(addsuffix .sync, $(ANDROID_BUILDS)): $$(basename $$@) @@ -371,7 +371,7 @@ $(addsuffix .sync, $(ANDROID_BUILDS)): $$(basename $$@) $(shell pwd) $(ANDROID_V8) $(addsuffix .check, $(ANDROID_BUILDS)): $$(basename $$@).sync - @tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \ + @gypfiles/run-tests-legacy.py $(TESTJOBS) --outdir=$(OUTDIR) \ --arch-and-mode=$(basename $@) \ --timeout=600 \ --command-prefix="tools/android-run.py" $(TESTFLAGS) @@ -380,7 +380,7 @@ $(addsuffix .check, $(ANDROID_ARCHES)): \ $(addprefix $$(basename $$@).,$(MODES)).check native.check: native - @tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR)/native \ + @gypfiles/run-tests-legacy.py $(TESTJOBS) --outdir=$(OUTDIR)/native \ --arch-and-mode=. $(TESTFLAGS) SUPERFASTTESTMODES = ia32.release @@ -391,18 +391,18 @@ COMMA = , EMPTY = SPACE = $(EMPTY) $(EMPTY) quickcheck: $(subst $(COMMA),$(SPACE),$(FASTCOMPILEMODES)) - tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \ + gypfiles/run-tests-legacy.py $(TESTJOBS) --outdir=$(OUTDIR) \ --arch-and-mode=$(SUPERFASTTESTMODES) $(TESTFLAGS) --quickcheck \ --download-data mozilla webkit - tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \ + gypfiles/run-tests-legacy.py $(TESTJOBS) --outdir=$(OUTDIR) \ --arch-and-mode=$(FASTTESTMODES) $(TESTFLAGS) --quickcheck qc: quickcheck turbocheck: $(subst $(COMMA),$(SPACE),$(FASTCOMPILEMODES)) - tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \ + gypfiles/run-tests-legacy.py $(TESTJOBS) --outdir=$(OUTDIR) \ --arch-and-mode=$(SUPERFASTTESTMODES) $(TESTFLAGS) \ --quickcheck --variants=turbofan --download-data mozilla webkit - tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \ + gypfiles/run-tests-legacy.py $(TESTJOBS) --outdir=$(OUTDIR) \ --arch-and-mode=$(FASTTESTMODES) $(TESTFLAGS) \ --quickcheck --variants=turbofan tc: turbocheck diff --git a/chromium/v8/gni/isolate.gni b/chromium/v8/gni/isolate.gni index 4b0ffdc413e..4bdf0c0fad2 100644 --- a/chromium/v8/gni/isolate.gni +++ b/chromium/v8/gni/isolate.gni @@ -156,8 +156,6 @@ template("v8_isolate_run") { "--config-variable", "icu_use_data_file_flag=$icu_use_data_file_flag", "--config-variable", - "is_gn=1", - "--config-variable", "msan=$msan", "--config-variable", "tsan=$tsan", diff --git a/chromium/v8/gypfiles/features.gypi b/chromium/v8/gypfiles/features.gypi index 964e81f46cc..1d3f67daee3 100644 --- a/chromium/v8/gypfiles/features.gypi +++ b/chromium/v8/gypfiles/features.gypi @@ -85,7 +85,7 @@ 'v8_check_microtasks_scopes_consistency%': 'false', # Enable concurrent marking. - 'v8_enable_concurrent_marking%': 1, + 'v8_enable_concurrent_marking%': 0, # Controls the threshold for on-heap/off-heap Typed Arrays. 'v8_typed_array_max_size_in_heap%': 64, @@ -158,7 +158,7 @@ }, # Debug 'Release': { 'variables': { - 'v8_enable_handle_zapping%': 1, + 'v8_enable_handle_zapping%': 0, }, 'conditions': [ ['v8_enable_handle_zapping==1', { diff --git a/chromium/v8/gypfiles/isolate.gypi b/chromium/v8/gypfiles/isolate.gypi index 11b05705307..149818c8d06 100644 --- a/chromium/v8/gypfiles/isolate.gypi +++ b/chromium/v8/gypfiles/isolate.gypi @@ -74,7 +74,6 @@ '--config-variable', 'gcmole=<(gcmole)', '--config-variable', 'has_valgrind=<(has_valgrind)', '--config-variable', 'icu_use_data_file_flag=<(icu_use_data_file_flag)', - '--config-variable', 'is_gn=0', '--config-variable', 'msan=<(msan)', '--config-variable', 'tsan=<(tsan)', '--config-variable', 'coverage=<(coverage)', diff --git a/chromium/v8/gypfiles/run-tests-legacy.py b/chromium/v8/gypfiles/run-tests-legacy.py new file mode 100755 index 00000000000..f1ea478c62d --- /dev/null +++ b/chromium/v8/gypfiles/run-tests-legacy.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# 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. + +""" +Legacy test-runner wrapper supporting a product of multiple architectures and +modes. +""" + +import argparse +import itertools +from os.path import abspath, dirname, join +import subprocess +import sys + +BASE_DIR = dirname(dirname(abspath(__file__))) +RUN_TESTS = join(BASE_DIR, 'tools', 'run-tests.py') + +def main(): + parser = argparse.ArgumentParser(description='Legacy test-runner wrapper') + parser.add_argument( + '--arch', help='Comma-separated architectures to run tests on') + parser.add_argument( + '--mode', help='Comma-separated modes to run tests on') + parser.add_argument( + '--arch-and-mode', + help='Architecture and mode in the format \'arch.mode\'', + ) + + args, remaining_args = parser.parse_known_args(sys.argv) + if (args.arch or args.mode) and args.arch_and_mode: + parser.error('The flags --arch-and-mode and --arch/--mode are exclusive.') + arch = (args.arch or 'ia32,x64,arm').split(',') + mode = (args.mode or 'release,debug').split(',') + if args.arch_and_mode: + arch_and_mode = map( + lambda am: am.split('.'), + args.arch_and_mode.split(',')) + arch = map(lambda am: am[0], arch_and_mode) + mode = map(lambda am: am[1], arch_and_mode) + + ret_code = 0 + for a, m in itertools.product(arch, mode): + ret_code |= subprocess.check_call( + [RUN_TESTS] + remaining_args[1:] + ['--arch', a, '--mode', m]) + return ret_code + +if __name__ == '__main__': + sys.exit(main()) diff --git a/chromium/v8/gypfiles/toolchain.gypi b/chromium/v8/gypfiles/toolchain.gypi index 5733d2d54ca..80844cecc67 100644 --- a/chromium/v8/gypfiles/toolchain.gypi +++ b/chromium/v8/gypfiles/toolchain.gypi @@ -32,6 +32,7 @@ 'msvs_use_common_release': 0, 'clang%': 0, 'asan%': 0, + 'cfi_vptr%': 0, 'lsan%': 0, 'msan%': 0, 'tsan%': 0, diff --git a/chromium/v8/include/v8-inspector.h b/chromium/v8/include/v8-inspector.h index 43bf3b4f60b..d0bb9b47fe4 100644 --- a/chromium/v8/include/v8-inspector.h +++ b/chromium/v8/include/v8-inspector.h @@ -211,6 +211,8 @@ class V8_EXPORT V8InspectorClient { // TODO(dgozman): this was added to support service worker shadow page. We // should not connect at all. virtual bool canExecuteScripts(int contextGroupId) { return true; } + + virtual void maxAsyncCallStackDepthChanged(int depth) {} }; class V8_EXPORT V8Inspector { diff --git a/chromium/v8/include/v8-version.h b/chromium/v8/include/v8-version.h index 4a42e6b48d7..46bb92f6506 100644 --- a/chromium/v8/include/v8-version.h +++ b/chromium/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 6 #define V8_MINOR_VERSION 3 #define V8_BUILD_NUMBER 292 -#define V8_PATCH_LEVEL 0 +#define V8_PATCH_LEVEL 46 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/chromium/v8/include/v8.h b/chromium/v8/include/v8.h index 5d6b04ad1a4..f1001533649 100644 --- a/chromium/v8/include/v8.h +++ b/chromium/v8/include/v8.h @@ -7976,9 +7976,8 @@ class V8_EXPORT V8 { * This function removes callback which was installed by * AddGCPrologueCallback function. */ - V8_INLINE static V8_DEPRECATED( - "Use isolate version", - void RemoveGCPrologueCallback(GCCallback callback)); + static V8_DEPRECATED("Use isolate version", + void RemoveGCPrologueCallback(GCCallback callback)); /** * Enables the host application to receive a notification after a @@ -7999,9 +7998,8 @@ class V8_EXPORT V8 { * This function removes callback which was installed by * AddGCEpilogueCallback function. */ - V8_INLINE static V8_DEPRECATED( - "Use isolate version", - void RemoveGCEpilogueCallback(GCCallback callback)); + static V8_DEPRECATED("Use isolate version", + void RemoveGCEpilogueCallback(GCCallback callback)); /** * Initializes V8. This function needs to be called before the first Isolate @@ -10385,19 +10383,6 @@ void V8::SetFatalErrorHandler(FatalErrorCallback callback) { isolate->SetFatalErrorHandler(callback); } -void V8::RemoveGCPrologueCallback(GCCallback callback) { - Isolate* isolate = Isolate::GetCurrent(); - isolate->RemoveGCPrologueCallback( - reinterpret_cast<Isolate::GCCallback>(callback)); -} - - -void V8::RemoveGCEpilogueCallback(GCCallback callback) { - Isolate* isolate = Isolate::GetCurrent(); - isolate->RemoveGCEpilogueCallback( - reinterpret_cast<Isolate::GCCallback>(callback)); -} - void V8::TerminateExecution(Isolate* isolate) { isolate->TerminateExecution(); } diff --git a/chromium/v8/src/api-natives.cc b/chromium/v8/src/api-natives.cc index a685d4e0fda..35759459c69 100644 --- a/chromium/v8/src/api-natives.cc +++ b/chromium/v8/src/api-natives.cc @@ -556,7 +556,7 @@ MaybeHandle<JSObject> ApiNatives::InstantiateRemoteObject( Handle<Map> object_map = isolate->factory()->NewMap( JS_SPECIAL_API_OBJECT_TYPE, JSObject::kHeaderSize + data->embedder_field_count() * kPointerSize, - HOLEY_SMI_ELEMENTS); + TERMINAL_FAST_ELEMENTS_KIND); object_map->SetConstructor(*constructor); object_map->set_is_access_check_needed(true); object_map->set_may_have_interesting_symbols(true); @@ -692,8 +692,8 @@ Handle<JSFunction> ApiNatives::CreateApiFunction( break; } - Handle<Map> map = - isolate->factory()->NewMap(type, instance_size, HOLEY_SMI_ELEMENTS); + Handle<Map> map = isolate->factory()->NewMap(type, instance_size, + TERMINAL_FAST_ELEMENTS_KIND); JSFunction::SetInitialMap(result, map, Handle<JSObject>::cast(prototype)); // Mark as undetectable if needed. diff --git a/chromium/v8/src/api.cc b/chromium/v8/src/api.cc index 9cbe3462a5e..300b88f70eb 100644 --- a/chromium/v8/src/api.cc +++ b/chromium/v8/src/api.cc @@ -8585,6 +8585,18 @@ void V8::AddGCEpilogueCallback(v8::GCCallback callback, GCType gc_type) { data, gc_type); } +void V8::RemoveGCPrologueCallback(GCCallback callback) { + void* data = reinterpret_cast<void*>(callback); + Isolate::GetCurrent()->RemoveGCPrologueCallback(CallGCCallbackWithoutIsolate, + data); +} + +void V8::RemoveGCEpilogueCallback(GCCallback callback) { + void* data = reinterpret_cast<void*>(callback); + Isolate::GetCurrent()->RemoveGCEpilogueCallback(CallGCCallbackWithoutIsolate, + data); +} + void Isolate::SetEmbedderHeapTracer(EmbedderHeapTracer* tracer) { i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this); isolate->heap()->SetEmbedderHeapTracer(tracer); diff --git a/chromium/v8/src/arm/assembler-arm.cc b/chromium/v8/src/arm/assembler-arm.cc index 9799950728d..c9aa9ef015c 100644 --- a/chromium/v8/src/arm/assembler-arm.cc +++ b/chromium/v8/src/arm/assembler-arm.cc @@ -2131,6 +2131,8 @@ void Assembler::strd(Register src1, Register src2, void Assembler::ldrex(Register dst, Register src, Condition cond) { // Instruction details available in ARM DDI 0406C.b, A8.8.75. // cond(31-28) | 00011001(27-20) | Rn(19-16) | Rt(15-12) | 111110011111(11-0) + DCHECK(dst != pc); + DCHECK(src != pc); emit(cond | B24 | B23 | B20 | src.code() * B16 | dst.code() * B12 | 0xf9f); } @@ -2139,6 +2141,11 @@ void Assembler::strex(Register src1, Register src2, Register dst, // Instruction details available in ARM DDI 0406C.b, A8.8.212. // cond(31-28) | 00011000(27-20) | Rn(19-16) | Rd(15-12) | 11111001(11-4) | // Rt(3-0) + DCHECK(dst != pc); + DCHECK(src1 != pc); + DCHECK(src2 != pc); + DCHECK(src1 != dst); + DCHECK(src1 != src2); emit(cond | B24 | B23 | dst.code() * B16 | src1.code() * B12 | 0xf9 * B4 | src2.code()); } @@ -2146,6 +2153,8 @@ void Assembler::strex(Register src1, Register src2, Register dst, void Assembler::ldrexb(Register dst, Register src, Condition cond) { // Instruction details available in ARM DDI 0406C.b, A8.8.76. // cond(31-28) | 00011101(27-20) | Rn(19-16) | Rt(15-12) | 111110011111(11-0) + DCHECK(dst != pc); + DCHECK(src != pc); emit(cond | B24 | B23 | B22 | B20 | src.code() * B16 | dst.code() * B12 | 0xf9f); } @@ -2155,6 +2164,11 @@ void Assembler::strexb(Register src1, Register src2, Register dst, // Instruction details available in ARM DDI 0406C.b, A8.8.213. // cond(31-28) | 00011100(27-20) | Rn(19-16) | Rd(15-12) | 11111001(11-4) | // Rt(3-0) + DCHECK(dst != pc); + DCHECK(src1 != pc); + DCHECK(src2 != pc); + DCHECK(src1 != dst); + DCHECK(src1 != src2); emit(cond | B24 | B23 | B22 | dst.code() * B16 | src1.code() * B12 | 0xf9 * B4 | src2.code()); } @@ -2162,6 +2176,8 @@ void Assembler::strexb(Register src1, Register src2, Register dst, void Assembler::ldrexh(Register dst, Register src, Condition cond) { // Instruction details available in ARM DDI 0406C.b, A8.8.78. // cond(31-28) | 00011111(27-20) | Rn(19-16) | Rt(15-12) | 111110011111(11-0) + DCHECK(dst != pc); + DCHECK(src != pc); emit(cond | B24 | B23 | B22 | B21 | B20 | src.code() * B16 | dst.code() * B12 | 0xf9f); } @@ -2171,6 +2187,11 @@ void Assembler::strexh(Register src1, Register src2, Register dst, // Instruction details available in ARM DDI 0406C.b, A8.8.215. // cond(31-28) | 00011110(27-20) | Rn(19-16) | Rd(15-12) | 11111001(11-4) | // Rt(3-0) + DCHECK(dst != pc); + DCHECK(src1 != pc); + DCHECK(src2 != pc); + DCHECK(src1 != dst); + DCHECK(src1 != src2); emit(cond | B24 | B23 | B22 | B21 | dst.code() * B16 | src1.code() * B12 | 0xf9 * B4 | src2.code()); } diff --git a/chromium/v8/src/arm/simulator-arm.cc b/chromium/v8/src/arm/simulator-arm.cc index c5b9b00adac..f83d6f2a2af 100644 --- a/chromium/v8/src/arm/simulator-arm.cc +++ b/chromium/v8/src/arm/simulator-arm.cc @@ -2176,6 +2176,8 @@ void Simulator::DecodeType01(Instruction* instr) { int rd = instr->RdValue(); int rt = instr->RmValue(); int rn = instr->RnValue(); + DCHECK_NE(rd, rn); + DCHECK_NE(rd, rt); int32_t addr = get_register(rn); switch (instr->Bits(22, 21)) { case 0: { diff --git a/chromium/v8/src/asmjs/asm-js.cc b/chromium/v8/src/asmjs/asm-js.cc index 583163e4086..97da2c2af2f 100644 --- a/chromium/v8/src/asmjs/asm-js.cc +++ b/chromium/v8/src/asmjs/asm-js.cc @@ -371,6 +371,7 @@ MaybeHandle<Object> AsmJs::InstantiateAsmWasm(Isolate* isolate, ReportInstantiationFailure(script, position, "Requires heap buffer"); return MaybeHandle<Object>(); } + memory->set_is_growable(false); size_t size = NumberToSize(memory->byte_length()); // TODO(mstarzinger): We currently only limit byte length of the buffer to // be a multiple of 8, we should enforce the stricter spec limits here. diff --git a/chromium/v8/src/asmjs/asm-parser.cc b/chromium/v8/src/asmjs/asm-parser.cc index 033e9bc3b35..d0eb1050f65 100644 --- a/chromium/v8/src/asmjs/asm-parser.cc +++ b/chromium/v8/src/asmjs/asm-parser.cc @@ -14,6 +14,7 @@ #include "src/base/optional.h" #include "src/flags.h" #include "src/parsing/scanner.h" +#include "src/wasm/wasm-limits.h" #include "src/wasm/wasm-opcodes.h" namespace v8 { @@ -781,6 +782,11 @@ void AsmJsParser::ValidateFunction() { current_function_builder_->AddLocal(kWasmI32); } + // Check against limit on number of local variables. + if (locals.size() + function_temp_locals_used_ > kV8MaxWasmFunctionLocals) { + FAIL("Number of local variables exceeds internal limit"); + } + // End function current_function_builder_->Emit(kExprEnd); @@ -866,6 +872,7 @@ void AsmJsParser::ValidateFunctionParams(ZoneVector<AsmType*>* params) { // 6.4 ValidateFunction - locals void AsmJsParser::ValidateFunctionLocals(size_t param_count, ZoneVector<ValueType>* locals) { + DCHECK(locals->empty()); // Local Variables. while (Peek(TOK(var))) { scanner_.EnterLocalScope(); diff --git a/chromium/v8/src/assembler.cc b/chromium/v8/src/assembler.cc index 8fb315234fc..b36c4941294 100644 --- a/chromium/v8/src/assembler.cc +++ b/chromium/v8/src/assembler.cc @@ -52,6 +52,7 @@ #include "src/debug/debug.h" #include "src/deoptimizer.h" #include "src/disassembler.h" +#include "src/elements.h" #include "src/execution.h" #include "src/ic/ic.h" #include "src/ic/stub-cache.h" @@ -1414,6 +1415,19 @@ ExternalReference ExternalReference::get_or_create_hash_raw(Isolate* isolate) { return ExternalReference(Redirect(isolate, FUNCTION_ADDR(f))); } +ExternalReference +ExternalReference::copy_fast_number_jsarray_elements_to_typed_array( + Isolate* isolate) { + return ExternalReference(Redirect( + isolate, FUNCTION_ADDR(CopyFastNumberJSArrayElementsToTypedArray))); +} + +ExternalReference ExternalReference::copy_typed_array_elements_to_typed_array( + Isolate* isolate) { + return ExternalReference( + Redirect(isolate, FUNCTION_ADDR(CopyTypedArrayElementsToTypedArray))); +} + ExternalReference ExternalReference::try_internalize_string_function( Isolate* isolate) { return ExternalReference(Redirect( diff --git a/chromium/v8/src/assembler.h b/chromium/v8/src/assembler.h index 2eae2d543f5..2ebe88d534d 100644 --- a/chromium/v8/src/assembler.h +++ b/chromium/v8/src/assembler.h @@ -985,6 +985,11 @@ class ExternalReference BASE_EMBEDDED { static ExternalReference get_or_create_hash_raw(Isolate* isolate); + static ExternalReference copy_fast_number_jsarray_elements_to_typed_array( + Isolate* isolate); + static ExternalReference copy_typed_array_elements_to_typed_array( + Isolate* isolate); + static ExternalReference page_flags(Page* page); static ExternalReference ForDeoptEntry(Address entry); diff --git a/chromium/v8/src/ast/scopes.cc b/chromium/v8/src/ast/scopes.cc index 189c22f0284..07eacd3fe97 100644 --- a/chromium/v8/src/ast/scopes.cc +++ b/chromium/v8/src/ast/scopes.cc @@ -1741,8 +1741,8 @@ void Scope::Print(int n) { if (is_declaration_scope() && AsDeclarationScope()->calls_sloppy_eval()) { Indent(n1, "// scope calls sloppy 'eval'\n"); } - if (is_declaration_scope() && AsDeclarationScope()->uses_super_property()) { - Indent(n1, "// scope uses 'super' property\n"); + if (is_declaration_scope() && AsDeclarationScope()->NeedsHomeObject()) { + Indent(n1, "// scope needs home object\n"); } if (inner_scope_calls_eval_) Indent(n1, "// inner scope calls 'eval'\n"); if (is_declaration_scope()) { diff --git a/chromium/v8/src/ast/scopes.h b/chromium/v8/src/ast/scopes.h index 9bd24ba1cce..fe155080276 100644 --- a/chromium/v8/src/ast/scopes.h +++ b/chromium/v8/src/ast/scopes.h @@ -666,14 +666,13 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope { // Inform the scope that the corresponding code uses "super". void RecordSuperPropertyUsage() { - DCHECK((IsConciseMethod(function_kind()) || - IsAccessorFunction(function_kind()) || - IsClassConstructor(function_kind()))); + DCHECK(IsConciseMethod(function_kind()) || + IsAccessorFunction(function_kind()) || + IsClassConstructor(function_kind())); scope_uses_super_property_ = true; } - // Does this scope access "super" property (super.foo). - bool uses_super_property() const { return scope_uses_super_property_; } + // Does this scope access "super" property (super.foo). bool NeedsHomeObject() const { return scope_uses_super_property_ || (inner_scope_calls_eval_ && (IsConciseMethod(function_kind()) || diff --git a/chromium/v8/src/base/platform/platform-aix.cc b/chromium/v8/src/base/platform/platform-aix.cc index dfc926ea981..6c1bde7b859 100644 --- a/chromium/v8/src/base/platform/platform-aix.cc +++ b/chromium/v8/src/base/platform/platform-aix.cc @@ -126,9 +126,6 @@ void* OS::ReserveAlignedRegion(size_t size, size_t alignment, void* hint, DCHECK(aligned_size == request_size); - address_ = static_cast<void*>(aligned_base); - size_ = aligned_size; - *allocated = aligned_size; return static_cast<void*>(aligned_base); } diff --git a/chromium/v8/src/bootstrapper.cc b/chromium/v8/src/bootstrapper.cc index a0c91f4fef1..fe7d63fa956 100644 --- a/chromium/v8/src/bootstrapper.cc +++ b/chromium/v8/src/bootstrapper.cc @@ -1034,8 +1034,8 @@ void Genesis::CreateJSProxyMaps() { // Allocate maps for all Proxy types. // Next to the default proxy, we need maps indicating callable and // constructable proxies. - Handle<Map> proxy_map = - factory()->NewMap(JS_PROXY_TYPE, JSProxy::kSize, PACKED_ELEMENTS); + Handle<Map> proxy_map = factory()->NewMap(JS_PROXY_TYPE, JSProxy::kSize, + TERMINAL_FAST_ELEMENTS_KIND); proxy_map->set_dictionary_map(true); proxy_map->set_may_have_interesting_symbols(true); native_context()->set_proxy_map(*proxy_map); @@ -1624,9 +1624,11 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object, array_function->shared()->SetConstructStub(*code); // Set up %ArrayPrototype%. + // The %ArrayPrototype% has TERMINAL_FAST_ELEMENTS_KIND in order to ensure + // that constant functions stay constant after turning prototype to setup + // mode and back when constant field tracking is enabled. Handle<JSArray> proto = - Handle<JSArray>::cast(factory->NewJSObject(array_function, TENURED)); - JSArray::Initialize(proto, 0); + factory->NewJSArray(0, TERMINAL_FAST_ELEMENTS_KIND, TENURED); JSFunction::SetPrototype(array_function, proto); native_context()->set_initial_array_prototype(*proto); @@ -5482,7 +5484,7 @@ Genesis::Genesis(Isolate* isolate, DCHECK_EQ(global_proxy_data->embedder_field_count(), global_proxy_template->InternalFieldCount()); Handle<Map> global_proxy_map = isolate->factory()->NewMap( - JS_GLOBAL_PROXY_TYPE, proxy_size, HOLEY_SMI_ELEMENTS); + JS_GLOBAL_PROXY_TYPE, proxy_size, TERMINAL_FAST_ELEMENTS_KIND); global_proxy_map->set_is_access_check_needed(true); global_proxy_map->set_has_hidden_prototype(true); global_proxy_map->set_may_have_interesting_symbols(true); diff --git a/chromium/v8/src/builtins/builtins-collections-gen.cc b/chromium/v8/src/builtins/builtins-collections-gen.cc index a1928632abd..4aa7fa310b1 100644 --- a/chromium/v8/src/builtins/builtins-collections-gen.cc +++ b/chromium/v8/src/builtins/builtins-collections-gen.cc @@ -314,10 +314,11 @@ TF_BUILTIN(MapConstructor, CollectionsBuiltinsAssembler) { BIND(&if_notobject); { - Node* const exception = MakeTypeError( - MessageTemplate::kIteratorValueNotAnObject, context, next_value); - var_exception.Bind(exception); - Goto(&if_exception); + Node* ret = CallRuntime( + Runtime::kThrowTypeError, context, + SmiConstant(MessageTemplate::kIteratorValueNotAnObject), next_value); + GotoIfException(ret, &if_exception, &var_exception); + Unreachable(); } } diff --git a/chromium/v8/src/builtins/builtins-definitions.h b/chromium/v8/src/builtins/builtins-definitions.h index 746051b6cd9..cc89c4e3650 100644 --- a/chromium/v8/src/builtins/builtins-definitions.h +++ b/chromium/v8/src/builtins/builtins-definitions.h @@ -1024,7 +1024,7 @@ namespace internal { /* ES6 #sec-%typedarray%.prototype.reverse */ \ CPP(TypedArrayPrototypeReverse) \ /* ES6 %TypedArray%.prototype.set */ \ - CPP(TypedArrayPrototypeSet) \ + TFJ(TypedArrayPrototypeSet, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \ /* ES6 #sec-%typedarray%.prototype.slice */ \ CPP(TypedArrayPrototypeSlice) \ /* ES6 #sec-get-%typedarray%.prototype-@@tostringtag */ \ diff --git a/chromium/v8/src/builtins/builtins-proxy-gen.cc b/chromium/v8/src/builtins/builtins-proxy-gen.cc index c4fd34290a8..29c5a4eaeb3 100644 --- a/chromium/v8/src/builtins/builtins-proxy-gen.cc +++ b/chromium/v8/src/builtins/builtins-proxy-gen.cc @@ -491,7 +491,7 @@ void ProxiesCodeStubAssembler::CheckGetSetTrapResult( Node* instance_type = LoadInstanceType(target); TryGetOwnProperty(context, target, target, map, instance_type, name, &if_found_value, &var_value, &var_details, &var_raw_value, - check_passed, &check_in_runtime); + check_passed, &check_in_runtime, kReturnAccessorPair); BIND(&if_found_value); { @@ -589,7 +589,7 @@ void ProxiesCodeStubAssembler::CheckHasTrapResult(Node* context, Node* target, Node* instance_type = LoadInstanceType(target); TryGetOwnProperty(context, target, target, target_map, instance_type, name, &if_found_value, &var_value, &var_details, &var_raw_value, - check_passed, if_bailout); + check_passed, if_bailout, kReturnAccessorPair); // 9.b. If targetDesc is not undefined, then (see 9.b.i. below). BIND(&if_found_value); diff --git a/chromium/v8/src/builtins/builtins-string-gen.cc b/chromium/v8/src/builtins/builtins-string-gen.cc index 9cb0e474e21..8d407b35e61 100644 --- a/chromium/v8/src/builtins/builtins-string-gen.cc +++ b/chromium/v8/src/builtins/builtins-string-gen.cc @@ -1052,9 +1052,9 @@ void StringBuiltinsAssembler::RequireObjectCoercible(Node* const context, } void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol( - Node* const context, Node* const object, Handle<Symbol> symbol, - const NodeFunction0& regexp_call, const NodeFunction1& generic_call, - CodeStubArguments* args) { + Node* const context, Node* const object, Node* const maybe_string, + Handle<Symbol> symbol, const NodeFunction0& regexp_call, + const NodeFunction1& generic_call, CodeStubArguments* args) { Label out(this); // Smis definitely don't have an attached symbol. @@ -1084,14 +1084,21 @@ void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol( } // Take the fast path for RegExps. + // There's two conditions: {object} needs to be a fast regexp, and + // {maybe_string} must be a string (we can't call ToString on the fast path + // since it may mutate {object}). { Label stub_call(this), slow_lookup(this); + GotoIf(TaggedIsSmi(maybe_string), &slow_lookup); + GotoIfNot(IsString(maybe_string), &slow_lookup); + RegExpBuiltinsAssembler regexp_asm(state()); regexp_asm.BranchIfFastRegExp(context, object, object_map, &stub_call, &slow_lookup); BIND(&stub_call); + // TODO(jgruber): Add a no-JS scope once it exists. Node* const result = regexp_call(); if (args == nullptr) { Return(result); @@ -1307,12 +1314,10 @@ TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) { // Redirect to replacer method if {search[@@replace]} is not undefined. MaybeCallFunctionAtSymbol( - context, search, isolate()->factory()->replace_symbol(), + context, search, receiver, isolate()->factory()->replace_symbol(), [=]() { - Node* const subject_string = ToString_Inline(context, receiver); - - return CallBuiltin(Builtins::kRegExpReplace, context, search, - subject_string, replace); + return CallBuiltin(Builtins::kRegExpReplace, context, search, receiver, + replace); }, [=](Node* fn) { Callable call_callable = CodeFactory::Call(isolate()); @@ -1550,12 +1555,10 @@ TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) { // Redirect to splitter method if {separator[@@split]} is not undefined. MaybeCallFunctionAtSymbol( - context, separator, isolate()->factory()->split_symbol(), + context, separator, receiver, isolate()->factory()->split_symbol(), [=]() { - Node* const subject_string = ToString_Inline(context, receiver); - - return CallBuiltin(Builtins::kRegExpSplit, context, separator, - subject_string, limit); + return CallBuiltin(Builtins::kRegExpSplit, context, separator, receiver, + limit); }, [=](Node* fn) { Callable call_callable = CodeFactory::Call(isolate()); diff --git a/chromium/v8/src/builtins/builtins-string-gen.h b/chromium/v8/src/builtins/builtins-string-gen.h index 607f7b6acb2..c9af3802707 100644 --- a/chromium/v8/src/builtins/builtins-string-gen.h +++ b/chromium/v8/src/builtins/builtins-string-gen.h @@ -82,9 +82,11 @@ class StringBuiltinsAssembler : public CodeStubAssembler { // } // // Contains fast paths for Smi and RegExp objects. + // Important: {regexp_call} may not contain any code that can call into JS. typedef std::function<Node*()> NodeFunction0; typedef std::function<Node*(Node* fn)> NodeFunction1; void MaybeCallFunctionAtSymbol(Node* const context, Node* const object, + Node* const maybe_string, Handle<Symbol> symbol, const NodeFunction0& regexp_call, const NodeFunction1& generic_call, diff --git a/chromium/v8/src/builtins/builtins-typedarray-gen.cc b/chromium/v8/src/builtins/builtins-typedarray-gen.cc index a58f3a4093f..07f122b9098 100644 --- a/chromium/v8/src/builtins/builtins-typedarray-gen.cc +++ b/chromium/v8/src/builtins/builtins-typedarray-gen.cc @@ -10,6 +10,10 @@ namespace v8 { namespace internal { +using compiler::Node; +template <class T> +using TNode = compiler::TNode<T>; + // This is needed for gc_mole which will compile this file without the full set // of GN defined macros. #ifndef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP @@ -41,9 +45,37 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler { Node* CalculateExternalPointer(Node* backing_store, Node* byte_offset); Node* LoadDataPtr(Node* typed_array); Node* ByteLengthIsValid(Node* byte_length); + + // Loads the element kind of TypedArray instance. + TNode<Word32T> LoadElementsKind(TNode<Object> typed_array); + + // Returns the byte size of an element for a TypedArray elements kind. + TNode<IntPtrT> GetTypedArrayElementSize(TNode<Word32T> elements_kind); + + // Fast path for setting a TypedArray (source) onto another TypedArray + // (target) at an element offset. + void SetTypedArraySource(TNode<Context> context, TNode<JSTypedArray> source, + TNode<JSTypedArray> target, TNode<IntPtrT> offset, + Label* call_runtime, Label* if_source_too_large); + + void SetJSArraySource(TNode<Context> context, TNode<JSArray> source, + TNode<JSTypedArray> target, TNode<IntPtrT> offset, + Label* call_runtime, Label* if_source_too_large); + + void CallCMemmove(TNode<IntPtrT> dest_ptr, TNode<IntPtrT> src_ptr, + TNode<IntPtrT> byte_length); + + void CallCCopyFastNumberJSArrayElementsToTypedArray( + TNode<Context> context, TNode<JSArray> source, TNode<JSTypedArray> dest, + TNode<IntPtrT> source_length, TNode<IntPtrT> offset); + + void CallCCopyTypedArrayElementsToTypedArray(TNode<JSTypedArray> source, + TNode<JSTypedArray> dest, + TNode<IntPtrT> source_length, + TNode<IntPtrT> offset); }; -compiler::Node* TypedArrayBuiltinsAssembler::LoadMapForType(Node* array) { +Node* TypedArrayBuiltinsAssembler::LoadMapForType(Node* array) { CSA_ASSERT(this, IsJSTypedArray(array)); Label unreachable(this), done(this); @@ -96,8 +128,8 @@ compiler::Node* TypedArrayBuiltinsAssembler::LoadMapForType(Node* array) { // 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, so we may // need to convert the float heap number to an intptr. -compiler::Node* TypedArrayBuiltinsAssembler::CalculateExternalPointer( - Node* backing_store, Node* byte_offset) { +Node* TypedArrayBuiltinsAssembler::CalculateExternalPointer(Node* backing_store, + Node* byte_offset) { return IntPtrAdd(backing_store, ChangeNumberToIntPtr(byte_offset)); } @@ -518,7 +550,7 @@ TF_BUILTIN(TypedArrayConstructByArrayBuffer, TypedArrayBuiltinsAssembler) { } } -compiler::Node* TypedArrayBuiltinsAssembler::LoadDataPtr(Node* typed_array) { +Node* TypedArrayBuiltinsAssembler::LoadDataPtr(Node* typed_array) { CSA_ASSERT(this, IsJSTypedArray(typed_array)); Node* elements = LoadElements(typed_array); CSA_ASSERT(this, IsFixedTypedArray(elements)); @@ -529,8 +561,7 @@ compiler::Node* TypedArrayBuiltinsAssembler::LoadDataPtr(Node* typed_array) { return IntPtrAdd(base_pointer, external_pointer); } -compiler::Node* TypedArrayBuiltinsAssembler::ByteLengthIsValid( - Node* byte_length) { +Node* TypedArrayBuiltinsAssembler::ByteLengthIsValid(Node* byte_length) { Label smi(this), done(this); VARIABLE(is_valid, MachineRepresentation::kWord32); GotoIf(TaggedIsSmi(byte_length), &smi); @@ -674,6 +705,309 @@ TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) { JSTypedArray::kLengthOffset); } +TNode<Word32T> TypedArrayBuiltinsAssembler::LoadElementsKind( + TNode<Object> typed_array) { + CSA_ASSERT(this, IsJSTypedArray(typed_array)); + return Int32Sub(LoadMapElementsKind(LoadMap(CAST(typed_array))), + Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)); +} + +TNode<IntPtrT> TypedArrayBuiltinsAssembler::GetTypedArrayElementSize( + TNode<Word32T> elements_kind) { + TVARIABLE(IntPtrT, element_size); + Label next(this), if_unknown_type(this, Label::kDeferred); + + size_t const kTypedElementsKindCount = LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND - + FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND + + 1; + + int32_t elements_kinds[kTypedElementsKindCount] = { +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ + TYPE##_ELEMENTS - FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND, + TYPED_ARRAYS(TYPED_ARRAY_CASE) +#undef TYPED_ARRAY_CASE + }; + +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ + Label if_##type##array(this); + TYPED_ARRAYS(TYPED_ARRAY_CASE) +#undef TYPED_ARRAY_CASE + + Label* elements_kind_labels[kTypedElementsKindCount] = { +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) &if_##type##array, + TYPED_ARRAYS(TYPED_ARRAY_CASE) +#undef TYPED_ARRAY_CASE + }; + + Switch(elements_kind, &if_unknown_type, elements_kinds, elements_kind_labels, + kTypedElementsKindCount); + +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ + BIND(&if_##type##array); \ + { \ + element_size = IntPtrConstant(size); \ + Goto(&next); \ + } + TYPED_ARRAYS(TYPED_ARRAY_CASE) +#undef TYPED_ARRAY_CASE + + BIND(&if_unknown_type); + { + element_size = IntPtrConstant(0); + Goto(&next); + } + BIND(&next); + return element_size; +} + +void TypedArrayBuiltinsAssembler::SetTypedArraySource( + TNode<Context> context, TNode<JSTypedArray> source, + TNode<JSTypedArray> target, TNode<IntPtrT> offset, Label* call_runtime, + Label* if_source_too_large) { + CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer( + LoadObjectField(source, JSTypedArray::kBufferOffset)))); + CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer( + LoadObjectField(target, JSTypedArray::kBufferOffset)))); + CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0))); + CSA_ASSERT(this, + IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue))); + + // Check for possible range errors. + + TNode<IntPtrT> source_length = + LoadAndUntagObjectField(source, JSTypedArray::kLengthOffset); + TNode<IntPtrT> target_length = + LoadAndUntagObjectField(target, JSTypedArray::kLengthOffset); + TNode<IntPtrT> required_target_length = IntPtrAdd(source_length, offset); + + GotoIf(IntPtrGreaterThan(required_target_length, target_length), + if_source_too_large); + + // Grab pointers and byte lengths we need later on. + + TNode<IntPtrT> target_data_ptr = UncheckedCast<IntPtrT>(LoadDataPtr(target)); + TNode<IntPtrT> source_data_ptr = UncheckedCast<IntPtrT>(LoadDataPtr(source)); + + TNode<Word32T> source_el_kind = LoadElementsKind(source); + TNode<Word32T> target_el_kind = LoadElementsKind(target); + + TNode<IntPtrT> source_el_size = GetTypedArrayElementSize(source_el_kind); + TNode<IntPtrT> target_el_size = GetTypedArrayElementSize(target_el_kind); + + // A note on byte lengths: both source- and target byte lengths must be valid, + // i.e. it must be possible to allocate an array of the given length. That + // means we're safe from overflows in the following multiplication. + TNode<IntPtrT> source_byte_length = IntPtrMul(source_length, source_el_size); + CSA_ASSERT(this, + IntPtrGreaterThanOrEqual(source_byte_length, IntPtrConstant(0))); + + Label call_memmove(this), fast_c_call(this), out(this); + Branch(Word32Equal(source_el_kind, target_el_kind), &call_memmove, + &fast_c_call); + + BIND(&call_memmove); + { + TNode<IntPtrT> target_start = + IntPtrAdd(target_data_ptr, IntPtrMul(offset, target_el_size)); + CallCMemmove(target_start, source_data_ptr, source_byte_length); + Goto(&out); + } + + BIND(&fast_c_call); + { + // Overlapping backing stores of different element kinds are handled in + // runtime. We're a bit conservative here and bail to runtime if ranges + // overlap and element kinds differ. + + TNode<IntPtrT> target_byte_length = + IntPtrMul(target_length, target_el_size); + CSA_ASSERT(this, + IntPtrGreaterThanOrEqual(target_byte_length, IntPtrConstant(0))); + + TNode<IntPtrT> target_data_end_ptr = + IntPtrAdd(target_data_ptr, target_byte_length); + TNode<IntPtrT> source_data_end_ptr = + IntPtrAdd(source_data_ptr, source_byte_length); + + GotoIfNot( + Word32Or(IntPtrLessThanOrEqual(target_data_end_ptr, source_data_ptr), + IntPtrLessThanOrEqual(source_data_end_ptr, target_data_ptr)), + call_runtime); + + TNode<IntPtrT> source_length = + LoadAndUntagObjectField(source, JSTypedArray::kLengthOffset); + CallCCopyTypedArrayElementsToTypedArray(source, target, source_length, + offset); + Goto(&out); + } + + BIND(&out); +} + +void TypedArrayBuiltinsAssembler::SetJSArraySource( + TNode<Context> context, TNode<JSArray> source, TNode<JSTypedArray> target, + TNode<IntPtrT> offset, Label* call_runtime, Label* if_source_too_large) { + CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0))); + CSA_ASSERT(this, + IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue))); + + TNode<IntPtrT> source_length = SmiUntag(LoadFastJSArrayLength(source)); + TNode<IntPtrT> target_length = + LoadAndUntagObjectField(target, JSTypedArray::kLengthOffset); + + // Maybe out of bounds? + GotoIf(IntPtrGreaterThan(IntPtrAdd(source_length, offset), target_length), + if_source_too_large); + + // Nothing to do if {source} is empty. + Label out(this), fast_c_call(this); + GotoIf(IntPtrEqual(source_length, IntPtrConstant(0)), &out); + + // Dispatch based on the source elements kind. + { + // These are the supported elements kinds in TryCopyElementsFastNumber. + int32_t values[] = { + PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS, + HOLEY_DOUBLE_ELEMENTS, + }; + Label* labels[] = { + &fast_c_call, &fast_c_call, &fast_c_call, &fast_c_call, + }; + STATIC_ASSERT(arraysize(values) == arraysize(labels)); + + TNode<Int32T> source_elements_kind = LoadMapElementsKind(LoadMap(source)); + Switch(source_elements_kind, call_runtime, values, labels, + arraysize(values)); + } + + BIND(&fast_c_call); + CallCCopyFastNumberJSArrayElementsToTypedArray(context, source, target, + source_length, offset); + Goto(&out); + BIND(&out); +} + +void TypedArrayBuiltinsAssembler::CallCMemmove(TNode<IntPtrT> dest_ptr, + TNode<IntPtrT> src_ptr, + TNode<IntPtrT> byte_length) { + TNode<ExternalReference> memmove = + ExternalConstant(ExternalReference::libc_memmove_function(isolate())); + CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(), + MachineType::Pointer(), MachineType::UintPtr(), memmove, + dest_ptr, src_ptr, byte_length); +} + +void TypedArrayBuiltinsAssembler:: + CallCCopyFastNumberJSArrayElementsToTypedArray(TNode<Context> context, + TNode<JSArray> source, + TNode<JSTypedArray> dest, + TNode<IntPtrT> source_length, + TNode<IntPtrT> offset) { + TNode<ExternalReference> f = ExternalConstant( + ExternalReference::copy_fast_number_jsarray_elements_to_typed_array( + isolate())); + CallCFunction5(MachineType::AnyTagged(), MachineType::AnyTagged(), + MachineType::AnyTagged(), MachineType::AnyTagged(), + MachineType::UintPtr(), MachineType::UintPtr(), f, context, + source, dest, source_length, offset); +} + +void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray( + TNode<JSTypedArray> source, TNode<JSTypedArray> dest, + TNode<IntPtrT> source_length, TNode<IntPtrT> offset) { + TNode<ExternalReference> f = ExternalConstant( + ExternalReference::copy_typed_array_elements_to_typed_array(isolate())); + CallCFunction4(MachineType::AnyTagged(), MachineType::AnyTagged(), + MachineType::AnyTagged(), MachineType::UintPtr(), + MachineType::UintPtr(), f, source, dest, source_length, + offset); +} + +// ES #sec-get-%typedarray%.prototype.set +TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) { + TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext)); + CodeStubArguments args( + this, ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount))); + + Label if_source_is_typed_array(this), if_source_is_fast_jsarray(this), + if_offset_is_out_of_bounds(this, Label::kDeferred), + if_source_too_large(this, Label::kDeferred), + if_typed_array_is_neutered(this, Label::kDeferred), + if_receiver_is_not_typedarray(this, Label::kDeferred); + + // Check the receiver is a typed array. + TNode<Object> receiver = args.GetReceiver(); + GotoIf(TaggedIsSmi(receiver), &if_receiver_is_not_typedarray); + GotoIfNot(IsJSTypedArray(receiver), &if_receiver_is_not_typedarray); + + // Normalize offset argument (using ToInteger) and handle heap number cases. + TNode<Object> offset = args.GetOptionalArgumentValue(1, SmiConstant(0)); + TNode<Object> offset_num = ToInteger(context, offset, kTruncateMinusZero); + CSA_ASSERT(this, IsNumberNormalized(offset_num)); + + // Since ToInteger always returns a Smi if the given value is within Smi + // range, and the only corner case of -0.0 has already been truncated to 0.0, + // we can simply throw unless the offset is a non-negative Smi. + // TODO(jgruber): It's an observable spec violation to throw here if + // {offset_num} is a positive number outside the Smi range. Per spec, we need + // to check for detached buffers and call the observable ToObject/ToLength + // operations first. + GotoIfNot(TaggedIsPositiveSmi(offset_num), &if_offset_is_out_of_bounds); + TNode<Smi> offset_smi = CAST(offset_num); + + // Check the receiver is not neutered. + TNode<Object> receiver_buffer = + LoadObjectField(CAST(receiver), JSTypedArray::kBufferOffset); + GotoIf(IsDetachedBuffer(receiver_buffer), &if_typed_array_is_neutered); + + // Check the source argument is valid and whether a fast path can be taken. + Label call_runtime(this); + TNode<Object> source = args.GetOptionalArgumentValue(0); + GotoIf(TaggedIsSmi(source), &call_runtime); + GotoIf(IsJSTypedArray(source), &if_source_is_typed_array); + BranchIfFastJSArray(source, context, &if_source_is_fast_jsarray, + &call_runtime); + + // Fast path for a typed array source argument. + BIND(&if_source_is_typed_array); + { + // Check the source argument is not neutered. + TNode<Object> source_buffer = + LoadObjectField(CAST(source), JSTypedArray::kBufferOffset); + GotoIf(IsDetachedBuffer(source_buffer), &if_typed_array_is_neutered); + + SetTypedArraySource(context, CAST(source), CAST(receiver), + SmiUntag(offset_smi), &call_runtime, + &if_source_too_large); + args.PopAndReturn(UndefinedConstant()); + } + + // Fast path for a fast JSArray source argument. + BIND(&if_source_is_fast_jsarray); + { + SetJSArraySource(context, CAST(source), CAST(receiver), + SmiUntag(offset_smi), &call_runtime, &if_source_too_large); + args.PopAndReturn(UndefinedConstant()); + } + + BIND(&call_runtime); + args.PopAndReturn(CallRuntime(Runtime::kTypedArraySet, context, receiver, + source, offset_smi)); + + BIND(&if_offset_is_out_of_bounds); + ThrowRangeError(context, MessageTemplate::kTypedArraySetOffsetOutOfBounds); + + BIND(&if_source_too_large); + ThrowRangeError(context, MessageTemplate::kTypedArraySetSourceTooLarge); + + BIND(&if_typed_array_is_neutered); + ThrowTypeError(context, MessageTemplate::kDetachedOperation, + "%TypedArray%.prototype.set"); + + BIND(&if_receiver_is_not_typedarray); + ThrowTypeError(context, MessageTemplate::kNotTypedArray); +} + // ES #sec-get-%typedarray%.prototype-@@tostringtag TF_BUILTIN(TypedArrayPrototypeToStringTag, TypedArrayBuiltinsAssembler) { Node* receiver = Parameter(Descriptor::kReceiver); diff --git a/chromium/v8/src/builtins/builtins-typedarray.cc b/chromium/v8/src/builtins/builtins-typedarray.cc index 3d40d3755f8..176a79965b2 100644 --- a/chromium/v8/src/builtins/builtins-typedarray.cc +++ b/chromium/v8/src/builtins/builtins-typedarray.cc @@ -277,302 +277,6 @@ BUILTIN(TypedArrayPrototypeReverse) { return *array; } -namespace { -Object* TypedArrayCopyElements(Handle<JSTypedArray> target, - Handle<JSReceiver> source, Object* length_obj) { - size_t length; - CHECK(TryNumberToSize(length_obj, &length)); - - ElementsAccessor* accessor = target->GetElementsAccessor(); - return accessor->CopyElements(source, target, length); -} - -enum class TypedArraySetResultCodes { - // Set from typed array of the same type. - // This is processed by TypedArraySetFastCases - SAME_TYPE, - // Set from typed array of the different type, overlapping in memory. - OVERLAPPING, - // Set from typed array of the different type, non-overlapping. - NONOVERLAPPING, - // Set from non-typed array. - NON_TYPED_ARRAY -}; - -MaybeHandle<Object> TypedArraySetFromArrayLike(Isolate* isolate, - Handle<JSTypedArray> target, - Handle<Object> source, - int source_length, int offset) { - DCHECK_GE(source_length, 0); - DCHECK_GE(offset, 0); - - for (int i = 0; i < source_length; i++) { - Handle<Object> value; - ASSIGN_RETURN_ON_EXCEPTION(isolate, value, - Object::GetElement(isolate, source, i), Object); - ASSIGN_RETURN_ON_EXCEPTION(isolate, value, - Object::SetElement(isolate, target, offset + i, - value, LanguageMode::STRICT), - Object); - } - - return target; -} - -MaybeHandle<Object> TypedArraySetFromOverlapping(Isolate* isolate, - Handle<JSTypedArray> target, - Handle<JSTypedArray> source, - int offset) { - DCHECK_GE(offset, 0); - - size_t sourceElementSize = source->element_size(); - size_t targetElementSize = target->element_size(); - - uint32_t source_length = source->length_value(); - if (source_length == 0) return target; - - // Copy left part. - - // First un-mutated byte after the next write - uint32_t target_ptr = 0; - CHECK(target->byte_offset()->ToUint32(&target_ptr)); - target_ptr += (offset + 1) * targetElementSize; - - // Next read at sourcePtr. We do not care for memory changing before - // sourcePtr - we have already copied it. - uint32_t source_ptr = 0; - CHECK(source->byte_offset()->ToUint32(&source_ptr)); - - uint32_t left_index; - for (left_index = 0; left_index < source_length && target_ptr <= source_ptr; - left_index++) { - Handle<Object> value; - ASSIGN_RETURN_ON_EXCEPTION(isolate, value, - Object::GetElement(isolate, source, left_index), - Object); - ASSIGN_RETURN_ON_EXCEPTION( - isolate, value, - Object::SetElement(isolate, target, offset + left_index, value, - LanguageMode::STRICT), - Object); - - target_ptr += targetElementSize; - source_ptr += sourceElementSize; - } - - // Copy right part; - // First unmutated byte before the next write - CHECK(target->byte_offset()->ToUint32(&target_ptr)); - target_ptr += (offset + source_length - 1) * targetElementSize; - - // Next read before sourcePtr. We do not care for memory changing after - // sourcePtr - we have already copied it. - CHECK(target->byte_offset()->ToUint32(&source_ptr)); - source_ptr += source_length * sourceElementSize; - - uint32_t right_index; - DCHECK_GE(source_length, 1); - for (right_index = source_length - 1; - right_index > left_index && target_ptr >= source_ptr; right_index--) { - Handle<Object> value; - ASSIGN_RETURN_ON_EXCEPTION(isolate, value, - Object::GetElement(isolate, source, right_index), - Object); - ASSIGN_RETURN_ON_EXCEPTION( - isolate, value, - Object::SetElement(isolate, target, offset + right_index, value, - LanguageMode::STRICT), - Object); - - target_ptr -= targetElementSize; - source_ptr -= sourceElementSize; - } - - std::vector<Handle<Object>> temp(right_index + 1 - left_index); - - for (uint32_t i = left_index; i <= right_index; i++) { - Handle<Object> value; - ASSIGN_RETURN_ON_EXCEPTION(isolate, value, - Object::GetElement(isolate, source, i), Object); - temp[i - left_index] = value; - } - - for (uint32_t i = left_index; i <= right_index; i++) { - Handle<Object> value; - - ASSIGN_RETURN_ON_EXCEPTION( - isolate, value, - Object::SetElement(isolate, target, offset + i, temp[i - left_index], - LanguageMode::STRICT), - Object); - } - - return target; -} - -MaybeHandle<Smi> TypedArraySetFastCases(Isolate* isolate, - Handle<JSTypedArray> target, - Handle<Object> source_obj, - Handle<Object> offset_obj) { - if (!source_obj->IsJSTypedArray()) { - return MaybeHandle<Smi>( - Smi::FromEnum(TypedArraySetResultCodes::NON_TYPED_ARRAY), isolate); - } - - Handle<JSTypedArray> source = Handle<JSTypedArray>::cast(source_obj); - - size_t offset = 0; - CHECK(TryNumberToSize(*offset_obj, &offset)); - size_t target_length = target->length_value(); - size_t source_length = source->length_value(); - size_t target_byte_length = NumberToSize(target->byte_length()); - size_t source_byte_length = NumberToSize(source->byte_length()); - if (offset > target_length || offset + source_length > target_length || - offset + source_length < offset) { // overflow - THROW_NEW_ERROR( - isolate, NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge), - Smi); - } - - size_t target_offset = NumberToSize(target->byte_offset()); - size_t source_offset = NumberToSize(source->byte_offset()); - uint8_t* target_base = - static_cast<uint8_t*>(target->GetBuffer()->backing_store()) + - target_offset; - uint8_t* source_base = - static_cast<uint8_t*>(source->GetBuffer()->backing_store()) + - source_offset; - - // Typed arrays of the same type: use memmove. - if (target->type() == source->type()) { - memmove(target_base + offset * target->element_size(), source_base, - source_byte_length); - return MaybeHandle<Smi>(Smi::FromEnum(TypedArraySetResultCodes::SAME_TYPE), - isolate); - } - - // Typed arrays of different types over the same backing store - if ((source_base <= target_base && - source_base + source_byte_length > target_base) || - (target_base <= source_base && - target_base + target_byte_length > source_base)) { - // We do not support overlapping ArrayBuffers - DCHECK(target->GetBuffer()->backing_store() == - source->GetBuffer()->backing_store()); - return MaybeHandle<Smi>( - Smi::FromEnum(TypedArraySetResultCodes::OVERLAPPING), isolate); - } else { // Non-overlapping typed arrays - return MaybeHandle<Smi>( - Smi::FromEnum(TypedArraySetResultCodes::NONOVERLAPPING), isolate); - } -} - -} // anonymous namespace - -// 22.2.3.23%TypedArray%.prototype.set ( overloaded [ , offset ] ) -BUILTIN(TypedArrayPrototypeSet) { - HandleScope scope(isolate); - - Handle<Object> target = args.receiver(); - Handle<Object> obj = args.atOrUndefined(isolate, 1); - Handle<Object> offset = args.atOrUndefined(isolate, 2); - - if (offset->IsUndefined(isolate)) { - offset = Handle<Object>(Smi::kZero, isolate); - } else { - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, offset, - Object::ToInteger(isolate, offset)); - } - - if (offset->Number() < 0) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewRangeError(MessageTemplate::kTypedArraySetNegativeOffset)); - } - - if (offset->Number() > Smi::kMaxValue) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge)); - } - - if (!target->IsJSTypedArray()) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewTypeError(MessageTemplate::kNotTypedArray)); - } - auto int_offset = static_cast<int>(offset->Number()); - - Handle<Smi> result_code; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, result_code, - TypedArraySetFastCases(isolate, Handle<JSTypedArray>::cast(target), obj, - offset)); - - switch (static_cast<TypedArraySetResultCodes>(result_code->value())) { - case TypedArraySetResultCodes::SAME_TYPE: { - break; - } - case TypedArraySetResultCodes::OVERLAPPING: { - RETURN_FAILURE_ON_EXCEPTION( - isolate, TypedArraySetFromOverlapping( - isolate, Handle<JSTypedArray>::cast(target), - Handle<JSTypedArray>::cast(obj), int_offset)); - break; - } - case TypedArraySetResultCodes::NONOVERLAPPING: { - if (int_offset == 0) { - TypedArrayCopyElements(Handle<JSTypedArray>::cast(target), - Handle<JSTypedArray>::cast(obj), - Handle<JSTypedArray>::cast(obj)->length()); - } else { - RETURN_FAILURE_ON_EXCEPTION( - isolate, - TypedArraySetFromArrayLike( - isolate, Handle<JSTypedArray>::cast(target), obj, - Handle<JSTypedArray>::cast(obj)->length_value(), int_offset)); - } - break; - } - case TypedArraySetResultCodes::NON_TYPED_ARRAY: { - if (obj->IsNumber()) { - // 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) - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewTypeError(MessageTemplate::kInvalidArgument)); - } - - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, obj, - Object::ToObject(isolate, obj)); - - Handle<Object> len; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, len, - Object::GetProperty(obj, isolate->factory()->length_string())); - if (len->IsUndefined(isolate)) { - break; - } - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len, - Object::ToLength(isolate, len)); - - DCHECK_GE(int_offset, 0); - if (int_offset + len->Number() > - Handle<JSTypedArray>::cast(target)->length_value()) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, - NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge)); - } - uint32_t int_l; - CHECK(DoubleToUint32IfEqualToSelf(len->Number(), &int_l)); - RETURN_FAILURE_ON_EXCEPTION( - isolate, TypedArraySetFromArrayLike( - isolate, Handle<JSTypedArray>::cast(target), obj, int_l, - int_offset)); - } break; - } - - return *isolate->factory()->undefined_value(); -} - BUILTIN(TypedArrayPrototypeSlice) { HandleScope scope(isolate); diff --git a/chromium/v8/src/code-stub-assembler.cc b/chromium/v8/src/code-stub-assembler.cc index 612d9b76af7..0c64d011d41 100644 --- a/chromium/v8/src/code-stub-assembler.cc +++ b/chromium/v8/src/code-stub-assembler.cc @@ -1253,8 +1253,8 @@ TNode<Int32T> CodeStubAssembler::LoadHashForJSObject( { Node* length_and_hash_int32 = LoadAndUntagToWord32ObjectField( properties_or_hash, PropertyArray::kLengthAndHashOffset); - var_hash.Bind(Word32And(length_and_hash_int32, - Int32Constant(PropertyArray::kHashMask))); + var_hash.Bind( + DecodeWord32<PropertyArray::HashField>(length_and_hash_int32)); Goto(&done); } @@ -2322,6 +2322,7 @@ Node* CodeStubAssembler::AllocateNameDictionary(Node* at_least_space_for) { Node* CodeStubAssembler::AllocateNameDictionaryWithCapacity(Node* capacity) { CSA_ASSERT(this, WordIsPowerOfTwo(capacity)); + CSA_ASSERT(this, IntPtrGreaterThan(capacity, IntPtrConstant(0))); Node* length = EntryToIndex<NameDictionary>(capacity); Node* store_size = IntPtrAdd(TimesPointerSize(length), IntPtrConstant(NameDictionary::kHeaderSize)); @@ -2644,7 +2645,8 @@ void CodeStubAssembler::InitializePropertyArrayLength(Node* property_array, CSA_ASSERT( this, IntPtrOrSmiLessThanOrEqual( - length, IntPtrOrSmiConstant(PropertyArray::kMaxLength, mode), mode)); + length, IntPtrOrSmiConstant(PropertyArray::LengthField::kMax, mode), + mode)); StoreObjectFieldNoWriteBarrier( property_array, PropertyArray::kLengthAndHashOffset, ParameterToTagged(length, mode), MachineRepresentation::kTaggedSigned); @@ -3539,6 +3541,23 @@ Node* CodeStubAssembler::ThrowIfNotInstanceType(Node* context, Node* value, return var_value_map.value(); } +void CodeStubAssembler::ThrowRangeError(Node* context, + MessageTemplate::Template message, + Node* arg0, Node* arg1, Node* arg2) { + Node* template_index = SmiConstant(message); + if (arg0 == nullptr) { + CallRuntime(Runtime::kThrowRangeError, context, template_index); + } else if (arg1 == nullptr) { + CallRuntime(Runtime::kThrowRangeError, context, template_index, arg0); + } else if (arg2 == nullptr) { + CallRuntime(Runtime::kThrowRangeError, context, template_index, arg0, arg1); + } else { + CallRuntime(Runtime::kThrowRangeError, context, template_index, arg0, arg1, + arg2); + } + Unreachable(); +} + void CodeStubAssembler::ThrowTypeError(Node* context, MessageTemplate::Template message, char const* arg0, char const* arg1) { @@ -6166,7 +6185,8 @@ void CodeStubAssembler::LoadPropertyFromGlobalDictionary(Node* dictionary, // Returns either the original value, or the result of the getter call. Node* CodeStubAssembler::CallGetterIfAccessor(Node* value, Node* details, Node* context, Node* receiver, - Label* if_bailout) { + Label* if_bailout, + GetOwnPropertyMode mode) { VARIABLE(var_value, MachineRepresentation::kTagged, value); Label done(this), if_accessor_info(this, Label::kDeferred); @@ -6178,23 +6198,26 @@ Node* CodeStubAssembler::CallGetterIfAccessor(Node* value, Node* details, // AccessorPair case. { - Node* accessor_pair = value; - Node* getter = LoadObjectField(accessor_pair, AccessorPair::kGetterOffset); - Node* getter_map = LoadMap(getter); - Node* instance_type = LoadMapInstanceType(getter_map); - // FunctionTemplateInfo getters are not supported yet. - GotoIf( - Word32Equal(instance_type, Int32Constant(FUNCTION_TEMPLATE_INFO_TYPE)), - if_bailout); - - // Return undefined if the {getter} is not callable. - var_value.Bind(UndefinedConstant()); - GotoIfNot(IsCallableMap(getter_map), &done); - - // Call the accessor. - Callable callable = CodeFactory::Call(isolate()); - Node* result = CallJS(callable, context, getter, receiver); - var_value.Bind(result); + if (mode == kCallJSGetter) { + Node* accessor_pair = value; + Node* getter = + LoadObjectField(accessor_pair, AccessorPair::kGetterOffset); + Node* getter_map = LoadMap(getter); + Node* instance_type = LoadMapInstanceType(getter_map); + // FunctionTemplateInfo getters are not supported yet. + GotoIf(Word32Equal(instance_type, + Int32Constant(FUNCTION_TEMPLATE_INFO_TYPE)), + if_bailout); + + // Return undefined if the {getter} is not callable. + var_value.Bind(UndefinedConstant()); + GotoIfNot(IsCallableMap(getter_map), &done); + + // Call the accessor. + Callable callable = CodeFactory::Call(isolate()); + Node* result = CallJS(callable, context, getter, receiver); + var_value.Bind(result); + } Goto(&done); } @@ -6264,14 +6287,14 @@ void CodeStubAssembler::TryGetOwnProperty( Label* if_not_found, Label* if_bailout) { TryGetOwnProperty(context, receiver, object, map, instance_type, unique_name, if_found_value, var_value, nullptr, nullptr, if_not_found, - if_bailout); + if_bailout, kCallJSGetter); } void CodeStubAssembler::TryGetOwnProperty( Node* context, Node* receiver, Node* object, Node* map, Node* instance_type, Node* unique_name, Label* if_found_value, Variable* var_value, Variable* var_details, Variable* var_raw_value, Label* if_not_found, - Label* if_bailout) { + Label* if_bailout, GetOwnPropertyMode mode) { DCHECK_EQ(MachineRepresentation::kTagged, var_value->rep()); Comment("TryGetOwnProperty"); @@ -6322,7 +6345,7 @@ void CodeStubAssembler::TryGetOwnProperty( var_raw_value->Bind(var_value->value()); } Node* value = CallGetterIfAccessor(var_value->value(), var_details->value(), - context, receiver, if_bailout); + context, receiver, if_bailout, mode); var_value->Bind(value); Goto(if_found_value); } diff --git a/chromium/v8/src/code-stub-assembler.h b/chromium/v8/src/code-stub-assembler.h index 7c7777e1acc..a2d5e800156 100644 --- a/chromium/v8/src/code-stub-assembler.h +++ b/chromium/v8/src/code-stub-assembler.h @@ -867,6 +867,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { Node* ThrowIfNotInstanceType(Node* context, Node* value, InstanceType instance_type, char const* method_name); + + void ThrowRangeError(Node* context, MessageTemplate::Template message, + Node* arg0 = nullptr, Node* arg1 = nullptr, + Node* arg2 = nullptr); void ThrowTypeError(Node* context, MessageTemplate::Template message, char const* arg0 = nullptr, char const* arg1 = nullptr); void ThrowTypeError(Node* context, MessageTemplate::Template message, @@ -1321,6 +1325,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { Node* unique_name, Label* if_found, Label* if_not_found, Label* if_bailout); + // Operating mode for TryGetOwnProperty and CallGetterIfAccessor + // kReturnAccessorPair is used when we're only getting the property descriptor + enum GetOwnPropertyMode { kCallJSGetter, kReturnAccessorPair }; // Tries to get {object}'s own {unique_name} property value. If the property // is an accessor then it also calls a getter. If the property is a double // field it re-wraps value in an immutable heap number. @@ -1332,7 +1339,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { Node* instance_type, Node* unique_name, Label* if_found, Variable* var_value, Variable* var_details, Variable* var_raw_value, - Label* if_not_found, Label* if_bailout); + Label* if_not_found, Label* if_bailout, + GetOwnPropertyMode mode); Node* GetProperty(Node* context, Node* receiver, Handle<Name> name) { return GetProperty(context, receiver, HeapConstant(name)); @@ -1669,7 +1677,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { Node* DescriptorArrayGetKey(Node* descriptors, Node* descriptor_number); Node* CallGetterIfAccessor(Node* value, Node* details, Node* context, - Node* receiver, Label* if_bailout); + Node* receiver, Label* if_bailout, + GetOwnPropertyMode mode = kCallJSGetter); Node* TryToIntptr(Node* key, Label* miss); diff --git a/chromium/v8/src/compiler/arm/code-generator-arm.cc b/chromium/v8/src/compiler/arm/code-generator-arm.cc index c2de64467fd..fa9f6a027e6 100644 --- a/chromium/v8/src/compiler/arm/code-generator-arm.cc +++ b/chromium/v8/src/compiler/arm/code-generator-arm.cc @@ -424,51 +424,51 @@ Condition FlagsConditionToCondition(FlagsCondition condition) { __ dmb(ISH); \ } while (0) -#define ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(load_instr, store_instr) \ - do { \ - Label exchange; \ - __ dmb(ISH); \ - __ bind(&exchange); \ - __ add(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \ - __ load_instr(i.OutputRegister(0), i.TempRegister(0)); \ - __ store_instr(i.TempRegister(0), i.InputRegister(2), i.TempRegister(0)); \ - __ teq(i.TempRegister(0), Operand(0)); \ - __ b(ne, &exchange); \ - __ dmb(ISH); \ +#define ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(load_instr, store_instr) \ + do { \ + Label exchange; \ + __ add(i.InputRegister(0), i.InputRegister(0), i.InputRegister(1)); \ + __ dmb(ISH); \ + __ bind(&exchange); \ + __ load_instr(i.OutputRegister(0), i.InputRegister(0)); \ + __ store_instr(i.TempRegister(0), i.InputRegister(2), i.InputRegister(0)); \ + __ teq(i.TempRegister(0), Operand(0)); \ + __ b(ne, &exchange); \ + __ dmb(ISH); \ } while (0) -#define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(load_instr, store_instr) \ +#define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(load_instr, store_instr) \ + do { \ + Label compareExchange; \ + Label exit; \ + __ add(i.InputRegister(0), i.InputRegister(0), i.InputRegister(1)); \ + __ dmb(ISH); \ + __ bind(&compareExchange); \ + __ load_instr(i.OutputRegister(0), i.InputRegister(0)); \ + __ teq(i.InputRegister(2), Operand(i.OutputRegister(0))); \ + __ b(ne, &exit); \ + __ store_instr(i.TempRegister(0), i.InputRegister(3), i.InputRegister(0)); \ + __ teq(i.TempRegister(0), Operand(0)); \ + __ b(ne, &compareExchange); \ + __ bind(&exit); \ + __ dmb(ISH); \ + } while (0) + +#define ASSEMBLE_ATOMIC_BINOP(load_instr, store_instr, bin_instr) \ do { \ - Label compareExchange; \ - Label exit; \ + Label binop; \ + __ add(i.InputRegister(0), i.InputRegister(0), i.InputRegister(1)); \ __ dmb(ISH); \ - __ bind(&compareExchange); \ - __ add(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \ - __ load_instr(i.OutputRegister(0), i.TempRegister(0)); \ - __ teq(i.TempRegister(1), Operand(i.OutputRegister(0))); \ - __ b(ne, &exit); \ - __ store_instr(i.TempRegister(0), i.InputRegister(3), i.TempRegister(0)); \ - __ teq(i.TempRegister(0), Operand(0)); \ - __ b(ne, &compareExchange); \ - __ bind(&exit); \ + __ bind(&binop); \ + __ load_instr(i.OutputRegister(0), i.InputRegister(0)); \ + __ bin_instr(i.TempRegister(0), i.OutputRegister(0), \ + Operand(i.InputRegister(2))); \ + __ store_instr(i.TempRegister(1), i.TempRegister(0), i.InputRegister(0)); \ + __ teq(i.TempRegister(1), Operand(0)); \ + __ b(ne, &binop); \ __ dmb(ISH); \ } while (0) -#define ASSEMBLE_ATOMIC_BINOP(load_instr, store_instr, bin_instr) \ - do { \ - Label binop; \ - __ add(i.TempRegister(0), i.InputRegister(0), i.InputRegister(1)); \ - __ dmb(ISH); \ - __ bind(&binop); \ - __ load_instr(i.OutputRegister(0), i.TempRegister(0)); \ - __ bin_instr(i.TempRegister(1), i.OutputRegister(0), \ - Operand(i.InputRegister(2))); \ - __ store_instr(i.TempRegister(1), i.TempRegister(1), i.TempRegister(0)); \ - __ teq(i.TempRegister(1), Operand(0)); \ - __ b(ne, &binop); \ - __ dmb(ISH); \ - } while (0) - #define ASSEMBLE_IEEE754_BINOP(name) \ do { \ /* TODO(bmeurer): We should really get rid of this special instruction, */ \ @@ -2588,25 +2588,24 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( ASSEMBLE_ATOMIC_EXCHANGE_INTEGER(ldrex, strex); break; case kAtomicCompareExchangeInt8: - __ uxtb(i.TempRegister(1), i.InputRegister(2)); + __ uxtb(i.InputRegister(2), i.InputRegister(2)); ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(ldrexb, strexb); __ sxtb(i.OutputRegister(0), i.OutputRegister(0)); break; case kAtomicCompareExchangeUint8: - __ uxtb(i.TempRegister(1), i.InputRegister(2)); + __ uxtb(i.InputRegister(2), i.InputRegister(2)); ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(ldrexb, strexb); break; case kAtomicCompareExchangeInt16: - __ uxth(i.TempRegister(1), i.InputRegister(2)); + __ uxth(i.InputRegister(2), i.InputRegister(2)); ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(ldrexh, strexh); __ sxth(i.OutputRegister(0), i.OutputRegister(0)); break; case kAtomicCompareExchangeUint16: - __ uxth(i.TempRegister(1), i.InputRegister(2)); + __ uxth(i.InputRegister(2), i.InputRegister(2)); ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(ldrexh, strexh); break; case kAtomicCompareExchangeWord32: - __ mov(i.TempRegister(1), i.InputRegister(2)); ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER(ldrex, strex); break; #define ATOMIC_BINOP_CASE(op, inst) \ @@ -2633,6 +2632,19 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( ATOMIC_BINOP_CASE(Or, orr) ATOMIC_BINOP_CASE(Xor, eor) #undef ATOMIC_BINOP_CASE +#undef ASSEMBLE_CHECKED_LOAD_FP +#undef ASSEMBLE_CHECKED_LOAD_INTEGER +#undef ASSEMBLE_CHECKED_STORE_FP +#undef ASSEMBLE_CHECKED_STORE_INTEGER +#undef ASSEMBLE_ATOMIC_LOAD_INTEGER +#undef ASSEMBLE_ATOMIC_STORE_INTEGER +#undef ASSEMBLE_ATOMIC_EXCHANGE_INTEGER +#undef ASSEMBLE_ATOMIC_COMPARE_EXCHANGE_INTEGER +#undef ASSEMBLE_ATOMIC_BINOP +#undef ASSEMBLE_IEEE754_BINOP +#undef ASSEMBLE_IEEE754_UNOP +#undef ASSEMBLE_NEON_NARROWING_OP +#undef ASSEMBLE_NEON_PAIRWISE_OP } return kSuccess; } // NOLINT(readability/fn_size) @@ -3226,6 +3238,7 @@ void CodeGenerator::AssembleJumpTable(Label** targets, size_t target_count) { } #undef __ +#undef kScratchReg } // namespace compiler } // namespace internal diff --git a/chromium/v8/src/compiler/arm/instruction-selector-arm.cc b/chromium/v8/src/compiler/arm/instruction-selector-arm.cc index 75021eb9d31..391356e960b 100644 --- a/chromium/v8/src/compiler/arm/instruction-selector-arm.cc +++ b/chromium/v8/src/compiler/arm/instruction-selector-arm.cc @@ -2275,10 +2275,10 @@ void InstructionSelector::VisitAtomicExchange(Node* node) { InstructionOperand inputs[3]; size_t input_count = 0; inputs[input_count++] = g.UseUniqueRegister(base); - inputs[input_count++] = g.UseUniqueRegister(index); + inputs[input_count++] = g.UseRegister(index); inputs[input_count++] = g.UseUniqueRegister(value); InstructionOperand outputs[1]; - outputs[0] = g.UseUniqueRegister(node); + outputs[0] = g.DefineAsRegister(node); InstructionOperand temp[1]; temp[0] = g.TempRegister(); InstructionCode code = opcode | AddressingModeField::encode(addressing_mode); @@ -2312,16 +2312,15 @@ void InstructionSelector::VisitAtomicCompareExchange(Node* node) { InstructionOperand inputs[4]; size_t input_count = 0; inputs[input_count++] = g.UseUniqueRegister(base); - inputs[input_count++] = g.UseUniqueRegister(index); + inputs[input_count++] = g.UseRegister(index); inputs[input_count++] = g.UseUniqueRegister(old_value); inputs[input_count++] = g.UseUniqueRegister(new_value); InstructionOperand outputs[1]; - outputs[0] = g.UseUniqueRegister(node); - InstructionOperand temp[2]; + outputs[0] = g.DefineAsRegister(node); + InstructionOperand temp[1]; temp[0] = g.TempRegister(); - temp[1] = g.TempRegister(); InstructionCode code = opcode | AddressingModeField::encode(addressing_mode); - Emit(code, 1, outputs, input_count, inputs, 2, temp); + Emit(code, 1, outputs, input_count, inputs, 1, temp); } void InstructionSelector::VisitAtomicBinaryOperation( @@ -2352,15 +2351,16 @@ void InstructionSelector::VisitAtomicBinaryOperation( InstructionOperand inputs[3]; size_t input_count = 0; inputs[input_count++] = g.UseUniqueRegister(base); - inputs[input_count++] = g.UseUniqueRegister(index); + inputs[input_count++] = g.UseRegister(index); inputs[input_count++] = g.UseUniqueRegister(value); InstructionOperand outputs[1]; - outputs[0] = g.UseUniqueRegister(node); + outputs[0] = g.DefineAsRegister(node); InstructionOperand temps[2]; - temps[0] = g.TempRegister(); - temps[1] = g.TempRegister(); + size_t temp_count = 0; + temps[temp_count++] = g.TempRegister(); + temps[temp_count++] = g.TempRegister(); InstructionCode code = opcode | AddressingModeField::encode(addressing_mode); - Emit(code, 1, outputs, input_count, inputs, 2, temps); + Emit(code, 1, outputs, input_count, inputs, temp_count, temps); } #define VISIT_ATOMIC_BINOP(op) \ diff --git a/chromium/v8/src/compiler/code-assembler.cc b/chromium/v8/src/compiler/code-assembler.cc index aa3ba471973..a0ed0af93db 100644 --- a/chromium/v8/src/compiler/code-assembler.cc +++ b/chromium/v8/src/compiler/code-assembler.cc @@ -1150,6 +1150,25 @@ Node* CodeAssembler::CallCFunction3WithCallerSavedRegisters( mode); } +Node* CodeAssembler::CallCFunction4( + MachineType return_type, MachineType arg0_type, MachineType arg1_type, + MachineType arg2_type, MachineType arg3_type, Node* function, Node* arg0, + Node* arg1, Node* arg2, Node* arg3) { + return raw_assembler()->CallCFunction4(return_type, arg0_type, arg1_type, + arg2_type, arg3_type, function, arg0, + arg1, arg2, arg3); +} + +Node* CodeAssembler::CallCFunction5( + MachineType return_type, MachineType arg0_type, MachineType arg1_type, + MachineType arg2_type, MachineType arg3_type, MachineType arg4_type, + Node* function, Node* arg0, Node* arg1, Node* arg2, Node* arg3, + Node* arg4) { + return raw_assembler()->CallCFunction5( + return_type, arg0_type, arg1_type, arg2_type, arg3_type, arg4_type, + function, arg0, arg1, arg2, arg3, arg4); +} + Node* CodeAssembler::CallCFunction6( MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType arg2_type, MachineType arg3_type, MachineType arg4_type, diff --git a/chromium/v8/src/compiler/code-assembler.h b/chromium/v8/src/compiler/code-assembler.h index 159d4d7c12b..64e959a1c09 100644 --- a/chromium/v8/src/compiler/code-assembler.h +++ b/chromium/v8/src/compiler/code-assembler.h @@ -805,6 +805,19 @@ class V8_EXPORT_PRIVATE CodeAssembler { MachineType arg2_type, Node* function, Node* arg0, Node* arg1, Node* arg2, SaveFPRegsMode mode); + // Call to a C function with four arguments. + Node* CallCFunction4(MachineType return_type, MachineType arg0_type, + MachineType arg1_type, MachineType arg2_type, + MachineType arg3_type, Node* function, Node* arg0, + Node* arg1, Node* arg2, Node* arg3); + + // Call to a C function with five arguments. + Node* CallCFunction5(MachineType return_type, MachineType arg0_type, + MachineType arg1_type, MachineType arg2_type, + MachineType arg3_type, MachineType arg4_type, + Node* function, Node* arg0, Node* arg1, Node* arg2, + Node* arg3, Node* arg4); + // Call to a C function with six arguments. Node* CallCFunction6(MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType arg2_type, diff --git a/chromium/v8/src/compiler/common-operator.cc b/chromium/v8/src/compiler/common-operator.cc index bda0e20ed58..1693e90ec24 100644 --- a/chromium/v8/src/compiler/common-operator.cc +++ b/chromium/v8/src/compiler/common-operator.cc @@ -1258,7 +1258,7 @@ ArgumentsStateType ArgumentsStateTypeOf(Operator const* op) { return OpParameter<ArgumentsStateType>(op); } -const Operator* CommonOperatorBuilder::ObjectState(int object_id, +const Operator* CommonOperatorBuilder::ObjectState(uint32_t object_id, int pointer_slots) { return new (zone()) Operator1<ObjectStateInfo>( // -- IrOpcode::kObjectState, Operator::kPure, // opcode @@ -1268,7 +1268,7 @@ const Operator* CommonOperatorBuilder::ObjectState(int object_id, } const Operator* CommonOperatorBuilder::TypedObjectState( - int object_id, const ZoneVector<MachineType>* types) { + uint32_t object_id, const ZoneVector<MachineType>* types) { return new (zone()) Operator1<TypedObjectStateInfo>( // -- IrOpcode::kTypedObjectState, Operator::kPure, // opcode "TypedObjectState", // name diff --git a/chromium/v8/src/compiler/common-operator.h b/chromium/v8/src/compiler/common-operator.h index 33ddaf96646..4f722676178 100644 --- a/chromium/v8/src/compiler/common-operator.h +++ b/chromium/v8/src/compiler/common-operator.h @@ -125,7 +125,8 @@ V8_EXPORT_PRIVATE int ParameterIndexOf(const Operator* const); const ParameterInfo& ParameterInfoOf(const Operator* const); struct ObjectStateInfo final : std::pair<uint32_t, int> { - using std::pair<uint32_t, int>::pair; + ObjectStateInfo(uint32_t object_id, int size) + : std::pair<uint32_t, int>(object_id, size) {} uint32_t object_id() const { return first; } int size() const { return second; } }; @@ -134,7 +135,10 @@ size_t hash_value(ObjectStateInfo const& p); struct TypedObjectStateInfo final : std::pair<uint32_t, const ZoneVector<MachineType>*> { - using std::pair<uint32_t, const ZoneVector<MachineType>*>::pair; + TypedObjectStateInfo(uint32_t object_id, + const ZoneVector<MachineType>* machine_types) + : std::pair<uint32_t, const ZoneVector<MachineType>*>(object_id, + machine_types) {} uint32_t object_id() const { return first; } const ZoneVector<MachineType>* machine_types() const { return second; } }; @@ -403,8 +407,8 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final SparseInputMask bitmask); const Operator* ArgumentsElementsState(ArgumentsStateType type); const Operator* ArgumentsLengthState(ArgumentsStateType type); - const Operator* ObjectState(int object_id, int pointer_slots); - const Operator* TypedObjectState(int object_id, + const Operator* ObjectState(uint32_t object_id, int pointer_slots); + const Operator* TypedObjectState(uint32_t object_id, const ZoneVector<MachineType>* types); const Operator* FrameState(BailoutId bailout_id, OutputFrameStateCombine state_combine, diff --git a/chromium/v8/src/compiler/effect-control-linearizer.cc b/chromium/v8/src/compiler/effect-control-linearizer.cc index 3e0ac820dd3..d886fda97a8 100644 --- a/chromium/v8/src/compiler/effect-control-linearizer.cc +++ b/chromium/v8/src/compiler/effect-control-linearizer.cc @@ -718,6 +718,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, case IrOpcode::kObjectIsCallable: result = LowerObjectIsCallable(node); break; + case IrOpcode::kObjectIsConstructor: + result = LowerObjectIsConstructor(node); + break; case IrOpcode::kObjectIsDetectableCallable: result = LowerObjectIsDetectableCallable(node); break; @@ -1931,6 +1934,31 @@ Node* EffectControlLinearizer::LowerObjectIsCallable(Node* node) { return done.PhiAt(0); } +Node* EffectControlLinearizer::LowerObjectIsConstructor(Node* node) { + Node* value = node->InputAt(0); + + auto if_smi = __ MakeDeferredLabel(); + auto done = __ MakeLabel(MachineRepresentation::kBit); + + Node* check = ObjectIsSmi(value); + __ GotoIf(check, &if_smi); + + Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); + Node* value_bit_field = + __ LoadField(AccessBuilder::ForMapBitField(), value_map); + Node* vfalse = + __ Word32Equal(__ Int32Constant(1 << Map::kIsConstructor), + __ Word32And(value_bit_field, + __ Int32Constant(1 << Map::kIsConstructor))); + __ Goto(&done, vfalse); + + __ Bind(&if_smi); + __ Goto(&done, __ Int32Constant(0)); + + __ Bind(&done); + return done.PhiAt(0); +} + Node* EffectControlLinearizer::LowerObjectIsDetectableCallable(Node* node) { Node* value = node->InputAt(0); diff --git a/chromium/v8/src/compiler/effect-control-linearizer.h b/chromium/v8/src/compiler/effect-control-linearizer.h index 46ef687d601..e17f097e9e6 100644 --- a/chromium/v8/src/compiler/effect-control-linearizer.h +++ b/chromium/v8/src/compiler/effect-control-linearizer.h @@ -86,6 +86,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { Node* LowerCheckedTruncateTaggedToWord32(Node* node, Node* frame_state); Node* LowerObjectIsArrayBufferView(Node* node); Node* LowerObjectIsCallable(Node* node); + Node* LowerObjectIsConstructor(Node* node); Node* LowerObjectIsDetectableCallable(Node* node); Node* LowerObjectIsMinusZero(Node* node); Node* LowerObjectIsNaN(Node* node); diff --git a/chromium/v8/src/compiler/escape-analysis.cc b/chromium/v8/src/compiler/escape-analysis.cc index 45829734c5d..ab2b06a9528 100644 --- a/chromium/v8/src/compiler/escape-analysis.cc +++ b/chromium/v8/src/compiler/escape-analysis.cc @@ -125,7 +125,18 @@ class VariableTracker { public: Scope(VariableTracker* tracker, Node* node, Reduction* reduction); ~Scope(); - Node* Get(Variable var) { return current_state_.Get(var); } + Maybe<Node*> Get(Variable var) { + Node* node = current_state_.Get(var); + if (node && node->opcode() == IrOpcode::kDead) { + // TODO(tebbi): We use {Dead} as a sentinel for uninitialized memory. + // Reading uninitialized memory can only happen in unreachable code. In + // this case, we have to mark the object as escaping to avoid dead nodes + // in the graph. This is a workaround that should be removed once we can + // handle dead nodes everywhere. + return Nothing<Node*>(); + } + return Just(node); + } void Set(Variable var, Node* node) { current_state_.Set(var, node); } private: @@ -585,14 +596,12 @@ void ReduceNode(const Operator* op, EscapeAnalysisTracker::Scope* current, Node* object = current->ValueInput(0); const VirtualObject* vobject = current->GetVirtualObject(object); Variable var; + Node* value; if (vobject && !vobject->HasEscaped() && - vobject->FieldAt(OffsetOfFieldAccess(op)).To(&var)) { - current->SetReplacement(current->Get(var)); + vobject->FieldAt(OffsetOfFieldAccess(op)).To(&var) && + current->Get(var).To(&value)) { + current->SetReplacement(value); } else { - // TODO(tebbi): At the moment, we mark objects as escaping if there - // is a load from an invalid location to avoid dead nodes. This is a - // workaround that should be removed once we can handle dead nodes - // everywhere. current->SetEscaped(object); } break; @@ -603,10 +612,11 @@ void ReduceNode(const Operator* op, EscapeAnalysisTracker::Scope* current, const VirtualObject* vobject = current->GetVirtualObject(object); int offset; Variable var; + Node* value; if (vobject && !vobject->HasEscaped() && OffsetOfElementsAccess(op, index).To(&offset) && - vobject->FieldAt(offset).To(&var)) { - current->SetReplacement(current->Get(var)); + vobject->FieldAt(offset).To(&var) && current->Get(var).To(&value)) { + current->SetReplacement(value); } else { current->SetEscaped(object); } @@ -656,9 +666,11 @@ void ReduceNode(const Operator* op, EscapeAnalysisTracker::Scope* current, Node* checked = current->ValueInput(0); const VirtualObject* vobject = current->GetVirtualObject(checked); Variable map_field; + Node* map; if (vobject && !vobject->HasEscaped() && - vobject->FieldAt(HeapObject::kMapOffset).To(&map_field)) { - if (Node* map = current->Get(map_field)) { + vobject->FieldAt(HeapObject::kMapOffset).To(&map_field) && + current->Get(map_field).To(&map)) { + if (map) { Type* const map_type = NodeProperties::GetType(map); if (map_type->IsHeapConstant() && params.maps().contains(ZoneHandleSet<Map>(bit_cast<Handle<Map>>( @@ -679,9 +691,11 @@ void ReduceNode(const Operator* op, EscapeAnalysisTracker::Scope* current, Node* object = current->ValueInput(0); const VirtualObject* vobject = current->GetVirtualObject(object); Variable map_field; + Node* object_map; if (vobject && !vobject->HasEscaped() && - vobject->FieldAt(HeapObject::kMapOffset).To(&map_field)) { - if (Node* object_map = current->Get(map_field)) { + vobject->FieldAt(HeapObject::kMapOffset).To(&map_field) && + current->Get(map_field).To(&object_map)) { + if (object_map) { current->SetReplacement(LowerCompareMapsWithoutLoad( object_map, CompareMapsParametersOf(op), jsgraph)); break; diff --git a/chromium/v8/src/compiler/escape-analysis.h b/chromium/v8/src/compiler/escape-analysis.h index e0d65cfacc1..504729bc811 100644 --- a/chromium/v8/src/compiler/escape-analysis.h +++ b/chromium/v8/src/compiler/escape-analysis.h @@ -130,7 +130,10 @@ class VirtualObject : public Dependable { } CHECK(!HasEscaped()); if (offset >= size()) { - // This can only happen in unreachable code. + // TODO(tebbi): Reading out-of-bounds can only happen in unreachable + // code. In this case, we have to mark the object as escaping to avoid + // dead nodes in the graph. This is a workaround that should be removed + // once we can handle dead nodes everywhere. return Nothing<Variable>(); } return Just(fields_.at(offset / kPointerSize)); diff --git a/chromium/v8/src/compiler/js-call-reducer.cc b/chromium/v8/src/compiler/js-call-reducer.cc index c8f2c9789df..d8fcf4553af 100644 --- a/chromium/v8/src/compiler/js-call-reducer.cc +++ b/chromium/v8/src/compiler/js-call-reducer.cc @@ -1298,6 +1298,56 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( } else { NodeProperties::ChangeOp( node, javascript()->Construct(arity + 2, frequency, feedback)); + Node* new_target = NodeProperties::GetValueInput(node, arity + 1); + Node* frame_state = NodeProperties::GetFrameStateInput(node); + Node* context = NodeProperties::GetContextInput(node); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + + // Check whether the given new target value is a constructor function. The + // replacement {JSConstruct} operator only checks the passed target value + // but relies on the new target value to be implicitly valid. + Node* check = + graph()->NewNode(simplified()->ObjectIsConstructor(), new_target); + Node* check_branch = + graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); + Node* check_fail = graph()->NewNode(common()->IfFalse(), check_branch); + Node* check_throw = check_fail = + graph()->NewNode(javascript()->CallRuntime(Runtime::kThrowTypeError, 2), + jsgraph()->Constant(MessageTemplate::kNotConstructor), + new_target, context, frame_state, effect, check_fail); + control = graph()->NewNode(common()->IfTrue(), check_branch); + NodeProperties::ReplaceControlInput(node, control); + + // Rewire potential exception edges. + Node* on_exception = nullptr; + if (NodeProperties::IsExceptionalCall(node, &on_exception)) { + // Create appropriate {IfException} and {IfSuccess} nodes. + Node* if_exception = + graph()->NewNode(common()->IfException(), check_throw, check_fail); + check_fail = graph()->NewNode(common()->IfSuccess(), check_fail); + + // Join the exception edges. + Node* merge = + graph()->NewNode(common()->Merge(2), if_exception, on_exception); + Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception, + on_exception, merge); + Node* phi = + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + if_exception, on_exception, merge); + ReplaceWithValue(on_exception, phi, ephi, merge); + merge->ReplaceInput(1, on_exception); + ephi->ReplaceInput(1, on_exception); + phi->ReplaceInput(1, on_exception); + } + + // The above %ThrowTypeError runtime call is an unconditional throw, making + // it impossible to return a successful completion in this case. We simply + // connect the successful completion to the graph end. + Node* terminate = + graph()->NewNode(common()->Throw(), check_throw, check_fail); + NodeProperties::MergeControlToEnd(graph(), common(), terminate); + Reduction const reduction = ReduceJSConstruct(node); return reduction.Changed() ? reduction : Changed(node); } diff --git a/chromium/v8/src/compiler/js-create-lowering.cc b/chromium/v8/src/compiler/js-create-lowering.cc index d740f7681cb..bd4f1069abb 100644 --- a/chromium/v8/src/compiler/js-create-lowering.cc +++ b/chromium/v8/src/compiler/js-create-lowering.cc @@ -255,13 +255,14 @@ Reduction JSCreateLowering::ReduceJSCreate(Node* node) { Node* const control = NodeProperties::GetControlInput(node); // Extract constructor and original constructor function. if (target_type->IsHeapConstant() && new_target_type->IsHeapConstant() && + target_type->AsHeapConstant()->Value()->IsJSFunction() && new_target_type->AsHeapConstant()->Value()->IsJSFunction()) { Handle<JSFunction> constructor = Handle<JSFunction>::cast(target_type->AsHeapConstant()->Value()); + if (!constructor->IsConstructor()) return NoChange(); Handle<JSFunction> original_constructor = Handle<JSFunction>::cast(new_target_type->AsHeapConstant()->Value()); - DCHECK(constructor->IsConstructor()); - DCHECK(original_constructor->IsConstructor()); + if (!original_constructor->IsConstructor()) return NoChange(); // Check if we can inline the allocation. if (IsAllocationInlineable(constructor, original_constructor)) { diff --git a/chromium/v8/src/compiler/js-native-context-specialization.cc b/chromium/v8/src/compiler/js-native-context-specialization.cc index 18093ec9e7e..06f059e24ea 100644 --- a/chromium/v8/src/compiler/js-native-context-specialization.cc +++ b/chromium/v8/src/compiler/js-native-context-specialization.cc @@ -2438,14 +2438,18 @@ Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore( jsgraph()->SmiConstant(PropertyArray::kNoHashSentinel)); hash = graph()->NewNode(common()->TypeGuard(Type::SignedSmall()), hash, control); + hash = + graph()->NewNode(simplified()->NumberShiftLeft(), hash, + jsgraph()->Constant(PropertyArray::HashField::kShift)); } else { hash = effect = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForPropertyArrayLengthAndHash()), properties, effect, control); effect = graph()->NewNode( common()->BeginRegion(RegionObservability::kNotObservable), effect); - hash = graph()->NewNode(simplified()->NumberBitwiseAnd(), hash, - jsgraph()->Constant(JSReceiver::kHashMask)); + hash = + graph()->NewNode(simplified()->NumberBitwiseAnd(), hash, + jsgraph()->Constant(PropertyArray::HashField::kMask)); } Node* new_length_and_hash = graph()->NewNode( diff --git a/chromium/v8/src/compiler/js-typed-lowering.cc b/chromium/v8/src/compiler/js-typed-lowering.cc index 73db9790015..6f50ba15a3c 100644 --- a/chromium/v8/src/compiler/js-typed-lowering.cc +++ b/chromium/v8/src/compiler/js-typed-lowering.cc @@ -668,6 +668,7 @@ Reduction JSTypedLowering::ReduceCreateConsString(Node* node) { // Morph the {node} into a {FinishRegion}. ReplaceWithValue(node, node, node, control); + NodeProperties::SetType(value, NodeProperties::GetType(node)); node->ReplaceInput(0, value); node->ReplaceInput(1, effect); node->TrimInputCount(2); diff --git a/chromium/v8/src/compiler/opcodes.h b/chromium/v8/src/compiler/opcodes.h index ef0a517001e..97f91b8cacc 100644 --- a/chromium/v8/src/compiler/opcodes.h +++ b/chromium/v8/src/compiler/opcodes.h @@ -355,6 +355,7 @@ V(TransitionAndStoreElement) \ V(ObjectIsArrayBufferView) \ V(ObjectIsCallable) \ + V(ObjectIsConstructor) \ V(ObjectIsDetectableCallable) \ V(ObjectIsMinusZero) \ V(ObjectIsNaN) \ diff --git a/chromium/v8/src/compiler/raw-machine-assembler.cc b/chromium/v8/src/compiler/raw-machine-assembler.cc index 89190f40a7d..b685fc5d66f 100644 --- a/chromium/v8/src/compiler/raw-machine-assembler.cc +++ b/chromium/v8/src/compiler/raw-machine-assembler.cc @@ -301,6 +301,41 @@ Node* RawMachineAssembler::CallCFunction3WithCallerSavedRegisters( arg0, arg1, arg2); } +Node* RawMachineAssembler::CallCFunction4( + MachineType return_type, MachineType arg0_type, MachineType arg1_type, + MachineType arg2_type, MachineType arg3_type, Node* function, Node* arg0, + Node* arg1, Node* arg2, Node* arg3) { + MachineSignature::Builder builder(zone(), 1, 4); + builder.AddReturn(return_type); + builder.AddParam(arg0_type); + builder.AddParam(arg1_type); + builder.AddParam(arg2_type); + builder.AddParam(arg3_type); + const CallDescriptor* descriptor = + Linkage::GetSimplifiedCDescriptor(zone(), builder.Build()); + + return AddNode(common()->Call(descriptor), function, arg0, arg1, arg2, arg3); +} + +Node* RawMachineAssembler::CallCFunction5( + MachineType return_type, MachineType arg0_type, MachineType arg1_type, + MachineType arg2_type, MachineType arg3_type, MachineType arg4_type, + Node* function, Node* arg0, Node* arg1, Node* arg2, Node* arg3, + Node* arg4) { + MachineSignature::Builder builder(zone(), 1, 5); + builder.AddReturn(return_type); + builder.AddParam(arg0_type); + builder.AddParam(arg1_type); + builder.AddParam(arg2_type); + builder.AddParam(arg3_type); + builder.AddParam(arg4_type); + const CallDescriptor* descriptor = + Linkage::GetSimplifiedCDescriptor(zone(), builder.Build()); + + return AddNode(common()->Call(descriptor), function, arg0, arg1, arg2, arg3, + arg4); +} + Node* RawMachineAssembler::CallCFunction6( MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType arg2_type, MachineType arg3_type, MachineType arg4_type, diff --git a/chromium/v8/src/compiler/raw-machine-assembler.h b/chromium/v8/src/compiler/raw-machine-assembler.h index 8eaaf256239..3ee91a1ff9f 100644 --- a/chromium/v8/src/compiler/raw-machine-assembler.h +++ b/chromium/v8/src/compiler/raw-machine-assembler.h @@ -778,6 +778,17 @@ class V8_EXPORT_PRIVATE RawMachineAssembler { MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType arg2_type, Node* function, Node* arg0, Node* arg1, Node* arg2, SaveFPRegsMode mode = kSaveFPRegs); + // Call to a C function with four arguments. + Node* CallCFunction4(MachineType return_type, MachineType arg0_type, + MachineType arg1_type, MachineType arg2_type, + MachineType arg3_type, Node* function, Node* arg0, + Node* arg1, Node* arg2, Node* arg3); + // Call to a C function with five arguments. + Node* CallCFunction5(MachineType return_type, MachineType arg0_type, + MachineType arg1_type, MachineType arg2_type, + MachineType arg3_type, MachineType arg4_type, + Node* function, Node* arg0, Node* arg1, Node* arg2, + Node* arg3, Node* arg4); // Call to a C function with six arguments. Node* CallCFunction6(MachineType return_type, MachineType arg0_type, MachineType arg1_type, MachineType arg2_type, diff --git a/chromium/v8/src/compiler/simplified-lowering.cc b/chromium/v8/src/compiler/simplified-lowering.cc index b11ce164e10..28634b8c9f3 100644 --- a/chromium/v8/src/compiler/simplified-lowering.cc +++ b/chromium/v8/src/compiler/simplified-lowering.cc @@ -2683,6 +2683,11 @@ class RepresentationSelector { VisitObjectIs(node, Type::Callable(), lowering); return; } + case IrOpcode::kObjectIsConstructor: { + // TODO(turbofan): Introduce a Type::Constructor? + VisitUnop(node, UseInfo::AnyTagged(), MachineRepresentation::kBit); + return; + } case IrOpcode::kObjectIsDetectableCallable: { VisitObjectIs(node, Type::DetectableCallable(), lowering); return; @@ -2928,19 +2933,8 @@ class RepresentationSelector { return SetOutput(node, MachineRepresentation::kTagged); case IrOpcode::kFindOrderedHashMapEntry: { - Type* const key_type = TypeOf(node->InputAt(1)); - if (key_type->Is(Type::Signed32())) { - VisitBinop(node, UseInfo::AnyTagged(), UseInfo::TruncatingWord32(), - MachineRepresentation::kWord32); - if (lower()) { - NodeProperties::ChangeOp( - node, - lowering->simplified()->FindOrderedHashMapEntryForInt32Key()); - } - } else { - VisitBinop(node, UseInfo::AnyTagged(), - MachineRepresentation::kTaggedSigned); - } + VisitBinop(node, UseInfo::AnyTagged(), + MachineRepresentation::kTaggedSigned); return; } diff --git a/chromium/v8/src/compiler/simplified-operator.cc b/chromium/v8/src/compiler/simplified-operator.cc index 45eca8ddf15..0d2333e126c 100644 --- a/chromium/v8/src/compiler/simplified-operator.cc +++ b/chromium/v8/src/compiler/simplified-operator.cc @@ -501,6 +501,7 @@ BailoutReason BailoutReasonOf(const Operator* op) { V(TruncateTaggedToFloat64, Operator::kNoProperties, 1, 0) \ V(ObjectIsArrayBufferView, Operator::kNoProperties, 1, 0) \ V(ObjectIsCallable, Operator::kNoProperties, 1, 0) \ + V(ObjectIsConstructor, Operator::kNoProperties, 1, 0) \ V(ObjectIsDetectableCallable, Operator::kNoProperties, 1, 0) \ V(ObjectIsMinusZero, Operator::kNoProperties, 1, 0) \ V(ObjectIsNaN, Operator::kNoProperties, 1, 0) \ diff --git a/chromium/v8/src/compiler/simplified-operator.h b/chromium/v8/src/compiler/simplified-operator.h index 5a237a1f54c..6d43bcac505 100644 --- a/chromium/v8/src/compiler/simplified-operator.h +++ b/chromium/v8/src/compiler/simplified-operator.h @@ -437,6 +437,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final const Operator* ObjectIsArrayBufferView(); const Operator* ObjectIsCallable(); + const Operator* ObjectIsConstructor(); const Operator* ObjectIsDetectableCallable(); const Operator* ObjectIsMinusZero(); const Operator* ObjectIsNaN(); diff --git a/chromium/v8/src/compiler/typer.cc b/chromium/v8/src/compiler/typer.cc index 9ea962b8659..2590342d2e4 100644 --- a/chromium/v8/src/compiler/typer.cc +++ b/chromium/v8/src/compiler/typer.cc @@ -289,6 +289,7 @@ class Typer::Visitor : public Reducer { static Type* ObjectIsArrayBufferView(Type*, Typer*); static Type* ObjectIsCallable(Type*, Typer*); + static Type* ObjectIsConstructor(Type*, Typer*); static Type* ObjectIsDetectableCallable(Type*, Typer*); static Type* ObjectIsMinusZero(Type*, Typer*); static Type* ObjectIsNaN(Type*, Typer*); @@ -523,6 +524,12 @@ Type* Typer::Visitor::ObjectIsCallable(Type* type, Typer* t) { return Type::Boolean(); } +Type* Typer::Visitor::ObjectIsConstructor(Type* type, Typer* t) { + // TODO(turbofan): Introduce a Type::Constructor? + if (!type->Maybe(Type::Callable())) return t->singleton_false_; + return Type::Boolean(); +} + Type* Typer::Visitor::ObjectIsDetectableCallable(Type* type, Typer* t) { if (type->Is(Type::DetectableCallable())) return t->singleton_true_; if (!type->Maybe(Type::DetectableCallable())) return t->singleton_false_; @@ -1986,6 +1993,10 @@ Type* Typer::Visitor::TypeObjectIsCallable(Node* node) { return TypeUnaryOp(node, ObjectIsCallable); } +Type* Typer::Visitor::TypeObjectIsConstructor(Node* node) { + return TypeUnaryOp(node, ObjectIsConstructor); +} + Type* Typer::Visitor::TypeObjectIsDetectableCallable(Node* node) { return TypeUnaryOp(node, ObjectIsDetectableCallable); } diff --git a/chromium/v8/src/compiler/verifier.cc b/chromium/v8/src/compiler/verifier.cc index 541818c3d8f..5869a0d491c 100644 --- a/chromium/v8/src/compiler/verifier.cc +++ b/chromium/v8/src/compiler/verifier.cc @@ -1007,6 +1007,7 @@ void Verifier::Visitor::Check(Node* node) { case IrOpcode::kObjectIsArrayBufferView: case IrOpcode::kObjectIsCallable: + case IrOpcode::kObjectIsConstructor: case IrOpcode::kObjectIsDetectableCallable: case IrOpcode::kObjectIsMinusZero: case IrOpcode::kObjectIsNaN: diff --git a/chromium/v8/src/compiler/wasm-compiler.cc b/chromium/v8/src/compiler/wasm-compiler.cc index 7bcdb5092a4..bc731b2bb8f 100644 --- a/chromium/v8/src/compiler/wasm-compiler.cc +++ b/chromium/v8/src/compiler/wasm-compiler.cc @@ -3221,6 +3221,10 @@ Node* WasmGraphBuilder::LoadMemSize() { static_cast<int32_t>(offsetof(WasmContext, mem_size))), *effect_, *control_); *effect_ = mem_size; + if (jsgraph()->machine()->Is64()) { + mem_size = graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), + mem_size); + } return mem_size; } @@ -3235,8 +3239,13 @@ Node* WasmGraphBuilder::CurrentMemoryPages() { // CurrentMemoryPages can not be called from asm.js. DCHECK_EQ(wasm::kWasmOrigin, env_->module->origin()); DCHECK_NOT_NULL(*mem_size_); + Node* mem_size = *mem_size_; + if (jsgraph()->machine()->Is64()) { + mem_size = graph()->NewNode(jsgraph()->machine()->TruncateInt64ToInt32(), + mem_size); + } return graph()->NewNode( - jsgraph()->machine()->Word32Shr(), *mem_size_, + jsgraph()->machine()->Word32Shr(), mem_size, jsgraph()->Int32Constant(WhichPowerOf2(wasm::WasmModule::kPageSize))); } @@ -3397,15 +3406,21 @@ void WasmGraphBuilder::BoundsCheckMem(MachineType memtype, Node* index, // The end offset is larger than the smallest memory. // Dynamically check the end offset against the actual memory size, which // is not known at compile time. - Node* cond = graph()->NewNode( - jsgraph()->machine()->Uint32LessThanOrEqual(), - jsgraph()->IntPtrConstant(static_cast<uintptr_t>(end_offset)), - *mem_size_); + Node* cond; + if (jsgraph()->machine()->Is32()) { + cond = graph()->NewNode(jsgraph()->machine()->Uint32LessThanOrEqual(), + jsgraph()->Int32Constant(end_offset), *mem_size_); + } else { + cond = graph()->NewNode( + jsgraph()->machine()->Uint64LessThanOrEqual(), + jsgraph()->Int64Constant(static_cast<int64_t>(end_offset)), + *mem_size_); + } TrapIfFalse(wasm::kTrapMemOutOfBounds, cond, position); } else { // The end offset is within the bounds of the smallest memory, so only // one check is required. Check to see if the index is also a constant. - Uint32Matcher m(index); + UintPtrMatcher m(index); if (m.HasValue()) { uint64_t index_val = m.Value(); if ((index_val + offset + access_size) <= min_size) { @@ -3416,12 +3431,22 @@ void WasmGraphBuilder::BoundsCheckMem(MachineType memtype, Node* index, } } - Node* effective_size = - graph()->NewNode(jsgraph()->machine()->Int32Sub(), *mem_size_, - jsgraph()->Int32Constant(end_offset - 1)); + Node* effective_size; + if (jsgraph()->machine()->Is32()) { + effective_size = + graph()->NewNode(jsgraph()->machine()->Int32Sub(), *mem_size_, + jsgraph()->Int32Constant(end_offset - 1)); + } else { + effective_size = graph()->NewNode( + jsgraph()->machine()->Int64Sub(), *mem_size_, + jsgraph()->Int64Constant(static_cast<int64_t>(end_offset - 1))); + } + + const Operator* less = jsgraph()->machine()->Is32() + ? jsgraph()->machine()->Uint32LessThan() + : jsgraph()->machine()->Uint64LessThan(); - Node* cond = graph()->NewNode(jsgraph()->machine()->Uint32LessThan(), index, - effective_size); + Node* cond = graph()->NewNode(less, index, effective_size); TrapIfFalse(wasm::kTrapMemOutOfBounds, cond, position); } @@ -3475,6 +3500,10 @@ Node* WasmGraphBuilder::LoadMem(wasm::ValueType type, MachineType memtype, wasm::WasmCodePosition position) { Node* load; + if (jsgraph()->machine()->Is64()) { + index = + graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), index); + } // Wasm semantics throw on OOB. Introduce explicit bounds check. if (!FLAG_wasm_trap_handler || !V8_TRAP_HANDLER_SUPPORTED) { BoundsCheckMem(memtype, index, offset, position); @@ -3530,6 +3559,10 @@ Node* WasmGraphBuilder::StoreMem(MachineType memtype, Node* index, wasm::ValueType type) { Node* store; + if (jsgraph()->machine()->Is64()) { + index = + graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), index); + } // Wasm semantics throw on OOB. Introduce explicit bounds check. if (!FLAG_wasm_trap_handler || !V8_TRAP_HANDLER_SUPPORTED) { BoundsCheckMem(memtype, index, offset, position); @@ -3576,6 +3609,10 @@ Node* WasmGraphBuilder::BuildAsmjsLoadMem(MachineType type, Node* index) { // asm.js semantics use CheckedLoad (i.e. OOB reads return 0ish). DCHECK_NOT_NULL(*mem_size_); DCHECK_NOT_NULL(*mem_start_); + if (jsgraph()->machine()->Is64()) { + index = + graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), index); + } const Operator* op = jsgraph()->machine()->CheckedLoad(type); Node* load = graph()->NewNode(op, *mem_start_, index, *mem_size_, *effect_, *control_); @@ -3589,6 +3626,10 @@ Node* WasmGraphBuilder::BuildAsmjsStoreMem(MachineType type, Node* index, // asm.js semantics use CheckedStore (i.e. ignore OOB writes). DCHECK_NOT_NULL(*mem_size_); DCHECK_NOT_NULL(*mem_start_); + if (jsgraph()->machine()->Is64()) { + index = + graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), index); + } const Operator* op = jsgraph()->machine()->CheckedStore(type.representation()); Node* store = graph()->NewNode(op, *mem_start_, index, *mem_size_, val, @@ -3609,7 +3650,7 @@ Node* WasmGraphBuilder::String(const char* string) { Graph* WasmGraphBuilder::graph() { return jsgraph()->graph(); } void WasmGraphBuilder::LowerInt64() { - if (!jsgraph()->machine()->Is32()) return; + if (jsgraph()->machine()->Is64()) return; Int64Lowering r(jsgraph()->graph(), jsgraph()->machine(), jsgraph()->common(), jsgraph()->zone(), sig_); r.LowerGraph(); diff --git a/chromium/v8/src/debug/debug-scope-iterator.cc b/chromium/v8/src/debug/debug-scope-iterator.cc index 5dc377375ee..2e06dccab68 100644 --- a/chromium/v8/src/debug/debug-scope-iterator.cc +++ b/chromium/v8/src/debug/debug-scope-iterator.cc @@ -15,13 +15,22 @@ namespace v8 { std::unique_ptr<debug::ScopeIterator> debug::ScopeIterator::CreateForFunction( v8::Isolate* v8_isolate, v8::Local<v8::Function> v8_func) { - internal::Handle<internal::JSFunction> func = - internal::Handle<internal::JSFunction>::cast(Utils::OpenHandle(*v8_func)); + internal::Handle<internal::JSReceiver> receiver = + internal::Handle<internal::JSReceiver>::cast(Utils::OpenHandle(*v8_func)); + + // Besides JSFunction and JSBoundFunction, {v8_func} could be an + // ObjectTemplate with a CallAsFunctionHandler. We only handle plain + // JSFunctions. + if (!receiver->IsJSFunction()) return nullptr; + + internal::Handle<internal::JSFunction> function = + internal::Handle<internal::JSFunction>::cast(receiver); + // Blink has function objects with callable map, JS_SPECIAL_API_OBJECT_TYPE // but without context on heap. - if (!func->has_context()) return nullptr; + if (!function->has_context()) return nullptr; return std::unique_ptr<debug::ScopeIterator>(new internal::DebugScopeIterator( - reinterpret_cast<internal::Isolate*>(v8_isolate), func)); + reinterpret_cast<internal::Isolate*>(v8_isolate), function)); } std::unique_ptr<debug::ScopeIterator> diff --git a/chromium/v8/src/deoptimizer.cc b/chromium/v8/src/deoptimizer.cc index b5d77803ab1..125ca932f70 100644 --- a/chromium/v8/src/deoptimizer.cc +++ b/chromium/v8/src/deoptimizer.cc @@ -2759,8 +2759,7 @@ void TranslatedValue::Handlify() { TranslatedFrame TranslatedFrame::InterpretedFrame( BailoutId bytecode_offset, SharedFunctionInfo* shared_info, int height) { - TranslatedFrame frame(kInterpretedFunction, shared_info->GetIsolate(), - shared_info, height); + TranslatedFrame frame(kInterpretedFunction, shared_info, height); frame.node_id_ = bytecode_offset; return frame; } @@ -2769,36 +2768,32 @@ TranslatedFrame TranslatedFrame::InterpretedFrame( TranslatedFrame TranslatedFrame::AccessorFrame( Kind kind, SharedFunctionInfo* shared_info) { DCHECK(kind == kSetter || kind == kGetter); - return TranslatedFrame(kind, shared_info->GetIsolate(), shared_info); + return TranslatedFrame(kind, shared_info); } TranslatedFrame TranslatedFrame::ArgumentsAdaptorFrame( SharedFunctionInfo* shared_info, int height) { - return TranslatedFrame(kArgumentsAdaptor, shared_info->GetIsolate(), - shared_info, height); + return TranslatedFrame(kArgumentsAdaptor, shared_info, height); } TranslatedFrame TranslatedFrame::ConstructStubFrame( BailoutId bailout_id, SharedFunctionInfo* shared_info, int height) { - TranslatedFrame frame(kConstructStub, shared_info->GetIsolate(), shared_info, - height); + TranslatedFrame frame(kConstructStub, shared_info, height); frame.node_id_ = bailout_id; return frame; } TranslatedFrame TranslatedFrame::BuiltinContinuationFrame( BailoutId bailout_id, SharedFunctionInfo* shared_info, int height) { - TranslatedFrame frame(kBuiltinContinuation, shared_info->GetIsolate(), - shared_info, height); + TranslatedFrame frame(kBuiltinContinuation, shared_info, height); frame.node_id_ = bailout_id; return frame; } TranslatedFrame TranslatedFrame::JavaScriptBuiltinContinuationFrame( BailoutId bailout_id, SharedFunctionInfo* shared_info, int height) { - TranslatedFrame frame(kJavaScriptBuiltinContinuation, - shared_info->GetIsolate(), shared_info, height); + TranslatedFrame frame(kJavaScriptBuiltinContinuation, shared_info, height); frame.node_id_ = bailout_id; return frame; } @@ -3462,10 +3457,32 @@ class TranslatedState::CapturedObjectMaterializer { int field_count) : state_(state), frame_index_(frame_index), field_count_(field_count) {} + // Ensure the properties never contain mutable heap numbers. This is necessary + // because the deoptimizer generalizes all maps to tagged representation + // fields (so mutable heap numbers are not allowed). + static void EnsurePropertiesGeneralized(Handle<Object> properties_or_hash) { + if (properties_or_hash->IsPropertyArray()) { + Handle<PropertyArray> properties = + Handle<PropertyArray>::cast(properties_or_hash); + int length = properties->length(); + for (int i = 0; i < length; i++) { + if (properties->get(i)->IsMutableHeapNumber()) { + Handle<HeapObject> box(HeapObject::cast(properties->get(i))); + box->set_map(properties->GetIsolate()->heap()->heap_number_map()); + } + } + } + } + Handle<Object> FieldAt(int* value_index) { CHECK(field_count_ > 0); --field_count_; - return state_->MaterializeAt(frame_index_, value_index); + Handle<Object> object = state_->MaterializeAt(frame_index_, value_index); + // This is a big hammer to make sure that the materialized objects do not + // have property arrays with mutable heap numbers (mutable heap numbers are + // bad because we generalize maps for all materialized objects). + EnsurePropertiesGeneralized(object); + return object; } ~CapturedObjectMaterializer() { CHECK_EQ(0, field_count_); } diff --git a/chromium/v8/src/deoptimizer.h b/chromium/v8/src/deoptimizer.h index a60c40db571..dcc56198124 100644 --- a/chromium/v8/src/deoptimizer.h +++ b/chromium/v8/src/deoptimizer.h @@ -205,14 +205,12 @@ class TranslatedFrame { static void AdvanceIterator(std::deque<TranslatedValue>::iterator* iter); - TranslatedFrame(Kind kind, Isolate* isolate, - SharedFunctionInfo* shared_info = nullptr, int height = 0) + TranslatedFrame(Kind kind, SharedFunctionInfo* shared_info = nullptr, + int height = 0) : kind_(kind), node_id_(BailoutId::None()), raw_shared_info_(shared_info), - height_(height), - isolate_(isolate) {} - + height_(height) {} void Add(const TranslatedValue& value) { values_.push_back(value); } void Handlify(); @@ -222,7 +220,6 @@ class TranslatedFrame { SharedFunctionInfo* raw_shared_info_; Handle<SharedFunctionInfo> shared_info_; int height_; - Isolate* isolate_; typedef std::deque<TranslatedValue> ValuesContainer; diff --git a/chromium/v8/src/elements.cc b/chromium/v8/src/elements.cc index 577f77ddc84..8c692ecab82 100644 --- a/chromium/v8/src/elements.cc +++ b/chromium/v8/src/elements.cc @@ -1020,13 +1020,14 @@ class ElementsAccessorBase : public ElementsAccessor { } Object* CopyElements(Handle<JSReceiver> source, Handle<JSObject> destination, - size_t length) final { - return Subclass::CopyElementsHandleImpl(source, destination, length); + size_t length, uint32_t offset) final { + return Subclass::CopyElementsHandleImpl(source, destination, length, + offset); } static Object* CopyElementsHandleImpl(Handle<JSReceiver> source, Handle<JSObject> destination, - size_t length) { + size_t length, uint32_t offset) { UNREACHABLE(); } @@ -3168,28 +3169,30 @@ class TypedElementsAccessor template <typename SourceTraits> static void CopyBetweenBackingStores(FixedTypedArrayBase* source, - BackingStore* dest, size_t length) { + BackingStore* dest, size_t length, + uint32_t offset) { FixedTypedArray<SourceTraits>* source_fta = FixedTypedArray<SourceTraits>::cast(source); for (uint32_t i = 0; i < length; i++) { typename SourceTraits::ElementType elem = source_fta->get_scalar(i); - dest->set(i, dest->from(elem)); + dest->set(offset + i, dest->from(elem)); } } - static void CopyElementsHandleFromTypedArray(Handle<JSTypedArray> source, - Handle<JSTypedArray> destination, - size_t length) { + static void CopyElementsFromTypedArray(JSTypedArray* source, + JSTypedArray* destination, + size_t length, uint32_t offset) { // The source is a typed array, so we know we don't need to do ToNumber // side-effects, as the source elements will always be a number or // undefined. DisallowHeapAllocation no_gc; - Handle<FixedTypedArrayBase> source_elements( - FixedTypedArrayBase::cast(source->elements())); - Handle<BackingStore> destination_elements( - BackingStore::cast(destination->elements())); + FixedTypedArrayBase* source_elements = + FixedTypedArrayBase::cast(source->elements()); + BackingStore* destination_elements = + BackingStore::cast(destination->elements()); + DCHECK_LE(offset + source->length(), destination->length()); DCHECK_GE(destination->length(), source->length()); DCHECK(source->length()->IsSmi()); DCHECK_EQ(Smi::FromInt(static_cast<int>(length)), source->length()); @@ -3219,15 +3222,16 @@ class TypedElementsAccessor // which have special conversion operations. if (same_type || (same_size && both_are_simple)) { size_t element_size = source->element_size(); - std::memcpy(dest_data, source_data, length * element_size); + std::memcpy(dest_data + offset * element_size, source_data, + length * element_size); } else { // We use scalar accessors below to avoid boxing/unboxing, so there are // no allocations. switch (source->GetElementsKind()) { -#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ - case TYPE##_ELEMENTS: \ - CopyBetweenBackingStores<Type##ArrayTraits>( \ - *source_elements, *destination_elements, length); \ +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ + case TYPE##_ELEMENTS: \ + CopyBetweenBackingStores<Type##ArrayTraits>( \ + source_elements, destination_elements, length, offset); \ break; TYPED_ARRAYS(TYPED_ARRAY_CASE) default: @@ -3238,23 +3242,27 @@ class TypedElementsAccessor } } - static bool HoleyPrototypeLookupRequired(Isolate* isolate, - Handle<JSArray> source) { + static bool HoleyPrototypeLookupRequired(Isolate* isolate, Context* context, + JSArray* source) { + DisallowHeapAllocation no_gc; + DisallowJavascriptExecution no_js(isolate); + Object* source_proto = source->map()->prototype(); + // Null prototypes are OK - we don't need to do prototype chain lookups on // them. if (source_proto->IsNull(isolate)) return false; if (source_proto->IsJSProxy()) return true; - DCHECK(source_proto->IsJSObject()); - if (!isolate->is_initial_array_prototype(JSObject::cast(source_proto))) { + if (!context->is_initial_array_prototype(JSObject::cast(source_proto))) { return true; } - return !isolate->IsFastArrayConstructorPrototypeChainIntact(); + + return !isolate->IsFastArrayConstructorPrototypeChainIntact(context); } - static bool TryCopyElementsHandleFastNumber(Handle<JSArray> source, - Handle<JSTypedArray> destination, - size_t length) { + static bool TryCopyElementsFastNumber(Context* context, JSArray* source, + JSTypedArray* destination, + size_t length, uint32_t offset) { Isolate* isolate = source->GetIsolate(); DisallowHeapAllocation no_gc; DisallowJavascriptExecution no_js(isolate); @@ -3267,7 +3275,7 @@ class TypedElementsAccessor // When the array has the original array prototype, and that prototype has // not been changed in a way that would affect lookups, we can just convert // the hole into undefined. - if (HoleyPrototypeLookupRequired(isolate, source)) return false; + if (HoleyPrototypeLookupRequired(isolate, context, source)) return false; Object* undefined = isolate->heap()->undefined_value(); @@ -3279,19 +3287,19 @@ class TypedElementsAccessor Object* elem = source_store->get(i); DCHECK(elem->IsSmi()); int int_value = Smi::ToInt(elem); - dest->set(i, dest->from(int_value)); + dest->set(offset + i, dest->from(int_value)); } return true; } else if (kind == HOLEY_SMI_ELEMENTS) { FixedArray* source_store = FixedArray::cast(source->elements()); for (uint32_t i = 0; i < length; i++) { if (source_store->is_the_hole(isolate, i)) { - dest->SetValue(i, undefined); + dest->SetValue(offset + i, undefined); } else { Object* elem = source_store->get(i); DCHECK(elem->IsSmi()); int int_value = Smi::ToInt(elem); - dest->set(i, dest->from(int_value)); + dest->set(offset + i, dest->from(int_value)); } } return true; @@ -3305,7 +3313,7 @@ class TypedElementsAccessor // Use the from_double conversion for this specific TypedArray type, // rather than relying on C++ to convert elem. double elem = source_store->get_scalar(i); - dest->set(i, dest->from(elem)); + dest->set(offset + i, dest->from(elem)); } return true; } else if (kind == HOLEY_DOUBLE_ELEMENTS) { @@ -3313,10 +3321,10 @@ class TypedElementsAccessor FixedDoubleArray::cast(source->elements()); for (uint32_t i = 0; i < length; i++) { if (source_store->is_the_hole(i)) { - dest->SetValue(i, undefined); + dest->SetValue(offset + i, undefined); } else { double elem = source_store->get_scalar(i); - dest->set(i, dest->from(elem)); + dest->set(offset + i, dest->from(elem)); } } return true; @@ -3326,7 +3334,7 @@ class TypedElementsAccessor static Object* CopyElementsHandleSlow(Handle<JSReceiver> source, Handle<JSTypedArray> destination, - size_t length) { + size_t length, uint32_t offset) { Isolate* isolate = source->GetIsolate(); Handle<BackingStore> destination_elements( BackingStore::cast(destination->elements())); @@ -3336,13 +3344,21 @@ class TypedElementsAccessor ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem, Object::GetProperty(&it)); ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem, Object::ToNumber(elem)); - // We don't need to check for buffer neutering here, because the - // source cannot be a TypedArray. + + if (V8_UNLIKELY(destination->WasNeutered())) { + const char* op = "set"; + const MessageTemplate::Template message = + MessageTemplate::kDetachedOperation; + Handle<String> operation = + isolate->factory()->NewStringFromAsciiChecked(op); + THROW_NEW_ERROR_RETURN_FAILURE(isolate, + NewTypeError(message, operation)); + } // The spec says we store the length, then get each element, so we don't // need to check changes to length. - destination_elements->SetValue(i, *elem); + destination_elements->SetValue(offset + i, *elem); } - return Smi::kZero; + return *isolate->factory()->undefined_value(); } // This doesn't guarantee that the destination array will be completely @@ -3350,28 +3366,32 @@ class TypedElementsAccessor // that is required. static Object* CopyElementsHandleImpl(Handle<JSReceiver> source, Handle<JSObject> destination, - size_t length) { + size_t length, uint32_t offset) { + Isolate* isolate = destination->GetIsolate(); Handle<JSTypedArray> destination_ta = Handle<JSTypedArray>::cast(destination); + DCHECK_LE(offset + length, destination_ta->length_value()); + + if (length == 0) return *isolate->factory()->undefined_value(); // All conversions from TypedArrays can be done without allocation. if (source->IsJSTypedArray()) { Handle<JSTypedArray> source_ta = Handle<JSTypedArray>::cast(source); - CopyElementsHandleFromTypedArray(source_ta, destination_ta, length); - return Smi::kZero; + CopyElementsFromTypedArray(*source_ta, *destination_ta, length, offset); + return *isolate->factory()->undefined_value(); } // Fast cases for packed numbers kinds where we don't need to allocate. if (source->IsJSArray()) { Handle<JSArray> source_array = Handle<JSArray>::cast(source); - if (TryCopyElementsHandleFastNumber(source_array, destination_ta, - length)) { - return Smi::kZero; + if (TryCopyElementsFastNumber(isolate->context(), *source_array, + *destination_ta, length, offset)) { + return *isolate->factory()->undefined_value(); } } // Final generic case that handles prototype chain lookups, getters, proxies // and observable side effects via valueOf, etc. - return CopyElementsHandleSlow(source, destination_ta, length); + return CopyElementsHandleSlow(source, destination_ta, length, offset); } }; @@ -4307,6 +4327,43 @@ MaybeHandle<Object> ArrayConstructInitializeElements(Handle<JSArray> array, return array; } +void CopyFastNumberJSArrayElementsToTypedArray(Context* context, + JSArray* source, + JSTypedArray* destination, + uintptr_t length, + uintptr_t offset) { + DCHECK(context->IsContext()); + DCHECK(source->IsJSArray()); + DCHECK(destination->IsJSTypedArray()); + + switch (destination->GetElementsKind()) { +#define TYPED_ARRAYS_CASE(Type, type, TYPE, ctype, size) \ + case TYPE##_ELEMENTS: \ + CHECK(Fixed##Type##ElementsAccessor::TryCopyElementsFastNumber( \ + context, source, destination, length, static_cast<uint32_t>(offset))); \ + break; + TYPED_ARRAYS(TYPED_ARRAYS_CASE) +#undef TYPED_ARRAYS_CASE + default: + UNREACHABLE(); + } +} + +void CopyTypedArrayElementsToTypedArray(JSTypedArray* source, + JSTypedArray* destination, + uintptr_t length, uintptr_t offset) { + switch (destination->GetElementsKind()) { +#define TYPED_ARRAYS_CASE(Type, type, TYPE, ctype, size) \ + case TYPE##_ELEMENTS: \ + Fixed##Type##ElementsAccessor::CopyElementsFromTypedArray( \ + source, destination, length, static_cast<uint32_t>(offset)); \ + break; + TYPED_ARRAYS(TYPED_ARRAYS_CASE) +#undef TYPED_ARRAYS_CASE + default: + UNREACHABLE(); + } +} void ElementsAccessor::InitializeOncePerProcess() { static ElementsAccessor* accessor_array[] = { diff --git a/chromium/v8/src/elements.h b/chromium/v8/src/elements.h index a43b6f73ad4..3f81be0c51b 100644 --- a/chromium/v8/src/elements.h +++ b/chromium/v8/src/elements.h @@ -192,7 +192,8 @@ class ElementsAccessor { Handle<FixedArrayBase> destination, int size) = 0; virtual Object* CopyElements(Handle<JSReceiver> source, - Handle<JSObject> destination, size_t length) = 0; + Handle<JSObject> destination, size_t length, + uint32_t offset = 0) = 0; virtual Handle<FixedArray> CreateListFromArrayLike(Isolate* isolate, Handle<JSObject> object, @@ -236,6 +237,17 @@ MUST_USE_RESULT MaybeHandle<Object> ArrayConstructInitializeElements( Handle<JSArray> array, Arguments* args); +// Called directly from CSA. +class JSTypedArray; +void CopyFastNumberJSArrayElementsToTypedArray(Context* context, + JSArray* source, + JSTypedArray* destination, + uintptr_t length, + uintptr_t offset); +void CopyTypedArrayElementsToTypedArray(JSTypedArray* source, + JSTypedArray* destination, + uintptr_t length, uintptr_t offset); + } // namespace internal } // namespace v8 diff --git a/chromium/v8/src/external-reference-table.cc b/chromium/v8/src/external-reference-table.cc index 5d3d0728eda..eeb668a25f2 100644 --- a/chromium/v8/src/external-reference-table.cc +++ b/chromium/v8/src/external-reference-table.cc @@ -268,6 +268,13 @@ void ExternalReferenceTable::AddReferences(Isolate* isolate) { "orderedhashmap_gethash_raw"); Add(ExternalReference::get_or_create_hash_raw(isolate).address(), "get_or_create_hash_raw"); + Add(ExternalReference::copy_fast_number_jsarray_elements_to_typed_array( + isolate) + .address(), + "copy_fast_number_jsarray_elements_to_typed_array"); + Add(ExternalReference::copy_typed_array_elements_to_typed_array(isolate) + .address(), + "copy_typed_array_elements_to_typed_array"); Add(ExternalReference::log_enter_external_function(isolate).address(), "Logger::EnterExternal"); Add(ExternalReference::log_leave_external_function(isolate).address(), diff --git a/chromium/v8/src/factory.cc b/chromium/v8/src/factory.cc index 6e8369fdd1b..7710b0c7883 100644 --- a/chromium/v8/src/factory.cc +++ b/chromium/v8/src/factory.cc @@ -181,6 +181,7 @@ Handle<FixedArray> Factory::NewFixedArray(int size, PretenureFlag pretenure) { Handle<PropertyArray> Factory::NewPropertyArray(int size, PretenureFlag pretenure) { DCHECK_LE(0, size); + if (size == 0) return empty_property_array(); CALL_HEAP_FUNCTION(isolate(), isolate()->heap()->AllocatePropertyArray(size, pretenure), PropertyArray); @@ -1331,6 +1332,7 @@ Handle<FixedArray> Factory::CopyFixedArrayAndGrow(Handle<FixedArray> array, Handle<PropertyArray> Factory::CopyPropertyArrayAndGrow( Handle<PropertyArray> array, int grow_by, PretenureFlag pretenure) { + DCHECK_LE(0, grow_by); CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->CopyArrayAndGrow(*array, grow_by, pretenure), @@ -1621,7 +1623,7 @@ Handle<JSFunction> Factory::NewFunction(Handle<String> name, Handle<Code> code, NewFunction(name, code, prototype, language_mode, prototype_mutability); ElementsKind elements_kind = - type == JS_ARRAY_TYPE ? PACKED_SMI_ELEMENTS : HOLEY_SMI_ELEMENTS; + type == JS_ARRAY_TYPE ? PACKED_SMI_ELEMENTS : TERMINAL_FAST_ELEMENTS_KIND; Handle<Map> initial_map = NewMap(type, instance_size, elements_kind); // TODO(littledan): Why do we have this is_generator test when // NewFunctionPrototype already handles finding an appropriately diff --git a/chromium/v8/src/flag-definitions.h b/chromium/v8/src/flag-definitions.h index c9d87fb5f1e..50a1e660c0f 100644 --- a/chromium/v8/src/flag-definitions.h +++ b/chromium/v8/src/flag-definitions.h @@ -266,7 +266,8 @@ HARMONY_SHIPPING(FLAG_SHIPPING_FEATURES) #undef FLAG_SHIPPING_FEATURES #ifdef V8_INTL_SUPPORT -DEFINE_BOOL(icu_timezone_data, true, "get information about timezones from ICU") +DEFINE_BOOL(icu_timezone_data, false, + "get information about timezones from ICU") #endif #ifdef V8_ENABLE_FUTURE diff --git a/chromium/v8/src/frames.cc b/chromium/v8/src/frames.cc index f33dba8ba29..d578a64ed3d 100644 --- a/chromium/v8/src/frames.cc +++ b/chromium/v8/src/frames.cc @@ -1886,10 +1886,10 @@ void JavaScriptFrame::Print(StringStream* accumulator, Context* context = NULL; if (this->context() != NULL && this->context()->IsContext()) { context = Context::cast(this->context()); - } - while (context->IsWithContext()) { - context = context->previous(); - DCHECK(context != NULL); + while (context->IsWithContext()) { + context = context->previous(); + DCHECK(context != NULL); + } } // Print heap-allocated local variables. diff --git a/chromium/v8/src/global-handles.cc b/chromium/v8/src/global-handles.cc index 504a92702a2..9ae13d59f43 100644 --- a/chromium/v8/src/global-handles.cc +++ b/chromium/v8/src/global-handles.cc @@ -160,14 +160,21 @@ class GlobalHandles::Node { bool IsInUse() const { return state() != FREE; } + bool IsPhantomCallback() const { + return weakness_type() == PHANTOM_WEAK || + weakness_type() == PHANTOM_WEAK_2_EMBEDDER_FIELDS; + } + + bool IsPhantomResetHandle() const { + return weakness_type() == PHANTOM_WEAK_RESET_HANDLE; + } + bool IsPendingPhantomCallback() const { - return state() == PENDING && - (weakness_type() == PHANTOM_WEAK || - weakness_type() == PHANTOM_WEAK_2_EMBEDDER_FIELDS); + return state() == PENDING && IsPhantomCallback(); } bool IsPendingPhantomResetHandle() const { - return state() == PENDING && weakness_type() == PHANTOM_WEAK_RESET_HANDLE; + return state() == PENDING && IsPhantomResetHandle(); } bool IsRetainer() const { @@ -613,29 +620,44 @@ bool GlobalHandles::IsWeak(Object** location) { } DISABLE_CFI_PERF -void GlobalHandles::IterateWeakRoots(RootVisitor* v) { +void GlobalHandles::IterateWeakRootsForFinalizers(RootVisitor* v) { for (NodeIterator it(this); !it.done(); it.Advance()) { Node* node = it.node(); - if (node->IsWeakRetainer()) { - // Pending weak phantom handles die immediately. Everything else survives. - if (node->IsPendingPhantomResetHandle()) { + if (node->IsWeakRetainer() && node->state() == Node::PENDING) { + DCHECK(!node->IsPhantomCallback()); + DCHECK(!node->IsPhantomResetHandle()); + // Finalizers need to survive. + v->VisitRootPointer(Root::kGlobalHandles, node->location()); + } + } +} + +DISABLE_CFI_PERF +void GlobalHandles::IterateWeakRootsForPhantomHandles( + WeakSlotCallback should_reset_handle) { + for (NodeIterator it(this); !it.done(); it.Advance()) { + Node* node = it.node(); + if (node->IsWeakRetainer() && should_reset_handle(node->location())) { + if (node->IsPhantomResetHandle()) { + node->MarkPending(); node->ResetPhantomHandle(); ++number_of_phantom_handle_resets_; - } else if (node->IsPendingPhantomCallback()) { + } else if (node->IsPhantomCallback()) { + node->MarkPending(); node->CollectPhantomCallbackData(isolate(), &pending_phantom_callbacks_); - } else { - v->VisitRootPointer(Root::kGlobalHandles, node->location()); } } } } - -void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) { +void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback should_reset_handle) { for (NodeIterator it(this); !it.done(); it.Advance()) { - if (it.node()->IsWeak() && f(it.node()->location())) { - it.node()->MarkPending(); + Node* node = it.node(); + if (node->IsWeak() && should_reset_handle(node->location())) { + if (!node->IsPhantomCallback() && !node->IsPhantomResetHandle()) { + node->MarkPending(); + } } } } diff --git a/chromium/v8/src/global-handles.h b/chromium/v8/src/global-handles.h index 4e3d8161b8d..b5c3b2191d7 100644 --- a/chromium/v8/src/global-handles.h +++ b/chromium/v8/src/global-handles.h @@ -133,12 +133,13 @@ class GlobalHandles { // and have class IDs void IterateWeakRootsInNewSpaceWithClassIds(v8::PersistentHandleVisitor* v); - // Iterates over all weak roots in heap. - void IterateWeakRoots(RootVisitor* v); + // Iterates over weak roots on the heap. + void IterateWeakRootsForFinalizers(RootVisitor* v); + void IterateWeakRootsForPhantomHandles(WeakSlotCallback should_reset_handle); - // Find all weak handles satisfying the callback predicate, mark - // them as pending. - void IdentifyWeakHandles(WeakSlotCallback f); + // Marks all handles that should be finalized based on the predicate + // |should_reset_handle| as pending. + void IdentifyWeakHandles(WeakSlotCallback should_reset_handle); // NOTE: Five ...NewSpace... functions below are used during // scavenge collections and iterate over sets of handles that are diff --git a/chromium/v8/src/heap/concurrent-marking.cc b/chromium/v8/src/heap/concurrent-marking.cc index 5a282e7f9cd..60bcbe9bab1 100644 --- a/chromium/v8/src/heap/concurrent-marking.cc +++ b/chromium/v8/src/heap/concurrent-marking.cc @@ -121,12 +121,6 @@ class ConcurrentMarkingVisitor final int VisitJSApiObject(Map* map, JSObject* object) { if (marking_state_.IsGrey(object)) { - int size = JSObject::BodyDescriptor::SizeOf(map, object); - VisitMapPointer(object, object->map_slot()); - // It is OK to iterate body of JS API object here because they do not have - // unboxed double fields. - DCHECK_IMPLIES(FLAG_unbox_double_fields, map->HasFastPointerLayout()); - JSObject::BodyDescriptor::IterateBody(object, size, this); // The main thread will do wrapper tracing in Blink. bailout_.Push(object); } diff --git a/chromium/v8/src/heap/heap.cc b/chromium/v8/src/heap/heap.cc index c5c0a7c54ea..458c6c7e094 100644 --- a/chromium/v8/src/heap/heap.cc +++ b/chromium/v8/src/heap/heap.cc @@ -2381,6 +2381,7 @@ AllocationResult Heap::AllocatePartialMap(InstanceType instance_type, Map::ConstructionCounter::encode(Map::kNoSlackTracking); map->set_bit_field3(bit_field3); map->set_weak_cell_cache(Smi::kZero); + map->set_elements_kind(TERMINAL_FAST_ELEMENTS_KIND); return map; } @@ -2388,6 +2389,11 @@ AllocationResult Heap::AllocatePartialMap(InstanceType instance_type, AllocationResult Heap::AllocateMap(InstanceType instance_type, int instance_size, ElementsKind elements_kind) { + STATIC_ASSERT(LAST_JS_OBJECT_TYPE == LAST_TYPE); + DCHECK_IMPLIES(instance_type >= FIRST_JS_OBJECT_TYPE && + !Map::CanHaveFastTransitionableElementsKind(instance_type), + IsDictionaryElementsKind(elements_kind) || + IsTerminalElementsKind(elements_kind)); HeapObject* result = nullptr; AllocationResult allocation = AllocateRaw(Map::kSize, MAP_SPACE); if (!allocation.To(&result)) return allocation; @@ -3760,7 +3766,10 @@ AllocationResult Heap::AllocateFixedArrayWithFiller(int length, AllocationResult Heap::AllocatePropertyArray(int length, PretenureFlag pretenure) { + // Allow length = 0 for the empty_property_array singleton. DCHECK_LE(0, length); + DCHECK_IMPLIES(length == 0, pretenure == TENURED); + DCHECK(!InNewSpace(undefined_value())); HeapObject* result = nullptr; { diff --git a/chromium/v8/src/heap/mark-compact.cc b/chromium/v8/src/heap/mark-compact.cc index 37bce9162b9..3d28a18c7af 100644 --- a/chromium/v8/src/heap/mark-compact.cc +++ b/chromium/v8/src/heap/mark-compact.cc @@ -2568,6 +2568,7 @@ void MarkCompactCollector::MarkLiveObjects() { TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_WEAK_CLOSURE_EPHEMERAL); ProcessEphemeralMarking(false); + DCHECK(marking_worklist()->IsEmpty()); } // The objects reachable from the roots, weak maps or object groups @@ -2584,12 +2585,12 @@ void MarkCompactCollector::MarkLiveObjects() { &IsUnmarkedHeapObject); ProcessMarkingWorklist(); } - // Then we mark the objects. { TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_WEAK_CLOSURE_WEAK_ROOTS); - heap()->isolate()->global_handles()->IterateWeakRoots(&root_visitor); + heap()->isolate()->global_handles()->IterateWeakRootsForFinalizers( + &root_visitor); ProcessMarkingWorklist(); } @@ -2605,6 +2606,12 @@ void MarkCompactCollector::MarkLiveObjects() { TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_WRAPPER_EPILOGUE); heap()->local_embedder_heap_tracer()->TraceEpilogue(); } + DCHECK(marking_worklist()->IsEmpty()); + } + + { + heap()->isolate()->global_handles()->IterateWeakRootsForPhantomHandles( + &IsUnmarkedHeapObject); } } diff --git a/chromium/v8/src/heap/setup-heap-internal.cc b/chromium/v8/src/heap/setup-heap-internal.cc index 25bb5d01b03..592fb53a7fd 100644 --- a/chromium/v8/src/heap/setup-heap-internal.cc +++ b/chromium/v8/src/heap/setup-heap-internal.cc @@ -103,10 +103,8 @@ bool Heap::CreateInitialMaps() { } ALLOCATE_PARTIAL_MAP(FIXED_ARRAY_TYPE, kVariableSizeSentinel, fixed_array); - fixed_array_map()->set_elements_kind(HOLEY_ELEMENTS); ALLOCATE_PARTIAL_MAP(FIXED_ARRAY_TYPE, kVariableSizeSentinel, fixed_cow_array) - fixed_cow_array_map()->set_elements_kind(HOLEY_ELEMENTS); DCHECK_NE(fixed_array_map(), fixed_cow_array_map()); ALLOCATE_PARTIAL_MAP(ODDBALL_TYPE, Oddball::kSize, undefined); diff --git a/chromium/v8/src/ic/accessor-assembler.cc b/chromium/v8/src/ic/accessor-assembler.cc index 5e269963201..2472febd032 100644 --- a/chromium/v8/src/ic/accessor-assembler.cc +++ b/chromium/v8/src/ic/accessor-assembler.cc @@ -537,43 +537,51 @@ void AccessorAssembler::HandleLoadICProtoHandlerCase( } } +void AccessorAssembler::EmitAccessCheck(Node* expected_native_context, + Node* context, Node* receiver, + Label* can_access, Label* miss) { + CSA_ASSERT(this, IsNativeContext(expected_native_context)); + + Node* native_context = LoadNativeContext(context); + GotoIf(WordEqual(expected_native_context, native_context), can_access); + // If the receiver is not a JSGlobalProxy then we miss. + GotoIfNot(IsJSGlobalProxy(receiver), miss); + // For JSGlobalProxy receiver try to compare security tokens of current + // and expected native contexts. + Node* expected_token = LoadContextElement(expected_native_context, + Context::SECURITY_TOKEN_INDEX); + Node* current_token = + LoadContextElement(native_context, Context::SECURITY_TOKEN_INDEX); + Branch(WordEqual(expected_token, current_token), can_access, miss); +} + Node* AccessorAssembler::EmitLoadICProtoArrayCheck(const LoadICParameters* p, Node* handler, Node* handler_length, Node* handler_flags, Label* miss) { - VARIABLE(start_index, MachineType::PointerRepresentation()); - start_index.Bind(IntPtrConstant(LoadHandler::kFirstPrototypeIndex)); + VARIABLE(var_start_index, MachineType::PointerRepresentation(), + IntPtrConstant(LoadHandler::kFirstPrototypeIndex)); Label can_access(this); GotoIfNot(IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_flags), &can_access); { // Skip this entry of a handler. - start_index.Bind(IntPtrConstant(LoadHandler::kFirstPrototypeIndex + 1)); + var_start_index.Bind(IntPtrConstant(LoadHandler::kFirstPrototypeIndex + 1)); int offset = FixedArray::OffsetOfElementAt(LoadHandler::kFirstPrototypeIndex); Node* expected_native_context = LoadWeakCellValue(LoadObjectField(handler, offset), miss); - CSA_ASSERT(this, IsNativeContext(expected_native_context)); - - Node* native_context = LoadNativeContext(p->context); - GotoIf(WordEqual(expected_native_context, native_context), &can_access); - // If the receiver is not a JSGlobalProxy then we miss. - GotoIfNot(IsJSGlobalProxy(p->receiver), miss); - // For JSGlobalProxy receiver try to compare security tokens of current - // and expected native contexts. - Node* expected_token = LoadContextElement(expected_native_context, - Context::SECURITY_TOKEN_INDEX); - Node* current_token = - LoadContextElement(native_context, Context::SECURITY_TOKEN_INDEX); - Branch(WordEqual(expected_token, current_token), &can_access, miss); + + EmitAccessCheck(expected_native_context, p->context, p->receiver, + &can_access, miss); } BIND(&can_access); - BuildFastLoop(start_index.value(), handler_length, - [this, p, handler, miss](Node* current) { + BuildFastLoop(var_start_index.value(), handler_length, + [=](Node* current) { Node* prototype_cell = LoadFixedArrayElement(handler, current); CheckPrototype(prototype_cell, p->name, miss); @@ -649,16 +657,19 @@ void AccessorAssembler::HandleStoreICHandlerCase( Label if_fast_smi(this), if_proxy(this); + STATIC_ASSERT(StoreHandler::kStoreGlobalProxy + 1 == + StoreHandler::kStoreNormal); STATIC_ASSERT(StoreHandler::kStoreNormal + 1 == StoreHandler::kProxy); STATIC_ASSERT(StoreHandler::kProxy + 1 == StoreHandler::kKindsNumber); Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word); GotoIf(IntPtrLessThan(handler_kind, - IntPtrConstant(StoreHandler::kStoreNormal)), + IntPtrConstant(StoreHandler::kStoreGlobalProxy)), &if_fast_smi); GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kProxy)), &if_proxy); - + CSA_ASSERT(this, WordEqual(handler_kind, + IntPtrConstant(StoreHandler::kStoreNormal))); Node* properties = LoadSlowProperties(holder); VARIABLE(var_name_index, MachineType::PointerRepresentation()); @@ -716,62 +727,11 @@ void AccessorAssembler::HandleStoreICHandlerCase( BIND(&store_global); { + // Load value or miss if the {handler} weak cell is cleared. Node* cell = LoadWeakCellValue(handler, miss); - CSA_ASSERT(this, IsPropertyCell(cell)); - - // Load the payload of the global parameter cell. A hole indicates that - // the cell has been invalidated and that the store must be handled by the - // runtime. - Node* cell_contents = LoadObjectField(cell, PropertyCell::kValueOffset); - Node* details = - LoadAndUntagToWord32ObjectField(cell, PropertyCell::kDetailsOffset); - Node* type = DecodeWord32<PropertyDetails::PropertyCellTypeField>(details); - - Label constant(this), store(this), not_smi(this); - - GotoIf( - Word32Equal( - type, Int32Constant(static_cast<int>(PropertyCellType::kConstant))), - &constant); - - GotoIf(IsTheHole(cell_contents), miss); - - GotoIf( - Word32Equal( - type, Int32Constant(static_cast<int>(PropertyCellType::kMutable))), - &store); - CSA_ASSERT(this, - Word32Or(Word32Equal(type, - Int32Constant(static_cast<int>( - PropertyCellType::kConstantType))), - Word32Equal(type, - Int32Constant(static_cast<int>( - PropertyCellType::kUndefined))))); - - GotoIfNot(TaggedIsSmi(cell_contents), ¬_smi); - GotoIfNot(TaggedIsSmi(p->value), miss); - Goto(&store); - - BIND(¬_smi); - { - GotoIf(TaggedIsSmi(p->value), miss); - Node* expected_map = LoadMap(cell_contents); - Node* map = LoadMap(p->value); - GotoIfNot(WordEqual(expected_map, map), miss); - Goto(&store); - } - - BIND(&store); - { - StoreObjectField(cell, PropertyCell::kValueOffset, p->value); - Return(p->value); - } - BIND(&constant); - { - GotoIfNot(WordEqual(cell_contents, p->value), miss); - Return(p->value); - } + ExitPoint direct_exit(this); + StoreGlobalIC_PropertyCellCase(cell, p->value, &direct_exit, miss); } } @@ -794,9 +754,11 @@ void AccessorAssembler::HandleStoreICElementHandlerCase( void AccessorAssembler::HandleStoreICProtoHandler( const StoreICParameters* p, Node* handler, Label* miss, ElementSupport support_elements) { + Comment("HandleStoreICProtoHandler"); + // IC dispatchers rely on these assumptions to be held. STATIC_ASSERT(FixedArray::kLengthOffset == - StoreHandler::kTransitionCellOffset); + StoreHandler::kTransitionOrHolderCellOffset); DCHECK_EQ(FixedArray::OffsetOfElementAt(StoreHandler::kSmiHandlerIndex), StoreHandler::kSmiHandlerOffset); DCHECK_EQ(FixedArray::OffsetOfElementAt(StoreHandler::kValidityCellIndex), @@ -817,56 +779,77 @@ void AccessorAssembler::HandleStoreICProtoHandler( Node* smi_or_code = LoadObjectField(handler, StoreHandler::kSmiHandlerOffset); Node* maybe_transition_cell = - LoadObjectField(handler, StoreHandler::kTransitionCellOffset); + LoadObjectField(handler, StoreHandler::kTransitionOrHolderCellOffset); Label array_handler(this), tuple_handler(this); Branch(TaggedIsSmi(maybe_transition_cell), &array_handler, &tuple_handler); - VARIABLE(var_transition, MachineRepresentation::kTagged); - Label if_transition(this), if_transition_to_constant(this), - if_store_normal(this), if_proxy(this), do_store(this); + VARIABLE(var_transition_map_or_holder, MachineRepresentation::kTagged); + Label do_store(this), if_transition_map(this), if_holder_object(this); BIND(&tuple_handler); { Node* transition = LoadWeakCellValue(maybe_transition_cell, miss); - var_transition.Bind(transition); + var_transition_map_or_holder.Bind(transition); Goto(&do_store); } BIND(&array_handler); { + VARIABLE(var_start_index, MachineType::PointerRepresentation(), + IntPtrConstant(StoreHandler::kFirstPrototypeIndex)); + + Comment("array_handler"); + Label can_access(this); + // Only Tuple3 handlers are allowed to have code handlers. + CSA_ASSERT(this, TaggedIsSmi(smi_or_code)); + GotoIfNot( + IsSetSmi(smi_or_code, StoreHandler::DoAccessCheckOnReceiverBits::kMask), + &can_access); + + { + // Skip this entry of a handler. + var_start_index.Bind( + IntPtrConstant(StoreHandler::kFirstPrototypeIndex + 1)); + + int offset = + FixedArray::OffsetOfElementAt(StoreHandler::kFirstPrototypeIndex); + Node* expected_native_context = + LoadWeakCellValue(LoadObjectField(handler, offset), miss); + + EmitAccessCheck(expected_native_context, p->context, p->receiver, + &can_access, miss); + } + BIND(&can_access); + Node* length = SmiUntag(maybe_transition_cell); - BuildFastLoop(IntPtrConstant(StoreHandler::kFirstPrototypeIndex), length, - [this, p, handler, miss](Node* current) { + BuildFastLoop(var_start_index.value(), length, + [=](Node* current) { Node* prototype_cell = LoadFixedArrayElement(handler, current); CheckPrototype(prototype_cell, p->name, miss); }, 1, INTPTR_PARAMETERS, IndexAdvanceMode::kPost); - Node* maybe_transition_cell = - LoadFixedArrayElement(handler, StoreHandler::kTransitionCellIndex); + Node* maybe_transition_cell = LoadFixedArrayElement( + handler, StoreHandler::kTransitionMapOrHolderCellIndex); Node* transition = LoadWeakCellValue(maybe_transition_cell, miss); - var_transition.Bind(transition); + var_transition_map_or_holder.Bind(transition); Goto(&do_store); } BIND(&do_store); { - Branch(SmiEqual(smi_or_code, SmiConstant(StoreHandler::kProxy)), &if_proxy, - &if_transition); + Node* transition = var_transition_map_or_holder.value(); + Branch(IsMap(transition), &if_transition_map, &if_holder_object); } - BIND(&if_proxy); + BIND(&if_transition_map); { - Node* proxy = var_transition.value(); - HandleStoreToProxy(p, proxy, miss, support_elements); - } + Label if_transition_to_constant(this), if_store_normal(this); - BIND(&if_transition); - { Node* holder = p->receiver; - Node* transition = var_transition.value(); + Node* transition_map = var_transition_map_or_holder.value(); - GotoIf(IsDeprecatedMap(transition), miss); + GotoIf(IsDeprecatedMap(transition_map), miss); if (support_elements == kSupportElements) { Label if_smi_handler(this); @@ -877,7 +860,7 @@ void AccessorAssembler::HandleStoreICProtoHandler( StoreTransitionDescriptor descriptor(isolate()); TailCallStub(descriptor, code_handler, p->context, p->receiver, p->name, - transition, p->value, p->slot, p->vector); + transition_map, p->value, p->slot, p->vector); BIND(&if_smi_handler); } @@ -892,9 +875,12 @@ void AccessorAssembler::HandleStoreICProtoHandler( GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kTransitionToConstant)), &if_transition_to_constant); + CSA_ASSERT(this, + WordEqual(handler_kind, + IntPtrConstant(StoreHandler::kTransitionToField))); // Handle transitioning field stores. - HandleStoreICSmiHandlerCase(handler_word, holder, p->value, transition, + HandleStoreICSmiHandlerCase(handler_word, holder, p->value, transition_map, miss); BIND(&if_transition_to_constant); @@ -907,7 +893,7 @@ void AccessorAssembler::HandleStoreICProtoHandler( IntPtrAdd(scaled_descriptor, IntPtrConstant(DescriptorArray::kFirstIndex + DescriptorArray::kEntryValueIndex)); - Node* descriptors = LoadMapDescriptors(transition); + Node* descriptors = LoadMapDescriptors(transition_map); CSA_ASSERT( this, UintPtrLessThan(descriptor, LoadAndUntagFixedArrayBaseLength(descriptors))); @@ -915,7 +901,7 @@ void AccessorAssembler::HandleStoreICProtoHandler( Node* constant = LoadFixedArrayElement(descriptors, value_index); GotoIf(WordNotEqual(p->value, constant), miss); - StoreMap(p->receiver, transition); + StoreMap(p->receiver, transition_map); Return(p->value); } @@ -955,6 +941,29 @@ void AccessorAssembler::HandleStoreICProtoHandler( } } } + BIND(&if_holder_object); + { + Label if_store_global_proxy(this); + Node* holder = var_transition_map_or_holder.value(); + + Node* smi_handler = smi_or_code; + CSA_ASSERT(this, TaggedIsSmi(smi_handler)); + Node* handler_word = SmiUntag(smi_handler); + + Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word); + GotoIf(WordEqual(handler_kind, + IntPtrConstant(StoreHandler::kStoreGlobalProxy)), + &if_store_global_proxy); + CSA_ASSERT(this, + WordEqual(handler_kind, IntPtrConstant(StoreHandler::kProxy))); + HandleStoreToProxy(p, holder, miss, support_elements); + + BIND(&if_store_global_proxy); + { + ExitPoint direct_exit(this); + StoreGlobalIC_PropertyCellCase(holder, p->value, &direct_exit, miss); + } + } } void AccessorAssembler::HandleStoreToProxy(const StoreICParameters* p, @@ -1174,7 +1183,7 @@ void AccessorAssembler::ExtendPropertiesBackingStore(Node* object, // TODO(gsathya): Clean up the type conversions by creating smarter // helpers that do the correct op based on the mode. VARIABLE(var_properties, MachineRepresentation::kTaggedPointer); - VARIABLE(var_hash, MachineRepresentation::kWord32); + VARIABLE(var_encoded_hash, MachineRepresentation::kWord32); VARIABLE(var_length, ParameterRepresentation(mode)); Node* properties = LoadObjectField(object, JSObject::kPropertiesOrHashOffset); @@ -1185,7 +1194,10 @@ void AccessorAssembler::ExtendPropertiesBackingStore(Node* object, BIND(&if_smi_hash); { - var_hash.Bind(SmiToWord32(properties)); + Node* hash = SmiToWord32(properties); + Node* encoded_hash = + Word32Shl(hash, Int32Constant(PropertyArray::HashField::kShift)); + var_encoded_hash.Bind(encoded_hash); var_length.Bind(IntPtrOrSmiConstant(0, mode)); var_properties.Bind(EmptyFixedArrayConstant()); Goto(&extend_store); @@ -1195,10 +1207,11 @@ void AccessorAssembler::ExtendPropertiesBackingStore(Node* object, { Node* length_and_hash_int32 = LoadAndUntagToWord32ObjectField( var_properties.value(), PropertyArray::kLengthAndHashOffset); - var_hash.Bind(Word32And(length_and_hash_int32, - Int32Constant(PropertyArray::kHashMask))); - Node* length_intptr = ChangeInt32ToIntPtr(Word32And( - length_and_hash_int32, Int32Constant(PropertyArray::kLengthMask))); + var_encoded_hash.Bind(Word32And( + length_and_hash_int32, Int32Constant(PropertyArray::HashField::kMask))); + Node* length_intptr = ChangeInt32ToIntPtr( + Word32And(length_and_hash_int32, + Int32Constant(PropertyArray::LengthField::kMask))); Node* length = WordToParameter(length_intptr, mode); var_length.Bind(length); Goto(&extend_store); @@ -1244,7 +1257,7 @@ void AccessorAssembler::ExtendPropertiesBackingStore(Node* object, Node* new_capacity_int32 = TruncateWordToWord32(ParameterToWord(new_capacity, mode)); Node* new_length_and_hash_int32 = - Word32Or(var_hash.value(), new_capacity_int32); + Word32Or(var_encoded_hash.value(), new_capacity_int32); StoreObjectField(new_properties, PropertyArray::kLengthAndHashOffset, SmiFromWord32(new_length_and_hash_int32)); StoreObjectField(object, JSObject::kPropertiesOrHashOffset, new_properties); @@ -2392,6 +2405,65 @@ void AccessorAssembler::StoreIC(const StoreICParameters* p) { } } +void AccessorAssembler::StoreGlobalIC_PropertyCellCase(Node* property_cell, + Node* value, + ExitPoint* exit_point, + Label* miss) { + Comment("StoreGlobalIC_TryPropertyCellCase"); + CSA_ASSERT(this, IsPropertyCell(property_cell)); + + // Load the payload of the global parameter cell. A hole indicates that + // the cell has been invalidated and that the store must be handled by the + // runtime. + Node* cell_contents = + LoadObjectField(property_cell, PropertyCell::kValueOffset); + Node* details = LoadAndUntagToWord32ObjectField(property_cell, + PropertyCell::kDetailsOffset); + Node* type = DecodeWord32<PropertyDetails::PropertyCellTypeField>(details); + + Label constant(this), store(this), not_smi(this); + + GotoIf(Word32Equal(type, Int32Constant( + static_cast<int>(PropertyCellType::kConstant))), + &constant); + + GotoIf(IsTheHole(cell_contents), miss); + + GotoIf(Word32Equal( + type, Int32Constant(static_cast<int>(PropertyCellType::kMutable))), + &store); + CSA_ASSERT(this, + Word32Or(Word32Equal(type, Int32Constant(static_cast<int>( + PropertyCellType::kConstantType))), + Word32Equal(type, Int32Constant(static_cast<int>( + PropertyCellType::kUndefined))))); + + GotoIfNot(TaggedIsSmi(cell_contents), ¬_smi); + GotoIfNot(TaggedIsSmi(value), miss); + Goto(&store); + + BIND(¬_smi); + { + GotoIf(TaggedIsSmi(value), miss); + Node* expected_map = LoadMap(cell_contents); + Node* map = LoadMap(value); + GotoIfNot(WordEqual(expected_map, map), miss); + Goto(&store); + } + + BIND(&store); + { + StoreObjectField(property_cell, PropertyCell::kValueOffset, value); + exit_point->Return(value); + } + + BIND(&constant); + { + GotoIfNot(WordEqual(cell_contents, value), miss); + exit_point->Return(value); + } +} + void AccessorAssembler::KeyedStoreIC(const StoreICParameters* p) { Label miss(this, Label::kDeferred); { diff --git a/chromium/v8/src/ic/accessor-assembler.h b/chromium/v8/src/ic/accessor-assembler.h index 1f330e58a4f..4fe1c0bbf99 100644 --- a/chromium/v8/src/ic/accessor-assembler.h +++ b/chromium/v8/src/ic/accessor-assembler.h @@ -115,6 +115,8 @@ class AccessorAssembler : public CodeStubAssembler { void KeyedLoadIC(const LoadICParameters* p); void KeyedLoadICGeneric(const LoadICParameters* p); void StoreIC(const StoreICParameters* p); + void StoreGlobalIC_PropertyCellCase(Node* property_cell, Node* value, + ExitPoint* exit_point, Label* miss); void KeyedStoreIC(const StoreICParameters* p); // IC dispatcher behavior. @@ -150,6 +152,9 @@ class AccessorAssembler : public CodeStubAssembler { Variable* var_double_value, Label* rebox_double, ExitPoint* exit_point); + void EmitAccessCheck(Node* expected_native_context, Node* context, + Node* receiver, Label* can_access, Label* miss); + Node* EmitLoadICProtoArrayCheck(const LoadICParameters* p, Node* handler, Node* handler_length, Node* handler_flags, Label* miss); diff --git a/chromium/v8/src/ic/handler-configuration-inl.h b/chromium/v8/src/ic/handler-configuration-inl.h index 26586141d77..dc1d5957239 100644 --- a/chromium/v8/src/ic/handler-configuration-inl.h +++ b/chromium/v8/src/ic/handler-configuration-inl.h @@ -108,6 +108,11 @@ Handle<Smi> LoadHandler::LoadElement(Isolate* isolate, return handle(Smi::FromInt(config), isolate); } +Handle<Smi> StoreHandler::StoreGlobalProxy(Isolate* isolate) { + int config = KindBits::encode(kStoreGlobalProxy); + return handle(Smi::FromInt(config), isolate); +} + Handle<Smi> StoreHandler::StoreNormal(Isolate* isolate) { int config = KindBits::encode(kStoreNormal); return handle(Smi::FromInt(config), isolate); @@ -118,6 +123,13 @@ Handle<Smi> StoreHandler::StoreProxy(Isolate* isolate) { return handle(Smi::FromInt(config), isolate); } +Handle<Smi> StoreHandler::EnableAccessCheckOnReceiver(Isolate* isolate, + Handle<Smi> smi_handler) { + int config = smi_handler->value(); + config = DoAccessCheckOnReceiverBits::update(config, true); + return handle(Smi::FromInt(config), isolate); +} + Handle<Smi> StoreHandler::StoreField(Isolate* isolate, Kind kind, int descriptor, FieldIndex field_index, Representation representation, @@ -184,15 +196,15 @@ Handle<Smi> StoreHandler::TransitionToConstant(Isolate* isolate, // static WeakCell* StoreHandler::GetTransitionCell(Object* handler) { if (handler->IsTuple3()) { - STATIC_ASSERT(kTransitionCellOffset == Tuple3::kValue1Offset); + STATIC_ASSERT(kTransitionOrHolderCellOffset == Tuple3::kValue1Offset); WeakCell* cell = WeakCell::cast(Tuple3::cast(handler)->value1()); DCHECK(!cell->cleared()); return cell; } DCHECK(handler->IsFixedArray()); - WeakCell* cell = - WeakCell::cast(FixedArray::cast(handler)->get(kTransitionCellIndex)); + WeakCell* cell = WeakCell::cast( + FixedArray::cast(handler)->get(kTransitionMapOrHolderCellIndex)); DCHECK(!cell->cleared()); return cell; } diff --git a/chromium/v8/src/ic/handler-configuration.cc b/chromium/v8/src/ic/handler-configuration.cc index e5ddf06d64a..b294c864a94 100644 --- a/chromium/v8/src/ic/handler-configuration.cc +++ b/chromium/v8/src/ic/handler-configuration.cc @@ -232,41 +232,59 @@ Handle<Object> StoreHandler::StoreElementTransition( Handle<Object> StoreHandler::StoreTransition(Isolate* isolate, Handle<Map> receiver_map, Handle<JSObject> holder, - Handle<Map> transition, + Handle<HeapObject> transition, Handle<Name> name) { - Handle<Object> smi_handler; - if (transition->is_dictionary_map()) { - smi_handler = StoreNormal(isolate); - } else { - int descriptor = transition->LastAdded(); - Handle<DescriptorArray> descriptors(transition->instance_descriptors()); - PropertyDetails details = descriptors->GetDetails(descriptor); - Representation representation = details.representation(); - DCHECK(!representation.IsNone()); + Handle<Smi> smi_handler; + Handle<WeakCell> transition_cell; + + if (transition->IsMap()) { + Handle<Map> transition_map = Handle<Map>::cast(transition); + if (transition_map->is_dictionary_map()) { + smi_handler = StoreNormal(isolate); + } else { + int descriptor = transition_map->LastAdded(); + Handle<DescriptorArray> descriptors( + transition_map->instance_descriptors()); + PropertyDetails details = descriptors->GetDetails(descriptor); + Representation representation = details.representation(); + DCHECK(!representation.IsNone()); - // Declarative handlers don't support access checks. - DCHECK(!transition->is_access_check_needed()); + // Declarative handlers don't support access checks. + DCHECK(!transition_map->is_access_check_needed()); - DCHECK_EQ(kData, details.kind()); - if (details.location() == kDescriptor) { - smi_handler = TransitionToConstant(isolate, descriptor); + DCHECK_EQ(kData, details.kind()); + if (details.location() == kDescriptor) { + smi_handler = TransitionToConstant(isolate, descriptor); + } else { + DCHECK_EQ(kField, details.location()); + bool extend_storage = Map::cast(transition_map->GetBackPointer()) + ->unused_property_fields() == 0; + + FieldIndex index = + FieldIndex::ForDescriptor(*transition_map, descriptor); + smi_handler = TransitionToField(isolate, descriptor, index, + representation, extend_storage); + } + } + // |holder| is either a receiver if the property is non-existent or + // one of the prototypes. + DCHECK(!holder.is_null()); + bool is_nonexistent = holder->map() == transition_map->GetBackPointer(); + if (is_nonexistent) holder = Handle<JSObject>::null(); + transition_cell = Map::WeakCellForMap(transition_map); + + } else { + DCHECK(transition->IsPropertyCell()); + if (receiver_map->IsJSGlobalObjectMap()) { + // TODO(ishell): this must be handled by StoreGlobalIC once it's finished. + return StoreGlobal(isolate, Handle<PropertyCell>::cast(transition)); } else { - DCHECK_EQ(kField, details.location()); - bool extend_storage = - Map::cast(transition->GetBackPointer())->unused_property_fields() == - 0; - - FieldIndex index = FieldIndex::ForDescriptor(*transition, descriptor); - smi_handler = TransitionToField(isolate, descriptor, index, - representation, extend_storage); + DCHECK(receiver_map->IsJSGlobalProxyMap()); + smi_handler = StoreGlobalProxy(isolate); + transition_cell = isolate->factory()->NewWeakCell(transition); } } - // |holder| is either a receiver if the property is non-existent or - // one of the prototypes. - DCHECK(!holder.is_null()); - bool is_nonexistent = holder->map() == transition->GetBackPointer(); - if (is_nonexistent) holder = Handle<JSObject>::null(); int checks_count = GetPrototypeCheckCount(isolate, receiver_map, holder, name); @@ -274,6 +292,12 @@ Handle<Object> StoreHandler::StoreTransition(Isolate* isolate, DCHECK_LE(0, checks_count); DCHECK(!receiver_map->IsJSGlobalObjectMap()); + if (receiver_map->is_access_check_needed()) { + DCHECK(!receiver_map->is_dictionary_map()); + DCHECK_LE(1, checks_count); // For native context. + smi_handler = EnableAccessCheckOnReceiver(isolate, smi_handler); + } + Handle<Object> validity_cell = Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate); if (validity_cell.is_null()) { @@ -281,8 +305,6 @@ Handle<Object> StoreHandler::StoreTransition(Isolate* isolate, validity_cell = handle(Smi::kZero, isolate); } - Handle<WeakCell> transition_cell = Map::WeakCellForMap(transition); - Factory* factory = isolate->factory(); if (checks_count == 0) { return factory->NewTuple3(transition_cell, smi_handler, validity_cell, @@ -292,19 +314,25 @@ Handle<Object> StoreHandler::StoreTransition(Isolate* isolate, factory->NewFixedArray(kFirstPrototypeIndex + checks_count, TENURED)); handler_array->set(kSmiHandlerIndex, *smi_handler); handler_array->set(kValidityCellIndex, *validity_cell); - handler_array->set(kTransitionCellIndex, *transition_cell); + handler_array->set(kTransitionMapOrHolderCellIndex, *transition_cell); InitPrototypeChecks(isolate, receiver_map, holder, name, handler_array, kFirstPrototypeIndex); return handler_array; } // static +Handle<Object> StoreHandler::StoreGlobal(Isolate* isolate, + Handle<PropertyCell> cell) { + return isolate->factory()->NewWeakCell(cell); +} + +// static Handle<Object> StoreHandler::StoreProxy(Isolate* isolate, Handle<Map> receiver_map, Handle<JSProxy> proxy, Handle<JSReceiver> receiver, Handle<Name> name) { - Handle<Object> smi_handler = StoreProxy(isolate); + Handle<Smi> smi_handler = StoreProxy(isolate); if (receiver.is_identical_to(proxy)) return smi_handler; @@ -312,6 +340,12 @@ Handle<Object> StoreHandler::StoreProxy(Isolate* isolate, DCHECK_LE(0, checks_count); + if (receiver_map->is_access_check_needed()) { + DCHECK(!receiver_map->is_dictionary_map()); + DCHECK_LE(1, checks_count); // For native context. + smi_handler = EnableAccessCheckOnReceiver(isolate, smi_handler); + } + Handle<Object> validity_cell = Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate); if (validity_cell.is_null()) { @@ -329,7 +363,7 @@ Handle<Object> StoreHandler::StoreProxy(Isolate* isolate, factory->NewFixedArray(kFirstPrototypeIndex + checks_count, TENURED)); handler_array->set(kSmiHandlerIndex, *smi_handler); handler_array->set(kValidityCellIndex, *validity_cell); - handler_array->set(kTransitionCellIndex, *holder_cell); + handler_array->set(kTransitionMapOrHolderCellIndex, *holder_cell); InitPrototypeChecks(isolate, receiver_map, proxy, name, handler_array, kFirstPrototypeIndex); return handler_array; diff --git a/chromium/v8/src/ic/handler-configuration.h b/chromium/v8/src/ic/handler-configuration.h index a4c83b1220b..87ff45a46ae 100644 --- a/chromium/v8/src/ic/handler-configuration.h +++ b/chromium/v8/src/ic/handler-configuration.h @@ -178,12 +178,13 @@ class StoreHandler { kStoreElement, kStoreField, kStoreConstField, + // TODO(ishell): remove once constant field tracking is done. + kTransitionToConstant = kStoreConstField, kTransitionToField, + kStoreGlobalProxy, kStoreNormal, kProxy, - kKindsNumber, // Keep last - // TODO(ishell): remove once constant field tracking is done. - kTransitionToConstant = kStoreConstField + kKindsNumber // Keep last }; class KindBits : public BitField<Kind, 0, 3> {}; @@ -191,12 +192,19 @@ class StoreHandler { static inline bool IsHandler(Object* maybe_handler); + // Applicable to kStoreGlobalProxy, kProxy kinds. + + // Defines whether access rights check should be done on receiver object. + class DoAccessCheckOnReceiverBits + : public BitField<bool, KindBits::kNext, 1> {}; + // Applicable to kStoreField, kTransitionToField and kTransitionToConstant // kinds. // Index of a value entry in the descriptor array. class DescriptorBits - : public BitField<unsigned, KindBits::kNext, kDescriptorIndexBitCount> {}; + : public BitField<unsigned, DoAccessCheckOnReceiverBits::kNext, + kDescriptorIndexBitCount> {}; // // Encoding when KindBits contains kTransitionToConstant. // @@ -221,7 +229,7 @@ class StoreHandler { // The layout of an Tuple3 handler representing a transitioning store // when prototype chain checks do not include non-existing lookups or access // checks. - static const int kTransitionCellOffset = Tuple3::kValue1Offset; + static const int kTransitionOrHolderCellOffset = Tuple3::kValue1Offset; static const int kSmiHandlerOffset = Tuple3::kValue2Offset; static const int kValidityCellOffset = Tuple3::kValue3Offset; @@ -233,7 +241,7 @@ class StoreHandler { // when prototype chain checks include non-existing lookups and access checks. static const int kSmiHandlerIndex = 0; static const int kValidityCellIndex = 1; - static const int kTransitionCellIndex = 2; + static const int kTransitionMapOrHolderCellIndex = 2; static const int kFirstPrototypeIndex = 3; // Creates a Smi-handler for storing a field to fast object. @@ -245,7 +253,7 @@ class StoreHandler { static Handle<Object> StoreTransition(Isolate* isolate, Handle<Map> receiver_map, Handle<JSObject> holder, - Handle<Map> transition, + Handle<HeapObject> transition, Handle<Name> name); static Handle<Object> StoreElementTransition(Isolate* isolate, @@ -258,6 +266,14 @@ class StoreHandler { Handle<JSReceiver> receiver, Handle<Name> name); + // Creates a handler for storing a property to the property cell of a global + // object. + static Handle<Object> StoreGlobal(Isolate* isolate, + Handle<PropertyCell> cell); + + // Creates a Smi-handler for storing a property to a global proxy object. + static inline Handle<Smi> StoreGlobalProxy(Isolate* isolate); + // Creates a Smi-handler for storing a property to a slow object. static inline Handle<Smi> StoreNormal(Isolate* isolate); @@ -265,6 +281,11 @@ class StoreHandler { static inline Handle<Smi> StoreProxy(Isolate* isolate); private: + // Sets DoAccessCheckOnReceiverBits in given Smi-handler. The receiver + // check is a part of a prototype chain check. + static inline Handle<Smi> EnableAccessCheckOnReceiver( + Isolate* isolate, Handle<Smi> smi_handler); + static inline Handle<Smi> StoreField(Isolate* isolate, Kind kind, int descriptor, FieldIndex field_index, Representation representation, diff --git a/chromium/v8/src/ic/ic.cc b/chromium/v8/src/ic/ic.cc index fe7e372c6bb..09920241eec 100644 --- a/chromium/v8/src/ic/ic.cc +++ b/chromium/v8/src/ic/ic.cc @@ -1366,23 +1366,19 @@ void StoreIC::UpdateCaches(LookupIterator* lookup, Handle<Object> value, TRACE_IC("StoreIC", lookup->name()); } -namespace { - -Handle<Object> StoreGlobal(Isolate* isolate, Handle<PropertyCell> cell) { - return isolate->factory()->NewWeakCell(cell); -} - -} // namespace - Handle<Object> StoreIC::GetMapIndependentHandler(LookupIterator* lookup) { switch (lookup->state()) { case LookupIterator::TRANSITION: { Handle<JSObject> holder = lookup->GetHolder<JSObject>(); - auto store_target = lookup->GetStoreTarget(); + Handle<JSObject> store_target = lookup->GetStoreTarget(); if (store_target->IsJSGlobalObject()) { TRACE_HANDLER_STATS(isolate(), StoreIC_StoreGlobalTransitionDH); - return StoreGlobal(isolate(), lookup->transition_cell()); + + Handle<Object> handler = StoreHandler::StoreTransition( + isolate(), receiver_map(), store_target, lookup->transition_cell(), + lookup->name()); + return handler; } // Currently not handled by CompileStoreTransition. if (!holder->HasFastProperties()) { @@ -1477,7 +1473,8 @@ Handle<Object> StoreIC::GetMapIndependentHandler(LookupIterator* lookup) { if (lookup->is_dictionary_holder()) { if (holder->IsJSGlobalObject()) { TRACE_HANDLER_STATS(isolate(), StoreIC_StoreGlobalDH); - return StoreGlobal(isolate(), lookup->GetPropertyCell()); + return StoreHandler::StoreGlobal(isolate(), + lookup->GetPropertyCell()); } TRACE_HANDLER_STATS(isolate(), StoreIC_StoreNormalDH); DCHECK(holder.is_identical_to(receiver)); diff --git a/chromium/v8/src/ic/keyed-store-generic.cc b/chromium/v8/src/ic/keyed-store-generic.cc index 0f4b3eee466..7ff72bb72fd 100644 --- a/chromium/v8/src/ic/keyed-store-generic.cc +++ b/chromium/v8/src/ic/keyed-store-generic.cc @@ -811,14 +811,14 @@ void KeyedStoreGenericAssembler::EmitGenericPropertyStore( BIND(&tuple3); { var_transition_cell.Bind(LoadObjectField( - maybe_handler, StoreHandler::kTransitionCellOffset)); + maybe_handler, StoreHandler::kTransitionOrHolderCellOffset)); Goto(&check_key); } BIND(&fixedarray); { var_transition_cell.Bind(LoadFixedArrayElement( - maybe_handler, StoreHandler::kTransitionCellIndex)); + maybe_handler, StoreHandler::kTransitionMapOrHolderCellIndex)); Goto(&check_key); } diff --git a/chromium/v8/src/inspector/inspector.gypi b/chromium/v8/src/inspector/inspector.gypi index 96614e692a8..d6443283f5a 100644 --- a/chromium/v8/src/inspector/inspector.gypi +++ b/chromium/v8/src/inspector/inspector.gypi @@ -31,8 +31,8 @@ 'inspector_all_sources': [ '<@(inspector_generated_sources)', '<(inspector_generated_injected_script)', - '../../include/v8-inspector.h', - '../../include/v8-inspector-protocol.h', + '../include/v8-inspector.h', + '../include/v8-inspector-protocol.h', 'inspector/injected-script.cc', 'inspector/injected-script.h', 'inspector/inspected-context.cc', diff --git a/chromium/v8/src/inspector/v8-debugger.cc b/chromium/v8/src/inspector/v8-debugger.cc index 981a622b62d..3e321a42757 100644 --- a/chromium/v8/src/inspector/v8-debugger.cc +++ b/chromium/v8/src/inspector/v8-debugger.cc @@ -669,10 +669,8 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties( } if (value->IsFunction()) { v8::Local<v8::Function> function = value.As<v8::Function>(); - v8::Local<v8::Value> boundFunction = function->GetBoundFunction(); v8::Local<v8::Value> scopes; - if (boundFunction->IsUndefined() && - functionScopes(context, function).ToLocal(&scopes)) { + if (functionScopes(context, function).ToLocal(&scopes)) { createDataProperty(context, properties, properties->Length(), toV8StringInternalized(m_isolate, "[[Scopes]]")); createDataProperty(context, properties, properties->Length(), scopes); @@ -720,6 +718,8 @@ void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) { if (m_maxAsyncCallStackDepth == maxAsyncCallStackDepth) return; // TODO(dgozman): ideally, this should be per context group. m_maxAsyncCallStackDepth = maxAsyncCallStackDepth; + m_inspector->client()->maxAsyncCallStackDepthChanged( + m_maxAsyncCallStackDepth); if (!maxAsyncCallStackDepth) allAsyncTasksCanceled(); } diff --git a/chromium/v8/src/isolate.cc b/chromium/v8/src/isolate.cc index 942938eded2..9312432763c 100644 --- a/chromium/v8/src/isolate.cc +++ b/chromium/v8/src/isolate.cc @@ -3055,16 +3055,17 @@ bool Isolate::IsInAnyContext(Object* object, uint32_t index) { return false; } -bool Isolate::IsFastArrayConstructorPrototypeChainIntact() { +bool Isolate::IsFastArrayConstructorPrototypeChainIntact(Context* context) { PropertyCell* no_elements_cell = heap()->array_protector(); bool cell_reports_intact = no_elements_cell->value()->IsSmi() && Smi::ToInt(no_elements_cell->value()) == kProtectorValid; #ifdef DEBUG + Context* native_context = context->native_context(); + Map* root_array_map = - raw_native_context()->GetInitialJSArrayMap(GetInitialFastElementsKind()); - Context* native_context = context()->native_context(); + native_context->GetInitialJSArrayMap(GetInitialFastElementsKind()); JSObject* initial_array_proto = JSObject::cast( native_context->get(Context::INITIAL_ARRAY_PROTOTYPE_INDEX)); JSObject* initial_object_proto = JSObject::cast( @@ -3093,8 +3094,11 @@ bool Isolate::IsFastArrayConstructorPrototypeChainIntact() { PrototypeIterator iter(this, initial_array_proto); if (iter.IsAtEnd() || iter.GetCurrent() != initial_object_proto) { DCHECK_EQ(false, cell_reports_intact); + DCHECK(!has_pending_exception()); return cell_reports_intact; } + DCHECK(!has_pending_exception()); + DCHECK(!has_pending_exception()); elements = initial_object_proto->elements(); if (elements != heap()->empty_fixed_array() && @@ -3108,12 +3112,15 @@ bool Isolate::IsFastArrayConstructorPrototypeChainIntact() { DCHECK_EQ(false, cell_reports_intact); return cell_reports_intact; } - #endif return cell_reports_intact; } +bool Isolate::IsFastArrayConstructorPrototypeChainIntact() { + return Isolate::IsFastArrayConstructorPrototypeChainIntact(context()); +} + bool Isolate::IsIsConcatSpreadableLookupChainIntact() { Cell* is_concat_spreadable_cell = heap()->is_concat_spreadable_protector(); bool is_is_concat_spreadable_set = diff --git a/chromium/v8/src/isolate.h b/chromium/v8/src/isolate.h index 43ffb8ce000..44a52508086 100644 --- a/chromium/v8/src/isolate.h +++ b/chromium/v8/src/isolate.h @@ -1070,7 +1070,13 @@ class Isolate { static const int kProtectorInvalid = 0; inline bool IsArrayConstructorIntact(); + + // The version with an explicit context parameter can be used when + // Isolate::context is not set up, e.g. when calling directly into C++ from + // CSA. + bool IsFastArrayConstructorPrototypeChainIntact(Context* context); bool IsFastArrayConstructorPrototypeChainIntact(); + inline bool IsArraySpeciesLookupChainIntact(); bool IsIsConcatSpreadableLookupChainIntact(); bool IsIsConcatSpreadableLookupChainIntact(JSReceiver* receiver); diff --git a/chromium/v8/src/log.cc b/chromium/v8/src/log.cc index 0eb9b250089..006acf71b95 100644 --- a/chromium/v8/src/log.cc +++ b/chromium/v8/src/log.cc @@ -1547,7 +1547,10 @@ static int EnumerateCompiledFunctions(Heap* heap, !Script::cast(maybe_script)->HasValidSource()) { continue; } - if (function->HasOptimizedCode()) { + // TODO(jarin) This leaves out deoptimized code that might still be on the + // stack. Also note that we will not log optimized code objects that are + // only on a type feedback vector. We should make this mroe precise. + if (function->IsOptimized()) { AddFunctionAndCode(sfi, AbstractCode::cast(function->code()), sfis, code_objects, compiled_funcs_count); ++compiled_funcs_count; diff --git a/chromium/v8/src/lookup.cc b/chromium/v8/src/lookup.cc index 75f008f105d..91d87ebbff9 100644 --- a/chromium/v8/src/lookup.cc +++ b/chromium/v8/src/lookup.cc @@ -94,21 +94,9 @@ LookupIterator LookupIterator::ForTransitionHandler( has_property); if (!transition_map->is_dictionary_map()) { - PropertyConstness new_constness = kConst; - if (FLAG_track_constant_fields) { - if (it.constness() == kConst) { - DCHECK_EQ(kData, it.property_details_.kind()); - // Check that current value matches new value otherwise we should make - // the property mutable. - if (!it.IsConstFieldValueEqualTo(*value)) new_constness = kMutable; - } - } else { - new_constness = kMutable; - } - int descriptor_number = transition_map->LastAdded(); Handle<Map> new_map = Map::PrepareForDataProperty( - transition_map, descriptor_number, new_constness, value); + transition_map, descriptor_number, kConst, value); // Reload information; this is no-op if nothing changed. it.property_details_ = new_map->instance_descriptors()->GetDetails(descriptor_number); diff --git a/chromium/v8/src/map-updater.cc b/chromium/v8/src/map-updater.cc index 05ef5fd3e21..3a9a9caf14c 100644 --- a/chromium/v8/src/map-updater.cc +++ b/chromium/v8/src/map-updater.cc @@ -123,8 +123,9 @@ Handle<Map> MapUpdater::ReconfigureToDataField(int descriptor, new_field_type_ = field_type; } - GeneralizeIfTransitionableFastElementsKind( - &new_constness_, &new_representation_, &new_field_type_); + Map::GeneralizeIfCanHaveTransitionableFastElementsKind( + isolate_, old_map_->instance_type(), &new_constness_, + &new_representation_, &new_field_type_); if (TryRecofigureToDataFieldInplace() == kEnd) return result_map_; if (FindRootMap() == kEnd) return result_map_; @@ -158,28 +159,6 @@ Handle<Map> MapUpdater::Update() { return result_map_; } -void MapUpdater::GeneralizeIfTransitionableFastElementsKind( - PropertyConstness* constness, Representation* representation, - Handle<FieldType>* field_type) { - DCHECK_EQ(is_transitionable_fast_elements_kind_, - IsTransitionableFastElementsKind(new_elements_kind_)); - if (is_transitionable_fast_elements_kind_ && - Map::IsInplaceGeneralizableField(*constness, *representation, - **field_type)) { - // We don't support propagation of field generalization through elements - // kind transitions because they are inserted into the transition tree - // before field transitions. In order to avoid complexity of handling - // such a case we ensure that all maps with transitionable elements kinds - // do not have fields that can be generalized in-place (without creation - // of a new map). - if (FLAG_track_constant_fields && FLAG_modify_map_inplace) { - *constness = kMutable; - } - DCHECK(representation->IsHeapObject()); - *field_type = FieldType::Any(isolate_); - } -} - void MapUpdater::GeneralizeField(Handle<Map> map, int modify_index, PropertyConstness new_constness, Representation new_representation, @@ -437,6 +416,7 @@ MapUpdater::State MapUpdater::FindTargetMap() { } Handle<DescriptorArray> MapUpdater::BuildDescriptorArray() { + InstanceType instance_type = old_map_->instance_type(); int target_nof = target_map_->NumberOfOwnDescriptors(); Handle<DescriptorArray> target_descriptors( target_map_->instance_descriptors(), isolate_); @@ -518,8 +498,9 @@ Handle<DescriptorArray> MapUpdater::BuildDescriptorArray() { old_details.representation(), old_field_type, next_representation, target_field_type, isolate_); - GeneralizeIfTransitionableFastElementsKind( - &next_constness, &next_representation, &next_field_type); + Map::GeneralizeIfCanHaveTransitionableFastElementsKind( + isolate_, instance_type, &next_constness, &next_representation, + &next_field_type); Handle<Object> wrapped_type(Map::WrapFieldType(next_field_type)); Descriptor d; diff --git a/chromium/v8/src/map-updater.h b/chromium/v8/src/map-updater.h index a1d052261c0..7c5e92f2bf2 100644 --- a/chromium/v8/src/map-updater.h +++ b/chromium/v8/src/map-updater.h @@ -148,10 +148,6 @@ class MapUpdater { Handle<DescriptorArray> descriptors, int descriptor, PropertyLocation location, Representation representation); - inline void GeneralizeIfTransitionableFastElementsKind( - PropertyConstness* constness, Representation* representation, - Handle<FieldType>* field_type); - void GeneralizeField(Handle<Map> map, int modify_index, PropertyConstness new_constness, Representation new_representation, diff --git a/chromium/v8/src/messages.cc b/chromium/v8/src/messages.cc index c1f08739dca..ddc5124cfcf 100644 --- a/chromium/v8/src/messages.cc +++ b/chromium/v8/src/messages.cc @@ -113,6 +113,9 @@ void MessageHandler::ReportMessage(Isolate* isolate, const MessageLocation* loc, } if (!maybe_stringified.ToHandle(&stringified)) { + DCHECK(isolate->has_pending_exception()); + isolate->clear_pending_exception(); + isolate->set_external_caught_exception(false); stringified = isolate->factory()->NewStringFromAsciiChecked("exception"); } diff --git a/chromium/v8/src/messages.h b/chromium/v8/src/messages.h index 51a62582157..9237f7a231e 100644 --- a/chromium/v8/src/messages.h +++ b/chromium/v8/src/messages.h @@ -543,7 +543,7 @@ class ErrorUtils : public AllStatic { T(ToPrecisionFormatRange, \ "toPrecision() argument must be between 1 and 100") \ T(ToRadixFormatRange, "toString() radix argument must be between 2 and 36") \ - T(TypedArraySetNegativeOffset, "Start offset is negative") \ + T(TypedArraySetOffsetOutOfBounds, "offset is out of bounds") \ T(TypedArraySetSourceTooLarge, "Source is too large") \ T(UnsupportedTimeZone, "Unsupported time zone specified %") \ T(ValueOutOfRange, "Value % out of range for % options property %") \ diff --git a/chromium/v8/src/objects-debug.cc b/chromium/v8/src/objects-debug.cc index 98ccfc74682..e403fe9b25d 100644 --- a/chromium/v8/src/objects-debug.cc +++ b/chromium/v8/src/objects-debug.cc @@ -424,6 +424,9 @@ void Map::MapVerify() { CHECK_IMPLIES(has_named_interceptor(), may_have_interesting_symbols()); CHECK_IMPLIES(is_dictionary_map(), may_have_interesting_symbols()); CHECK_IMPLIES(is_access_check_needed(), may_have_interesting_symbols()); + CHECK_IMPLIES(IsJSObjectMap() && !CanHaveFastTransitionableElementsKind(), + IsDictionaryElementsKind(elements_kind()) || + IsTerminalElementsKind(elements_kind())); } @@ -456,6 +459,12 @@ void FixedArray::FixedArrayVerify() { } void PropertyArray::PropertyArrayVerify() { + if (length() == 0) { + CHECK_EQ(this, this->GetHeap()->empty_property_array()); + return; + } + // There are no empty PropertyArrays. + CHECK_LT(0, length()); for (int i = 0; i < length(); i++) { Object* e = get(i); VerifyPointer(e); diff --git a/chromium/v8/src/objects-inl.h b/chromium/v8/src/objects-inl.h index 7212a43d27a..f29c4d8c492 100644 --- a/chromium/v8/src/objects-inl.h +++ b/chromium/v8/src/objects-inl.h @@ -2654,33 +2654,31 @@ SYNCHRONIZED_SMI_ACCESSORS(FixedArrayBase, length, kLengthOffset) int PropertyArray::length() const { Object* value_obj = READ_FIELD(this, kLengthAndHashOffset); int value = Smi::ToInt(value_obj); - return value & kLengthMask; + return LengthField::decode(value); } void PropertyArray::initialize_length(int len) { SLOW_DCHECK(len >= 0); - SLOW_DCHECK(len < kMaxLength); + SLOW_DCHECK(len < LengthField::kMax); WRITE_FIELD(this, kLengthAndHashOffset, Smi::FromInt(len)); } int PropertyArray::synchronized_length() const { Object* value_obj = ACQUIRE_READ_FIELD(this, kLengthAndHashOffset); int value = Smi::ToInt(value_obj); - return value & kLengthMask; + return LengthField::decode(value); } int PropertyArray::Hash() const { Object* value_obj = READ_FIELD(this, kLengthAndHashOffset); int value = Smi::ToInt(value_obj); - int hash = value & kHashMask; - return hash; + return HashField::decode(value); } -void PropertyArray::SetHash(int masked_hash) { - DCHECK_EQ(masked_hash & JSReceiver::kHashMask, masked_hash); +void PropertyArray::SetHash(int hash) { Object* value_obj = READ_FIELD(this, kLengthAndHashOffset); int value = Smi::ToInt(value_obj); - value = (value & kLengthMask) | masked_hash; + value = HashField::update(value, hash); WRITE_FIELD(this, kLengthAndHashOffset, Smi::FromInt(value)); } @@ -5039,12 +5037,12 @@ void JSArrayBuffer::set_has_guard_region(bool value) { set_bit_field(HasGuardRegion::update(bit_field(), value)); } -bool JSArrayBuffer::is_wasm_buffer() { - return IsWasmBuffer::decode(bit_field()); +bool JSArrayBuffer::is_growable() { + return IsGrowable::decode(bit_field()); } -void JSArrayBuffer::set_is_wasm_buffer(bool value) { - set_bit_field(IsWasmBuffer::update(bit_field(), value)); +void JSArrayBuffer::set_is_growable(bool value) { + set_bit_field(IsGrowable::update(bit_field(), value)); } Object* JSArrayBufferView::byte_offset() const { diff --git a/chromium/v8/src/objects-printer.cc b/chromium/v8/src/objects-printer.cc index 7dbbdc5a627..33928a5aa71 100644 --- a/chromium/v8/src/objects-printer.cc +++ b/chromium/v8/src/objects-printer.cc @@ -495,7 +495,12 @@ static void JSObjectPrintHeader(std::ostream& os, JSObject* obj, static void JSObjectPrintBody(std::ostream& os, JSObject* obj, // NOLINT bool print_elements = true) { - os << "\n - properties = " << Brief(obj->raw_properties_or_hash()) << " {"; + os << "\n - properties = "; + Object* properties_or_hash = obj->raw_properties_or_hash(); + if (!properties_or_hash->IsSmi()) { + os << Brief(properties_or_hash); + } + os << " {"; if (obj->PrintProperties(os)) os << "\n "; os << "}\n"; if (print_elements && obj->elements()->length() > 0) { @@ -972,7 +977,7 @@ void JSArrayBuffer::JSArrayBufferPrint(std::ostream& os) { // NOLINT if (was_neutered()) os << "\n - neutered"; if (is_shared()) os << "\n - shared"; if (has_guard_region()) os << "\n - has_guard_region"; - if (is_wasm_buffer()) os << "\n - wasm_buffer"; + if (is_growable()) os << "\n - growable"; JSObjectPrintBody(os, this, !was_neutered()); } diff --git a/chromium/v8/src/objects.cc b/chromium/v8/src/objects.cc index 7b7728c3014..b61d1eca324 100644 --- a/chromium/v8/src/objects.cc +++ b/chromium/v8/src/objects.cc @@ -3664,18 +3664,12 @@ MaybeHandle<Map> Map::CopyWithField(Handle<Map> map, Handle<Name> name, int index = map->NextFreePropertyIndex(); if (map->instance_type() == JS_CONTEXT_EXTENSION_OBJECT_TYPE) { + constness = kMutable; representation = Representation::Tagged(); type = FieldType::Any(isolate); - } else if (IsTransitionableFastElementsKind(map->elements_kind()) && - IsInplaceGeneralizableField(constness, representation, *type)) { - // We don't support propagation of field generalization through elements - // kind transitions because they are inserted into the transition tree - // before field transitions. In order to avoid complexity of handling - // such a case we ensure that all maps with transitionable elements kinds - // do not have fields that can be generalized in-place (without creation - // of a new map). - DCHECK(representation.IsHeapObject()); - type = FieldType::Any(isolate); + } else { + Map::GeneralizeIfCanHaveTransitionableFastElementsKind( + isolate, map->instance_type(), &constness, &representation, &type); } Handle<Object> wrapped_type(WrapFieldType(type)); @@ -4051,9 +4045,7 @@ void MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) { } } - if (external > 0) { - object->SetProperties(*array); - } + object->SetProperties(*array); // Create filler object past the new instance size. int new_instance_size = new_map->instance_size(); @@ -4257,6 +4249,10 @@ int Map::NumberOfFields() const { return result; } +bool Map::HasOutOfObjectProperties() const { + return GetInObjectProperties() < NumberOfFields(); +} + void DescriptorArray::GeneralizeAllFields() { int length = number_of_descriptors(); for (int i = 0; i < length; i++) { @@ -5843,10 +5839,8 @@ void JSObject::AllocateStorageForMap(Handle<JSObject> object, Handle<Map> map) { storage = isolate->factory()->NewFixedArray(inobject); } - Handle<PropertyArray> array; - if (external > 0) { - array = isolate->factory()->NewPropertyArray(external); - } + Handle<PropertyArray> array = + isolate->factory()->NewPropertyArray(external); for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) { PropertyDetails details = descriptors->GetDetails(i); @@ -5862,9 +5856,7 @@ void JSObject::AllocateStorageForMap(Handle<JSObject> object, Handle<Map> map) { } } - if (external > 0) { - object->SetProperties(*array); - } + object->SetProperties(*array); if (!FLAG_unbox_double_fields) { for (int i = 0; i < inobject; i++) { @@ -6231,6 +6223,9 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object, Handle<PropertyArray> fields = factory->NewPropertyArray(number_of_allocated_fields); + bool is_transitionable_elements_kind = + IsTransitionableFastElementsKind(old_map->elements_kind()); + // Fill in the instance descriptor and the fields. int current_offset = 0; for (int i = 0; i < instance_descriptor_length; i++) { @@ -6258,8 +6253,14 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object, d = Descriptor::DataConstant(key, handle(value, isolate), details.attributes()); } else { + // Ensure that we make constant field only when elements kind is not + // transitionable. + PropertyConstness constness = + FLAG_track_constant_fields && !is_transitionable_elements_kind + ? kConst + : kMutable; d = Descriptor::DataField( - key, current_offset, details.attributes(), kDefaultFieldConstness, + key, current_offset, details.attributes(), constness, // TODO(verwaest): value->OptimalRepresentation(); Representation::Tagged(), FieldType::Any(isolate)); } @@ -6373,22 +6374,25 @@ Handle<SeededNumberDictionary> JSObject::NormalizeElements( namespace { -Object* SetHashAndUpdateProperties(HeapObject* properties, int masked_hash) { - DCHECK_NE(PropertyArray::kNoHashSentinel, masked_hash); - DCHECK_EQ(masked_hash & JSReceiver::kHashMask, masked_hash); +Object* SetHashAndUpdateProperties(HeapObject* properties, int hash) { + DCHECK_NE(PropertyArray::kNoHashSentinel, hash); + DCHECK(PropertyArray::HashField::is_valid(hash)); - if (properties == properties->GetHeap()->empty_fixed_array() || - properties == properties->GetHeap()->empty_property_dictionary()) { - return Smi::FromInt(masked_hash); + Heap* heap = properties->GetHeap(); + if (properties == heap->empty_fixed_array() || + properties == heap->empty_property_array() || + properties == heap->empty_property_dictionary()) { + return Smi::FromInt(hash); } if (properties->IsPropertyArray()) { - PropertyArray::cast(properties)->SetHash(masked_hash); + PropertyArray::cast(properties)->SetHash(hash); + DCHECK_LT(0, PropertyArray::cast(properties)->length()); return properties; } DCHECK(properties->IsDictionary()); - NameDictionary::cast(properties)->SetHash(masked_hash); + NameDictionary::cast(properties)->SetHash(hash); return properties; } @@ -6419,18 +6423,21 @@ int GetIdentityHashHelper(Isolate* isolate, JSReceiver* object) { } } // namespace -void JSReceiver::SetIdentityHash(int masked_hash) { +void JSReceiver::SetIdentityHash(int hash) { DisallowHeapAllocation no_gc; - DCHECK_NE(PropertyArray::kNoHashSentinel, masked_hash); - DCHECK_EQ(masked_hash & JSReceiver::kHashMask, masked_hash); + DCHECK_NE(PropertyArray::kNoHashSentinel, hash); + DCHECK(PropertyArray::HashField::is_valid(hash)); HeapObject* existing_properties = HeapObject::cast(raw_properties_or_hash()); Object* new_properties = - SetHashAndUpdateProperties(existing_properties, masked_hash); + SetHashAndUpdateProperties(existing_properties, hash); set_raw_properties_or_hash(new_properties); } void JSReceiver::SetProperties(HeapObject* properties) { + DCHECK_IMPLIES(properties->IsPropertyArray() && + PropertyArray::cast(properties)->length() == 0, + properties == properties->GetHeap()->empty_property_array()); DisallowHeapAllocation no_gc; Isolate* isolate = properties->GetIsolate(); int hash = GetIdentityHashHelper(isolate, this); @@ -6481,11 +6488,11 @@ Smi* JSObject::GetOrCreateIdentityHash(Isolate* isolate) { return Smi::cast(hash_obj); } - int masked_hash = isolate->GenerateIdentityHash(JSReceiver::kHashMask); - DCHECK_NE(PropertyArray::kNoHashSentinel, masked_hash); + int hash = isolate->GenerateIdentityHash(PropertyArray::HashField::kMax); + DCHECK_NE(PropertyArray::kNoHashSentinel, hash); - SetIdentityHash(masked_hash); - return Smi::FromInt(masked_hash); + SetIdentityHash(hash); + return Smi::FromInt(hash); } Object* JSProxy::GetIdentityHash() { return hash(); } @@ -9397,6 +9404,13 @@ void Map::InstallDescriptors(Handle<Map> parent, Handle<Map> child, Handle<Map> Map::CopyAsElementsKind(Handle<Map> map, ElementsKind kind, TransitionFlag flag) { + // Only certain objects are allowed to have non-terminal fast transitional + // elements kinds. + DCHECK(map->IsJSObjectMap()); + DCHECK_IMPLIES( + !map->CanHaveFastTransitionableElementsKind(), + IsDictionaryElementsKind(kind) || IsTerminalElementsKind(kind)); + Map* maybe_elements_transition_map = NULL; if (flag == INSERT_TRANSITION) { // Ensure we are requested to add elements kind transition "near the root". @@ -12779,6 +12793,10 @@ bool CanSubclassHaveInobjectProperties(InstanceType instance_type) { case JS_VALUE_TYPE: case JS_WEAK_MAP_TYPE: case JS_WEAK_SET_TYPE: + case WASM_INSTANCE_TYPE: + case WASM_MEMORY_TYPE: + case WASM_MODULE_TYPE: + case WASM_TABLE_TYPE: return true; case BIGINT_TYPE: @@ -15170,6 +15188,9 @@ bool JSObject::WouldConvertToSlowElements(uint32_t index) { static ElementsKind BestFittingFastElementsKind(JSObject* object) { + if (!object->map()->CanHaveFastTransitionableElementsKind()) { + return HOLEY_ELEMENTS; + } if (object->HasSloppyArgumentsElements()) { return FAST_SLOPPY_ARGUMENTS_ELEMENTS; } diff --git a/chromium/v8/src/objects.h b/chromium/v8/src/objects.h index 18911a77fa3..895d92ba319 100644 --- a/chromium/v8/src/objects.h +++ b/chromium/v8/src/objects.h @@ -1950,17 +1950,10 @@ class PropertyArray : public HeapObject { // No weak fields. typedef BodyDescriptor BodyDescriptorWeak; - static const int kLengthMask = 0x3ff; -#if V8_TARGET_ARCH_64_BIT - static const int kHashMask = 0x7ffffc00; - STATIC_ASSERT(kLengthMask + kHashMask == 0x7fffffff); -#else - static const int kHashMask = 0x3ffffc00; - STATIC_ASSERT(kLengthMask + kHashMask == 0x3fffffff); -#endif - - static const int kMaxLength = kLengthMask; - STATIC_ASSERT(kMaxLength > kMaxNumberOfDescriptors); + static const int kLengthFieldSize = 10; + class LengthField : public BitField<int, 0, kLengthFieldSize> {}; + class HashField : public BitField<int, kLengthFieldSize, + kSmiValueSize - kLengthFieldSize - 1> {}; static const int kNoHashSentinel = 0; @@ -1984,6 +1977,9 @@ class JSReceiver: public HeapObject { // Gets slow properties for non-global objects. inline NameDictionary* property_dictionary() const; + // Sets the properties backing store and makes sure any existing hash is moved + // to the new properties store. To clear out the properties store, pass in the + // empty_fixed_array(), the hash will be maintained in this case as well. void SetProperties(HeapObject* properties); // There are five possible values for the properties offset. @@ -2187,7 +2183,7 @@ class JSReceiver: public HeapObject { MUST_USE_RESULT static MaybeHandle<FixedArray> GetOwnEntries( Handle<JSReceiver> object, PropertyFilter filter); - static const int kHashMask = PropertyArray::kHashMask; + static const int kHashMask = PropertyArray::HashField::kMask; // Layout description. static const int kPropertiesOrHashOffset = HeapObject::kHeaderSize; @@ -6249,10 +6245,8 @@ class JSArrayBuffer: public JSObject { inline bool has_guard_region() const; inline void set_has_guard_region(bool value); - // TODO(gdeepti): This flag is introduced to disable asm.js optimizations in - // js-typer-lowering.cc, remove when the asm.js case is fixed. - inline bool is_wasm_buffer(); - inline void set_is_wasm_buffer(bool value); + inline bool is_growable(); + inline void set_is_growable(bool value); DECL_CAST(JSArrayBuffer) @@ -6312,7 +6306,7 @@ class JSArrayBuffer: public JSObject { class WasNeutered : public BitField<bool, 3, 1> {}; class IsShared : public BitField<bool, 4, 1> {}; class HasGuardRegion : public BitField<bool, 5, 1> {}; - class IsWasmBuffer : public BitField<bool, 6, 1> {}; + class IsGrowable : public BitField<bool, 6, 1> {}; private: DISALLOW_IMPLICIT_CONSTRUCTORS(JSArrayBuffer); @@ -6387,7 +6381,7 @@ class JSTypedArray: public JSArrayBufferView { DECL_PRINTER(JSTypedArray) DECL_VERIFIER(JSTypedArray) - static const int kLengthOffset = kViewSize + kPointerSize; + static const int kLengthOffset = kViewSize; static const int kSize = kLengthOffset + kPointerSize; static const int kSizeWithEmbedderFields = diff --git a/chromium/v8/src/objects/dictionary.h b/chromium/v8/src/objects/dictionary.h index 0297a7f28b3..11cf8b11639 100644 --- a/chromium/v8/src/objects/dictionary.h +++ b/chromium/v8/src/objects/dictionary.h @@ -138,14 +138,16 @@ class BaseNameDictionary : public Dictionary<Derived, Shape> { return Smi::ToInt(this->get(kNextEnumerationIndexIndex)); } - void SetHash(int masked_hash) { - DCHECK_EQ(masked_hash & JSReceiver::kHashMask, masked_hash); - this->set(kObjectHashIndex, Smi::FromInt(masked_hash)); + void SetHash(int hash) { + DCHECK(PropertyArray::HashField::is_valid(hash)); + this->set(kObjectHashIndex, Smi::FromInt(hash)); } int Hash() const { Object* hash_obj = this->get(kObjectHashIndex); - return Smi::ToInt(hash_obj); + int hash = Smi::ToInt(hash_obj); + DCHECK(PropertyArray::HashField::is_valid(hash)); + return hash; } // Creates a new dictionary. diff --git a/chromium/v8/src/objects/map-inl.h b/chromium/v8/src/objects/map-inl.h index aab6e786689..a5421a32cad 100644 --- a/chromium/v8/src/objects/map-inl.h +++ b/chromium/v8/src/objects/map-inl.h @@ -42,6 +42,39 @@ bool Map::IsInplaceGeneralizableField(PropertyConstness constness, return false; } +bool Map::CanHaveFastTransitionableElementsKind(InstanceType instance_type) { + return instance_type == JS_ARRAY_TYPE || instance_type == JS_VALUE_TYPE || + instance_type == JS_ARGUMENTS_TYPE; +} + +bool Map::CanHaveFastTransitionableElementsKind() const { + return CanHaveFastTransitionableElementsKind(instance_type()); +} + +// static +void Map::GeneralizeIfCanHaveTransitionableFastElementsKind( + Isolate* isolate, InstanceType instance_type, PropertyConstness* constness, + Representation* representation, Handle<FieldType>* field_type) { + if (CanHaveFastTransitionableElementsKind(instance_type)) { + // We don't support propagation of field generalization through elements + // kind transitions because they are inserted into the transition tree + // before field transitions. In order to avoid complexity of handling + // such a case we ensure that all maps with transitionable elements kinds + // do not have fields that can be generalized in-place (without creation + // of a new map). + if (FLAG_track_constant_fields && FLAG_modify_map_inplace) { + // The constness is either already kMutable or should become kMutable if + // it was kConst. + *constness = kMutable; + } + if (representation->IsHeapObject()) { + // The field type is either already Any or should become Any if it was + // something else. + *field_type = FieldType::Any(isolate); + } + } +} + int NormalizedMapCache::GetIndex(Handle<Map> map) { return map->Hash() % NormalizedMapCache::kEntries; } diff --git a/chromium/v8/src/objects/map.h b/chromium/v8/src/objects/map.h index 2bbf7ac1ce9..5806a24ae0f 100644 --- a/chromium/v8/src/objects/map.h +++ b/chromium/v8/src/objects/map.h @@ -406,6 +406,8 @@ class Map : public HeapObject { int NumberOfFields() const; + bool HasOutOfObjectProperties() const; + // Returns true if transition to the given map requires special // synchronization with the concurrent marker. bool TransitionRequiresSynchronizationWithGC(Map* target) const; @@ -436,6 +438,18 @@ class Map : public HeapObject { Representation representation, FieldType* field_type); + // Generalizes constness, representation and field_type if objects with given + // instance type can have fast elements that can be transitioned by stubs or + // optimized code to more general elements kind. + // This generalization is necessary in order to ensure that elements kind + // transitions performed by stubs / optimized code don't silently transition + // kMutable fields back to kConst state or fields with HeapObject + // representation and "Any" type back to "Class" type. + static inline void GeneralizeIfCanHaveTransitionableFastElementsKind( + Isolate* isolate, InstanceType instance_type, + PropertyConstness* constness, Representation* representation, + Handle<FieldType>* field_type); + static Handle<Map> ReconfigureProperty(Handle<Map> map, int modify_index, PropertyKind new_kind, PropertyAttributes new_attributes, @@ -823,6 +837,16 @@ class Map : public HeapObject { static VisitorId GetVisitorId(Map* map); + // Returns true if objects with given instance type are allowed to have + // fast transitionable elements kinds. This predicate is used to ensure + // that objects that can have transitionable fast elements kind will not + // get in-place generalizable fields because the elements kind transition + // performed by stubs or optimized code can't properly generalize such + // fields. + static inline bool CanHaveFastTransitionableElementsKind( + InstanceType instance_type); + inline bool CanHaveFastTransitionableElementsKind() const; + private: // Returns the map that this (root) map transitions to if its elements_kind // is changed to |elements_kind|, or |nullptr| if no such map is cached yet. diff --git a/chromium/v8/src/parsing/parser.cc b/chromium/v8/src/parsing/parser.cc index 7eebfbdafce..a554d7d2423 100644 --- a/chromium/v8/src/parsing/parser.cc +++ b/chromium/v8/src/parsing/parser.cc @@ -2790,7 +2790,7 @@ Parser::LazyParsingResult Parser::SkipFunction( DCHECK(log_); log_->LogFunction(function_scope->start_position(), function_scope->end_position(), *num_parameters, - language_mode(), function_scope->uses_super_property(), + language_mode(), function_scope->NeedsHomeObject(), logger->num_inner_functions()); } return kLazyParsingComplete; @@ -3383,8 +3383,9 @@ void Parser::ParseOnBackground(ParseInfo* info) { if (result != NULL) *info->cached_data() = logger.GetScriptData(); log_ = NULL; } - if (FLAG_runtime_stats & - v8::tracing::TracingCategoryObserver::ENABLED_BY_TRACING) { + if (runtime_call_stats_ && + (FLAG_runtime_stats & + v8::tracing::TracingCategoryObserver::ENABLED_BY_TRACING)) { auto value = v8::tracing::TracedValue::Create(); runtime_call_stats_->Dump(value.get()); TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.runtime_stats"), diff --git a/chromium/v8/src/parsing/preparsed-scope-data.cc b/chromium/v8/src/parsing/preparsed-scope-data.cc index f73a61bfb6b..8d2ce2d1a55 100644 --- a/chromium/v8/src/parsing/preparsed-scope-data.cc +++ b/chromium/v8/src/parsing/preparsed-scope-data.cc @@ -101,16 +101,16 @@ void ProducedPreParsedScopeData::ByteData::WriteUint32(uint32_t data) { } void ProducedPreParsedScopeData::ByteData::OverwriteFirstUint32(uint32_t data) { - size_t position = 0; + auto it = backing_store_.begin(); #ifdef DEBUG // Check that that position already holds an item of the expected size. DCHECK_GE(backing_store_.size(), kUint32Size); - DCHECK_EQ(backing_store_[0], kUint32Size); - ++position; + DCHECK_EQ(*it, kUint32Size); + ++it; #endif const uint8_t* d = reinterpret_cast<uint8_t*>(&data); for (size_t i = 0; i < 4; ++i) { - backing_store_[position + i] = *d++; + *it++ = *d++; } } @@ -123,7 +123,7 @@ void ProducedPreParsedScopeData::ByteData::WriteUint8(uint8_t data) { } Handle<PodArray<uint8_t>> ProducedPreParsedScopeData::ByteData::Serialize( - Isolate* isolate) const { + Isolate* isolate) { Handle<PodArray<uint8_t>> array = PodArray<uint8_t>::New( isolate, static_cast<int>(backing_store_.size()), TENURED); @@ -193,7 +193,7 @@ void ProducedPreParsedScopeData::DataGatheringScope::MarkFunctionAsSkippable( produced_preparsed_scope_data_->parent_->AddSkippableFunction( function_scope_->start_position(), end_position, function_scope_->num_parameters(), num_inner_functions, - function_scope_->language_mode(), function_scope_->uses_super_property()); + function_scope_->language_mode(), function_scope_->NeedsHomeObject()); } void ProducedPreParsedScopeData::AddSkippableFunction( @@ -251,7 +251,7 @@ void ProducedPreParsedScopeData::SaveScopeAllocationData( } MaybeHandle<PreParsedScopeData> ProducedPreParsedScopeData::Serialize( - Isolate* isolate) const { + Isolate* isolate) { if (!previously_produced_preparsed_scope_data_.is_null()) { DCHECK(!bailed_out_); DCHECK_EQ(data_for_inner_functions_.size(), 0); @@ -336,12 +336,6 @@ bool ProducedPreParsedScopeData::ScopeIsSkippableFunctionScope(Scope* scope) { void ProducedPreParsedScopeData::SaveDataForScope(Scope* scope) { DCHECK_NE(scope->end_position(), kNoSourcePosition); - // We're not trying to save data for default constructors because the - // PreParser doesn't construct them. - DCHECK_IMPLIES(scope->scope_type() == ScopeType::FUNCTION_SCOPE, - (scope->AsDeclarationScope()->function_kind() & - kDefaultConstructor) == 0); - if (!ScopeNeedsData(scope)) { return; } diff --git a/chromium/v8/src/parsing/preparsed-scope-data.h b/chromium/v8/src/parsing/preparsed-scope-data.h index 205de42207a..290bfba2fd4 100644 --- a/chromium/v8/src/parsing/preparsed-scope-data.h +++ b/chromium/v8/src/parsing/preparsed-scope-data.h @@ -13,7 +13,7 @@ #include "src/handles.h" #include "src/objects/shared-function-info.h" #include "src/parsing/preparse-data.h" -#include "src/zone/zone-containers.h" +#include "src/zone/zone-chunk-list.h" namespace v8 { namespace internal { @@ -77,12 +77,12 @@ class ProducedPreParsedScopeData : public ZoneObject { // For overwriting previously written data at position 0. void OverwriteFirstUint32(uint32_t data); - Handle<PodArray<uint8_t>> Serialize(Isolate* isolate) const; + Handle<PodArray<uint8_t>> Serialize(Isolate* isolate); size_t size() const { return backing_store_.size(); } private: - ZoneDeque<uint8_t> backing_store_; + ZoneChunkList<uint8_t> backing_store_; }; // Create a ProducedPreParsedScopeData object which will collect data as we @@ -145,7 +145,7 @@ class ProducedPreParsedScopeData : public ZoneObject { // If there is data (if the Scope contains skippable inner functions), move // the data into the heap and return a Handle to it; otherwise return a null // MaybeHandle. - MaybeHandle<PreParsedScopeData> Serialize(Isolate* isolate) const; + MaybeHandle<PreParsedScopeData> Serialize(Isolate* isolate); static bool ScopeNeedsData(Scope* scope); static bool ScopeIsSkippableFunctionScope(Scope* scope); @@ -163,7 +163,7 @@ class ProducedPreParsedScopeData : public ZoneObject { ProducedPreParsedScopeData* parent_; ByteData* byte_data_; - ZoneDeque<ProducedPreParsedScopeData*> data_for_inner_functions_; + ZoneChunkList<ProducedPreParsedScopeData*> data_for_inner_functions_; // Whether we've given up producing the data for this function. bool bailed_out_; diff --git a/chromium/v8/src/parsing/preparser.cc b/chromium/v8/src/parsing/preparser.cc index 289059bc7f3..c31fd4af8e9 100644 --- a/chromium/v8/src/parsing/preparser.cc +++ b/chromium/v8/src/parsing/preparser.cc @@ -206,7 +206,8 @@ PreParser::PreParseResult PreParser::PreParseFunction( } } - if (!IsArrowFunction(kind) && track_unresolved_variables_) { + if (!IsArrowFunction(kind) && track_unresolved_variables_ && + result == kLazyParsingComplete) { CreateFunctionNameAssignment(function_name, function_type, function_scope); // Declare arguments after parsing the function since lexical 'arguments' diff --git a/chromium/v8/src/parsing/preparser.h b/chromium/v8/src/parsing/preparser.h index 0403bcdec61..275c5e9e0bd 100644 --- a/chromium/v8/src/parsing/preparser.h +++ b/chromium/v8/src/parsing/preparser.h @@ -1124,7 +1124,22 @@ class PreParser : public ParserBase<PreParser> { ClassInfo* class_info, int pos, int end_pos, bool* ok) { bool has_default_constructor = !class_info->has_seen_constructor; // Account for the default constructor. - if (has_default_constructor) GetNextFunctionLiteralId(); + if (has_default_constructor) { + // Creating and disposing of a FunctionState makes tracking of + // next_function_is_likely_called match what Parser does. TODO(marja): + // Make the lazy function + next_function_is_likely_called + default ctor + // logic less surprising. Default ctors shouldn't affect the laziness of + // functions. + bool has_extends = class_info->extends.IsNull(); + FunctionKind kind = has_extends ? FunctionKind::kDefaultDerivedConstructor + : FunctionKind::kDefaultBaseConstructor; + DeclarationScope* function_scope = NewFunctionScope(kind); + SetLanguageMode(function_scope, STRICT); + function_scope->set_start_position(pos); + function_scope->set_end_position(pos); + FunctionState function_state(&function_state_, &scope_, function_scope); + GetNextFunctionLiteralId(); + } return PreParserExpression::Default(); } diff --git a/chromium/v8/src/ppc/simulator-ppc.cc b/chromium/v8/src/ppc/simulator-ppc.cc index ef6e64a9f36..0f90700c81c 100644 --- a/chromium/v8/src/ppc/simulator-ppc.cc +++ b/chromium/v8/src/ppc/simulator-ppc.cc @@ -1334,7 +1334,6 @@ void Simulator::SoftwareInterrupt(Instruction* instr) { int arg0_regnum = 3; intptr_t result_buffer = 0; bool uses_result_buffer = - redirection->type() == ExternalReference::BUILTIN_CALL_TRIPLE || (redirection->type() == ExternalReference::BUILTIN_CALL_PAIR && !ABI_RETURNS_OBJECT_PAIRS_IN_REGS); if (uses_result_buffer) { diff --git a/chromium/v8/src/regexp/jsregexp.cc b/chromium/v8/src/regexp/jsregexp.cc index 399c6153c5a..52ed47cf533 100644 --- a/chromium/v8/src/regexp/jsregexp.cc +++ b/chromium/v8/src/regexp/jsregexp.cc @@ -5905,7 +5905,7 @@ Vector<const int> CharacterRange::GetWordBounds() { return Vector<const int>(kWordRanges, kWordRangeCount - 1); } - +// static void CharacterRange::AddCaseEquivalents(Isolate* isolate, Zone* zone, ZoneList<CharacterRange>* ranges, bool is_one_byte) { @@ -5914,12 +5914,12 @@ void CharacterRange::AddCaseEquivalents(Isolate* isolate, Zone* zone, for (int i = 0; i < range_count; i++) { CharacterRange range = ranges->at(i); uc32 bottom = range.from(); - if (bottom > String::kMaxUtf16CodeUnit) return; + if (bottom > String::kMaxUtf16CodeUnit) continue; uc32 top = Min(range.to(), String::kMaxUtf16CodeUnit); // Nothing to be done for surrogates. - if (bottom >= kLeadSurrogateStart && top <= kTrailSurrogateEnd) return; + if (bottom >= kLeadSurrogateStart && top <= kTrailSurrogateEnd) continue; if (is_one_byte && !RangeContainsLatin1Equivalents(range)) { - if (bottom > String::kMaxOneByteCharCode) return; + if (bottom > String::kMaxOneByteCharCode) continue; if (top > String::kMaxOneByteCharCode) top = String::kMaxOneByteCharCode; } unibrow::uchar chars[unibrow::Ecma262UnCanonicalize::kMaxWidth]; diff --git a/chromium/v8/src/runtime/runtime-intl.cc b/chromium/v8/src/runtime/runtime-intl.cc index 22acba28cd1..783450c8ef1 100644 --- a/chromium/v8/src/runtime/runtime-intl.cc +++ b/chromium/v8/src/runtime/runtime-intl.cc @@ -68,18 +68,21 @@ RUNTIME_FUNCTION(Runtime_CanonicalizeLanguageTag) { v8::String::Utf8Value locale_id(v8_isolate, v8::Utils::ToLocal(locale_id_str)); + // TODO(jshin): uloc_{for,to}TanguageTag can fail even for a structually valid + // language tag if it's too long (much longer than 100 chars). Even if we + // allocate a longer buffer, ICU will still fail if it's too long. Either + // propose to Ecma 402 to put a limit on the locale length or change ICU to + // handle long locale names better. See + // https://ssl.icu-project.org/trac/ticket/13417 . + // Return value which denotes invalid language tag. - // TODO(jshin): Can uloc_{for,to}TanguageTag fail even for structually valid - // language tags? If not, just add CHECK instead of returning 'invalid-tag'. const char* const kInvalidTag = "invalid-tag"; UErrorCode error = U_ZERO_ERROR; char icu_result[ULOC_FULLNAME_CAPACITY]; - int icu_length = 0; - - uloc_forLanguageTag(*locale_id, icu_result, ULOC_FULLNAME_CAPACITY, - &icu_length, &error); - if (U_FAILURE(error) || icu_length == 0) { + uloc_forLanguageTag(*locale_id, icu_result, ULOC_FULLNAME_CAPACITY, nullptr, + &error); + if (U_FAILURE(error) || error == U_STRING_NOT_TERMINATED_WARNING) { return *factory->NewStringFromAsciiChecked(kInvalidTag); } @@ -88,7 +91,7 @@ RUNTIME_FUNCTION(Runtime_CanonicalizeLanguageTag) { // Force strict BCP47 rules. uloc_toLanguageTag(icu_result, result, ULOC_FULLNAME_CAPACITY, TRUE, &error); - if (U_FAILURE(error)) { + if (U_FAILURE(error) || error == U_STRING_NOT_TERMINATED_WARNING) { return *factory->NewStringFromAsciiChecked(kInvalidTag); } @@ -134,7 +137,7 @@ RUNTIME_FUNCTION(Runtime_AvailableLocalesOf) { error = U_ZERO_ERROR; // No need to force strict BCP47 rules. uloc_toLanguageTag(icu_name, result, ULOC_FULLNAME_CAPACITY, FALSE, &error); - if (U_FAILURE(error)) { + if (U_FAILURE(error) || error == U_STRING_NOT_TERMINATED_WARNING) { // This shouldn't happen, but lets not break the user. continue; } @@ -173,91 +176,6 @@ RUNTIME_FUNCTION(Runtime_GetDefaultICULocale) { return *factory->NewStringFromStaticChars("und"); } -RUNTIME_FUNCTION(Runtime_GetLanguageTagVariants) { - HandleScope scope(isolate); - v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate); - Factory* factory = isolate->factory(); - - DCHECK_EQ(1, args.length()); - - CONVERT_ARG_HANDLE_CHECKED(JSArray, input, 0); - - uint32_t length = static_cast<uint32_t>(input->length()->Number()); - // Set some limit to prevent fuzz tests from going OOM. - // Can be bumped when callers' requirements change. - if (length >= 100) return isolate->ThrowIllegalOperation(); - Handle<FixedArray> output = factory->NewFixedArray(length); - Handle<Name> maximized = factory->NewStringFromStaticChars("maximized"); - Handle<Name> base = factory->NewStringFromStaticChars("base"); - for (unsigned int i = 0; i < length; ++i) { - Handle<Object> locale_id; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, locale_id, JSReceiver::GetElement(isolate, input, i)); - if (!locale_id->IsString()) { - return isolate->Throw(*factory->illegal_argument_string()); - } - - v8::String::Utf8Value utf8_locale_id( - v8_isolate, v8::Utils::ToLocal(Handle<String>::cast(locale_id))); - - UErrorCode error = U_ZERO_ERROR; - - // Convert from BCP47 to ICU format. - // de-DE-u-co-phonebk -> de_DE@collation=phonebook - char icu_locale[ULOC_FULLNAME_CAPACITY]; - int icu_locale_length = 0; - uloc_forLanguageTag(*utf8_locale_id, icu_locale, ULOC_FULLNAME_CAPACITY, - &icu_locale_length, &error); - if (U_FAILURE(error) || icu_locale_length == 0) { - return isolate->Throw(*factory->illegal_argument_string()); - } - - // Maximize the locale. - // de_DE@collation=phonebook -> de_Latn_DE@collation=phonebook - char icu_max_locale[ULOC_FULLNAME_CAPACITY]; - uloc_addLikelySubtags(icu_locale, icu_max_locale, ULOC_FULLNAME_CAPACITY, - &error); - - // Remove extensions from maximized locale. - // de_Latn_DE@collation=phonebook -> de_Latn_DE - char icu_base_max_locale[ULOC_FULLNAME_CAPACITY]; - uloc_getBaseName(icu_max_locale, icu_base_max_locale, - ULOC_FULLNAME_CAPACITY, &error); - - // Get original name without extensions. - // de_DE@collation=phonebook -> de_DE - char icu_base_locale[ULOC_FULLNAME_CAPACITY]; - uloc_getBaseName(icu_locale, icu_base_locale, ULOC_FULLNAME_CAPACITY, - &error); - - // Convert from ICU locale format to BCP47 format. - // de_Latn_DE -> de-Latn-DE - char base_max_locale[ULOC_FULLNAME_CAPACITY]; - uloc_toLanguageTag(icu_base_max_locale, base_max_locale, - ULOC_FULLNAME_CAPACITY, FALSE, &error); - - // de_DE -> de-DE - char base_locale[ULOC_FULLNAME_CAPACITY]; - uloc_toLanguageTag(icu_base_locale, base_locale, ULOC_FULLNAME_CAPACITY, - FALSE, &error); - - if (U_FAILURE(error)) { - return isolate->Throw(*factory->illegal_argument_string()); - } - - Handle<JSObject> result = factory->NewJSObject(isolate->object_function()); - Handle<String> value = factory->NewStringFromAsciiChecked(base_max_locale); - JSObject::AddProperty(result, maximized, value, NONE); - value = factory->NewStringFromAsciiChecked(base_locale); - JSObject::AddProperty(result, base, value, NONE); - output->set(i, *result); - } - - Handle<JSArray> result = factory->NewJSArrayWithElements(output); - result->set_length(Smi::FromInt(length)); - return *result; -} - RUNTIME_FUNCTION(Runtime_IsInitializedIntlObject) { HandleScope scope(isolate); diff --git a/chromium/v8/src/runtime/runtime-object.cc b/chromium/v8/src/runtime/runtime-object.cc index 711a55e2ff7..4c8805eb254 100644 --- a/chromium/v8/src/runtime/runtime-object.cc +++ b/chromium/v8/src/runtime/runtime-object.cc @@ -163,16 +163,23 @@ bool DeleteObjectPropertyFast(Isolate* isolate, Handle<JSReceiver> receiver, if (details.location() == kField) { isolate->heap()->NotifyObjectLayoutChange(*receiver, map->instance_size(), no_allocation); - Object* filler = isolate->heap()->one_pointer_filler_map(); FieldIndex index = FieldIndex::ForPropertyIndex(map, details.field_index()); - JSObject::cast(*receiver)->RawFastPropertyAtPut(index, filler); - // We must clear any recorded slot for the deleted property, because - // subsequent object modifications might put a raw double there. - // Slot clearing is the reason why this entire function cannot currently - // be implemented in the DeleteProperty stub. - if (index.is_inobject() && !map->IsUnboxedDoubleField(index)) { - isolate->heap()->ClearRecordedSlot( - *receiver, HeapObject::RawField(*receiver, index.offset())); + // Special case deleting the last out-of object property. + if (!index.is_inobject() && index.outobject_array_index() == 0) { + DCHECK(!Map::cast(backpointer)->HasOutOfObjectProperties()); + // Clear out the properties backing store. + receiver->SetProperties(isolate->heap()->empty_fixed_array()); + } else { + Object* filler = isolate->heap()->one_pointer_filler_map(); + JSObject::cast(*receiver)->RawFastPropertyAtPut(index, filler); + // We must clear any recorded slot for the deleted property, because + // subsequent object modifications might put a raw double there. + // Slot clearing is the reason why this entire function cannot currently + // be implemented in the DeleteProperty stub. + if (index.is_inobject() && !map->IsUnboxedDoubleField(index)) { + isolate->heap()->ClearRecordedSlot( + *receiver, HeapObject::RawField(*receiver, index.offset())); + } } } // If the map was marked stable before, then there could be optimized code @@ -182,6 +189,10 @@ bool DeleteObjectPropertyFast(Isolate* isolate, Handle<JSReceiver> receiver, map->NotifyLeafMapLayoutChange(); // Finally, perform the map rollback. receiver->synchronized_set_map(Map::cast(backpointer)); +#if VERIFY_HEAP + receiver->HeapObjectVerify(); + receiver->property_array()->PropertyArrayVerify(); +#endif return true; } diff --git a/chromium/v8/src/runtime/runtime-proxy.cc b/chromium/v8/src/runtime/runtime-proxy.cc index 028f3e2046c..472cbdf79d6 100644 --- a/chromium/v8/src/runtime/runtime-proxy.cc +++ b/chromium/v8/src/runtime/runtime-proxy.cc @@ -51,11 +51,11 @@ RUNTIME_FUNCTION(Runtime_GetPropertyWithReceiver) { DCHECK_EQ(3, args.length()); CONVERT_ARG_HANDLE_CHECKED(JSReceiver, holder, 0); - CONVERT_ARG_HANDLE_CHECKED(Object, name, 1); + CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 2); - bool success; - LookupIterator it = LookupIterator::PropertyOrElement(isolate, receiver, name, + bool success = false; + LookupIterator it = LookupIterator::PropertyOrElement(isolate, receiver, key, &success, holder); if (!success) { DCHECK(isolate->has_pending_exception()); @@ -69,15 +69,18 @@ RUNTIME_FUNCTION(Runtime_SetPropertyWithReceiver) { DCHECK_EQ(5, args.length()); CONVERT_ARG_HANDLE_CHECKED(JSReceiver, holder, 0); - CONVERT_ARG_HANDLE_CHECKED(Object, name, 1); + CONVERT_ARG_HANDLE_CHECKED(Object, key, 1); CONVERT_ARG_HANDLE_CHECKED(Object, value, 2); CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 3); CONVERT_LANGUAGE_MODE_ARG_CHECKED(language_mode, 4); - bool success; - LookupIterator it = LookupIterator::PropertyOrElement(isolate, receiver, name, + bool success = false; + LookupIterator it = LookupIterator::PropertyOrElement(isolate, receiver, key, &success, holder); - + if (!success) { + DCHECK(isolate->has_pending_exception()); + return isolate->heap()->exception(); + } Maybe<bool> result = Object::SetSuperProperty( &it, value, language_mode, Object::MAY_BE_STORE_FROM_KEYED); MAYBE_RETURN(result, isolate->heap()->exception()); diff --git a/chromium/v8/src/runtime/runtime-test.cc b/chromium/v8/src/runtime/runtime-test.cc index 587c1dc251e..19a4af50d1c 100644 --- a/chromium/v8/src/runtime/runtime-test.cc +++ b/chromium/v8/src/runtime/runtime-test.cc @@ -892,6 +892,7 @@ RUNTIME_FUNCTION(Runtime_GetWasmRecoveredTrapCount) { return isolate->heap()->ToBoolean(obj->Has##Name()); \ } +ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(SmiElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ObjectElements) ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(SmiOrObjectElements) @@ -1070,5 +1071,16 @@ RUNTIME_FUNCTION(Runtime_WasmTraceMemory) { return isolate->heap()->undefined_value(); } +RUNTIME_FUNCTION(Runtime_CompleteInobjectSlackTracking) { + HandleScope scope(isolate); + DCHECK_EQ(1, args.length()); + + CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0); + object->map()->CompleteInobjectSlackTracking(); + + return isolate->heap()->undefined_value(); +} + + } // namespace internal } // namespace v8 diff --git a/chromium/v8/src/runtime/runtime-typedarray.cc b/chromium/v8/src/runtime/runtime-typedarray.cc index 58a2bbdcfc8..8dfa8f166c2 100644 --- a/chromium/v8/src/runtime/runtime-typedarray.cc +++ b/chromium/v8/src/runtime/runtime-typedarray.cc @@ -214,5 +214,148 @@ RUNTIME_FUNCTION(Runtime_TypedArraySpeciesCreateByLength) { return *result_array; } +namespace { + +Object* TypedArraySetFromOverlapping(Isolate* isolate, + Handle<JSTypedArray> target, + Handle<JSTypedArray> source, + uint32_t offset) { +#ifdef DEBUG + Handle<FixedTypedArrayBase> source_elements( + FixedTypedArrayBase::cast(source->elements())); + Handle<FixedTypedArrayBase> target_elements( + FixedTypedArrayBase::cast(target->elements())); + uint8_t* source_data = static_cast<uint8_t*>(source_elements->DataPtr()); + uint8_t* target_data = static_cast<uint8_t*>(target_elements->DataPtr()); + size_t source_byte_length = NumberToSize(source->byte_length()); + size_t target_byte_length = NumberToSize(target->byte_length()); + + CHECK_LE(offset + source->length(), target->length()); + CHECK_GE(target->length(), source->length()); + CHECK(source->length()->IsSmi()); + + CHECK(!target->WasNeutered()); + CHECK(!source->WasNeutered()); + + // Assert that target and source in fact overlapping. + CHECK(target_data + target_byte_length > source_data && + source_data + source_byte_length > target_data); +#endif + + size_t sourceElementSize = source->element_size(); + size_t targetElementSize = target->element_size(); + + uint32_t source_length = source->length_value(); + if (source_length == 0) return isolate->heap()->undefined_value(); + + // Copy left part. + + // First un-mutated byte after the next write + uint32_t target_ptr = 0; + CHECK(target->byte_offset()->ToUint32(&target_ptr)); + target_ptr += (offset + 1) * targetElementSize; + + // Next read at sourcePtr. We do not care for memory changing before + // sourcePtr - we have already copied it. + uint32_t source_ptr = 0; + CHECK(source->byte_offset()->ToUint32(&source_ptr)); + + ElementsAccessor* source_accessor = source->GetElementsAccessor(); + ElementsAccessor* target_accessor = target->GetElementsAccessor(); + + uint32_t left_index; + for (left_index = 0; left_index < source_length && target_ptr <= source_ptr; + left_index++) { + Handle<Object> value = source_accessor->Get(source, left_index); + target_accessor->Set(target, offset + left_index, *value); + + target_ptr += targetElementSize; + source_ptr += sourceElementSize; + } + + // Copy right part; + // First unmutated byte before the next write + CHECK(target->byte_offset()->ToUint32(&target_ptr)); + target_ptr += (offset + source_length - 1) * targetElementSize; + + // Next read before sourcePtr. We do not care for memory changing after + // sourcePtr - we have already copied it. + CHECK(target->byte_offset()->ToUint32(&source_ptr)); + source_ptr += source_length * sourceElementSize; + + uint32_t right_index; + DCHECK_GE(source_length, 1); + for (right_index = source_length - 1; + right_index > left_index && target_ptr >= source_ptr; right_index--) { + Handle<Object> value = source_accessor->Get(source, right_index); + target_accessor->Set(target, offset + right_index, *value); + + target_ptr -= targetElementSize; + source_ptr -= sourceElementSize; + } + + std::vector<Handle<Object>> temp(right_index + 1 - left_index); + + for (uint32_t i = left_index; i <= right_index; i++) { + temp[i - left_index] = source_accessor->Get(source, i); + } + + for (uint32_t i = left_index; i <= right_index; i++) { + target_accessor->Set(target, offset + i, *temp[i - left_index]); + } + + return isolate->heap()->undefined_value(); +} + +} // namespace + +// 22.2.3.23 %TypedArray%.prototype.set ( overloaded [ , offset ] ) +RUNTIME_FUNCTION(Runtime_TypedArraySet) { + HandleScope scope(isolate); + Handle<JSTypedArray> target = args.at<JSTypedArray>(0); + Handle<Object> obj = args.at(1); + Handle<Smi> offset = args.at<Smi>(2); + + DCHECK(!target->WasNeutered()); // Checked in TypedArrayPrototypeSet. + DCHECK_LE(0, offset->value()); + + const uint32_t uint_offset = static_cast<uint32_t>(offset->value()); + + if (obj->IsNumber()) { + // 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) + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError(MessageTemplate::kInvalidArgument)); + } else if (obj->IsJSTypedArray()) { + // The non-overlapping case is handled in CSA. + Handle<JSTypedArray> source = Handle<JSTypedArray>::cast(obj); + return TypedArraySetFromOverlapping(isolate, target, source, uint_offset); + } + + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, obj, + Object::ToObject(isolate, obj)); + + Handle<Object> len; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, len, + Object::GetProperty(obj, isolate->factory()->length_string())); + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len, + Object::ToLength(isolate, len)); + + if (uint_offset + len->Number() > target->length_value()) { + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge)); + } + + uint32_t int_l; + CHECK(DoubleToUint32IfEqualToSelf(len->Number(), &int_l)); + + Handle<JSReceiver> source = Handle<JSReceiver>::cast(obj); + ElementsAccessor* accessor = target->GetElementsAccessor(); + return accessor->CopyElements(source, target, int_l, uint_offset); +} + } // namespace internal } // namespace v8 diff --git a/chromium/v8/src/runtime/runtime.h b/chromium/v8/src/runtime/runtime.h index a84efc10328..e7084a8ccaa 100644 --- a/chromium/v8/src/runtime/runtime.h +++ b/chromium/v8/src/runtime/runtime.h @@ -252,7 +252,6 @@ namespace internal { F(CanonicalizeLanguageTag, 1, 1) \ F(AvailableLocalesOf, 1, 1) \ F(GetDefaultICULocale, 0, 1) \ - F(GetLanguageTagVariants, 1, 1) \ F(IsInitializedIntlObject, 1, 1) \ F(IsInitializedIntlObjectOfType, 2, 1) \ F(MarkAsInitializedIntlObjectOfType, 2, 1) \ @@ -588,6 +587,7 @@ namespace internal { F(TraceExit, 1, 1) \ F(HaveSameMap, 2, 1) \ F(InNewSpace, 1, 1) \ + F(HasFastElements, 1, 1) \ F(HasSmiElements, 1, 1) \ F(HasObjectElements, 1, 1) \ F(HasSmiOrObjectElements, 1, 1) \ @@ -622,7 +622,8 @@ namespace internal { F(HeapObjectVerify, 1, 1) \ F(WasmNumInterpretedCalls, 1, 1) \ F(RedirectToWasmInterpreter, 2, 1) \ - F(WasmTraceMemory, 4, 1) + F(WasmTraceMemory, 4, 1) \ + F(CompleteInobjectSlackTracking, 1, 1) #define FOR_EACH_INTRINSIC_TYPEDARRAY(F) \ F(ArrayBufferGetByteLength, 1, 1) \ @@ -634,6 +635,7 @@ namespace internal { F(TypedArrayGetLength, 1, 1) \ F(TypedArrayGetBuffer, 1, 1) \ F(TypedArraySortFast, 1, 1) \ + F(TypedArraySet, 2, 1) \ F(IsTypedArray, 1, 1) \ F(IsSharedTypedArray, 1, 1) \ F(IsSharedIntegerTypedArray, 1, 1) \ diff --git a/chromium/v8/src/v8.gyp b/chromium/v8/src/v8.gyp index b4f1dfefd3a..89eb271f618 100644 --- a/chromium/v8/src/v8.gyp +++ b/chromium/v8/src/v8.gyp @@ -42,7 +42,7 @@ { 'target_name': 'v8', 'dependencies_traverse': 1, - 'dependencies': ['v8_maybe_snapshot'], + 'dependencies': ['v8_maybe_snapshot', 'v8_dump_build_config#target'], 'conditions': [ ['want_separate_host_toolset==1', { 'toolsets': ['host', 'target'], @@ -2510,5 +2510,42 @@ }], ], }, + { + 'target_name': 'v8_dump_build_config', + 'type': 'none', + 'variables': { + }, + 'actions': [ + { + 'action_name': 'v8_dump_build_config', + 'inputs': [ + '../tools/testrunner/utils/dump_build_config_gyp.py', + ], + 'outputs': [ + '<(PRODUCT_DIR)/v8_build_config.json', + ], + 'action': [ + 'python', + '../tools/testrunner/utils/dump_build_config_gyp.py', + '<(PRODUCT_DIR)/v8_build_config.json', + 'dcheck_always_on=<(dcheck_always_on)', + 'is_asan=<(asan)', + 'is_cfi=<(cfi_vptr)', + 'is_component_build=<(component)', + 'is_debug=<(CONFIGURATION_NAME)', + # Not available in gyp. + 'is_gcov_coverage=0', + 'is_msan=<(msan)', + 'is_tsan=<(tsan)', + # Not available in gyp. + 'is_ubsan_vptr=0', + 'target_cpu=<(target_arch)', + 'v8_enable_i18n_support=<(v8_enable_i18n_support)', + 'v8_target_cpu=<(v8_target_arch)', + 'v8_use_snapshot=<(v8_use_snapshot)', + ], + }, + ], + }, ], } diff --git a/chromium/v8/src/wasm/module-compiler.cc b/chromium/v8/src/wasm/module-compiler.cc index bc4945b923e..e42c139ce1c 100644 --- a/chromium/v8/src/wasm/module-compiler.cc +++ b/chromium/v8/src/wasm/module-compiler.cc @@ -1798,7 +1798,6 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() { Handle<JSArrayBuffer> memory = memory_.ToHandleChecked(); // Set externally passed ArrayBuffer non neuterable. memory->set_is_neuterable(false); - memory->set_is_wasm_buffer(true); DCHECK_IMPLIES(trap_handler::UseTrapHandler(), module_->is_asm_js() || memory->has_guard_region()); diff --git a/chromium/v8/src/wasm/wasm-js.cc b/chromium/v8/src/wasm/wasm-js.cc index 5e624d48f1f..6a017365aad 100644 --- a/chromium/v8/src/wasm/wasm-js.cc +++ b/chromium/v8/src/wasm/wasm-js.cc @@ -752,6 +752,10 @@ void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) { max_size64 = i::FLAG_wasm_max_mem_pages; } i::Handle<i::JSArrayBuffer> old_buffer(receiver->array_buffer()); + if (!old_buffer->is_growable()) { + thrower.RangeError("This memory cannot be grown"); + return; + } uint32_t old_size = old_buffer->byte_length()->Number() / i::wasm::kSpecMaxWasmMemoryPages; int64_t new_size64 = old_size + delta_size; diff --git a/chromium/v8/src/wasm/wasm-memory.cc b/chromium/v8/src/wasm/wasm-memory.cc index 2b5ae0052d9..4ddda981895 100644 --- a/chromium/v8/src/wasm/wasm-memory.cc +++ b/chromium/v8/src/wasm/wasm-memory.cc @@ -67,7 +67,7 @@ Handle<JSArrayBuffer> SetupArrayBuffer(Isolate* isolate, void* allocation_base, allocation_length, backing_store, static_cast<int>(size), shared); buffer->set_is_neuterable(false); - buffer->set_is_wasm_buffer(true); + buffer->set_is_growable(true); buffer->set_has_guard_region(enable_guard_regions); return buffer; } diff --git a/chromium/v8/src/wasm/wasm-objects.cc b/chromium/v8/src/wasm/wasm-objects.cc index 8d47293a630..012aa6644bb 100644 --- a/chromium/v8/src/wasm/wasm-objects.cc +++ b/chromium/v8/src/wasm/wasm-objects.cc @@ -315,6 +315,7 @@ namespace { Handle<JSArrayBuffer> GrowMemoryBuffer(Isolate* isolate, Handle<JSArrayBuffer> old_buffer, uint32_t pages, uint32_t maximum_pages) { + if (!old_buffer->is_growable()) return Handle<JSArrayBuffer>::null(); Address old_mem_start = nullptr; uint32_t old_size = 0; if (!old_buffer.is_null()) { @@ -459,6 +460,7 @@ int32_t WasmMemoryObject::Grow(Isolate* isolate, Handle<WasmMemoryObject> memory_object, uint32_t pages) { Handle<JSArrayBuffer> old_buffer(memory_object->array_buffer()); + if (!old_buffer->is_growable()) return -1; uint32_t old_size = 0; CHECK(old_buffer->byte_length()->ToUint32(&old_size)); Handle<JSArrayBuffer> new_buffer; diff --git a/chromium/v8/tools/foozzie/v8_foozzie.py b/chromium/v8/tools/foozzie/v8_foozzie.py index 0c6d65d1e89..21781786526 100755 --- a/chromium/v8/tools/foozzie/v8_foozzie.py +++ b/chromium/v8/tools/foozzie/v8_foozzie.py @@ -127,7 +127,7 @@ def infer_arch(d8): executable. """ with open(os.path.join(os.path.dirname(d8), 'v8_build_config.json')) as f: - arch = json.load(f)['v8_current_cpu'] + arch = json.load(f)['v8_target_cpu'] return 'ia32' if arch == 'x86' else arch diff --git a/chromium/v8/tools/mb/docs/design_spec.md b/chromium/v8/tools/mb/docs/design_spec.md index 33fda806e8a..fb202da74e9 100644 --- a/chromium/v8/tools/mb/docs/design_spec.md +++ b/chromium/v8/tools/mb/docs/design_spec.md @@ -411,9 +411,9 @@ config file change, however. ### Non-goals * MB is not intended to replace direct invocation of GN or GYP for - complicated build scenarios (aka ChromeOS), where multiple flags need + complicated build scenarios (a.k.a. Chrome OS), where multiple flags need to be set to user-defined paths for specific toolchains (e.g., where - ChromeOS needs to specify specific board types and compilers). + Chrome OS needs to specify specific board types and compilers). * MB is not intended at this time to be something developers use frequently, or to add a lot of features to. We hope to be able to get rid of it once diff --git a/chromium/v8/tools/mb/docs/user_guide.md b/chromium/v8/tools/mb/docs/user_guide.md index b897fa4d671..a7d72c88395 100644 --- a/chromium/v8/tools/mb/docs/user_guide.md +++ b/chromium/v8/tools/mb/docs/user_guide.md @@ -20,7 +20,7 @@ For more discussion of MB, see also [the design spec](design_spec.md). ### `mb analyze` -`mb analyze` is responsible for determining what targets are affected by +`mb analyze` is reponsible for determining what targets are affected by a list of files (e.g., the list of files in a patch on a trybot): ``` @@ -45,12 +45,12 @@ a single object with the following fields: reflect the stuff we might want to build *in addition to* the list passed in `test_targets`. Targets in this list will be treated specially, in the following way: if a given target is a "meta" - (GN: group, GYP: none) target like 'blink_tests' or - 'chromium_builder_tests', or even the ninja-specific 'all' target, - then only the *dependencies* of the target that are affected by - the modified files will be rebuilt (not the target itself, which - might also cause unaffected dependencies to be rebuilt). An empty - list will be treated as if there are no additional targets to build. + (GN: group, GYP: none) target like 'blink_tests' or or even the + ninja-specific 'all' target, then only the *dependencies* of the + target that are affected by the modified files will be rebuilt + (not the target itself, which might also cause unaffected dependencies + to be rebuilt). An empty list will be treated as if there are no additional + targets to build. Empty lists for both `test_targets` and `additional_compile_targets` would cause no work to be done, so will result in an error. * `targets`: a legacy field that resembled a union of `compile_targets` @@ -167,6 +167,21 @@ The `-f/--config-file` and `-q/--quiet` flags work as documented for This is mostly useful as a presubmit check and for verifying changes to the config file. +### `mb gerrit-buildbucket-config` + +Generates a gerrit buildbucket configuration file and prints it to +stdout. This file contains the list of trybots shown in gerrit's UI. + +The master copy of the buildbucket.config file lives +in a separate branch of the chromium repository. Run `mb +gerrit-buildbucket-config > buildbucket.config.new && git fetch origin +refs/meta/config:refs/remotes/origin/meta/config && git checkout +-t -b meta_config origin/meta/config && mv buildbucket.config.new +buildbucket.config` to update the file. + +Note that after committing, `git cl upload` will not work. Instead, use `git +push origin HEAD:refs/for/refs/meta/config` to upload the CL for review. + ## Isolates and Swarming `mb gen` is also responsible for generating the `.isolate` and diff --git a/chromium/v8/tools/mb/mb.py b/chromium/v8/tools/mb/mb.py index 86a5e575fd2..9a6600225b8 100755 --- a/chromium/v8/tools/mb/mb.py +++ b/chromium/v8/tools/mb/mb.py @@ -10,6 +10,10 @@ MB is a wrapper script for GYP and GN that can be used to generate build files for sets of canned configurations and analyze them. """ +# TODO(thomasanderson): Remove this comment. It is added to +# workaround https://crbug.com/736215 for CL +# https://codereview.chromium.org/2974603002/ + from __future__ import print_function import argparse @@ -46,11 +50,14 @@ class MetaBuildWrapper(object): self.chromium_src_dir = CHROMIUM_SRC_DIR self.default_config = os.path.join(self.chromium_src_dir, 'infra', 'mb', 'mb_config.pyl') + self.default_isolate_map = os.path.join(self.chromium_src_dir, 'infra', + 'mb', 'gn_isolate_map.pyl') self.executable = sys.executable self.platform = sys.platform self.sep = os.sep self.args = argparse.Namespace() self.configs = {} + self.luci_tryservers = {} self.masters = {} self.mixins = {} @@ -62,7 +69,7 @@ class MetaBuildWrapper(object): self.DumpInputFiles() return ret except KeyboardInterrupt: - self.Print('interrupted, exiting', stream=sys.stderr) + self.Print('interrupted, exiting') return 130 except Exception: self.DumpInputFiles() @@ -79,13 +86,18 @@ class MetaBuildWrapper(object): help='master name to look up config from') subp.add_argument('-c', '--config', help='configuration to analyze') - subp.add_argument('--phase', type=int, - help=('build phase for a given build ' - '(int in [1, 2, ...))')) + subp.add_argument('--phase', + help='optional phase name (used when builders ' + 'do multiple compiles with different ' + 'arguments in a single build)') subp.add_argument('-f', '--config-file', metavar='PATH', default=self.default_config, help='path to config file ' - '(default is //tools/mb/mb_config.pyl)') + '(default is %(default)s)') + subp.add_argument('-i', '--isolate-map-file', metavar='PATH', + default=self.default_isolate_map, + help='path to isolate map file ' + '(default is %(default)s)') subp.add_argument('-g', '--goma-dir', help='path to goma directory') subp.add_argument('--gyp-script', metavar='PATH', @@ -121,6 +133,16 @@ class MetaBuildWrapper(object): 'as a JSON object.') subp.set_defaults(func=self.CmdAnalyze) + subp = subps.add_parser('export', + help='print out the expanded configuration for' + 'each builder as a JSON object') + subp.add_argument('-f', '--config-file', metavar='PATH', + default=self.default_config, + help='path to config file (default is %(default)s)') + subp.add_argument('-g', '--goma-dir', + help='path to goma directory') + subp.set_defaults(func=self.CmdExport) + subp = subps.add_parser('gen', help='generate a new set of build files') AddCommonOptions(subp) @@ -192,16 +214,14 @@ class MetaBuildWrapper(object): help='validate the config file') subp.add_argument('-f', '--config-file', metavar='PATH', default=self.default_config, - help='path to config file ' - '(default is //infra/mb/mb_config.pyl)') + help='path to config file (default is %(default)s)') subp.set_defaults(func=self.CmdValidate) subp = subps.add_parser('audit', help='Audit the config file to track progress') subp.add_argument('-f', '--config-file', metavar='PATH', default=self.default_config, - help='path to config file ' - '(default is //infra/mb/mb_config.pyl)') + help='path to config file (default is %(default)s)') subp.add_argument('-i', '--internal', action='store_true', help='check internal masters also') subp.add_argument('-m', '--master', action='append', @@ -217,6 +237,14 @@ class MetaBuildWrapper(object): ' do compiles') subp.set_defaults(func=self.CmdAudit) + subp = subps.add_parser('gerrit-buildbucket-config', + help='Print buildbucket.config for gerrit ' + '(see MB user guide)') + subp.add_argument('-f', '--config-file', metavar='PATH', + default=self.default_config, + help='path to config file (default is %(default)s)') + subp.set_defaults(func=self.CmdBuildbucket) + subp = subps.add_parser('help', help='Get help on a subcommand.') subp.add_argument(nargs='?', action='store', dest='subcommand', @@ -225,12 +253,16 @@ class MetaBuildWrapper(object): self.args = parser.parse_args(argv) + # TODO(machenbach): This prepares passing swarming targets to isolate on the + # infra side. + self.args.swarming_targets_file = None + def DumpInputFiles(self): def DumpContentsOfFilePassedTo(arg_name, path): if path and self.Exists(path): self.Print("\n# To recreate the file passed to %s:" % arg_name) - self.Print("%% cat > %s <<EOF)" % path) + self.Print("%% cat > %s <<EOF" % path) contents = self.ReadFile(path) self.Print(contents) self.Print("EOF\n%\n") @@ -250,6 +282,34 @@ class MetaBuildWrapper(object): else: return self.RunGYPAnalyze(vals) + def CmdExport(self): + self.ReadConfigFile() + obj = {} + for master, builders in self.masters.items(): + obj[master] = {} + for builder in builders: + config = self.masters[master][builder] + if not config: + continue + + if isinstance(config, dict): + args = {k: self.FlattenConfig(v)['gn_args'] + for k, v in config.items()} + elif config.startswith('//'): + args = config + else: + args = self.FlattenConfig(config)['gn_args'] + if 'error' in args: + continue + + obj[master][builder] = args + + # Dump object and trim trailing whitespace. + s = '\n'.join(l.rstrip() for l in + json.dumps(obj, sort_keys=True, indent=2).splitlines()) + self.Print(s) + return 0 + def CmdGen(self): vals = self.Lookup() self.ClobberIfNeeded(vals) @@ -270,7 +330,7 @@ class MetaBuildWrapper(object): return 1 if vals['type'] == 'gn': - return self.RunGNIsolate(vals) + return self.RunGNIsolate() else: return self.Build('%s_run' % self.args.target[0]) @@ -300,7 +360,7 @@ class MetaBuildWrapper(object): ret = self.Build(target) if ret: return ret - ret = self.RunGNIsolate(vals) + ret = self.RunGNIsolate() if ret: return ret else: @@ -322,6 +382,25 @@ class MetaBuildWrapper(object): return ret + def CmdBuildbucket(self): + self.ReadConfigFile() + + self.Print('# This file was generated using ' + '"tools/mb/mb.py gerrit-buildbucket-config".') + + for luci_tryserver in sorted(self.luci_tryservers): + self.Print('[bucket "luci.%s"]' % luci_tryserver) + for bot in sorted(self.luci_tryservers[luci_tryserver]): + self.Print('\tbuilder = %s' % bot) + + for master in sorted(self.masters): + if master.startswith('tryserver.'): + self.Print('[bucket "master.%s"]' % master) + for bot in sorted(self.masters[master]): + self.Print('\tbuilder = %s' % bot) + + return 0 + def CmdValidate(self, print_ok=True): errs = [] @@ -332,8 +411,8 @@ class MetaBuildWrapper(object): all_configs = {} for master in self.masters: for config in self.masters[master].values(): - if isinstance(config, list): - for c in config: + if isinstance(config, dict): + for c in config.values(): all_configs[c] = master else: all_configs[config] = master @@ -461,8 +540,8 @@ class MetaBuildWrapper(object): config = self.masters[master][builder] if config == 'tbd': tbd.add(builder) - elif isinstance(config, list): - vals = self.FlattenConfig(config[0]) + elif isinstance(config, dict): + vals = self.FlattenConfig(config.values()[0]) if vals['type'] == 'gyp': gyp.add(builder) else: @@ -504,7 +583,7 @@ class MetaBuildWrapper(object): def GetConfig(self): build_dir = self.args.path[0] - vals = {} + vals = self.DefaultVals() if self.args.builder or self.args.master or self.args.config: vals = self.Lookup() if vals['type'] == 'gn': @@ -528,14 +607,12 @@ class MetaBuildWrapper(object): mb_type = self.ReadFile(mb_type_path).strip() if mb_type == 'gn': - vals = self.GNValsFromDir(build_dir) - else: - vals = {} + vals['gn_args'] = self.GNArgsFromDir(build_dir) vals['type'] = mb_type return vals - def GNValsFromDir(self, build_dir): + def GNArgsFromDir(self, build_dir): args_contents = "" gn_args_path = self.PathJoin(self.ToAbsPath(build_dir), 'args.gn') if self.Exists(gn_args_path): @@ -547,27 +624,18 @@ class MetaBuildWrapper(object): val = ' '.join(fields[2:]) gn_args.append('%s=%s' % (name, val)) - return { - 'gn_args': ' '.join(gn_args), - 'type': 'gn', - } + return ' '.join(gn_args) def Lookup(self): - vals = self.ReadBotConfig() + vals = self.ReadIOSBotConfig() if not vals: self.ReadConfigFile() config = self.ConfigFromArgs() if config.startswith('//'): if not self.Exists(self.ToAbsPath(config)): raise MBErr('args file "%s" not found' % config) - vals = { - 'args_file': config, - 'cros_passthrough': False, - 'gn_args': '', - 'gyp_crosscompile': False, - 'gyp_defines': '', - 'type': 'gn', - } + vals = self.DefaultVals() + vals['args_file'] = config else: if not config in self.configs: raise MBErr('Config "%s" not found in %s' % @@ -576,13 +644,14 @@ class MetaBuildWrapper(object): # Do some basic sanity checking on the config so that we # don't have to do this in every caller. - assert 'type' in vals, 'No meta-build type specified in the config' + if 'type' not in vals: + vals['type'] = 'gn' assert vals['type'] in ('gn', 'gyp'), ( 'Unknown meta-build type "%s"' % vals['gn_args']) return vals - def ReadBotConfig(self): + def ReadIOSBotConfig(self): if not self.args.master or not self.args.builder: return {} path = self.PathJoin(self.chromium_src_dir, 'ios', 'build', 'bots', @@ -598,14 +667,11 @@ class MetaBuildWrapper(object): gyp_defines = ' '.join(gyp_vals) gn_args = ' '.join(contents.get('gn_args', [])) - return { - 'args_file': '', - 'cros_passthrough': False, - 'gn_args': gn_args, - 'gyp_crosscompile': False, - 'gyp_defines': gyp_defines, - 'type': contents.get('mb_type', ''), - } + vals = self.DefaultVals() + vals['gn_args'] = gn_args + vals['gyp_defines'] = gyp_defines + vals['type'] = contents.get('mb_type', 'gn') + return vals def ReadConfigFile(self): if not self.Exists(self.args.config_file): @@ -618,9 +684,20 @@ class MetaBuildWrapper(object): (self.args.config_file, e)) self.configs = contents['configs'] + self.luci_tryservers = contents.get('luci_tryservers', {}) self.masters = contents['masters'] self.mixins = contents['mixins'] + def ReadIsolateMap(self): + if not self.Exists(self.args.isolate_map_file): + raise MBErr('isolate map file not found at %s' % + self.args.isolate_map_file) + try: + return ast.literal_eval(self.ReadFile(self.args.isolate_map_file)) + except SyntaxError as e: + raise MBErr('Failed to parse isolate map file "%s": %s' % + (self.args.isolate_map_file, e)) + def ConfigFromArgs(self): if self.args.config: if self.args.master or self.args.builder: @@ -642,15 +719,15 @@ class MetaBuildWrapper(object): (self.args.builder, self.args.master, self.args.config_file)) config = self.masters[self.args.master][self.args.builder] - if isinstance(config, list): + if isinstance(config, dict): if self.args.phase is None: raise MBErr('Must specify a build --phase for %s on %s' % (self.args.builder, self.args.master)) - phase = int(self.args.phase) - if phase < 1 or phase > len(config): - raise MBErr('Phase %d out of bounds for %s on %s' % + phase = str(self.args.phase) + if phase not in config: + raise MBErr('Phase %s doesn\'t exist for %s on %s' % (phase, self.args.builder, self.args.master)) - return config[phase-1] + return config[phase] if self.args.phase is not None: raise MBErr('Must not specify a build --phase for %s on %s' % @@ -659,19 +736,22 @@ class MetaBuildWrapper(object): def FlattenConfig(self, config): mixins = self.configs[config] - vals = { + vals = self.DefaultVals() + + visited = [] + self.FlattenMixins(mixins, vals, visited) + return vals + + def DefaultVals(self): + return { 'args_file': '', 'cros_passthrough': False, - 'gn_args': [], + 'gn_args': '', 'gyp_defines': '', 'gyp_crosscompile': False, - 'type': None, + 'type': 'gn', } - visited = [] - self.FlattenMixins(mixins, vals, visited) - return vals - def FlattenMixins(self, mixins, vals, visited): for m in mixins: if m not in self.mixins: @@ -683,6 +763,11 @@ class MetaBuildWrapper(object): if 'cros_passthrough' in mixin_vals: vals['cros_passthrough'] = mixin_vals['cros_passthrough'] + if 'args_file' in mixin_vals: + if vals['args_file']: + raise MBErr('args_file specified multiple times in mixins ' + 'for %s on %s' % (self.args.builder, self.args.master)) + vals['args_file'] = mixin_vals['args_file'] if 'gn_args' in mixin_vals: if vals['gn_args']: vals['gn_args'] += ' ' + mixin_vals['gn_args'] @@ -732,11 +817,13 @@ class MetaBuildWrapper(object): self.MaybeMakeDirectory(build_dir) self.WriteFile(mb_type_path, new_mb_type) - def RunGNGen(self, vals): + def RunGNGen(self, vals, compute_grit_inputs_for_analyze=False): build_dir = self.args.path[0] cmd = self.GNCmd('gen', build_dir, '--check') gn_args = self.GNArgs(vals) + if compute_grit_inputs_for_analyze: + gn_args += ' compute_grit_inputs_for_analyze=true' # Since GN hasn't run yet, the build directory may not even exist. self.MaybeMakeDirectory(self.ToAbsPath(build_dir)) @@ -748,7 +835,7 @@ class MetaBuildWrapper(object): if getattr(self.args, 'swarming_targets_file', None): # We need GN to generate the list of runtime dependencies for # the compile targets listed (one per line) in the file so - # we can run them via swarming. We use ninja_to_gn.pyl to convert + # we can run them via swarming. We use gn_isolate_map.pyl to convert # the compile targets to the matching GN labels. path = self.args.swarming_targets_file if not self.Exists(path): @@ -756,34 +843,17 @@ class MetaBuildWrapper(object): output_path=None) contents = self.ReadFile(path) swarming_targets = set(contents.splitlines()) - gn_isolate_map = ast.literal_eval(self.ReadFile(self.PathJoin( - self.chromium_src_dir, 'testing', 'buildbot', 'gn_isolate_map.pyl'))) - gn_labels = [] - err = '' - for target in swarming_targets: - target_name = self.GNTargetName(target) - if not target_name in gn_isolate_map: - err += ('test target "%s" not found\n' % target_name) - elif gn_isolate_map[target_name]['type'] == 'unknown': - err += ('test target "%s" type is unknown\n' % target_name) - else: - gn_labels.append(gn_isolate_map[target_name]['label']) + isolate_map = self.ReadIsolateMap() + err, labels = self.MapTargetsToLabels(isolate_map, swarming_targets) if err: - raise MBErr('Error: Failed to match swarming targets to %s:\n%s' % - ('//testing/buildbot/gn_isolate_map.pyl', err)) + raise MBErr(err) gn_runtime_deps_path = self.ToAbsPath(build_dir, 'runtime_deps') - self.WriteFile(gn_runtime_deps_path, '\n'.join(gn_labels) + '\n') + self.WriteFile(gn_runtime_deps_path, '\n'.join(labels) + '\n') cmd.append('--runtime-deps-list-file=%s' % gn_runtime_deps_path) - # Override msvs infra environment variables. - # TODO(machenbach): Remove after GYP_MSVS_VERSION is removed on infra side. - env = {} - env.update(os.environ) - env['GYP_MSVS_VERSION'] = '2017' - - ret, _, _ = self.Run(cmd, env=env) + ret, _, _ = self.Run(cmd) if ret: # If `gn gen` failed, we should exit early rather than trying to # generate isolates. Run() will have already logged any error output. @@ -796,24 +866,23 @@ class MetaBuildWrapper(object): # Android targets may be either android_apk or executable. The former # will result in runtime_deps associated with the stamp file, while the # latter will result in runtime_deps associated with the executable. - target_name = self.GNTargetName(target) - label = gn_isolate_map[target_name]['label'] + label = isolate_map[target]['label'] runtime_deps_targets = [ - target_name + '.runtime_deps', + target + '.runtime_deps', 'obj/%s.stamp.runtime_deps' % label.replace(':', '/')] - elif gn_isolate_map[target]['type'] == 'gpu_browser_test': - if self.platform == 'win32': - runtime_deps_targets = ['browser_tests.exe.runtime_deps'] - else: - runtime_deps_targets = ['browser_tests.runtime_deps'] - elif (gn_isolate_map[target]['type'] == 'script' or - gn_isolate_map[target].get('label_type') == 'group'): + elif (isolate_map[target]['type'] == 'script' or + isolate_map[target].get('label_type') == 'group'): # For script targets, the build target is usually a group, # for which gn generates the runtime_deps next to the stamp file - # for the label, which lives under the obj/ directory. - label = gn_isolate_map[target]['label'] + # for the label, which lives under the obj/ directory, but it may + # also be an executable. + label = isolate_map[target]['label'] runtime_deps_targets = [ 'obj/%s.stamp.runtime_deps' % label.replace(':', '/')] + if self.platform == 'win32': + runtime_deps_targets += [ target + '.exe.runtime_deps' ] + else: + runtime_deps_targets += [ target + '.runtime_deps' ] elif self.platform == 'win32': runtime_deps_targets = [target + '.exe.runtime_deps'] else: @@ -827,26 +896,22 @@ class MetaBuildWrapper(object): raise MBErr('did not generate any of %s' % ', '.join(runtime_deps_targets)) - command, extra_files = self.GetIsolateCommand(target, vals, - gn_isolate_map) - runtime_deps = self.ReadFile(runtime_deps_path).splitlines() - self.WriteIsolateFiles(build_dir, command, target, runtime_deps, - extra_files) + self.WriteIsolateFiles(build_dir, target, runtime_deps) return 0 - def RunGNIsolate(self, vals): - gn_isolate_map = ast.literal_eval(self.ReadFile(self.PathJoin( - self.chromium_src_dir, 'testing', 'buildbot', 'gn_isolate_map.pyl'))) + def RunGNIsolate(self): + target = self.args.target[0] + isolate_map = self.ReadIsolateMap() + err, labels = self.MapTargetsToLabels(isolate_map, [target]) + if err: + raise MBErr(err) + label = labels[0] build_dir = self.args.path[0] - target = self.args.target[0] - target_name = self.GNTargetName(target) - command, extra_files = self.GetIsolateCommand(target, vals, gn_isolate_map) - label = gn_isolate_map[target_name]['label'] cmd = self.GNCmd('desc', build_dir, label, 'runtime_deps') ret, out, _ = self.Call(cmd) if ret: @@ -856,8 +921,7 @@ class MetaBuildWrapper(object): runtime_deps = out.splitlines() - self.WriteIsolateFiles(build_dir, command, target, runtime_deps, - extra_files) + self.WriteIsolateFiles(build_dir, target, runtime_deps) ret, _, _ = self.Run([ self.executable, @@ -871,14 +935,12 @@ class MetaBuildWrapper(object): return ret - def WriteIsolateFiles(self, build_dir, command, target, runtime_deps, - extra_files): + def WriteIsolateFiles(self, build_dir, target, runtime_deps): isolate_path = self.ToAbsPath(build_dir, target + '.isolate') self.WriteFile(isolate_path, pprint.pformat({ 'variables': { - 'command': command, - 'files': sorted(runtime_deps + extra_files), + 'files': sorted(runtime_deps), } }) + '\n') @@ -896,6 +958,27 @@ class MetaBuildWrapper(object): isolate_path + 'd.gen.json', ) + def MapTargetsToLabels(self, isolate_map, targets): + labels = [] + err = '' + + for target in targets: + if target == 'all': + labels.append(target) + elif target.startswith('//'): + labels.append(target) + else: + if target in isolate_map: + if isolate_map[target]['type'] == 'unknown': + err += ('test target "%s" type is unknown\n' % target) + else: + labels.append(isolate_map[target]['label']) + else: + err += ('target "%s" not found in ' + '//infra/mb/gn_isolate_map.pyl\n' % target) + + return err, labels + def GNCmd(self, subcommand, path, *args): if self.platform == 'linux2': subdir, exe = 'linux64', 'gn' @@ -905,9 +988,9 @@ class MetaBuildWrapper(object): subdir, exe = 'win', 'gn.exe' gn_path = self.PathJoin(self.chromium_src_dir, 'buildtools', subdir, exe) - return [gn_path, subcommand, path] + list(args) + def GNArgs(self, vals): if vals['cros_passthrough']: if not 'GN_ARGS' in os.environ: @@ -972,109 +1055,6 @@ class MetaBuildWrapper(object): return ret - def GetIsolateCommand(self, target, vals, gn_isolate_map): - android = 'target_os="android"' in vals['gn_args'] - - # This needs to mirror the settings in //build/config/ui.gni: - # use_x11 = is_linux && !use_ozone. - use_x11 = (self.platform == 'linux2' and - not android and - not 'use_ozone=true' in vals['gn_args']) - - asan = 'is_asan=true' in vals['gn_args'] - msan = 'is_msan=true' in vals['gn_args'] - tsan = 'is_tsan=true' in vals['gn_args'] - - target_name = self.GNTargetName(target) - test_type = gn_isolate_map[target_name]['type'] - - executable = gn_isolate_map[target_name].get('executable', target_name) - executable_suffix = '.exe' if self.platform == 'win32' else '' - - cmdline = [] - extra_files = [] - - if android and test_type != "script": - logdog_command = [ - '--logdog-bin-cmd', './../../bin/logdog_butler', - '--project', 'chromium', - '--service-account-json', - '/creds/service_accounts/service-account-luci-logdog-publisher.json', - '--prefix', 'android/swarming/logcats/${SWARMING_TASK_ID}', - '--source', '${ISOLATED_OUTDIR}/logcats', - '--name', 'unified_logcats', - ] - test_cmdline = [ - self.PathJoin('bin', 'run_%s' % target_name), - '--logcat-output-file', '${ISOLATED_OUTDIR}/logcats', - '--target-devices-file', '${SWARMING_BOT_FILE}', - '-v' - ] - cmdline = (['./../../build/android/test_wrapper/logdog_wrapper.py'] - + logdog_command + test_cmdline) - elif use_x11 and test_type == 'windowed_test_launcher': - extra_files = [ - '../../testing/test_env.py', - '../../testing/xvfb.py', - ] - cmdline = [ - '../../testing/xvfb.py', - '.', - './' + str(executable) + executable_suffix, - '--brave-new-test-launcher', - '--test-launcher-bot-mode', - '--asan=%d' % asan, - '--msan=%d' % msan, - '--tsan=%d' % tsan, - ] - elif test_type in ('windowed_test_launcher', 'console_test_launcher'): - extra_files = [ - '../../testing/test_env.py' - ] - cmdline = [ - '../../testing/test_env.py', - './' + str(executable) + executable_suffix, - '--brave-new-test-launcher', - '--test-launcher-bot-mode', - '--asan=%d' % asan, - '--msan=%d' % msan, - '--tsan=%d' % tsan, - ] - elif test_type == 'gpu_browser_test': - extra_files = [ - '../../testing/test_env.py' - ] - gtest_filter = gn_isolate_map[target]['gtest_filter'] - cmdline = [ - '../../testing/test_env.py', - './browser_tests' + executable_suffix, - '--test-launcher-bot-mode', - '--enable-gpu', - '--test-launcher-jobs=1', - '--gtest_filter=%s' % gtest_filter, - ] - elif test_type == 'script': - extra_files = [ - '../../testing/test_env.py' - ] - cmdline = [ - '../../testing/test_env.py', - '../../' + self.ToSrcRelPath(gn_isolate_map[target]['script']) - ] - elif test_type in ('raw'): - extra_files = [] - cmdline = [ - './' + str(target) + executable_suffix, - ] - - else: - self.WriteFailureAndRaise('No command line for %s found (test type %s).' - % (target, test_type), output_path=None) - - cmdline += gn_isolate_map[target_name].get('args', []) - - return cmdline, extra_files - def ToAbsPath(self, build_path, *comps): return self.PathJoin(self.chromium_src_dir, self.ToSrcRelPath(build_path), @@ -1167,12 +1147,18 @@ class MetaBuildWrapper(object): return cmd, env def RunGNAnalyze(self, vals): - # analyze runs before 'gn gen' now, so we need to run gn gen + # Analyze runs before 'gn gen' now, so we need to run gn gen # in order to ensure that we have a build directory. - ret = self.RunGNGen(vals) + ret = self.RunGNGen(vals, compute_grit_inputs_for_analyze=True) if ret: return ret + build_path = self.args.path[0] + input_path = self.args.input_path[0] + gn_input_path = input_path + '.gn' + output_path = self.args.output_path[0] + gn_output_path = output_path + '.gn' + inp = self.ReadInputJSON(['files', 'test_targets', 'additional_compile_targets']) if self.args.verbose: @@ -1181,26 +1167,6 @@ class MetaBuildWrapper(object): self.PrintJSON(inp) self.Print() - # TODO(crbug.com/555273) - currently GN treats targets and - # additional_compile_targets identically since we can't tell the - # difference between a target that is a group in GN and one that isn't. - # We should eventually fix this and treat the two types differently. - targets = (set(inp['test_targets']) | - set(inp['additional_compile_targets'])) - - output_path = self.args.output_path[0] - - # Bail out early if a GN file was modified, since 'gn refs' won't know - # what to do about it. Also, bail out early if 'all' was asked for, - # since we can't deal with it yet. - if (any(f.endswith('.gn') or f.endswith('.gni') for f in inp['files']) or - 'all' in targets): - self.WriteJSON({ - 'status': 'Found dependency (all)', - 'compile_targets': sorted(targets), - 'test_targets': sorted(targets & set(inp['test_targets'])), - }, output_path) - return 0 # This shouldn't normally happen, but could due to unusual race conditions, # like a try job that gets scheduled before a patch lands but runs after @@ -1214,68 +1180,103 @@ class MetaBuildWrapper(object): }, output_path) return 0 - ret = 0 - response_file = self.TempFile() - response_file.write('\n'.join(inp['files']) + '\n') - response_file.close() + gn_inp = {} + gn_inp['files'] = ['//' + f for f in inp['files'] if not f.startswith('//')] + + isolate_map = self.ReadIsolateMap() + err, gn_inp['additional_compile_targets'] = self.MapTargetsToLabels( + isolate_map, inp['additional_compile_targets']) + if err: + raise MBErr(err) + + err, gn_inp['test_targets'] = self.MapTargetsToLabels( + isolate_map, inp['test_targets']) + if err: + raise MBErr(err) + labels_to_targets = {} + for i, label in enumerate(gn_inp['test_targets']): + labels_to_targets[label] = inp['test_targets'][i] - matching_targets = set() try: - cmd = self.GNCmd('refs', - self.args.path[0], - '@%s' % response_file.name, - '--all', - '--as=output') - ret, out, _ = self.Run(cmd, force_verbose=False) - if ret and not 'The input matches no targets' in out: - self.WriteFailureAndRaise('gn refs returned %d: %s' % (ret, out), - output_path) - build_dir = self.ToSrcRelPath(self.args.path[0]) + self.sep - for output in out.splitlines(): - build_output = output.replace(build_dir, '') - if build_output in targets: - matching_targets.add(build_output) - - cmd = self.GNCmd('refs', - self.args.path[0], - '@%s' % response_file.name, - '--all') - ret, out, _ = self.Run(cmd, force_verbose=False) - if ret and not 'The input matches no targets' in out: - self.WriteFailureAndRaise('gn refs returned %d: %s' % (ret, out), - output_path) - for label in out.splitlines(): - build_target = label[2:] - # We want to accept 'chrome/android:chrome_public_apk' and - # just 'chrome_public_apk'. This may result in too many targets - # getting built, but we can adjust that later if need be. - for input_target in targets: - if (input_target == build_target or - build_target.endswith(':' + input_target)): - matching_targets.add(input_target) - finally: - self.RemoveFile(response_file.name) + self.WriteJSON(gn_inp, gn_input_path) + cmd = self.GNCmd('analyze', build_path, gn_input_path, gn_output_path) + ret, _, _ = self.Run(cmd, force_verbose=True) + if ret: + return ret - if matching_targets: - self.WriteJSON({ - 'status': 'Found dependency', - 'compile_targets': sorted(matching_targets), - 'test_targets': sorted(matching_targets & - set(inp['test_targets'])), - }, output_path) - else: - self.WriteJSON({ - 'status': 'No dependency', - 'compile_targets': [], - 'test_targets': [], - }, output_path) + gn_outp_str = self.ReadFile(gn_output_path) + try: + gn_outp = json.loads(gn_outp_str) + except Exception as e: + self.Print("Failed to parse the JSON string GN returned: %s\n%s" + % (repr(gn_outp_str), str(e))) + raise - if self.args.verbose: - outp = json.loads(self.ReadFile(output_path)) - self.Print() - self.Print('analyze output:') - self.PrintJSON(outp) - self.Print() + outp = {} + if 'status' in gn_outp: + outp['status'] = gn_outp['status'] + if 'error' in gn_outp: + outp['error'] = gn_outp['error'] + if 'invalid_targets' in gn_outp: + outp['invalid_targets'] = gn_outp['invalid_targets'] + if 'compile_targets' in gn_outp: + all_input_compile_targets = sorted( + set(inp['test_targets'] + inp['additional_compile_targets'])) + + # If we're building 'all', we can throw away the rest of the targets + # since they're redundant. + if 'all' in gn_outp['compile_targets']: + outp['compile_targets'] = ['all'] + else: + outp['compile_targets'] = gn_outp['compile_targets'] + + # crbug.com/736215: When GN returns targets back, for targets in + # the default toolchain, GN will have generated a phony ninja + # target matching the label, and so we can safely (and easily) + # transform any GN label into the matching ninja target. For + # targets in other toolchains, though, GN doesn't generate the + # phony targets, and we don't know how to turn the labels into + # compile targets. In this case, we also conservatively give up + # and build everything. Probably the right thing to do here is + # to have GN return the compile targets directly. + if any("(" in target for target in outp['compile_targets']): + self.Print('WARNING: targets with non-default toolchains were ' + 'found, building everything instead.') + outp['compile_targets'] = all_input_compile_targets + else: + outp['compile_targets'] = [ + label.replace('//', '') for label in outp['compile_targets']] + + # Windows has a maximum command line length of 8k; even Linux + # maxes out at 128k; if analyze returns a *really long* list of + # targets, we just give up and conservatively build everything instead. + # Probably the right thing here is for ninja to support response + # files as input on the command line + # (see https://github.com/ninja-build/ninja/issues/1355). + if len(' '.join(outp['compile_targets'])) > 7*1024: + self.Print('WARNING: Too many compile targets were affected.') + self.Print('WARNING: Building everything instead to avoid ' + 'command-line length issues.') + outp['compile_targets'] = all_input_compile_targets + + + if 'test_targets' in gn_outp: + outp['test_targets'] = [ + labels_to_targets[label] for label in gn_outp['test_targets']] + + if self.args.verbose: + self.Print() + self.Print('analyze output:') + self.PrintJSON(outp) + self.Print() + + self.WriteJSON(outp, output_path) + + finally: + if self.Exists(gn_input_path): + self.RemoveFile(gn_input_path) + if self.Exists(gn_output_path): + self.RemoveFile(gn_output_path) return 0 @@ -1358,9 +1359,6 @@ class MetaBuildWrapper(object): def PrintJSON(self, obj): self.Print(json.dumps(obj, indent=2, sort_keys=True)) - def GNTargetName(self, target): - return target - def Build(self, target): build_dir = self.ToSrcRelPath(self.args.path[0]) ninja_cmd = ['ninja', '-C', build_dir] diff --git a/chromium/v8/tools/mb/mb_unittest.py b/chromium/v8/tools/mb/mb_unittest.py index ac58c0284f9..15763750da5 100755 --- a/chromium/v8/tools/mb/mb_unittest.py +++ b/chromium/v8/tools/mb/mb_unittest.py @@ -23,12 +23,15 @@ class FakeMBW(mb.MetaBuildWrapper): if win32: self.chromium_src_dir = 'c:\\fake_src' self.default_config = 'c:\\fake_src\\tools\\mb\\mb_config.pyl' + self.default_isolate_map = ('c:\\fake_src\\testing\\buildbot\\' + 'gn_isolate_map.pyl') self.platform = 'win32' self.executable = 'c:\\python\\python.exe' self.sep = '\\' else: self.chromium_src_dir = '/fake_src' self.default_config = '/fake_src/tools/mb/mb_config.pyl' + self.default_isolate_map = '/fake_src/testing/buildbot/gn_isolate_map.pyl' self.executable = '/usr/bin/python' self.platform = 'linux2' self.sep = '/' @@ -115,10 +118,14 @@ TEST_CONFIG = """\ 'fake_gn_debug_builder': 'gn_debug_goma', 'fake_gyp_builder': 'gyp_debug', 'fake_gn_args_bot': '//build/args/bots/fake_master/fake_gn_args_bot.gn', - 'fake_multi_phase': ['gn_phase_1', 'gn_phase_2'], + 'fake_multi_phase': { 'phase_1': 'gn_phase_1', 'phase_2': 'gn_phase_2'}, + 'fake_args_file': 'args_file_goma', + 'fake_args_file_twice': 'args_file_twice', }, }, 'configs': { + 'args_file_goma': ['args_file', 'goma'], + 'args_file_twice': ['args_file', 'args_file'], 'gyp_rel_bot': ['gyp', 'rel', 'goma'], 'gn_debug_goma': ['gn', 'debug', 'goma'], 'gyp_debug': ['gyp', 'debug', 'fake_feature1'], @@ -141,6 +148,9 @@ TEST_CONFIG = """\ 'gn_args': 'use_goma=true', 'gyp_defines': 'goma=1', }, + 'args_file': { + 'args_file': '//build/args/fake.gn', + }, 'phase_1': { 'gn_args': 'phase=1', 'gyp_args': 'phase=1', @@ -159,35 +169,6 @@ TEST_CONFIG = """\ } """ - -TEST_BAD_CONFIG = """\ -{ - 'configs': { - 'gn_rel_bot_1': ['gn', 'rel', 'chrome_with_codecs'], - 'gn_rel_bot_2': ['gn', 'rel', 'bad_nested_config'], - }, - 'masters': { - 'chromium': { - 'a': 'gn_rel_bot_1', - 'b': 'gn_rel_bot_2', - }, - }, - 'mixins': { - 'gn': {'type': 'gn'}, - 'chrome_with_codecs': { - 'gn_args': 'proprietary_codecs=true', - }, - 'bad_nested_config': { - 'mixins': ['chrome_with_codecs'], - }, - 'rel': { - 'gn_args': 'is_debug=false', - }, - }, -} -""" - - GYP_HACKS_CONFIG = """\ { 'masters': { @@ -211,12 +192,43 @@ GYP_HACKS_CONFIG = """\ } """ +TRYSERVER_CONFIG = """\ +{ + 'masters': { + 'not_a_tryserver': { + 'fake_builder': 'fake_config', + }, + 'tryserver.chromium.linux': { + 'try_builder': 'fake_config', + }, + 'tryserver.chromium.mac': { + 'try_builder2': 'fake_config', + }, + }, + 'luci_tryservers': { + 'luci_tryserver1': ['luci_builder1'], + 'luci_tryserver2': ['luci_builder2'], + }, + 'configs': {}, + 'mixins': {}, +} +""" + class UnitTest(unittest.TestCase): def fake_mbw(self, files=None, win32=False): mbw = FakeMBW(win32=win32) mbw.files.setdefault(mbw.default_config, TEST_CONFIG) mbw.files.setdefault( + mbw.ToAbsPath('//testing/buildbot/gn_isolate_map.pyl'), + '''{ + "foo_unittests": { + "label": "//foo:foo_unittests", + "type": "console_test_launcher", + "args": [], + }, + }''') + mbw.files.setdefault( mbw.ToAbsPath('//build/args/bots/fake_master/fake_gn_args_bot.gn'), 'is_debug = false\n') if files: @@ -268,78 +280,104 @@ class UnitTest(unittest.TestCase): ['/fake_src/out/Debug', '/fake_src/out/Debug']) self.assertEqual(mbw.files['/fake_src/out/Debug/mb_type'], 'gyp') - def test_gn_analyze(self): - files = {'/tmp/in.json': """{\ + def test_analyze(self): + files = {'/tmp/in.json': '''{\ "files": ["foo/foo_unittest.cc"], - "test_targets": ["foo_unittests", "bar_unittests"], - "additional_compile_targets": [] - }"""} + "test_targets": ["foo_unittests"], + "additional_compile_targets": ["all"] + }''', + '/tmp/out.json.gn': '''{\ + "status": "Found dependency", + "compile_targets": ["//foo:foo_unittests"], + "test_targets": ["//foo:foo_unittests"] + }'''} mbw = self.fake_mbw(files) - mbw.Call = lambda cmd, env=None, buffer_output=True: ( - 0, 'out/Default/foo_unittests\n', '') + mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '') self.check(['analyze', '-c', 'gn_debug_goma', '//out/Default', '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0) out = json.loads(mbw.files['/tmp/out.json']) self.assertEqual(out, { 'status': 'Found dependency', - 'compile_targets': ['foo_unittests'], + 'compile_targets': ['foo:foo_unittests'], 'test_targets': ['foo_unittests'] }) - def test_gn_analyze_fails(self): - files = {'/tmp/in.json': """{\ + def test_analyze_optimizes_compile_for_all(self): + files = {'/tmp/in.json': '''{\ "files": ["foo/foo_unittest.cc"], - "test_targets": ["foo_unittests", "bar_unittests"], - "additional_compile_targets": [] - }"""} + "test_targets": ["foo_unittests"], + "additional_compile_targets": ["all"] + }''', + '/tmp/out.json.gn': '''{\ + "status": "Found dependency", + "compile_targets": ["//foo:foo_unittests", "all"], + "test_targets": ["//foo:foo_unittests"] + }'''} mbw = self.fake_mbw(files) - mbw.Call = lambda cmd, env=None, buffer_output=True: (1, '', '') + mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '') self.check(['analyze', '-c', 'gn_debug_goma', '//out/Default', - '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=1) + '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0) + out = json.loads(mbw.files['/tmp/out.json']) + + # check that 'foo_unittests' is not in the compile_targets + self.assertEqual(['all'], out['compile_targets']) - def test_gn_analyze_all(self): - files = {'/tmp/in.json': """{\ + def test_analyze_handles_other_toolchains(self): + files = {'/tmp/in.json': '''{\ "files": ["foo/foo_unittest.cc"], - "test_targets": ["bar_unittests"], + "test_targets": ["foo_unittests"], "additional_compile_targets": ["all"] - }"""} + }''', + '/tmp/out.json.gn': '''{\ + "status": "Found dependency", + "compile_targets": ["//foo:foo_unittests", + "//foo:foo_unittests(bar)"], + "test_targets": ["//foo:foo_unittests"] + }'''} + mbw = self.fake_mbw(files) - mbw.Call = lambda cmd, env=None, buffer_output=True: ( - 0, 'out/Default/foo_unittests\n', '') + mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '') + self.check(['analyze', '-c', 'gn_debug_goma', '//out/Default', '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0) out = json.loads(mbw.files['/tmp/out.json']) - self.assertEqual(out, { - 'status': 'Found dependency (all)', - 'compile_targets': ['all', 'bar_unittests'], - 'test_targets': ['bar_unittests'], - }) - def test_gn_analyze_missing_file(self): - files = {'/tmp/in.json': """{\ + # crbug.com/736215: If GN returns a label containing a toolchain, + # MB (and Ninja) don't know how to handle it; to work around this, + # we give up and just build everything we were asked to build. The + # output compile_targets should include all of the input test_targets and + # additional_compile_targets. + self.assertEqual(['all', 'foo_unittests'], out['compile_targets']) + + def test_analyze_handles_way_too_many_results(self): + too_many_files = ', '.join(['"//foo:foo%d"' % i for i in xrange(4 * 1024)]) + files = {'/tmp/in.json': '''{\ "files": ["foo/foo_unittest.cc"], - "test_targets": ["bar_unittests"], - "additional_compile_targets": [] - }"""} + "test_targets": ["foo_unittests"], + "additional_compile_targets": ["all"] + }''', + '/tmp/out.json.gn': '''{\ + "status": "Found dependency", + "compile_targets": [''' + too_many_files + '''], + "test_targets": ["//foo:foo_unittests"] + }'''} + mbw = self.fake_mbw(files) - mbw.cmds = [ - (0, '', ''), - (1, 'The input matches no targets, configs, or files\n', ''), - (1, 'The input matches no targets, configs, or files\n', ''), - ] + mbw.Call = lambda cmd, env=None, buffer_output=True: (0, '', '') self.check(['analyze', '-c', 'gn_debug_goma', '//out/Default', '/tmp/in.json', '/tmp/out.json'], mbw=mbw, ret=0) out = json.loads(mbw.files['/tmp/out.json']) - self.assertEqual(out, { - 'status': 'No dependency', - 'compile_targets': [], - 'test_targets': [], - }) + + # If GN returns so many compile targets that we might have command-line + # issues, we should give up and just build everything we were asked to + # build. The output compile_targets should include all of the input + # test_targets and additional_compile_targets. + self.assertEqual(['all', 'foo_unittests'], out['compile_targets']) def test_gn_gen(self): mbw = self.fake_mbw() @@ -373,12 +411,27 @@ class UnitTest(unittest.TestCase): mbw.files['/fake_src/out/Debug/args.gn'], 'import("//build/args/bots/fake_master/fake_gn_args_bot.gn")\n') + def test_gn_gen_args_file_mixins(self): + mbw = self.fake_mbw() + self.check(['gen', '-m', 'fake_master', '-b', 'fake_args_file', + '//out/Debug'], mbw=mbw, ret=0) + + self.assertEqual( + mbw.files['/fake_src/out/Debug/args.gn'], + ('import("//build/args/fake.gn")\n' + 'use_goma = true\n')) + + mbw = self.fake_mbw() + self.check(['gen', '-m', 'fake_master', '-b', 'fake_args_file_twice', + '//out/Debug'], mbw=mbw, ret=1) def test_gn_gen_fails(self): mbw = self.fake_mbw() mbw.Call = lambda cmd, env=None, buffer_output=True: (1, '', '') self.check(['gen', '-c', 'gn_debug_goma', '//out/Default'], mbw=mbw, ret=1) + # TODO(machenbach): Comment back in after swarming file parameter is used. + """ def test_gn_gen_swarming(self): files = { '/tmp/swarming_targets': 'base_unittests\n', @@ -403,6 +456,34 @@ class UnitTest(unittest.TestCase): self.assertIn('/fake_src/out/Default/base_unittests.isolated.gen.json', mbw.files) + def test_gn_gen_swarming_script(self): + files = { + '/tmp/swarming_targets': 'cc_perftests\n', + '/fake_src/testing/buildbot/gn_isolate_map.pyl': ( + "{'cc_perftests': {" + " 'label': '//cc:cc_perftests'," + " 'type': 'script'," + " 'script': '/fake_src/out/Default/test_script.py'," + " 'args': []," + "}}\n" + ), + 'c:\\fake_src\out\Default\cc_perftests.exe.runtime_deps': ( + "cc_perftests\n" + ), + } + mbw = self.fake_mbw(files=files, win32=True) + self.check(['gen', + '-c', 'gn_debug_goma', + '--swarming-targets-file', '/tmp/swarming_targets', + '--isolate-map-file', + '/fake_src/testing/buildbot/gn_isolate_map.pyl', + '//out/Default'], mbw=mbw, ret=0) + self.assertIn('c:\\fake_src\\out\\Default\\cc_perftests.isolate', + mbw.files) + self.assertIn('c:\\fake_src\\out\\Default\\cc_perftests.isolated.gen.json', + mbw.files) + """ # pylint: disable=pointless-string-statement + def test_gn_isolate(self): files = { '/fake_src/out/Default/toolchain.ninja': "", @@ -509,27 +590,23 @@ class UnitTest(unittest.TestCase): # Check that passing a --phase to a single-phase builder fails. mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_gn_builder', - '--phase', '1'], - ret=1) + '--phase', 'phase_1'], ret=1) self.assertIn('Must not specify a build --phase', mbw.out) - # Check different ranges; 0 and 3 are out of bounds, 1 and 2 should work. + # Check that passing a wrong phase key to a multi-phase builder fails. mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_multi_phase', - '--phase', '0'], ret=1) - self.assertIn('Phase 0 out of bounds', mbw.out) + '--phase', 'wrong_phase'], ret=1) + self.assertIn('Phase wrong_phase doesn\'t exist', mbw.out) + # Check that passing a correct phase key to a multi-phase builder passes. mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_multi_phase', - '--phase', '1'], ret=0) + '--phase', 'phase_1'], ret=0) self.assertIn('phase = 1', mbw.out) mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_multi_phase', - '--phase', '2'], ret=0) + '--phase', 'phase_2'], ret=0) self.assertIn('phase = 2', mbw.out) - mbw = self.check(['lookup', '-m', 'fake_master', '-b', 'fake_multi_phase', - '--phase', '3'], ret=1) - self.assertIn('Phase 3 out of bounds', mbw.out) - def test_validate(self): mbw = self.fake_mbw() self.check(['validate'], mbw=mbw, ret=0) @@ -544,28 +621,21 @@ class UnitTest(unittest.TestCase): "LLVM_FORCE_HEAD_REVISION=1\n" "python build/gyp_chromium -G output_dir=_path_\n")) - -if __name__ == '__main__': - unittest.main() - - def test_validate(self): - mbw = self.fake_mbw() - self.check(['validate'], mbw=mbw, ret=0) - - def test_bad_validate(self): - mbw = self.fake_mbw() - mbw.files[mbw.default_config] = TEST_BAD_CONFIG - self.check(['validate'], mbw=mbw, ret=1) - - def test_gyp_env_hacks(self): + def test_buildbucket(self): mbw = self.fake_mbw() - mbw.files[mbw.default_config] = GYP_HACKS_CONFIG - self.check(['lookup', '-c', 'fake_config'], mbw=mbw, + mbw.files[mbw.default_config] = TRYSERVER_CONFIG + self.check(['gerrit-buildbucket-config'], mbw=mbw, ret=0, - out=("GYP_DEFINES='foo=bar baz=1'\n" - "GYP_LINK_CONCURRENCY=1\n" - "LLVM_FORCE_HEAD_REVISION=1\n" - "python build/gyp_chromium -G output_dir=_path_\n")) + out=('# This file was generated using ' + '"tools/mb/mb.py gerrit-buildbucket-config".\n' + '[bucket "luci.luci_tryserver1"]\n' + '\tbuilder = luci_builder1\n' + '[bucket "luci.luci_tryserver2"]\n' + '\tbuilder = luci_builder2\n' + '[bucket "master.tryserver.chromium.linux"]\n' + '\tbuilder = try_builder\n' + '[bucket "master.tryserver.chromium.mac"]\n' + '\tbuilder = try_builder2\n')) if __name__ == '__main__': diff --git a/chromium/v8/tools/testrunner/testrunner.isolate b/chromium/v8/tools/testrunner/testrunner.isolate index e29f1df98df..56667c20215 100644 --- a/chromium/v8/tools/testrunner/testrunner.isolate +++ b/chromium/v8/tools/testrunner/testrunner.isolate @@ -7,6 +7,7 @@ '../run-tests.py', ], 'files': [ + '<(PRODUCT_DIR)/v8_build_config.json', '../run-tests.py', './' ], @@ -20,12 +21,5 @@ ], }, }], - ['is_gn==1', { - 'variables': { - 'files': [ - '<(PRODUCT_DIR)/v8_build_config.json', - ], - }, - }], ], } diff --git a/chromium/v8/tools/testrunner/utils/dump_build_config.py b/chromium/v8/tools/testrunner/utils/dump_build_config.py index bd57b5f34e2..b691bb3dc8d 100644 --- a/chromium/v8/tools/testrunner/utils/dump_build_config.py +++ b/chromium/v8/tools/testrunner/utils/dump_build_config.py @@ -15,7 +15,7 @@ import json import os import sys -assert len(sys.argv) > 1 +assert len(sys.argv) > 2 def as_json(kv): assert '=' in kv @@ -23,4 +23,4 @@ def as_json(kv): return k, json.loads(v) with open(sys.argv[1], 'w') as f: - json.dump(dict(as_json(kv) for kv in sys.argv[2:]), f) + json.dump(dict(map(as_json, sys.argv[2:])), f) diff --git a/chromium/v8/tools/testrunner/utils/dump_build_config_gyp.py b/chromium/v8/tools/testrunner/utils/dump_build_config_gyp.py new file mode 100644 index 00000000000..7f726271314 --- /dev/null +++ b/chromium/v8/tools/testrunner/utils/dump_build_config_gyp.py @@ -0,0 +1,54 @@ +# 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. + +"""The same as dump_build_config.py but for gyp legacy. + +Expected to be called like: +dump_build_config.py path/to/file.json [key1=value1 ...] + +Raw gyp values are supported - they will be tranformed into valid json. +""" +# TODO(machenbach): Remove this when gyp is deprecated. + +import json +import os +import sys + +assert len(sys.argv) > 2 + + +GYP_GN_CONVERSION = { + 'is_component_build': { + 'shared_library': 'true', + 'static_library': 'false', + }, + 'is_debug': { + 'Debug': 'true', + 'Release': 'false', + }, +} + +DEFAULT_CONVERSION ={ + '0': 'false', + '1': 'true', + 'ia32': 'x86', +} + +def gyp_to_gn(key, value): + value = GYP_GN_CONVERSION.get(key, DEFAULT_CONVERSION).get(value, value) + value = value if value in ['true', 'false'] else '"{0}"'.format(value) + return value + +def as_json(kv): + assert '=' in kv + k, v = kv.split('=', 1) + v2 = gyp_to_gn(k, v) + try: + return k, json.loads(v2) + except ValueError as e: + print(k, v, v2) + raise e + +with open(sys.argv[1], 'w') as f: + json.dump(dict(map(as_json, sys.argv[2:])), f) diff --git a/chromium/v8/tools/whitespace.txt b/chromium/v8/tools/whitespace.txt index 83c467a908f..37b1d825531 100644 --- a/chromium/v8/tools/whitespace.txt +++ b/chromium/v8/tools/whitespace.txt @@ -8,3 +8,4 @@ The doubles heard this and started to unbox. The Smi looked at them when a crazy v8-autoroll account showed up...... The autoroller bought a round of Himbeerbrause. Suddenly..... The bartender starts to shake the bottles.......... +. |