diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-10-26 13:57:00 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-11-02 11:31:01 +0000 |
commit | 1943b3c2a1dcee36c233724fc4ee7613d71b9cf6 (patch) | |
tree | 8c1b5f12357025c197da5427ae02cfdc2f3570d6 /chromium/v8/src/d8 | |
parent | 21ba0c5d4bf8fba15dddd97cd693bad2358b77fd (diff) | |
download | qtwebengine-chromium-1943b3c2a1dcee36c233724fc4ee7613d71b9cf6.tar.gz |
BASELINE: Update Chromium to 94.0.4606.111
Change-Id: I924781584def20fc800bedf6ff41fdb96c438193
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/v8/src/d8')
-rw-r--r-- | chromium/v8/src/d8/OWNERS | 1 | ||||
-rw-r--r-- | chromium/v8/src/d8/d8-platforms.cc | 16 | ||||
-rw-r--r-- | chromium/v8/src/d8/d8-posix.cc | 4 | ||||
-rw-r--r-- | chromium/v8/src/d8/d8-test.cc | 342 | ||||
-rw-r--r-- | chromium/v8/src/d8/d8.cc | 548 | ||||
-rw-r--r-- | chromium/v8/src/d8/d8.h | 79 |
6 files changed, 799 insertions, 191 deletions
diff --git a/chromium/v8/src/d8/OWNERS b/chromium/v8/src/d8/OWNERS index a96bac9f5da..8d147e1642d 100644 --- a/chromium/v8/src/d8/OWNERS +++ b/chromium/v8/src/d8/OWNERS @@ -1,5 +1,4 @@ cbruni@chromium.org clemensb@chromium.org marja@chromium.org -ulan@chromium.org verwaest@chromium.org diff --git a/chromium/v8/src/d8/d8-platforms.cc b/chromium/v8/src/d8/d8-platforms.cc index 40cba2c69e2..722b2bc4e2e 100644 --- a/chromium/v8/src/d8/d8-platforms.cc +++ b/chromium/v8/src/d8/d8-platforms.cc @@ -2,9 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "src/d8/d8-platforms.h" + #include <memory> #include <unordered_map> +#include "include/libplatform/libplatform.h" #include "include/v8-platform.h" #include "src/base/logging.h" #include "src/base/macros.h" @@ -12,7 +15,6 @@ #include "src/base/platform/platform.h" #include "src/base/platform/time.h" #include "src/base/utils/random-number-generator.h" -#include "src/d8/d8-platforms.h" namespace v8 { @@ -43,7 +45,12 @@ class PredictablePlatform final : public Platform { return platform_->GetForegroundTaskRunner(isolate); } - int NumberOfWorkerThreads() override { return 0; } + int NumberOfWorkerThreads() override { + // The predictable platform executes everything on the main thread, but we + // still pretend to have the default number of worker threads to not + // unnecessarily change behaviour of the platform. + return platform_->NumberOfWorkerThreads(); + } void CallOnWorkerThread(std::unique_ptr<Task> task) override { // We post worker tasks on the foreground task runner of the @@ -68,7 +75,10 @@ class PredictablePlatform final : public Platform { std::unique_ptr<JobHandle> PostJob( TaskPriority priority, std::unique_ptr<JobTask> job_task) override { - return platform_->PostJob(priority, std::move(job_task)); + // Do not call {platform_->PostJob} here, as this would create a job that + // posts tasks directly to the underlying default platform. + return platform::NewDefaultJobHandle(this, priority, std::move(job_task), + NumberOfWorkerThreads()); } double MonotonicallyIncreasingTime() override { diff --git a/chromium/v8/src/d8/d8-posix.cc b/chromium/v8/src/d8/d8-posix.cc index fa30b9153d4..05e475f5387 100644 --- a/chromium/v8/src/d8/d8-posix.cc +++ b/chromium/v8/src/d8/d8-posix.cc @@ -318,7 +318,7 @@ static Local<Value> GetStdout(Isolate* isolate, int child_fd, .ToLocalChecked(); accumulator = String::Concat(isolate, accumulator, addition); fullness = bytes_read + fullness - length; - base::Memcpy(buffer, buffer + length, fullness); + memcpy(buffer, buffer + length, fullness); } } while (bytes_read != 0); return accumulator; @@ -343,7 +343,7 @@ static Local<Value> GetStdout(Isolate* isolate, int child_fd, // Get exit status of child. static bool WaitForChild(Isolate* isolate, int pid, - ZombieProtector& child_waiter, // NOLINT + ZombieProtector& child_waiter, const struct timeval& start_time, int read_timeout, int total_timeout) { #ifdef HAS_WAITID diff --git a/chromium/v8/src/d8/d8-test.cc b/chromium/v8/src/d8/d8-test.cc index 741b838b760..635a1f45141 100644 --- a/chromium/v8/src/d8/d8-test.cc +++ b/chromium/v8/src/d8/d8-test.cc @@ -5,6 +5,7 @@ #include "src/d8/d8.h" #include "include/v8-fast-api-calls.h" +#include "src/api/api-inl.h" // This file exposes a d8.test.fast_c_api object, which adds testing facility // for writing mjsunit tests that exercise fast API calls. The fast_c_api object @@ -55,14 +56,6 @@ class FastCApiObject { static_cast<double>(arg_i64) + static_cast<double>(arg_u64) + static_cast<double>(arg_f32) + arg_f64; } - static double AddAllFastCallback_5Args(Local<Object> receiver, - bool should_fallback, int32_t arg_i32, - uint32_t arg_u32, int64_t arg_i64, - uint64_t arg_u64, float arg_f32, - FastApiCallbackOptions& options) { - return AddAllFastCallback(receiver, should_fallback, arg_i32, arg_u32, - arg_i64, arg_u64, arg_f32, 0, options); - } static void AddAllSlowCallback(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = args.GetIsolate(); @@ -99,6 +92,190 @@ class FastCApiObject { args.GetReturnValue().Set(Number::New(isolate, sum)); } +#ifdef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE + typedef double Type; + static constexpr CTypeInfo type_info = CTypeInfo(CTypeInfo::Type::kFloat64); +#else + typedef int32_t Type; + static constexpr CTypeInfo type_info = CTypeInfo(CTypeInfo::Type::kInt32); +#endif // V8_ENABLE_FP_PARAMS_IN_C_LINKAGE + static Type AddAllSequenceFastCallback(Local<Object> receiver, + bool should_fallback, + Local<Array> seq_arg, + FastApiCallbackOptions& options) { + FastCApiObject* self = UnwrapObject(receiver); + CHECK_SELF_OR_FALLBACK(0); + self->fast_call_count_++; + + if (should_fallback) { + options.fallback = 1; + return 0; + } + + uint32_t length = seq_arg->Length(); + if (length > 1024) { + options.fallback = 1; + return 0; + } + + Type buffer[1024]; + bool result = TryCopyAndConvertArrayToCppBuffer<&type_info, Type>( + seq_arg, buffer, 1024); + if (!result) { + options.fallback = 1; + return 0; + } + DCHECK_EQ(seq_arg->Length(), length); + + Type sum = 0; + for (uint32_t i = 0; i < length; ++i) { + sum += buffer[i]; + } + + return sum; + } + static void AddAllSequenceSlowCallback( + const FunctionCallbackInfo<Value>& args) { + Isolate* isolate = args.GetIsolate(); + + FastCApiObject* self = UnwrapObject(args.This()); + CHECK_SELF_OR_THROW(); + + HandleScope handle_scope(isolate); + + if (args.Length() < 2) { + self->slow_call_count_++; + isolate->ThrowError("This method expects at least 2 arguments."); + return; + } + if (args[1]->IsTypedArray()) { + AddAllTypedArraySlowCallback(args); + return; + } + self->slow_call_count_++; + if (args[1]->IsUndefined()) { + Type dummy_result = 0; + args.GetReturnValue().Set(Number::New(isolate, dummy_result)); + return; + } + if (!args[1]->IsArray()) { + isolate->ThrowError("This method expects an array as a second argument."); + return; + } + + Local<Array> seq_arg = args[1].As<Array>(); + uint32_t length = seq_arg->Length(); + if (length > 1024) { + isolate->ThrowError( + "Invalid length of array, must be between 0 and 1024."); + return; + } + + Type sum = 0; + for (uint32_t i = 0; i < length; ++i) { + v8::Local<v8::Value> element = + seq_arg + ->Get(isolate->GetCurrentContext(), + v8::Integer::NewFromUnsigned(isolate, i)) + .ToLocalChecked(); + if (element->IsNumber()) { + double value = element->ToNumber(isolate->GetCurrentContext()) + .ToLocalChecked() + ->Value(); + sum += value; + } else if (element->IsUndefined()) { + // Hole: ignore the element. + } else { + isolate->ThrowError("unexpected element type in JSArray"); + return; + } + } + args.GetReturnValue().Set(Number::New(isolate, sum)); + } + template <typename T> + static Type AddAllTypedArrayFastCallback( + Local<Object> receiver, bool should_fallback, + const FastApiTypedArray<T>& typed_array_arg, + FastApiCallbackOptions& options) { + FastCApiObject* self = UnwrapObject(receiver); + CHECK_SELF_OR_FALLBACK(0); + self->fast_call_count_++; + + if (should_fallback) { + options.fallback = 1; + return 0; + } + + T sum = 0; + for (unsigned i = 0; i < typed_array_arg.length(); ++i) { + sum += typed_array_arg.get(i); + } + return static_cast<Type>(sum); + } + static void AddAllTypedArraySlowCallback( + const FunctionCallbackInfo<Value>& args) { + Isolate* isolate = args.GetIsolate(); + + FastCApiObject* self = UnwrapObject(args.This()); + CHECK_SELF_OR_THROW(); + self->slow_call_count_++; + + HandleScope handle_scope(isolate); + + if (args.Length() < 2) { + isolate->ThrowError("This method expects at least 2 arguments."); + return; + } + if (!args[1]->IsTypedArray()) { + isolate->ThrowError( + "This method expects a TypedArray as a second argument."); + return; + } + + Local<TypedArray> typed_array_arg = args[1].As<TypedArray>(); + size_t length = typed_array_arg->Length(); + + void* data = typed_array_arg->Buffer()->GetBackingStore()->Data(); + if (typed_array_arg->IsInt32Array() || typed_array_arg->IsUint32Array() || + typed_array_arg->IsBigInt64Array() || + typed_array_arg->IsBigUint64Array()) { + int64_t sum = 0; + for (unsigned i = 0; i < length; ++i) { + if (typed_array_arg->IsInt32Array()) { + sum += static_cast<int32_t*>(data)[i]; + } else if (typed_array_arg->IsUint32Array()) { + sum += static_cast<uint32_t*>(data)[i]; + } else if (typed_array_arg->IsBigInt64Array()) { + sum += static_cast<int64_t*>(data)[i]; + } else if (typed_array_arg->IsBigUint64Array()) { + sum += static_cast<uint64_t*>(data)[i]; + } + } + args.GetReturnValue().Set(Number::New(isolate, sum)); + } else if (typed_array_arg->IsFloat32Array() || + typed_array_arg->IsFloat64Array()) { + double sum = 0; + for (unsigned i = 0; i < length; ++i) { + if (typed_array_arg->IsFloat32Array()) { + sum += static_cast<float*>(data)[i]; + } else if (typed_array_arg->IsFloat64Array()) { + sum += static_cast<double*>(data)[i]; + } + } + args.GetReturnValue().Set(Number::New(isolate, sum)); + } else { + isolate->ThrowError("TypedArray type is not supported."); + return; + } + } + + static int32_t AddAllIntInvalidCallback(Local<Object> receiver, + bool should_fallback, int32_t arg_i32, + FastApiCallbackOptions& options) { + // This should never be called + UNREACHABLE(); + } + static int Add32BitIntFastCallback(v8::Local<v8::Object> receiver, bool should_fallback, int32_t arg_i32, uint32_t arg_u32, @@ -134,6 +311,66 @@ class FastCApiObject { args.GetReturnValue().Set(Number::New(isolate, sum)); } + static int AddAll32BitIntFastCallback_6Args( + Local<Object> receiver, bool should_fallback, int32_t arg1_i32, + int32_t arg2_i32, int32_t arg3_i32, uint32_t arg4_u32, uint32_t arg5_u32, + uint32_t arg6_u32, FastApiCallbackOptions& options) { + FastCApiObject* self = UnwrapObject(receiver); + CHECK_SELF_OR_FALLBACK(0); + self->fast_call_count_++; + + if (should_fallback) { + options.fallback = 1; + return 0; + } + + int64_t result = static_cast<int64_t>(arg1_i32) + arg2_i32 + arg3_i32 + + arg4_u32 + arg5_u32 + arg6_u32; + if (result > INT_MAX) return INT_MAX; + if (result < INT_MIN) return INT_MIN; + return static_cast<int>(result); + } + static int AddAll32BitIntFastCallback_5Args( + Local<Object> receiver, bool should_fallback, int32_t arg1_i32, + int32_t arg2_i32, int32_t arg3_i32, uint32_t arg4_u32, uint32_t arg5_u32, + FastApiCallbackOptions& options) { + return AddAll32BitIntFastCallback_6Args(receiver, should_fallback, arg1_i32, + arg2_i32, arg3_i32, arg4_u32, + arg5_u32, 0, options); + } + static void AddAll32BitIntSlowCallback( + const FunctionCallbackInfo<Value>& args) { + Isolate* isolate = args.GetIsolate(); + + FastCApiObject* self = UnwrapObject(args.This()); + CHECK_SELF_OR_THROW(); + self->slow_call_count_++; + + HandleScope handle_scope(isolate); + + double sum = 0; + if (args.Length() > 1 && args[1]->IsNumber()) { + sum += args[1]->Int32Value(isolate->GetCurrentContext()).FromJust(); + } + if (args.Length() > 2 && args[2]->IsNumber()) { + sum += args[2]->Int32Value(isolate->GetCurrentContext()).FromJust(); + } + if (args.Length() > 3 && args[3]->IsNumber()) { + sum += args[3]->Int32Value(isolate->GetCurrentContext()).FromJust(); + } + if (args.Length() > 4 && args[4]->IsNumber()) { + sum += args[4]->Uint32Value(isolate->GetCurrentContext()).FromJust(); + } + if (args.Length() > 5 && args[5]->IsNumber()) { + sum += args[5]->Uint32Value(isolate->GetCurrentContext()).FromJust(); + } + if (args.Length() > 6 && args[6]->IsNumber()) { + sum += args[6]->Uint32Value(isolate->GetCurrentContext()).FromJust(); + } + + args.GetReturnValue().Set(Number::New(isolate, sum)); + } + static bool IsFastCApiObjectFastCallback(v8::Local<v8::Object> receiver, bool should_fallback, v8::Local<v8::Value> arg, @@ -230,7 +467,8 @@ class FastCApiObject { static bool IsValidApiObject(Local<Object> object) { i::Address addr = *reinterpret_cast<i::Address*>(*object); auto instance_type = i::Internals::GetInstanceType(addr); - return (instance_type == i::Internals::kJSApiObjectType || + return (base::IsInRange(instance_type, i::Internals::kFirstJSApiObjectType, + i::Internals::kLastJSApiObjectType) || instance_type == i::Internals::kJSSpecialApiObjectType); } static FastCApiObject* UnwrapObject(Local<Object> object) { @@ -290,15 +528,87 @@ Local<FunctionTemplate> Shell::CreateTestFastCApiTemplate(Isolate* isolate) { ConstructorBehavior::kThrow, SideEffectType::kHasSideEffect, &add_all_c_func)); - // To test function overloads. - CFunction add_all_5args_c_func = - CFunction::Make(FastCApiObject::AddAllFastCallback_5Args); - const CFunction c_function_overloads[] = {add_all_c_func, - add_all_5args_c_func}; + CFunction add_all_seq_c_func = + CFunction::Make(FastCApiObject::AddAllSequenceFastCallback); + api_obj_ctor->PrototypeTemplate()->Set( + isolate, "add_all_sequence", + FunctionTemplate::New( + isolate, FastCApiObject::AddAllSequenceSlowCallback, Local<Value>(), + signature, 1, ConstructorBehavior::kThrow, + SideEffectType::kHasSideEffect, &add_all_seq_c_func)); + + CFunction add_all_int32_typed_array_c_func = + CFunction::Make(FastCApiObject::AddAllTypedArrayFastCallback<int32_t>); + api_obj_ctor->PrototypeTemplate()->Set( + isolate, "add_all_int32_typed_array", + FunctionTemplate::New( + isolate, FastCApiObject::AddAllTypedArraySlowCallback, + Local<Value>(), signature, 1, ConstructorBehavior::kThrow, + SideEffectType::kHasSideEffect, &add_all_int32_typed_array_c_func)); + + CFunction add_all_int64_typed_array_c_func = + CFunction::Make(FastCApiObject::AddAllTypedArrayFastCallback<int64_t>); + api_obj_ctor->PrototypeTemplate()->Set( + isolate, "add_all_int64_typed_array", + FunctionTemplate::New( + isolate, FastCApiObject::AddAllTypedArraySlowCallback, + Local<Value>(), signature, 1, ConstructorBehavior::kThrow, + SideEffectType::kHasSideEffect, &add_all_int64_typed_array_c_func)); + + CFunction add_all_uint64_typed_array_c_func = + CFunction::Make(FastCApiObject::AddAllTypedArrayFastCallback<uint64_t>); + api_obj_ctor->PrototypeTemplate()->Set( + isolate, "add_all_uint64_typed_array", + FunctionTemplate::New( + isolate, FastCApiObject::AddAllTypedArraySlowCallback, + Local<Value>(), signature, 1, ConstructorBehavior::kThrow, + SideEffectType::kHasSideEffect, + &add_all_uint64_typed_array_c_func)); + + CFunction add_all_uint32_typed_array_c_func = + CFunction::Make(FastCApiObject::AddAllTypedArrayFastCallback<uint32_t>); + api_obj_ctor->PrototypeTemplate()->Set( + isolate, "add_all_uint32_typed_array", + FunctionTemplate::New( + isolate, FastCApiObject::AddAllTypedArraySlowCallback, + Local<Value>(), signature, 1, ConstructorBehavior::kThrow, + SideEffectType::kHasSideEffect, + &add_all_uint32_typed_array_c_func)); + + const CFunction add_all_overloads[] = { + add_all_uint32_typed_array_c_func, + add_all_seq_c_func, + }; + api_obj_ctor->PrototypeTemplate()->Set( + isolate, "add_all_overload", + FunctionTemplate::NewWithCFunctionOverloads( + isolate, FastCApiObject::AddAllSequenceSlowCallback, Local<Value>(), + signature, 1, ConstructorBehavior::kThrow, + SideEffectType::kHasSideEffect, {add_all_overloads, 2})); + + CFunction add_all_int_invalid_func = + CFunction::Make(FastCApiObject::AddAllIntInvalidCallback); + const CFunction add_all_invalid_overloads[] = { + add_all_int_invalid_func, + add_all_seq_c_func, + }; + api_obj_ctor->PrototypeTemplate()->Set( + isolate, "add_all_invalid_overload", + FunctionTemplate::NewWithCFunctionOverloads( + isolate, FastCApiObject::AddAllSequenceSlowCallback, Local<Value>(), + signature, 1, ConstructorBehavior::kThrow, + SideEffectType::kHasSideEffect, {add_all_invalid_overloads, 2})); + + CFunction add_all_32bit_int_6args_c_func = + CFunction::Make(FastCApiObject::AddAll32BitIntFastCallback_6Args); + CFunction add_all_32bit_int_5args_c_func = + CFunction::Make(FastCApiObject::AddAll32BitIntFastCallback_5Args); + const CFunction c_function_overloads[] = {add_all_32bit_int_6args_c_func, + add_all_32bit_int_5args_c_func}; api_obj_ctor->PrototypeTemplate()->Set( - isolate, "overloaded_add_all", + isolate, "overloaded_add_all_32bit_int", FunctionTemplate::NewWithCFunctionOverloads( - isolate, FastCApiObject::AddAllSlowCallback, Local<Value>(), + isolate, FastCApiObject::AddAll32BitIntSlowCallback, Local<Value>(), signature, 1, ConstructorBehavior::kThrow, SideEffectType::kHasSideEffect, {c_function_overloads, 2})); diff --git a/chromium/v8/src/d8/d8.cc b/chromium/v8/src/d8/d8.cc index 8c9a4c3ae60..2b831bc7473 100644 --- a/chromium/v8/src/d8/d8.cc +++ b/chromium/v8/src/d8/d8.cc @@ -34,6 +34,7 @@ #include "src/base/platform/wrappers.h" #include "src/base/sanitizer/msan.h" #include "src/base/sys-info.h" +#include "src/base/utils/random-number-generator.h" #include "src/d8/d8-console.h" #include "src/d8/d8-platforms.h" #include "src/d8/d8.h" @@ -78,9 +79,9 @@ #endif #if !defined(_WIN32) && !defined(_WIN64) -#include <unistd.h> // NOLINT +#include <unistd.h> #else -#include <windows.h> // NOLINT +#include <windows.h> #endif // !defined(_WIN32) && !defined(_WIN64) #ifndef DCHECK @@ -485,7 +486,7 @@ ScriptCompiler::CachedData* Shell::LookupCodeCache(Isolate* isolate, if (entry != cached_code_map_.end() && entry->second) { int length = entry->second->length; uint8_t* cache = new uint8_t[length]; - base::Memcpy(cache, entry->second->data, length); + memcpy(cache, entry->second->data, length); ScriptCompiler::CachedData* cached_data = new ScriptCompiler::CachedData( cache, length, ScriptCompiler::CachedData::BufferOwned); return cached_data; @@ -502,7 +503,7 @@ void Shell::StoreInCodeCache(Isolate* isolate, Local<Value> source, DCHECK(*key); int length = cache_data->length; uint8_t* cache = new uint8_t[length]; - base::Memcpy(cache, cache_data->data, length); + memcpy(cache, cache_data->data, length); cached_code_map_[*key] = std::unique_ptr<ScriptCompiler::CachedData>( new ScriptCompiler::CachedData(cache, length, ScriptCompiler::CachedData::BufferOwned)); @@ -643,8 +644,8 @@ bool Shell::ExecuteString(Isolate* isolate, Local<String> source, Local<Value> name, PrintResult print_result, ReportExceptions report_exceptions, ProcessMessageQueue process_message_queue) { + i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); if (i::FLAG_parse_only) { - i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::VMState<PARSER> state(i_isolate); i::Handle<i::String> str = Utils::OpenHandle(*(source)); @@ -654,7 +655,7 @@ bool Shell::ExecuteString(Isolate* isolate, Local<String> source, i::UnoptimizedCompileFlags flags = i::UnoptimizedCompileFlags::ForToplevelCompile( i_isolate, true, i::construct_language_mode(i::FLAG_use_strict), - i::REPLMode::kNo); + i::REPLMode::kNo, ScriptType::kClassic, i::FLAG_lazy); if (options.compile_options == v8::ScriptCompiler::kEagerCompile) { flags.set_is_eager(true); @@ -680,6 +681,15 @@ bool Shell::ExecuteString(Isolate* isolate, Local<String> source, TryCatch try_catch(isolate); try_catch.SetVerbose(report_exceptions == kReportExceptions); + // Explicitly check for stack overflows. This method can be called + // recursively, and since we consume quite some stack space for the C++ + // frames, the stack check in the called frame might be too late. + if (i::StackLimitCheck{i_isolate}.HasOverflowed()) { + i_isolate->StackOverflow(); + i_isolate->OptionalRescheduleException(false); + return false; + } + MaybeLocal<Value> maybe_result; bool success = true; { @@ -730,8 +740,10 @@ bool Shell::ExecuteString(Isolate* isolate, Local<String> source, data->realm_current_ = data->realm_switch_; if (options.web_snapshot_config) { - std::vector<std::string> exports; - if (!ReadLines(options.web_snapshot_config, exports)) { + MaybeLocal<PrimitiveArray> maybe_exports = + ReadLines(isolate, options.web_snapshot_config); + Local<PrimitiveArray> exports; + if (!maybe_exports.ToLocal(&exports)) { isolate->ThrowError("Web snapshots: unable to read config"); CHECK(try_catch.HasCaught()); ReportException(isolate, &try_catch); @@ -950,10 +962,10 @@ MaybeLocal<Module> Shell::FetchModuleTree(Local<Module> referrer, ModuleType module_type) { DCHECK(IsAbsolutePath(file_name)); Isolate* isolate = context->GetIsolate(); - Local<String> source_text = ReadFile(isolate, file_name.c_str()); + Local<String> source_text = ReadFile(isolate, file_name.c_str(), false); if (source_text.IsEmpty() && options.fuzzy_module_file_extensions) { std::string fallback_file_name = file_name + ".js"; - source_text = ReadFile(isolate, fallback_file_name.c_str()); + source_text = ReadFile(isolate, fallback_file_name.c_str(), false); if (source_text.IsEmpty()) { fallback_file_name = file_name + ".mjs"; source_text = ReadFile(isolate, fallback_file_name.c_str()); @@ -1386,6 +1398,10 @@ PerIsolateData::PerIsolateData(Isolate* isolate) async_hooks_wrapper_ = new AsyncHooks(isolate); } ignore_unhandled_promises_ = false; + // TODO(v8:11525): Use methods on global Snapshot objects with + // signature checks. + HandleScope scope(isolate); + Shell::CreateSnapshotTemplate(isolate); } PerIsolateData::~PerIsolateData() { @@ -1481,6 +1497,14 @@ void PerIsolateData::SetTestApiObjectCtor(Local<FunctionTemplate> ctor) { test_api_object_ctor_.Reset(isolate_, ctor); } +Local<FunctionTemplate> PerIsolateData::GetSnapshotObjectCtor() const { + return snapshot_object_ctor_.Get(isolate_); +} + +void PerIsolateData::SetSnapshotObjectCtor(Local<FunctionTemplate> ctor) { + snapshot_object_ctor_.Reset(isolate_, ctor); +} + PerIsolateData::RealmScope::RealmScope(PerIsolateData* data) : data_(data) { data_->realm_count_ = 1; data_->realm_current_ = 0; @@ -1503,6 +1527,24 @@ PerIsolateData::RealmScope::~RealmScope() { delete[] data_->realms_; } +PerIsolateData::ExplicitRealmScope::ExplicitRealmScope(PerIsolateData* data, + int index) + : data_(data), index_(index) { + realm_ = Local<Context>::New(data->isolate_, data->realms_[index_]); + realm_->Enter(); + previous_index_ = data->realm_current_; + data->realm_current_ = data->realm_switch_ = index_; +} + +PerIsolateData::ExplicitRealmScope::~ExplicitRealmScope() { + realm_->Exit(); + data_->realm_current_ = data_->realm_switch_ = previous_index_; +} + +Local<Context> PerIsolateData::ExplicitRealmScope::context() const { + return realm_; +} + int PerIsolateData::RealmFind(Local<Context> context) { for (int i = 0; i < realm_count_; ++i) { if (realms_[i] == context) return i; @@ -1550,8 +1592,11 @@ void Shell::PerformanceMeasureMemory( Local<Object> object = args[0].As<Object>(); Local<Value> value = TryGetValue(isolate, context, object, "detailed") .FromMaybe(Local<Value>()); - if (!value.IsEmpty() && value->IsBoolean() && - value->BooleanValue(isolate)) { + if (value.IsEmpty()) { + // Exception was thrown and scheduled, so return from the callback. + return; + } + if (value->IsBoolean() && value->BooleanValue(isolate)) { mode = v8::MeasureMemoryMode::kDetailed; } } @@ -1604,8 +1649,22 @@ void Shell::RealmGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) { PerIsolateData* data = PerIsolateData::Get(args.GetIsolate()); int index = data->RealmIndexOrThrow(args, 0); if (index == -1) return; - args.GetReturnValue().Set( - Local<Context>::New(args.GetIsolate(), data->realms_[index])->Global()); + // TODO(chromium:324812): Ideally Context::Global should never return raw + // global objects but return a global proxy. Currently it returns global + // object when the global proxy is detached from the global object. The + // following is a workaround till we fix Context::Global so we don't leak + // global objects. + Local<Object> global = + Local<Context>::New(args.GetIsolate(), data->realms_[index])->Global(); + i::Handle<i::Object> i_global = Utils::OpenHandle(*global); + if (i_global->IsJSGlobalObject()) { + i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); + i::Handle<i::JSObject> i_global_proxy = + handle(i::Handle<i::JSGlobalObject>::cast(i_global)->global_proxy(), + i_isolate); + global = Utils::ToLocal(i_global_proxy); + } + args.GetReturnValue().Set(global); } MaybeLocal<Context> Shell::CreateRealm( @@ -1741,32 +1800,34 @@ void Shell::RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args) { PerIsolateData* data = PerIsolateData::Get(isolate); int index = data->RealmIndexOrThrow(args, 0); if (index == -1) return; - if (args.Length() < 2 || !args[1]->IsString()) { - args.GetIsolate()->ThrowError("Invalid argument"); + if (args.Length() < 2) { + isolate->ThrowError("Invalid argument"); + return; + } + + Local<String> source; + if (!ReadSource(args, 1, CodeType::kString).ToLocal(&source)) { + isolate->ThrowError("Invalid argument"); return; } ScriptOrigin origin(isolate, String::NewFromUtf8Literal(isolate, "(d8)", NewStringType::kInternalized)); - ScriptCompiler::Source script_source( - args[1]->ToString(isolate->GetCurrentContext()).ToLocalChecked(), origin); + ScriptCompiler::Source script_source(source, origin); Local<UnboundScript> script; if (!ScriptCompiler::CompileUnboundScript(isolate, &script_source) .ToLocal(&script)) { return; } - Local<Context> realm = Local<Context>::New(isolate, data->realms_[index]); - realm->Enter(); - int previous_index = data->realm_current_; - data->realm_current_ = data->realm_switch_ = index; Local<Value> result; - if (!script->BindToCurrentContext()->Run(realm).ToLocal(&result)) { - realm->Exit(); - data->realm_current_ = data->realm_switch_ = previous_index; - return; + { + PerIsolateData::ExplicitRealmScope realm_scope(data, index); + if (!script->BindToCurrentContext() + ->Run(realm_scope.context()) + .ToLocal(&result)) { + return; + } } - realm->Exit(); - data->realm_current_ = data->realm_switch_ = previous_index; args.GetReturnValue().Set(result); } @@ -1786,6 +1847,96 @@ void Shell::RealmSharedSet(Local<String> property, Local<Value> value, data->realm_shared_.Reset(isolate, value); } +// Realm.takeWebSnapshot(index, exports) takes a snapshot of the list of exports +// in the realm with the specified index and returns the result. +void Shell::RealmTakeWebSnapshot( + const v8::FunctionCallbackInfo<v8::Value>& args) { + Isolate* isolate = args.GetIsolate(); + if (args.Length() < 2 || !args[1]->IsArray()) { + isolate->ThrowError("Invalid argument"); + return; + } + PerIsolateData* data = PerIsolateData::Get(isolate); + int index = data->RealmIndexOrThrow(args, 0); + if (index == -1) return; + // Create a Local<PrimitiveArray> from the exports array. + Local<Context> current_context = isolate->GetCurrentContext(); + Local<Array> exports_array = args[1].As<Array>(); + int length = exports_array->Length(); + Local<PrimitiveArray> exports = PrimitiveArray::New(isolate, length); + for (int i = 0; i < length; ++i) { + Local<Value> value; + Local<String> str; + if (!exports_array->Get(current_context, i).ToLocal(&value) || + !value->ToString(current_context).ToLocal(&str) || str.IsEmpty()) { + isolate->ThrowError("Invalid argument"); + return; + } + exports->Set(isolate, i, str); + } + // Take the snapshot in the specified Realm. + auto snapshot_data_shared = std::make_shared<i::WebSnapshotData>(); + { + PerIsolateData::ExplicitRealmScope realm_scope(data, index); + i::WebSnapshotSerializer serializer(isolate); + if (!serializer.TakeSnapshot(realm_scope.context(), exports, + *snapshot_data_shared)) { + args.GetReturnValue().Set(Undefined(isolate)); + return; + } + } + // Create a snapshot object and store the WebSnapshotData as an embedder + // field. TODO(v8:11525): Use methods on global Snapshot objects with + // signature checks. + i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); + i::Handle<i::Object> snapshot_data_managed = + i::Managed<i::WebSnapshotData>::FromSharedPtr( + i_isolate, snapshot_data_shared->buffer_size, snapshot_data_shared); + v8::Local<v8::Value> shapshot_data = Utils::ToLocal(snapshot_data_managed); + Local<ObjectTemplate> snapshot_template = + data->GetSnapshotObjectCtor()->InstanceTemplate(); + Local<Object> snapshot_instance = + snapshot_template->NewInstance(isolate->GetCurrentContext()) + .ToLocalChecked(); + snapshot_instance->SetInternalField(0, shapshot_data); + args.GetReturnValue().Set(snapshot_instance); +} + +// Realm.useWebSnapshot(index, snapshot) deserializes the snapshot in the realm +// with the specified index. +void Shell::RealmUseWebSnapshot( + const v8::FunctionCallbackInfo<v8::Value>& args) { + Isolate* isolate = args.GetIsolate(); + if (args.Length() < 2 || !args[1]->IsObject()) { + isolate->ThrowError("Invalid argument"); + return; + } + PerIsolateData* data = PerIsolateData::Get(isolate); + int index = data->RealmIndexOrThrow(args, 0); + if (index == -1) return; + // Restore the snapshot data from the snapshot object. + Local<Object> snapshot_instance = args[1].As<Object>(); + Local<FunctionTemplate> snapshot_template = data->GetSnapshotObjectCtor(); + if (!snapshot_template->HasInstance(snapshot_instance)) { + isolate->ThrowError("Invalid argument"); + return; + } + v8::Local<v8::Value> snapshot_data = snapshot_instance->GetInternalField(0); + i::Handle<i::Object> snapshot_data_handle = Utils::OpenHandle(*snapshot_data); + auto snapshot_data_managed = + i::Handle<i::Managed<i::WebSnapshotData>>::cast(snapshot_data_handle); + std::shared_ptr<i::WebSnapshotData> snapshot_data_shared = + snapshot_data_managed->get(); + // Deserialize the snapshot in the specified Realm. + { + PerIsolateData::ExplicitRealmScope realm_scope(data, index); + i::WebSnapshotDeserializer deserializer(isolate); + bool success = deserializer.UseWebSnapshot( + snapshot_data_shared->buffer, snapshot_data_shared->buffer_size); + args.GetReturnValue().Set(success); + } +} + void Shell::LogGetAndStop(const v8::FunctionCallbackInfo<v8::Value>& args) { Isolate* isolate = args.GetIsolate(); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); @@ -1943,11 +2094,14 @@ void Shell::AsyncHooksTriggerAsyncId( void Shell::SetPromiseHooks(const v8::FunctionCallbackInfo<v8::Value>& args) { Isolate* isolate = args.GetIsolate(); if (i::FLAG_correctness_fuzzer_suppressions) { - // Make sure we have no pending promises if correctness fuzzing is active. - // Due to fast-paths we might have not created all intermediate promises - // that aren't spec visible. However, the promise hook might expose them - // and cause different output. - isolate->PerformMicrotaskCheckpoint(); + // Setting promise hoooks dynamically has unexpected timing side-effects + // with certain promise optimizations. We might not get all callbacks for + // previously scheduled Promises or optimized code-paths that skip Promise + // creation. + isolate->ThrowError( + "d8.promise.setHooks is disabled with " + "--correctness-fuzzer-suppressions"); + return; } Local<Context> context = isolate->GetCurrentContext(); HandleScope handle_scope(isolate); @@ -2006,14 +2160,14 @@ void Shell::PrintErr(const v8::FunctionCallbackInfo<v8::Value>& args) { WriteAndFlush(stderr, args); } -void Shell::Write(const v8::FunctionCallbackInfo<v8::Value>& args) { +void Shell::WriteStdout(const v8::FunctionCallbackInfo<v8::Value>& args) { WriteToFile(stdout, args); } -void Shell::Read(const v8::FunctionCallbackInfo<v8::Value>& args) { - String::Utf8Value file(args.GetIsolate(), args[0]); - if (*file == nullptr) { - args.GetIsolate()->ThrowError("Error loading file"); +void Shell::ReadFile(const v8::FunctionCallbackInfo<v8::Value>& args) { + String::Utf8Value file_name(args.GetIsolate(), args[0]); + if (*file_name == nullptr) { + args.GetIsolate()->ThrowError("Error converting filename to string"); return; } if (args.Length() == 2) { @@ -2023,11 +2177,8 @@ void Shell::Read(const v8::FunctionCallbackInfo<v8::Value>& args) { return; } } - Local<String> source = ReadFile(args.GetIsolate(), *file); - if (source.IsEmpty()) { - args.GetIsolate()->ThrowError("Error loading file"); - return; - } + Local<String> source = ReadFile(args.GetIsolate(), *file_name); + if (source.IsEmpty()) return; args.GetReturnValue().Set(source); } @@ -2068,26 +2219,30 @@ Local<String> Shell::ReadFromStdin(Isolate* isolate) { } } -void Shell::Load(const v8::FunctionCallbackInfo<v8::Value>& args) { +void Shell::ExecuteFile(const v8::FunctionCallbackInfo<v8::Value>& args) { + Isolate* isolate = args.GetIsolate(); for (int i = 0; i < args.Length(); i++) { - HandleScope handle_scope(args.GetIsolate()); - String::Utf8Value file(args.GetIsolate(), args[i]); - if (*file == nullptr) { - args.GetIsolate()->ThrowError("Error loading file"); - return; - } - Local<String> source = ReadFile(args.GetIsolate(), *file); - if (source.IsEmpty()) { - args.GetIsolate()->ThrowError("Error loading file"); + HandleScope handle_scope(isolate); + String::Utf8Value file_name(isolate, args[i]); + if (*file_name == nullptr) { + std::ostringstream oss; + oss << "Cannot convert file[" << i << "] name to string."; + isolate->ThrowError( + String::NewFromUtf8(isolate, oss.str().c_str()).ToLocalChecked()); return; } + Local<String> source = ReadFile(isolate, *file_name); + if (source.IsEmpty()) return; if (!ExecuteString( args.GetIsolate(), source, - String::NewFromUtf8(args.GetIsolate(), *file).ToLocalChecked(), + String::NewFromUtf8(isolate, *file_name).ToLocalChecked(), kNoPrintResult, options.quiet_load ? kNoReportExceptions : kReportExceptions, kNoProcessMessageQueue)) { - args.GetIsolate()->ThrowError("Error executing file"); + std::ostringstream oss; + oss << "Error executing file: \"" << *file_name << '"'; + isolate->ThrowError( + String::NewFromUtf8(isolate, oss.str().c_str()).ToLocalChecked()); return; } } @@ -2102,35 +2257,33 @@ void Shell::SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args) { PerIsolateData::Get(isolate)->SetTimeout(callback, context); } -enum WorkerType { kClassic, kString, kFunction, kInvalid, kNone }; - -void ReadWorkerTypeAndArguments(const v8::FunctionCallbackInfo<v8::Value>& args, - WorkerType* worker_type, - Local<Value>* arguments = nullptr) { +void Shell::ReadCodeTypeAndArguments( + const v8::FunctionCallbackInfo<v8::Value>& args, int index, + CodeType* code_type, Local<Value>* arguments) { Isolate* isolate = args.GetIsolate(); - if (args.Length() > 1 && args[1]->IsObject()) { - Local<Object> object = args[1].As<Object>(); + if (args.Length() > index && args[index]->IsObject()) { + Local<Object> object = args[index].As<Object>(); Local<Context> context = isolate->GetCurrentContext(); Local<Value> value; if (!TryGetValue(isolate, context, object, "type").ToLocal(&value)) { - *worker_type = WorkerType::kNone; + *code_type = CodeType::kNone; return; } if (!value->IsString()) { - *worker_type = WorkerType::kInvalid; + *code_type = CodeType::kInvalid; return; } Local<String> worker_type_string = value->ToString(context).ToLocalChecked(); String::Utf8Value str(isolate, worker_type_string); - if (strcmp("string", *str) == 0) { - *worker_type = WorkerType::kString; - } else if (strcmp("classic", *str) == 0) { - *worker_type = WorkerType::kClassic; + if (strcmp("classic", *str) == 0) { + *code_type = CodeType::kFileName; + } else if (strcmp("string", *str) == 0) { + *code_type = CodeType::kString; } else if (strcmp("function", *str) == 0) { - *worker_type = WorkerType::kFunction; + *code_type = CodeType::kFunction; } else { - *worker_type = WorkerType::kInvalid; + *code_type = CodeType::kInvalid; } if (arguments != nullptr) { bool got_arguments = @@ -2138,13 +2291,14 @@ void ReadWorkerTypeAndArguments(const v8::FunctionCallbackInfo<v8::Value>& args, USE(got_arguments); } } else { - *worker_type = WorkerType::kNone; + *code_type = CodeType::kNone; } } -bool FunctionAndArgumentsToString(Local<Function> function, - Local<Value> arguments, Local<String>* source, - Isolate* isolate) { +bool Shell::FunctionAndArgumentsToString(Local<Function> function, + Local<Value> arguments, + Local<String>* source, + Isolate* isolate) { Local<Context> context = isolate->GetCurrentContext(); MaybeLocal<String> maybe_function_string = function->FunctionProtoToString(context); @@ -2187,6 +2341,54 @@ bool FunctionAndArgumentsToString(Local<Function> function, return true; } +// ReadSource() supports reading source code through `args[index]` as specified +// by the `default_type` or an optional options bag provided in `args[index+1]` +// (e.g. `options={type: 'code_type', arguments:[...]}`). +MaybeLocal<String> Shell::ReadSource( + const v8::FunctionCallbackInfo<v8::Value>& args, int index, + CodeType default_type) { + CodeType code_type; + Local<Value> arguments; + ReadCodeTypeAndArguments(args, index + 1, &code_type, &arguments); + + Isolate* isolate = args.GetIsolate(); + Local<String> source; + if (code_type == CodeType::kNone) { + code_type = default_type; + } + switch (code_type) { + case CodeType::kFunction: + if (!args[index]->IsFunction()) { + return MaybeLocal<String>(); + } + // Source: ( function_to_string )( params ) + if (!FunctionAndArgumentsToString(args[index].As<Function>(), arguments, + &source, isolate)) { + return MaybeLocal<String>(); + } + break; + case CodeType::kFileName: { + if (!args[index]->IsString()) { + return MaybeLocal<String>(); + } + String::Utf8Value filename(isolate, args[index]); + source = Shell::ReadFile(isolate, *filename); + if (source.IsEmpty()) return MaybeLocal<String>(); + break; + } + case CodeType::kString: + if (!args[index]->IsString()) { + return MaybeLocal<String>(); + } + source = args[index].As<String>(); + break; + case CodeType::kNone: + case CodeType::kInvalid: + return MaybeLocal<String>(); + } + return source; +} + void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) { Isolate* isolate = args.GetIsolate(); HandleScope handle_scope(isolate); @@ -2196,48 +2398,9 @@ void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) { } Local<String> source; - if (args[0]->IsFunction()) { - // d8 supports `options={type: 'function', arguments:[...]}`, which means - // the first argument is a function with the code to be ran. Restrictions - // apply; in particular the function will be converted to a string and the - // Worker constructed based on it. - WorkerType worker_type; - Local<Value> arguments; - ReadWorkerTypeAndArguments(args, &worker_type, &arguments); - if (worker_type != WorkerType::kFunction) { - isolate->ThrowError("Invalid or missing worker type"); - return; - } - - // Source: ( function_to_string )( params ) - if (!FunctionAndArgumentsToString(args[0].As<Function>(), arguments, - &source, isolate)) { - return; - } - } else { - // d8 honors `options={type: 'string'}`, which means the first argument is - // not a filename but string of script to be run. - bool load_from_file = true; - WorkerType worker_type; - ReadWorkerTypeAndArguments(args, &worker_type); - if (worker_type == WorkerType::kString) { - load_from_file = false; - } else if (worker_type != WorkerType::kNone && - worker_type != WorkerType::kClassic) { - isolate->ThrowError("Invalid worker type"); - return; - } - - if (load_from_file) { - String::Utf8Value filename(isolate, args[0]); - source = ReadFile(isolate, *filename); - if (source.IsEmpty()) { - args.GetIsolate()->ThrowError("Error loading worker script"); - return; - } - } else { - source = args[0].As<String>(); - } + if (!ReadSource(args, 0, CodeType::kFileName).ToLocal(&source)) { + isolate->ThrowError("Invalid argument"); + return; } if (!args.IsConstructCall()) { @@ -2629,13 +2792,16 @@ Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) { global_template->Set(isolate, "print", FunctionTemplate::New(isolate, Print)); global_template->Set(isolate, "printErr", FunctionTemplate::New(isolate, PrintErr)); - global_template->Set(isolate, "write", FunctionTemplate::New(isolate, Write)); - global_template->Set(isolate, "read", FunctionTemplate::New(isolate, Read)); + global_template->Set(isolate, "write", + FunctionTemplate::New(isolate, WriteStdout)); + global_template->Set(isolate, "read", + FunctionTemplate::New(isolate, ReadFile)); global_template->Set(isolate, "readbuffer", FunctionTemplate::New(isolate, ReadBuffer)); global_template->Set(isolate, "readline", FunctionTemplate::New(isolate, ReadLine)); - global_template->Set(isolate, "load", FunctionTemplate::New(isolate, Load)); + global_template->Set(isolate, "load", + FunctionTemplate::New(isolate, ExecuteFile)); global_template->Set(isolate, "setTimeout", FunctionTemplate::New(isolate, SetTimeout)); // Some Emscripten-generated code tries to call 'quit', which in turn would @@ -2675,6 +2841,13 @@ Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) { Local<ObjectTemplate> Shell::CreateOSTemplate(Isolate* isolate) { Local<ObjectTemplate> os_template = ObjectTemplate::New(isolate); AddOSMethods(isolate, os_template); + os_template->Set(isolate, "name", + v8::String::NewFromUtf8Literal(isolate, V8_TARGET_OS_STRING), + PropertyAttribute::ReadOnly); + os_template->Set( + isolate, "d8Path", + v8::String::NewFromUtf8(isolate, options.d8_path).ToLocalChecked(), + PropertyAttribute::ReadOnly); return os_template; } @@ -2768,18 +2941,39 @@ Local<ObjectTemplate> Shell::CreateRealmTemplate(Isolate* isolate) { FunctionTemplate::New(isolate, RealmEval)); realm_template->SetAccessor(String::NewFromUtf8Literal(isolate, "shared"), RealmSharedGet, RealmSharedSet); + if (options.d8_web_snapshot_api) { + realm_template->Set(isolate, "takeWebSnapshot", + FunctionTemplate::New(isolate, RealmTakeWebSnapshot)); + realm_template->Set(isolate, "useWebSnapshot", + FunctionTemplate::New(isolate, RealmUseWebSnapshot)); + } return realm_template; } +Local<FunctionTemplate> Shell::CreateSnapshotTemplate(Isolate* isolate) { + Local<FunctionTemplate> snapshot_template = FunctionTemplate::New(isolate); + snapshot_template->InstanceTemplate()->SetInternalFieldCount(1); + PerIsolateData::Get(isolate)->SetSnapshotObjectCtor(snapshot_template); + return snapshot_template; +} Local<ObjectTemplate> Shell::CreateD8Template(Isolate* isolate) { Local<ObjectTemplate> d8_template = ObjectTemplate::New(isolate); { + Local<ObjectTemplate> file_template = ObjectTemplate::New(isolate); + file_template->Set(isolate, "read", + FunctionTemplate::New(isolate, Shell::ReadFile)); + file_template->Set(isolate, "execute", + FunctionTemplate::New(isolate, Shell::ExecuteFile)); + d8_template->Set(isolate, "file", file_template); + } + { Local<ObjectTemplate> log_template = ObjectTemplate::New(isolate); log_template->Set(isolate, "getAndStop", FunctionTemplate::New(isolate, LogGetAndStop)); d8_template->Set(isolate, "log", log_template); - + } + { Local<ObjectTemplate> dom_template = ObjectTemplate::New(isolate); dom_template->Set(isolate, "Div", Shell::CreateNodeTemplates(isolate)); d8_template->Set(isolate, "dom", dom_template); @@ -2932,6 +3126,11 @@ void Shell::Initialize(Isolate* isolate, D8Console* console, debug::SetConsoleDelegate(isolate, console); } +Local<String> Shell::WasmLoadSourceMapCallback(Isolate* isolate, + const char* path) { + return Shell::ReadFile(isolate, path, false); +} + Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) { // This needs to be a critical section since this is not thread-safe base::MutexGuard lock_guard(context_mutex_.Pointer()); @@ -2941,7 +3140,7 @@ Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) { Local<Context> context = Context::New(isolate, nullptr, global_template); DCHECK(!context.IsEmpty()); if (i::FLAG_perf_prof_annotate_wasm || i::FLAG_vtune_prof_annotate_wasm) { - isolate->SetWasmLoadSourceMapCallback(ReadFile); + isolate->SetWasmLoadSourceMapCallback(Shell::WasmLoadSourceMapCallback); } InitializeModuleEmbedderData(context); if (options.include_arguments) { @@ -3247,18 +3446,33 @@ char* Shell::ReadChars(const char* name, int* size_out) { return chars; } -bool Shell::ReadLines(const char* name, std::vector<std::string>& lines) { +MaybeLocal<PrimitiveArray> Shell::ReadLines(Isolate* isolate, + const char* name) { int length; const char* data = reinterpret_cast<const char*>(ReadChars(name, &length)); if (data == nullptr) { - return false; + return MaybeLocal<PrimitiveArray>(); } std::stringstream stream(data); std::string line; + std::vector<std::string> lines; while (std::getline(stream, line, '\n')) { lines.emplace_back(line); } - return true; + // Create a Local<PrimitiveArray> off the read lines. + int size = static_cast<int>(lines.size()); + Local<PrimitiveArray> exports = PrimitiveArray::New(isolate, size); + for (int i = 0; i < size; ++i) { + MaybeLocal<String> maybe_str = v8::String::NewFromUtf8( + isolate, lines[i].c_str(), NewStringType::kNormal, + static_cast<int>(lines[i].length())); + Local<String> str; + if (!maybe_str.ToLocal(&str)) { + return MaybeLocal<PrimitiveArray>(); + } + exports->Set(isolate, i, str); + } + return exports; } void Shell::ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args) { @@ -3291,11 +3505,20 @@ void Shell::ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args) { } // Reads a file into a v8 string. -Local<String> Shell::ReadFile(Isolate* isolate, const char* name) { +Local<String> Shell::ReadFile(Isolate* isolate, const char* name, + bool should_throw) { std::unique_ptr<base::OS::MemoryMappedFile> file( base::OS::MemoryMappedFile::open( name, base::OS::MemoryMappedFile::FileMode::kReadOnly)); - if (!file) return Local<String>(); + if (!file) { + if (should_throw) { + std::ostringstream oss; + oss << "Error loading file: \"" << name << '"'; + isolate->ThrowError( + v8::String::NewFromUtf8(isolate, oss.str().c_str()).ToLocalChecked()); + } + return Local<String>(); + } int size = static_cast<int>(file->size()); char* chars = static_cast<char*>(file->memory()); @@ -3593,7 +3816,7 @@ bool SourceGroup::Execute(Isolate* isolate) { HandleScope handle_scope(isolate); Local<String> file_name = String::NewFromUtf8(isolate, arg).ToLocalChecked(); - Local<String> source = ReadFile(isolate, arg); + Local<String> source = Shell::ReadFile(isolate, arg); if (source.IsEmpty()) { printf("Error reading '%s'\n", arg); base::OS::ExitProcess(1); @@ -3610,10 +3833,6 @@ bool SourceGroup::Execute(Isolate* isolate) { return success; } -Local<String> SourceGroup::ReadFile(Isolate* isolate, const char* name) { - return Shell::ReadFile(isolate, name); -} - SourceGroup::IsolateThread::IsolateThread(SourceGroup* group) : base::Thread(GetThreadOptions("IsolateThread")), group_(group) {} @@ -3908,7 +4127,7 @@ void Worker::ExecuteInThread() { NewStringType::kInternalized)); Local<Value> onmessage; if (maybe_onmessage.ToLocal(&onmessage) && onmessage->IsFunction()) { - // Now wait for messages + // Now wait for messages. ProcessMessages(); } } @@ -3961,6 +4180,7 @@ void Worker::PostMessageOut(const v8::FunctionCallbackInfo<v8::Value>& args) { bool Shell::SetOptions(int argc, char* argv[]) { bool logfile_per_isolate = false; bool no_always_opt = false; + options.d8_path = argv[0]; for (int i = 0; i < argc; i++) { if (strcmp(argv[i], "--") == 0) { argv[i] = nullptr; @@ -4120,6 +4340,9 @@ bool Shell::SetOptions(int argc, char* argv[]) { } else if (strncmp(argv[i], "--web-snapshot-config=", 22) == 0) { options.web_snapshot_config = argv[i] + 22; argv[i] = nullptr; + } else if (strcmp(argv[i], "--experimental-d8-web-snapshot-api") == 0) { + options.d8_web_snapshot_api = true; + argv[i] = nullptr; } else if (strcmp(argv[i], "--compile-only") == 0) { options.compile_only = true; argv[i] = nullptr; @@ -4141,6 +4364,8 @@ bool Shell::SetOptions(int argc, char* argv[]) { } else if (strcmp(argv[i], "--enable-system-instrumentation") == 0) { options.enable_system_instrumentation = true; options.trace_enabled = true; + // This needs to be manually triggered for JIT ETW events to work. + i::FLAG_enable_system_instrumentation = true; #if defined(V8_OS_WIN) // Guard this bc the flag has a lot of overhead and is not currently used // by macos @@ -4148,6 +4373,14 @@ bool Shell::SetOptions(int argc, char* argv[]) { #endif argv[i] = nullptr; #endif +#if V8_ENABLE_WEBASSEMBLY + } else if (strcmp(argv[i], "--wasm-trap-handler") == 0) { + options.wasm_trap_handler = true; + argv[i] = nullptr; + } else if (strcmp(argv[i], "--no-wasm-trap-handler") == 0) { + options.wasm_trap_handler = false; + argv[i] = nullptr; +#endif // V8_ENABLE_WEBASSEMBLY } } @@ -4220,6 +4453,7 @@ int Shell::RunMain(Isolate* isolate, bool last_run) { } HandleScope scope(isolate); Local<Context> context = CreateEvaluationContext(isolate); + CreateSnapshotTemplate(isolate); bool use_existing_context = last_run && use_interactive_shell(); if (use_existing_context) { // Keep using the same context in the interactive shell. @@ -4328,18 +4562,22 @@ bool ProcessMessages( i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::SaveAndSwitchContext saved_context(i_isolate, i::Context()); SealHandleScope shs(isolate); - while (v8::platform::PumpMessageLoop(g_default_platform, isolate, - behavior())) { - MicrotasksScope::PerformCheckpoint(isolate); - + for (bool ran_tasks = true; ran_tasks;) { + // Execute one foreground task (if one exists), then microtasks. + ran_tasks = v8::platform::PumpMessageLoop(g_default_platform, isolate, + behavior()); + if (ran_tasks) MicrotasksScope::PerformCheckpoint(isolate); + + // In predictable mode we push all background tasks into the foreground + // task queue of the {kProcessGlobalPredictablePlatformWorkerTaskQueue} + // isolate. We execute all background tasks after running one foreground + // task. if (i::FLAG_verify_predictable) { - // In predictable mode we push all background tasks into the foreground - // task queue of the {kProcessGlobalPredictablePlatformWorkerTaskQueue} - // isolate. We execute the tasks after one foreground task has been - // executed. while (v8::platform::PumpMessageLoop( g_default_platform, - kProcessGlobalPredictablePlatformWorkerTaskQueue, behavior())) { + kProcessGlobalPredictablePlatformWorkerTaskQueue, + platform::MessageLoopBehavior::kDoNotWait)) { + ran_tasks = true; } } } @@ -4348,13 +4586,9 @@ bool ProcessMessages( 50.0 / base::Time::kMillisecondsPerSecond); } bool ran_set_timeout = false; - if (!RunSetTimeoutCallback(isolate, &ran_set_timeout)) { - return false; - } - + if (!RunSetTimeoutCallback(isolate, &ran_set_timeout)) return false; if (!ran_set_timeout) return true; } - return true; } } // anonymous namespace @@ -4369,6 +4603,17 @@ bool Shell::CompleteMessageLoop(Isolate* isolate) { return should_wait ? platform::MessageLoopBehavior::kWaitForWork : platform::MessageLoopBehavior::kDoNotWait; }; + if (i::FLAG_verify_predictable) { + bool ran_tasks = ProcessMessages( + isolate, [] { return platform::MessageLoopBehavior::kDoNotWait; }); + if (get_waiting_behaviour() == + platform::MessageLoopBehavior::kWaitForWork) { + FATAL( + "There is outstanding work after executing all tasks in predictable " + "mode -- this would deadlock."); + } + return ran_tasks; + } return ProcessMessages(isolate, get_waiting_behaviour); } @@ -4841,9 +5086,9 @@ int Shell::Main(int argc, char* argv[]) { } #if V8_ENABLE_WEBASSEMBLY - if (V8_TRAP_HANDLER_SUPPORTED && i::FLAG_wasm_trap_handler) { - constexpr bool use_default_trap_handler = true; - if (!v8::V8::EnableWebAssemblyTrapHandler(use_default_trap_handler)) { + if (V8_TRAP_HANDLER_SUPPORTED && options.wasm_trap_handler) { + constexpr bool kUseDefaultTrapHandler = true; + if (!v8::V8::EnableWebAssemblyTrapHandler(kUseDefaultTrapHandler)) { FATAL("Could not register trap handler"); } } @@ -4960,8 +5205,7 @@ int Shell::Main(int argc, char* argv[]) { RunShell(isolate); } - if (i::FLAG_trace_ignition_dispatches && - i::FLAG_trace_ignition_dispatches_output_file != nullptr) { + if (i::FLAG_trace_ignition_dispatches_output_file != nullptr) { WriteIgnitionDispatchCountersFile(isolate); } @@ -5035,9 +5279,7 @@ int Shell::Main(int argc, char* argv[]) { } // namespace v8 -#ifndef GOOGLE3 int main(int argc, char* argv[]) { return v8::Shell::Main(argc, argv); } -#endif #undef CHECK #undef DCHECK diff --git a/chromium/v8/src/d8/d8.h b/chromium/v8/src/d8/d8.h index 75c80461156..9d3cc4f6d2e 100644 --- a/chromium/v8/src/d8/d8.h +++ b/chromium/v8/src/d8/d8.h @@ -115,7 +115,6 @@ class SourceGroup { base::Thread* thread_; void ExitShell(int exit_code); - Local<String> ReadFile(Isolate* isolate, const char* name); const char** argv_; int begin_offset_; @@ -255,6 +254,22 @@ class PerIsolateData { PerIsolateData* data_; }; + // Contrary to RealmScope (which creates a new Realm), ExplicitRealmScope + // allows for entering an existing Realm, as specified by its index. + class V8_NODISCARD ExplicitRealmScope { + public: + explicit ExplicitRealmScope(PerIsolateData* data, int index); + ~ExplicitRealmScope(); + + Local<Context> context() const; + + private: + PerIsolateData* data_; + Local<Context> realm_; + int index_; + int previous_index_; + }; + inline void SetTimeout(Local<Function> callback, Local<Context> context); inline MaybeLocal<Function> GetTimeoutCallback(); inline MaybeLocal<Context> GetTimeoutContext(); @@ -274,6 +289,9 @@ class PerIsolateData { Local<FunctionTemplate> GetTestApiObjectCtor() const; void SetTestApiObjectCtor(Local<FunctionTemplate> ctor); + Local<FunctionTemplate> GetSnapshotObjectCtor() const; + void SetSnapshotObjectCtor(Local<FunctionTemplate> ctor); + private: friend class Shell; friend class RealmScope; @@ -293,6 +311,7 @@ class PerIsolateData { std::unordered_set<DynamicImportData*> import_data_; #endif Global<FunctionTemplate> test_api_object_ctor_; + Global<FunctionTemplate> snapshot_object_ctor_; int RealmIndexOrThrow(const v8::FunctionCallbackInfo<v8::Value>& args, int arg_offset); @@ -322,7 +341,7 @@ class ShellOptions { DisallowReassignment(const char* name, T value) : name_(name), value_(value) {} - operator T() const { return value_; } // NOLINT + operator T() const { return value_; } T get() const { return value_; } DisallowReassignment& operator=(T value) { if (check_d8_flag_contradictions) { @@ -348,6 +367,7 @@ class ShellOptions { bool specified_ = false; }; + DisallowReassignment<const char*> d8_path = {"d8-path", ""}; DisallowReassignment<bool> fuzzilli_coverage_statistics = { "fuzzilli-coverage-statistics", false}; DisallowReassignment<bool> fuzzilli_enable_builtins_coverage = { @@ -406,8 +426,13 @@ class ShellOptions { "enable-system-instrumentation", false}; DisallowReassignment<const char*> web_snapshot_config = { "web-snapshot-config", nullptr}; + DisallowReassignment<bool> d8_web_snapshot_api = { + "experimental-d8-web-snapshot-api", false}; DisallowReassignment<bool> compile_only = {"compile-only", false}; DisallowReassignment<int> repeat_compile = {"repeat-compile", 1}; +#if V8_ENABLE_WEBASSEMBLY + DisallowReassignment<bool> wasm_trap_handler = {"wasm-trap-handler", true}; +#endif // V8_ENABLE_WEBASSEMBLY }; class Shell : public i::AllStatic { @@ -421,6 +446,7 @@ class Shell : public i::AllStatic { kProcessMessageQueue = true, kNoProcessMessageQueue = false }; + enum class CodeType { kFileName, kString, kFunction, kInvalid, kNone }; static bool ExecuteString(Isolate* isolate, Local<String> source, Local<Value> name, PrintResult print_result, @@ -431,7 +457,10 @@ class Shell : public i::AllStatic { static void ReportException(Isolate* isolate, Local<Message> message, Local<Value> exception); static void ReportException(Isolate* isolate, TryCatch* try_catch); - static Local<String> ReadFile(Isolate* isolate, const char* name); + static Local<String> ReadFile(Isolate* isolate, const char* name, + bool should_throw = true); + static Local<String> WasmLoadSourceMapCallback(Isolate* isolate, + const char* name); static Local<Context> CreateEvaluationContext(Isolate* isolate); static int RunMain(Isolate* isolate, bool last_run); static int Main(int argc, char* argv[]); @@ -476,6 +505,10 @@ class Shell : public i::AllStatic { const PropertyCallbackInfo<Value>& info); static void RealmSharedSet(Local<String> property, Local<Value> value, const PropertyCallbackInfo<void>& info); + static void RealmTakeWebSnapshot( + const v8::FunctionCallbackInfo<v8::Value>& args); + static void RealmUseWebSnapshot( + const v8::FunctionCallbackInfo<v8::Value>& args); static void LogGetAndStop(const v8::FunctionCallbackInfo<v8::Value>& args); static void TestVerifySourcePositions( @@ -492,23 +525,34 @@ class Shell : public i::AllStatic { static void Print(const v8::FunctionCallbackInfo<v8::Value>& args); static void PrintErr(const v8::FunctionCallbackInfo<v8::Value>& args); - static void Write(const v8::FunctionCallbackInfo<v8::Value>& args); + static void WriteStdout(const v8::FunctionCallbackInfo<v8::Value>& args); static void WaitUntilDone(const v8::FunctionCallbackInfo<v8::Value>& args); static void NotifyDone(const v8::FunctionCallbackInfo<v8::Value>& args); static void QuitOnce(v8::FunctionCallbackInfo<v8::Value>* args); static void Quit(const v8::FunctionCallbackInfo<v8::Value>& args); static void Version(const v8::FunctionCallbackInfo<v8::Value>& args); - static void Read(const v8::FunctionCallbackInfo<v8::Value>& args); + static void ReadFile(const v8::FunctionCallbackInfo<v8::Value>& args); static char* ReadChars(const char* name, int* size_out); - static bool ReadLines(const char* name, std::vector<std::string>& lines); + static MaybeLocal<PrimitiveArray> ReadLines(Isolate* isolate, + const char* name); static void ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args); static Local<String> ReadFromStdin(Isolate* isolate); static void ReadLine(const v8::FunctionCallbackInfo<v8::Value>& args) { args.GetReturnValue().Set(ReadFromStdin(args.GetIsolate())); } static void WriteChars(const char* name, uint8_t* buffer, size_t buffer_size); - static void Load(const v8::FunctionCallbackInfo<v8::Value>& args); + static void ExecuteFile(const v8::FunctionCallbackInfo<v8::Value>& args); static void SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args); + static void ReadCodeTypeAndArguments( + const v8::FunctionCallbackInfo<v8::Value>& args, int index, + CodeType* code_type, Local<Value>* arguments = nullptr); + static bool FunctionAndArgumentsToString(Local<Function> function, + Local<Value> arguments, + Local<String>* source, + Isolate* isolate); + static MaybeLocal<String> ReadSource( + const v8::FunctionCallbackInfo<v8::Value>& args, int index, + CodeType default_type); static void WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args); static void WorkerPostMessage( const v8::FunctionCallbackInfo<v8::Value>& args); @@ -529,29 +573,30 @@ class Shell : public i::AllStatic { // milliseconds on the total running time of the program. Exceptions are // thrown on timeouts or other errors or if the exit status of the program // indicates an error. - // + static void System(const v8::FunctionCallbackInfo<v8::Value>& args); + // os.chdir(dir) changes directory to the given directory. Throws an // exception/ on error. - // + static void ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args); + // os.setenv(variable, value) sets an environment variable. Repeated calls to // this method leak memory due to the API of setenv in the standard C library. - // + static void SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args); + static void UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args); + // os.umask(alue) calls the umask system call and returns the old umask. - // + static void SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args); + // os.mkdirp(name, mask) creates a directory. The mask (if present) is anded // with the current umask. Intermediate directories are created if necessary. // An exception is not thrown if the directory already exists. Analogous to // the "mkdir -p" command. - static void System(const v8::FunctionCallbackInfo<v8::Value>& args); - static void ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args); - static void SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args); - static void UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args); - static void SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args); static void MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args); static void RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args); static MaybeLocal<Promise> HostImportModuleDynamically( Local<Context> context, Local<ScriptOrModule> referrer, Local<String> specifier, Local<FixedArray> import_assertions); + static void ModuleResolutionSuccessCallback( const v8::FunctionCallbackInfo<v8::Value>& info); static void ModuleResolutionFailureCallback( @@ -601,6 +646,8 @@ class Shell : public i::AllStatic { static void PromiseRejectCallback(v8::PromiseRejectMessage reject_message); + static Local<FunctionTemplate> CreateSnapshotTemplate(Isolate* isolate); + private: static Global<Context> evaluation_context_; static base::OnceType quit_once_; |