diff options
author | Michaƫl Zasso <targos@protonmail.com> | 2023-03-30 12:11:08 +0200 |
---|---|---|
committer | Node.js GitHub Bot <github-bot@iojs.org> | 2023-03-31 14:15:23 +0000 |
commit | f226350fcbebd4449fb0034fdaffa147e4de28ea (patch) | |
tree | 8896397ec8829c238012bfbe9781f4e2d94708bc /deps/v8/src/d8/d8.cc | |
parent | 10928cb0a4643a11c02af7bab93fc4b5abe2ce7d (diff) | |
download | node-new-f226350fcbebd4449fb0034fdaffa147e4de28ea.tar.gz |
deps: update V8 to 11.3.244.4
PR-URL: https://github.com/nodejs/node/pull/47251
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: Richard Lau <rlau@redhat.com>
Diffstat (limited to 'deps/v8/src/d8/d8.cc')
-rw-r--r-- | deps/v8/src/d8/d8.cc | 913 |
1 files changed, 457 insertions, 456 deletions
diff --git a/deps/v8/src/d8/d8.cc b/deps/v8/src/d8/d8.cc index 31ed6d7526..6a31685e46 100644 --- a/deps/v8/src/d8/d8.cc +++ b/deps/v8/src/d8/d8.cc @@ -18,6 +18,8 @@ #include <utility> #include <vector> +#include "v8-isolate.h" + #ifdef ENABLE_VTUNE_JIT_INTERFACE #include "src/third_party/vtune/v8-vtune.h" #endif @@ -69,7 +71,6 @@ #include "src/tasks/cancelable-task.h" #include "src/utils/ostreams.h" #include "src/utils/utils.h" -#include "src/web-snapshot/web-snapshot.h" #if V8_OS_POSIX #include <signal.h> @@ -80,7 +81,8 @@ #endif // V8_FUZZILLI #ifdef V8_USE_PERFETTO -#include "perfetto/tracing.h" +#include "perfetto/tracing/track_event.h" +#include "perfetto/tracing/track_event_legacy.h" #endif // V8_USE_PERFETTO #ifdef V8_INTL_SUPPORT @@ -341,6 +343,19 @@ class MultiMappedAllocator : public ArrayBufferAllocatorBase { v8::Platform* g_default_platform; std::unique_ptr<v8::Platform> g_platform; +template <int N> +bool ThrowError(Isolate* isolate, const char (&message)[N]) { + if (isolate->IsExecutionTerminating()) return false; + isolate->ThrowError(message); + return true; +} + +bool ThrowError(Isolate* isolate, Local<String> message) { + if (isolate->IsExecutionTerminating()) return false; + isolate->ThrowError(message); + return true; +} + static MaybeLocal<Value> TryGetValue(v8::Isolate* isolate, Local<Context> context, Local<v8::Object> object, @@ -358,13 +373,13 @@ static Local<Value> GetValue(v8::Isolate* isolate, Local<Context> context, std::shared_ptr<Worker> GetWorkerFromInternalField(Isolate* isolate, Local<Object> object) { if (object->InternalFieldCount() != 1) { - isolate->ThrowError("this is not a Worker"); + ThrowError(isolate, "this is not a Worker"); return nullptr; } i::Handle<i::Object> handle = Utils::OpenHandle(*object->GetInternalField(0)); if (handle->IsSmi()) { - isolate->ThrowError("Worker is defunct because main thread is terminating"); + ThrowError(isolate, "Worker is defunct because main thread is terminating"); return nullptr; } auto managed = i::Handle<i::Managed<Worker>>::cast(handle); @@ -465,6 +480,9 @@ CounterCollection* Shell::counters_ = &local_counters_; base::LazyMutex Shell::context_mutex_; const base::TimeTicks Shell::kInitialTicks = base::TimeTicks::Now(); Global<Function> Shell::stringify_function_; +base::Mutex Shell::profiler_end_callback_lock_; +std::map<Isolate*, std::pair<Global<Function>, Global<Context>>> + Shell::profiler_end_callback_; base::LazyMutex Shell::workers_mutex_; bool Shell::allow_new_workers_ = true; std::unordered_set<std::shared_ptr<Worker>> Shell::running_workers_; @@ -911,52 +929,6 @@ bool Shell::ExecuteString(Isolate* isolate, Local<String> source, return success; } -bool Shell::TakeWebSnapshot(Isolate* isolate) { - PerIsolateData* data = PerIsolateData::Get(isolate); - Local<Context> realm = - Local<Context>::New(isolate, data->realms_[data->realm_current_]); - Context::Scope context_scope(realm); - Local<Context> context(isolate->GetCurrentContext()); - - v8::TryCatch try_catch(isolate); - try_catch.SetVerbose(true); - const char* web_snapshot_output_file_name = "web.snap"; - if (options.web_snapshot_output) { - web_snapshot_output_file_name = options.web_snapshot_output; - } - - if (!options.web_snapshot_config) { - isolate->ThrowError( - "Web snapshots: --web-snapshot-config is needed when " - "--web-snapshot-output is passed"); - CHECK(try_catch.HasCaught()); - ReportException(isolate, &try_catch); - return false; - } - - 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); - return false; - } - - i::WebSnapshotSerializer serializer(isolate); - i::WebSnapshotData snapshot_data; - if (serializer.TakeSnapshot(context, exports, snapshot_data)) { - DCHECK_NOT_NULL(snapshot_data.buffer); - WriteChars(web_snapshot_output_file_name, snapshot_data.buffer, - snapshot_data.buffer_size); - } else { - CHECK(try_catch.HasCaught()); - return false; - } - return true; -} - namespace { bool IsAbsolutePath(const std::string& path) { @@ -1075,8 +1047,8 @@ MaybeLocal<Module> Shell::FetchModuleTree(Local<Module> referrer, CHECK(specifier_it != module_data->module_to_specifier_map.end()); msg += "\n imported by " + specifier_it->second; } - isolate->ThrowError( - v8::String::NewFromUtf8(isolate, msg.c_str()).ToLocalChecked()); + ThrowError(isolate, + v8::String::NewFromUtf8(isolate, msg.c_str()).ToLocalChecked()); return MaybeLocal<Module>(); } @@ -1139,7 +1111,7 @@ MaybeLocal<Module> Shell::FetchModuleTree(Local<Module> referrer, context, import_assertions, true); if (request_module_type == ModuleType::kInvalid) { - isolate->ThrowError("Invalid module type was asserted"); + ThrowError(isolate, "Invalid module type was asserted"); return MaybeLocal<Module>(); } @@ -1317,6 +1289,10 @@ MaybeLocal<Context> Shell::HostCreateShadowRealmContext( InitializeModuleEmbedderData(context); std::shared_ptr<ModuleEmbedderData> initiator_data = GetModuleDataFromContext(initiator_context); + + // ShadowRealms are synchronously accessible and are always in the same origin + // as the initiator context. + context->SetSecurityToken(initiator_context->GetSecurityToken()); shadow_realm_data->origin = initiator_data->origin; return context; @@ -1348,7 +1324,7 @@ void Shell::DoHostImportModuleDynamically(void* import_data) { try_catch.SetVerbose(true); if (module_type == ModuleType::kInvalid) { - isolate->ThrowError("Invalid module type was asserted"); + ThrowError(isolate, "Invalid module type was asserted"); CHECK(try_catch.HasCaught()); resolver->Reject(realm, try_catch.Exception()).ToChecked(); return; @@ -1492,44 +1468,6 @@ bool Shell::ExecuteModule(Isolate* isolate, const char* file_name) { return true; } -bool Shell::ExecuteWebSnapshot(Isolate* isolate, const char* file_name) { - HandleScope handle_scope(isolate); - - PerIsolateData* data = PerIsolateData::Get(isolate); - Local<Context> realm = data->realms_[data->realm_current_].Get(isolate); - Context::Scope context_scope(realm); - - std::string absolute_path = NormalizePath(file_name, GetWorkingDirectory()); - - int length = 0; - std::unique_ptr<uint8_t[]> snapshot_data( - reinterpret_cast<uint8_t*>(ReadChars(absolute_path.c_str(), &length))); - if (length == 0) { - TryCatch try_catch(isolate); - isolate->ThrowError("Could not read the web snapshot file"); - CHECK(try_catch.HasCaught()); - ReportException(isolate, &try_catch); - return false; - } else { - for (int r = 0; r < DeserializationRunCount(); ++r) { - bool skip_exports = r > 0; - i::WebSnapshotDeserializer deserializer(isolate, snapshot_data.get(), - static_cast<size_t>(length)); - if (!deserializer.Deserialize({}, skip_exports)) { - // d8 is calling into the internal APIs which won't do - // ReportPendingMessages in all error paths (it's supposed to be done at - // the API boundary). Call it here. - auto i_isolate = reinterpret_cast<i::Isolate*>(isolate); - if (i_isolate->has_pending_exception()) { - i_isolate->ReportPendingMessages(); - } - return false; - } - } - } - return true; -} - // Treat every line as a JSON value and parse it. bool Shell::LoadJSON(Isolate* isolate, const char* file_name) { HandleScope handle_scope(isolate); @@ -1572,10 +1510,6 @@ 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() { @@ -1636,6 +1570,7 @@ void PerIsolateData::AddUnhandledPromise(Local<Promise> promise, int PerIsolateData::HandleUnhandledPromiseRejections() { // Avoid recursive calls to HandleUnhandledPromiseRejections. if (ignore_unhandled_promises_) return 0; + if (isolate_->IsExecutionTerminating()) return 0; ignore_unhandled_promises_ = true; v8::HandleScope scope(isolate_); // Ignore promises that get added during error reporting. @@ -1671,14 +1606,6 @@ 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); -} - Local<FunctionTemplate> PerIsolateData::GetDomNodeCtor() const { return dom_node_ctor_.Get(isolate_); } @@ -1730,14 +1657,14 @@ int PerIsolateData::RealmFind(Local<Context> context) { int PerIsolateData::RealmIndexOrThrow( const v8::FunctionCallbackInfo<v8::Value>& args, int arg_offset) { if (args.Length() < arg_offset || !args[arg_offset]->IsNumber()) { - args.GetIsolate()->ThrowError("Invalid argument"); + ThrowError(args.GetIsolate(), "Invalid argument"); return -1; } int index = args[arg_offset] ->Int32Value(args.GetIsolate()->GetCurrentContext()) .FromMaybe(-1); if (index < 0 || index >= realm_count_ || realms_[index].IsEmpty()) { - args.GetIsolate()->ThrowError("Invalid realm index"); + ThrowError(args.GetIsolate(), "Invalid realm index"); return -1; } return index; @@ -1763,7 +1690,7 @@ uint64_t Shell::GetTracingTimestampFromPerformanceTimestamp( base::TimeDelta::FromMillisecondsD(performance_timestamp); // See TracingController::CurrentTimestampMicroseconds(). int64_t internal_value = (delta + kInitialTicks).ToInternalValue(); - DCHECK(internal_value >= 0); + DCHECK_GE(internal_value, 0); return internal_value; } @@ -1779,7 +1706,7 @@ void Shell::PerformanceMark(const v8::FunctionCallbackInfo<v8::Value>& args) { Local<Context> context = isolate->GetCurrentContext(); if (args.Length() < 1 || !args[0]->IsString()) { - args.GetIsolate()->ThrowError("Invalid 'name' argument"); + ThrowError(args.GetIsolate(), "Invalid 'name' argument"); return; } Local<String> name = args[0].As<String>(); @@ -1818,7 +1745,7 @@ void Shell::PerformanceMeasure( Local<Context> context = isolate->GetCurrentContext(); if (args.Length() < 1 || !args[0]->IsString()) { - args.GetIsolate()->ThrowError("Invalid 'name' argument"); + ThrowError(args.GetIsolate(), "Invalid 'name' argument"); return; } v8::Local<String> name = args[0].As<String>(); @@ -1827,8 +1754,8 @@ void Shell::PerformanceMeasure( if (args.Length() >= 2) { Local<Value> start_mark = args[1].As<Value>(); if (!start_mark->IsObject()) { - args.GetIsolate()->ThrowError( - "Invalid 'startMark' argument: Not an Object"); + ThrowError(args.GetIsolate(), + "Invalid 'startMark' argument: Not an Object"); return; } Local<Value> start_time_field; @@ -1838,14 +1765,14 @@ void Shell::PerformanceMeasure( return; } if (!start_time_field->IsNumber()) { - args.GetIsolate()->ThrowError( - "Invalid 'startMark' argument: No numeric 'startTime' field"); + ThrowError(args.GetIsolate(), + "Invalid 'startMark' argument: No numeric 'startTime' field"); return; } start_timestamp = start_time_field.As<Number>()->Value(); } if (args.Length() > 2) { - args.GetIsolate()->ThrowError("Too many arguments"); + ThrowError(args.GetIsolate(), "Too many arguments"); return; } @@ -1931,7 +1858,7 @@ void Shell::RealmOwner(const v8::FunctionCallbackInfo<v8::Value>& args) { Isolate* isolate = args.GetIsolate(); PerIsolateData* data = PerIsolateData::Get(isolate); if (args.Length() < 1 || !args[0]->IsObject()) { - args.GetIsolate()->ThrowError("Invalid argument"); + ThrowError(args.GetIsolate(), "Invalid argument"); return; } Local<Object> object = @@ -1943,7 +1870,7 @@ void Shell::RealmOwner(const v8::FunctionCallbackInfo<v8::Value>& args) { } Local<Context> creation_context; if (!object->GetCreationContext().ToLocal(&creation_context)) { - args.GetIsolate()->ThrowError("object doesn't have creation context"); + ThrowError(args.GetIsolate(), "object doesn't have creation context"); return; } int index = data->RealmFind(creation_context); @@ -2017,7 +1944,6 @@ void Shell::DisposeRealm(const v8::FunctionCallbackInfo<v8::Value>& args, // ContextDisposedNotification expects the disposed context to be entered. v8::Context::Scope scope(context); isolate->ContextDisposedNotification(); - isolate->IdleNotificationDeadline(g_platform->MonotonicallyIncreasingTime()); } // Realm.create() creates a new realm with a distinct security token @@ -2046,7 +1972,7 @@ void Shell::RealmNavigate(const v8::FunctionCallbackInfo<v8::Value>& args) { if (index == -1) return; if (index == 0 || index == data->realm_current_ || index == data->realm_switch_) { - args.GetIsolate()->ThrowError("Invalid realm index"); + ThrowError(args.GetIsolate(), "Invalid realm index"); return; } @@ -2075,7 +2001,7 @@ void Shell::RealmDetachGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) { if (index == -1) return; if (index == 0 || index == data->realm_current_ || index == data->realm_switch_) { - args.GetIsolate()->ThrowError("Invalid realm index"); + ThrowError(args.GetIsolate(), "Invalid realm index"); return; } @@ -2092,7 +2018,7 @@ void Shell::RealmDispose(const v8::FunctionCallbackInfo<v8::Value>& args) { if (index == -1) return; if (index == 0 || index == data->realm_current_ || index == data->realm_switch_) { - args.GetIsolate()->ThrowError("Invalid realm index"); + ThrowError(args.GetIsolate(), "Invalid realm index"); return; } DisposeRealm(args, index); @@ -2114,13 +2040,13 @@ void Shell::RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args) { int index = data->RealmIndexOrThrow(args, 0); if (index == -1) return; if (args.Length() < 2) { - isolate->ThrowError("Invalid argument"); + ThrowError(isolate, "Invalid argument"); return; } Local<String> source; if (!ReadSource(args, 1, CodeType::kString).ToLocal(&source)) { - isolate->ThrowError("Invalid argument"); + ThrowError(isolate, "Invalid argument"); return; } ScriptOrigin origin = @@ -2161,100 +2087,6 @@ 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>(); - { - TryCatch try_catch(isolate); - try_catch.SetVerbose(true); - PerIsolateData::ExplicitRealmScope realm_scope(data, index); - i::WebSnapshotSerializer serializer(isolate); - if (!serializer.TakeSnapshot(realm_scope.context(), exports, - *snapshot_data_shared)) { - CHECK(try_catch.HasCaught()); - 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, - snapshot_data_shared->buffer, - snapshot_data_shared->buffer_size); - bool success = deserializer.Deserialize(); - 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); @@ -2262,18 +2094,18 @@ void Shell::LogGetAndStop(const v8::FunctionCallbackInfo<v8::Value>& args) { std::string file_name = i_isolate->v8_file_logger()->file_name(); if (!i::LogFile::IsLoggingToTemporaryFile(file_name)) { - isolate->ThrowError("Only capturing from temporary files is supported."); + ThrowError(isolate, "Only capturing from temporary files is supported."); return; } if (!i_isolate->v8_file_logger()->is_logging()) { - isolate->ThrowError("Logging not enabled."); + ThrowError(isolate, "Logging not enabled."); return; } std::string raw_log; FILE* log_file = i_isolate->v8_file_logger()->TearDownAndGetLogFile(); if (!log_file) { - isolate->ThrowError("Log file does not exist."); + ThrowError(isolate, "Log file does not exist."); return; } @@ -2282,7 +2114,7 @@ void Shell::LogGetAndStop(const v8::FunctionCallbackInfo<v8::Value>& args) { base::Fclose(log_file); if (!exists) { - isolate->ThrowError("Unable to read log file."); + ThrowError(isolate, "Unable to read log file."); return; } Local<String> result = @@ -2298,14 +2130,14 @@ void Shell::TestVerifySourcePositions( Isolate* isolate = args.GetIsolate(); // Check if the argument is a valid function. if (args.Length() != 1) { - isolate->ThrowError("Expected function as single argument."); + ThrowError(isolate, "Expected function as single argument."); return; } auto arg_handle = Utils::OpenHandle(*args[0]); if (!arg_handle->IsHeapObject() || !i::Handle<i::HeapObject>::cast(arg_handle) ->IsJSFunctionOrBoundFunctionOrWrappedFunction()) { - isolate->ThrowError("Expected function as single argument."); + ThrowError(isolate, "Expected function as single argument."); return; } @@ -2321,7 +2153,7 @@ void Shell::TestVerifySourcePositions( auto bound_target = bound_function->bound_target_function(); if (!bound_target.IsJSFunctionOrBoundFunctionOrWrappedFunction()) { internal::AllowGarbageCollection allow_gc; - isolate->ThrowError("Expected function as bound target."); + ThrowError(isolate, "Expected function as bound target."); return; } callable = handle( @@ -2331,7 +2163,7 @@ void Shell::TestVerifySourcePositions( i::Handle<i::JSFunction> function = i::Handle<i::JSFunction>::cast(callable); if (!function->shared().HasBytecodeArray()) { - isolate->ThrowError("Function has no BytecodeArray attached."); + ThrowError(isolate, "Function has no BytecodeArray attached."); return; } i::Handle<i::BytecodeArray> bytecodes = @@ -2341,10 +2173,10 @@ void Shell::TestVerifySourcePositions( i::Handle<i::ByteArray> bytecode_offsets; std::unique_ptr<i::baseline::BytecodeOffsetIterator> offset_iterator; if (has_baseline) { - bytecode_offsets = - handle(i::ByteArray::cast( - function->shared().GetCode().bytecode_offset_table()), - i_isolate); + bytecode_offsets = handle( + i::ByteArray::cast( + function->shared().GetCode(i_isolate).bytecode_offset_table()), + i_isolate); offset_iterator = std::make_unique<i::baseline::BytecodeOffsetIterator>( bytecode_offsets, bytecodes); // A freshly initiated BytecodeOffsetIterator points to the prologue. @@ -2357,7 +2189,7 @@ void Shell::TestVerifySourcePositions( if (has_baseline) { if (offset_iterator->current_bytecode_offset() != bytecode_iterator.current_offset()) { - isolate->ThrowError("Baseline bytecode offset mismatch."); + ThrowError(isolate, "Baseline bytecode offset mismatch."); return; } // Check that we map every address to this bytecode correctly. @@ -2369,8 +2201,8 @@ void Shell::TestVerifySourcePositions( pc_lookup.AdvanceToPCOffset(pc); if (pc_lookup.current_bytecode_offset() != bytecode_iterator.current_offset()) { - isolate->ThrowError( - "Baseline bytecode offset mismatch for PC lookup."); + ThrowError(isolate, + "Baseline bytecode offset mismatch for PC lookup."); return; } } @@ -2378,14 +2210,14 @@ void Shell::TestVerifySourcePositions( bytecode_iterator.Advance(); if (has_baseline && !bytecode_iterator.done()) { if (offset_iterator->done()) { - isolate->ThrowError("Missing bytecode(s) in baseline offset mapping."); + ThrowError(isolate, "Missing bytecode(s) in baseline offset mapping."); return; } offset_iterator->Advance(); } } if (has_baseline && !offset_iterator->done()) { - isolate->ThrowError("Excess offsets in baseline offset mapping."); + ThrowError(isolate, "Excess offsets in baseline offset mapping."); return; } } @@ -2442,9 +2274,9 @@ void Shell::SetPromiseHooks(const v8::FunctionCallbackInfo<v8::Value>& args) { // 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"); + ThrowError(isolate, + "d8.promise.setHooks is disabled with " + "--correctness-fuzzer-suppressions"); return; } #ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS @@ -2459,9 +2291,9 @@ void Shell::SetPromiseHooks(const v8::FunctionCallbackInfo<v8::Value>& args) { args.GetReturnValue().Set(v8::Undefined(isolate)); #else // V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS - isolate->ThrowError( - "d8.promise.setHooks is disabled due to missing build flag " - "v8_enabale_javascript_in_promise_hooks"); + ThrowError(isolate, + "d8.promise.setHooks is disabled due to missing build flag " + "v8_enabale_javascript_in_promise_hooks"); #endif // V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS } @@ -2494,7 +2326,7 @@ void Shell::SerializerDeserialize( Local<Context> context = isolate->GetCurrentContext(); if (!args[0]->IsArrayBuffer()) { - isolate->ThrowError("Can only deserialize from an ArrayBuffer"); + ThrowError(isolate, "Can only deserialize from an ArrayBuffer"); return; } std::shared_ptr<BackingStore> backing_store = @@ -2509,10 +2341,75 @@ void Shell::SerializerDeserialize( args.GetReturnValue().Set(result); } -void WriteToFile(FILE* file, const v8::FunctionCallbackInfo<v8::Value>& args) { - for (int i = 0; i < args.Length(); i++) { +void Shell::ProfilerSetOnProfileEndListener( + const v8::FunctionCallbackInfo<v8::Value>& args) { + Isolate* isolate = args.GetIsolate(); + HandleScope handle_scope(isolate); + if (!args[0]->IsFunction()) { + ThrowError(isolate, "The OnProfileEnd listener has to be a function"); + return; + } + base::MutexGuard lock_guard(&profiler_end_callback_lock_); + profiler_end_callback_[isolate] = + std::make_pair(Global<Function>(isolate, args[0].As<Function>()), + Global<Context>(isolate, isolate->GetCurrentContext())); +} + +bool Shell::HasOnProfileEndListener(Isolate* isolate) { + base::MutexGuard lock_guard(&profiler_end_callback_lock_); + return profiler_end_callback_.find(isolate) != profiler_end_callback_.end(); +} + +void Shell::ResetOnProfileEndListener(Isolate* isolate) { + // If the inspector is enabled, then the installed console is not the + // D8Console. + if (options.enable_inspector) return; + { + base::MutexGuard lock_guard(&profiler_end_callback_lock_); + profiler_end_callback_.erase(isolate); + } + + i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); + D8Console* console = + reinterpret_cast<D8Console*>(i_isolate->console_delegate()); + if (console) { + console->DisposeProfiler(); + } +} + +void Shell::ProfilerTriggerSample( + const v8::FunctionCallbackInfo<v8::Value>& args) { + Isolate* isolate = args.GetIsolate(); + i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); + D8Console* console = + reinterpret_cast<D8Console*>(i_isolate->console_delegate()); + if (console && console->profiler()) { + console->profiler()->CollectSample(isolate); + } +} + +void Shell::TriggerOnProfileEndListener(Isolate* isolate, std::string profile) { + CHECK(HasOnProfileEndListener(isolate)); + Local<Function> callback; + Local<Context> context; + Local<Value> argv[1] = { + String::NewFromUtf8(isolate, profile.c_str()).ToLocalChecked()}; + { + base::MutexGuard lock_guard(&profiler_end_callback_lock_); + auto& callback_pair = profiler_end_callback_[isolate]; + callback = callback_pair.first.Get(isolate); + context = callback_pair.second.Get(isolate); + } + TryCatch try_catch(isolate); + try_catch.SetVerbose(true); + USE(callback->Call(context, Undefined(isolate), 1, argv)); +} + +void WriteToFile(FILE* file, const v8::FunctionCallbackInfo<v8::Value>& args, + int first_arg_index = 0) { + for (int i = first_arg_index; i < args.Length(); i++) { HandleScope handle_scope(args.GetIsolate()); - if (i != 0) { + if (i != first_arg_index) { fprintf(file, " "); } @@ -2558,10 +2455,59 @@ void Shell::WriteStdout(const v8::FunctionCallbackInfo<v8::Value>& args) { WriteToFile(stdout, args); } +// There are two overloads of writeFile(). +// +// The first parameter is always the filename. +// +// If there are exactly 2 arguments, and the second argument is an ArrayBuffer +// or an ArrayBufferView, write the binary contents into the file. +// +// Otherwise, convert arguments to UTF-8 strings, and write them to the file, +// separated by space. +void Shell::WriteFile(const v8::FunctionCallbackInfo<v8::Value>& args) { + String::Utf8Value file_name(args.GetIsolate(), args[0]); + if (*file_name == nullptr) { + ThrowError(args.GetIsolate(), "Error converting filename to string"); + return; + } + FILE* file; + if (args.Length() == 2 && + (args[1]->IsArrayBuffer() || args[1]->IsArrayBufferView())) { + file = base::Fopen(*file_name, "wb"); + if (file == nullptr) { + ThrowError(args.GetIsolate(), "Error opening file"); + return; + } + + void* data; + size_t length; + if (args[1]->IsArrayBuffer()) { + Local<v8::ArrayBuffer> buffer = Local<v8::ArrayBuffer>::Cast(args[1]); + length = buffer->ByteLength(); + data = buffer->Data(); + } else { + Local<v8::ArrayBufferView> buffer_view = + Local<v8::ArrayBufferView>::Cast(args[1]); + length = buffer_view->ByteLength(); + data = static_cast<uint8_t*>(buffer_view->Buffer()->Data()) + + buffer_view->ByteOffset(); + } + fwrite(data, 1, length, file); + } else { + file = base::Fopen(*file_name, "w"); + if (file == nullptr) { + ThrowError(args.GetIsolate(), "Error opening file"); + return; + } + WriteToFile(file, args, 1); + } + base::Fclose(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"); + ThrowError(args.GetIsolate(), "Error converting filename to string"); return; } if (args.Length() == 2) { @@ -2624,7 +2570,8 @@ void Shell::ExecuteFile(const v8::FunctionCallbackInfo<v8::Value>& args) { if (*file_name == nullptr) { std::ostringstream oss; oss << "Cannot convert file[" << i << "] name to string."; - isolate->ThrowError( + ThrowError( + isolate, String::NewFromUtf8(isolate, oss.str().c_str()).ToLocalChecked()); return; } @@ -2638,7 +2585,8 @@ void Shell::ExecuteFile(const v8::FunctionCallbackInfo<v8::Value>& args) { kNoProcessMessageQueue)) { std::ostringstream oss; oss << "Error executing file: \"" << *file_name << '"'; - isolate->ThrowError( + ThrowError( + isolate, String::NewFromUtf8(isolate, oss.str().c_str()).ToLocalChecked()); return; } @@ -2701,7 +2649,7 @@ bool Shell::FunctionAndArgumentsToString(Local<Function> function, function->FunctionProtoToString(context); Local<String> function_string; if (!maybe_function_string.ToLocal(&function_string)) { - isolate->ThrowError("Failed to convert function to string"); + ThrowError(isolate, "Failed to convert function to string"); return false; } *source = String::NewFromUtf8Literal(isolate, "("); @@ -2710,7 +2658,7 @@ bool Shell::FunctionAndArgumentsToString(Local<Function> function, *source = String::Concat(isolate, *source, middle); if (!arguments.IsEmpty() && !arguments->IsUndefined()) { if (!arguments->IsArray()) { - isolate->ThrowError("'arguments' must be an array"); + ThrowError(isolate, "'arguments' must be an array"); return false; } Local<String> comma = String::NewFromUtf8Literal(isolate, ","); @@ -2722,12 +2670,12 @@ bool Shell::FunctionAndArgumentsToString(Local<Function> function, MaybeLocal<Value> maybe_argument = array->Get(context, i); Local<Value> argument; if (!maybe_argument.ToLocal(&argument)) { - isolate->ThrowError("Failed to get argument"); + ThrowError(isolate, "Failed to get argument"); return false; } Local<String> argument_string; if (!JSON::Stringify(context, argument).ToLocal(&argument_string)) { - isolate->ThrowError("Failed to convert argument to string"); + ThrowError(isolate, "Failed to convert argument to string"); return false; } *source = String::Concat(isolate, *source, argument_string); @@ -2791,18 +2739,18 @@ void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) { Isolate* isolate = args.GetIsolate(); HandleScope handle_scope(isolate); if (args.Length() < 1 || (!args[0]->IsString() && !args[0]->IsFunction())) { - isolate->ThrowError("1st argument must be a string or a function"); + ThrowError(isolate, "1st argument must be a string or a function"); return; } Local<String> source; if (!ReadSource(args, 0, CodeType::kFileName).ToLocal(&source)) { - isolate->ThrowError("Invalid argument"); + ThrowError(isolate, "Invalid argument"); return; } if (!args.IsConstructCall()) { - isolate->ThrowError("Worker must be constructed with new"); + ThrowError(isolate, "Worker must be constructed with new"); return; } @@ -2821,7 +2769,7 @@ void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) { String::Utf8Value script(isolate, source); if (!*script) { - isolate->ThrowError("Can't get worker script"); + ThrowError(isolate, "Can't get worker script"); return; } @@ -2835,7 +2783,7 @@ void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) { i_isolate, kWorkerSizeEstimate, worker); args.Holder()->SetInternalField(0, Utils::ToLocal(managed)); if (!Worker::StartWorkerThread(isolate, std::move(worker))) { - isolate->ThrowError("Can't start thread"); + ThrowError(isolate, "Can't start thread"); return; } } @@ -2846,7 +2794,7 @@ void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) { HandleScope handle_scope(isolate); if (args.Length() < 1) { - isolate->ThrowError("Invalid argument"); + ThrowError(isolate, "Invalid argument"); return; } @@ -2913,6 +2861,7 @@ void Shell::QuitOnce(v8::FunctionCallbackInfo<v8::Value>* args) { ->Int32Value(args->GetIsolate()->GetCurrentContext()) .FromMaybe(0); Isolate* isolate = args->GetIsolate(); + ResetOnProfileEndListener(isolate); isolate->Exit(); // As we exit the process anyway, we do not dispose the platform and other @@ -2923,10 +2872,30 @@ void Shell::QuitOnce(v8::FunctionCallbackInfo<v8::Value>* args) { i_isolate->thread_manager()->Unlock(); } + // When disposing the shared space isolate, the workers (client isolates) need + // to be terminated first. + if (i_isolate->is_shared_space_isolate()) { + i::ParkedScope parked(i_isolate->main_thread_local_isolate()); + WaitForRunningWorkers(parked); + } + OnExit(isolate, false); base::OS::ExitProcess(exit_code); } +void Shell::Terminate(const v8::FunctionCallbackInfo<v8::Value>& args) { + // Triggering termination from JS can cause some non-determinism thus we + // skip it for correctness fuzzing. + // Termination also currently breaks Fuzzilli's REPRL mechanism as the + // scheduled termination will prevent the next testcase sent by Fuzzilli from + // being processed. This will in turn desynchronize the communication + // between d8 and Fuzzilli, leading to a crash. + if (!i::v8_flags.correctness_fuzzer_suppressions && !fuzzilli_reprl) { + auto v8_isolate = args.GetIsolate(); + if (!v8_isolate->IsExecutionTerminating()) v8_isolate->TerminateExecution(); + } +} + void Shell::Quit(const v8::FunctionCallbackInfo<v8::Value>& args) { base::CallOnce(&quit_once_, &QuitOnce, const_cast<v8::FunctionCallbackInfo<v8::Value>*>(&args)); @@ -2961,7 +2930,6 @@ void Shell::Fuzzilli(const v8::FunctionCallbackInfo<v8::Value>& args) { if (*operation == nullptr) { return; } - if (strcmp(*operation, "FUZZILLI_CRASH") == 0) { auto arg = args[1] ->Int32Value(args.GetIsolate()->GetCurrentContext()) @@ -3273,6 +3241,10 @@ Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) { FunctionTemplate::New(isolate, PrintErr)); global_template->Set(isolate, "write", FunctionTemplate::New(isolate, WriteStdout)); + if (!i::v8_flags.fuzzing) { + global_template->Set(isolate, "writeFile", + FunctionTemplate::New(isolate, WriteFile)); + } global_template->Set(isolate, "read", FunctionTemplate::New(isolate, ReadFile)); global_template->Set(isolate, "readbuffer", @@ -3436,21 +3408,9 @@ 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); { @@ -3535,6 +3495,21 @@ Local<ObjectTemplate> Shell::CreateD8Template(Isolate* isolate) { Local<Signature>(), 1)); d8_template->Set(isolate, "serializer", serializer_template); } + { + Local<ObjectTemplate> profiler_template = ObjectTemplate::New(isolate); + profiler_template->Set( + isolate, "setOnProfileEndListener", + FunctionTemplate::New(isolate, ProfilerSetOnProfileEndListener)); + profiler_template->Set( + isolate, "triggerSample", + FunctionTemplate::New(isolate, ProfilerTriggerSample)); + d8_template->Set(isolate, "profiler", profiler_template); + } + d8_template->Set(isolate, "terminate", + FunctionTemplate::New(isolate, Terminate)); + if (!options.omit_quit) { + d8_template->Set(isolate, "quit", FunctionTemplate::New(isolate, Quit)); + } return d8_template; } @@ -3645,25 +3620,6 @@ void Shell::Initialize(Isolate* isolate, D8Console* console, [](Local<Object> host, v8::AccessType type, Local<Value> data) {}); } -#ifdef V8_FUZZILLI - // Let the parent process (Fuzzilli) know we are ready. - if (options.fuzzilli_enable_builtins_coverage) { - cov_init_builtins_edges(static_cast<uint32_t>( - i::BasicBlockProfiler::Get() - ->GetCoverageBitmap(reinterpret_cast<i::Isolate*>(isolate)) - .size())); - } - char helo[] = "HELO"; - if (write(REPRL_CWFD, helo, 4) != 4 || read(REPRL_CRFD, helo, 4) != 4) { - fuzzilli_reprl = false; - } - - if (memcmp(helo, "HELO", 4) != 0) { - fprintf(stderr, "Invalid response from parent\n"); - _exit(-1); - } -#endif // V8_FUZZILLI - debug::SetConsoleDelegate(isolate, console); } @@ -3672,7 +3628,7 @@ Local<String> Shell::WasmLoadSourceMapCallback(Isolate* isolate, return Shell::ReadFile(isolate, path, false).ToLocalChecked(); } -Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) { +MaybeLocal<Context> Shell::CreateEvaluationContext(Isolate* isolate) { // This needs to be a critical section since this is not thread-safe i::ParkedMutexGuard lock_guard( reinterpret_cast<i::Isolate*>(isolate)->main_thread_local_isolate(), @@ -3681,8 +3637,10 @@ Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) { Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate); EscapableHandleScope handle_scope(isolate); Local<Context> context = Context::New(isolate, nullptr, global_template); - DCHECK_IMPLIES(context.IsEmpty(), isolate->IsExecutionTerminating()); - if (context.IsEmpty()) return {}; + if (context.IsEmpty()) { + DCHECK(isolate->IsExecutionTerminating()); + return {}; + } if (i::v8_flags.perf_prof_annotate_wasm || i::v8_flags.vtune_prof_annotate_wasm) { isolate->SetWasmLoadSourceMapCallback(Shell::WasmLoadSourceMapCallback); @@ -4033,13 +3991,13 @@ void Shell::ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args) { String::Utf8Value filename(isolate, args[0]); int length; if (*filename == nullptr) { - isolate->ThrowError("Error loading file"); + ThrowError(isolate, "Error loading file"); return; } uint8_t* data = reinterpret_cast<uint8_t*>(ReadChars(*filename, &length)); if (data == nullptr) { - isolate->ThrowError("Error reading file"); + ThrowError(isolate, "Error reading file"); return; } Local<v8::ArrayBuffer> buffer = ArrayBuffer::New(isolate, length); @@ -4059,10 +4017,10 @@ MaybeLocal<String> Shell::ReadFile(Isolate* isolate, const char* name, if (should_throw) { std::ostringstream oss; oss << "Error loading file: " << name; - isolate->ThrowError( - v8::String::NewFromUtf8( - isolate, oss.str().substr(0, String::kMaxLength).c_str()) - .ToLocalChecked()); + ThrowError(isolate, + v8::String::NewFromUtf8( + isolate, oss.str().substr(0, String::kMaxLength).c_str()) + .ToLocalChecked()); } return MaybeLocal<String>(); } @@ -4175,7 +4133,8 @@ class InspectorClient : public v8_inspector::V8InspectorClient { inspector_ = v8_inspector::V8Inspector::create(isolate_, this); session_ = inspector_->connect(1, channel_.get(), v8_inspector::StringView(), - v8_inspector::V8Inspector::kFullyTrusted); + v8_inspector::V8Inspector::kFullyTrusted, + v8_inspector::V8Inspector::kNotWaitingForDebugger); context->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this); inspector_->contextCreated(v8_inspector::V8ContextInfo( context, kContextGroupId, v8_inspector::StringView())); @@ -4339,15 +4298,6 @@ bool SourceGroup::Execute(Isolate* isolate) { break; } continue; - } else if (strcmp(arg, "--web-snapshot") == 0 && i + 1 < end_offset_) { - // Treat the next file as a web snapshot. - arg = argv_[++i]; - Shell::set_script_executed(); - if (!Shell::ExecuteWebSnapshot(isolate, arg)) { - success = false; - break; - } - continue; } else if (strcmp(arg, "--json") == 0 && i + 1 < end_offset_) { // Treat the next file as a JSON file. arg = argv_[++i]; @@ -4380,13 +4330,6 @@ bool SourceGroup::Execute(Isolate* isolate) { break; } } - if (!success) { - return false; - } - if (Shell::options.web_snapshot_config || - Shell::options.web_snapshot_output) { - success = Shell::TakeWebSnapshot(isolate); - } return success; } @@ -4398,32 +4341,42 @@ void SourceGroup::ExecuteInThread() { create_params.array_buffer_allocator = Shell::array_buffer_allocator; Isolate* isolate = Isolate::New(create_params); Shell::SetWaitUntilDone(isolate, false); - D8Console console(isolate); - Shell::Initialize(isolate, &console, false); - for (int i = 0; i < Shell::options.stress_runs; ++i) { - { - next_semaphore_.ParkedWait( - reinterpret_cast<i::Isolate*>(isolate)->main_thread_local_isolate()); - } - { - Isolate::Scope iscope(isolate); - PerIsolateData data(isolate); + { + Isolate::Scope isolate_scope(isolate); + D8Console console(isolate); + Shell::Initialize(isolate, &console, false); + PerIsolateData data(isolate); + + for (int i = 0; i < Shell::options.stress_runs; ++i) { + { + next_semaphore_.ParkedWait(reinterpret_cast<i::Isolate*>(isolate) + ->main_thread_local_isolate()); + } { - HandleScope scope(isolate); - Local<Context> context = Shell::CreateEvaluationContext(isolate); { - Context::Scope cscope(context); - InspectorClient inspector_client(context, - Shell::options.enable_inspector); - PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate)); - Execute(isolate); - Shell::CompleteMessageLoop(isolate); + HandleScope scope(isolate); + Local<Context> context; + if (!Shell::CreateEvaluationContext(isolate).ToLocal(&context)) { + DCHECK(isolate->IsExecutionTerminating()); + break; + } + { + Context::Scope context_scope(context); + InspectorClient inspector_client(context, + Shell::options.enable_inspector); + PerIsolateData::RealmScope realm_scope( + PerIsolateData::Get(isolate)); + Execute(isolate); + Shell::CompleteMessageLoop(isolate); + } } + Shell::CollectGarbage(isolate); } - Shell::CollectGarbage(isolate); + done_semaphore_.Signal(); } - done_semaphore_.Signal(); + + Shell::ResetOnProfileEndListener(isolate); } isolate->Dispose(); @@ -4597,7 +4550,7 @@ void Worker::ProcessMessage(std::unique_ptr<SerializationData> data) { DCHECK_NOT_NULL(isolate_); HandleScope scope(isolate_); Local<Context> context = context_.Get(isolate_); - Context::Scope cscope(context); + Context::Scope context_scope(context); Local<Object> global = context->Global(); // Get the message handler. @@ -4612,6 +4565,7 @@ void Worker::ProcessMessage(std::unique_ptr<SerializationData> data) { try_catch.SetVerbose(true); Local<Value> value; if (Shell::DeserializeValue(isolate_, std::move(data)).ToLocal(&value)) { + DCHECK(!isolate_->IsExecutionTerminating()); Local<Value> argv[] = {value}; MaybeLocal<Value> result = onmessage_fun->Call(context, global, 1, argv); USE(result); @@ -4646,72 +4600,81 @@ void Worker::ExecuteInThread() { // The Worker is now ready to receive messages. started_semaphore_.Signal(); - D8Console console(isolate_); - Shell::Initialize(isolate_, &console, false); - // This is not really a loop, but the loop allows us to break out of this - // block easily. - for (bool execute = true; execute; execute = false) { - Isolate::Scope iscope(isolate_); - { - HandleScope scope(isolate_); - PerIsolateData data(isolate_); - Local<Context> context = Shell::CreateEvaluationContext(isolate_); - if (context.IsEmpty()) break; - context_.Reset(isolate_, context); + { + Isolate::Scope isolate_scope(isolate_); + D8Console console(isolate_); + Shell::Initialize(isolate_, &console, false); + PerIsolateData data(isolate_); + // This is not really a loop, but the loop allows us to break out of this + // block easily. + for (bool execute = true; execute; execute = false) { { - Context::Scope cscope(context); - PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate_)); - - Local<Object> global = context->Global(); - Local<Value> this_value = External::New(isolate_, this); - Local<FunctionTemplate> postmessage_fun_template = - FunctionTemplate::New(isolate_, PostMessageOut, this_value); - - Local<Function> postmessage_fun; - if (postmessage_fun_template->GetFunction(context).ToLocal( - &postmessage_fun)) { - global - ->Set(context, + HandleScope scope(isolate_); + Local<Context> context; + if (!Shell::CreateEvaluationContext(isolate_).ToLocal(&context)) { + DCHECK(isolate_->IsExecutionTerminating()); + break; + } + context_.Reset(isolate_, context); + { + Context::Scope context_scope(context); + PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate_)); + + Local<Object> global = context->Global(); + Local<Value> this_value = External::New(isolate_, this); + Local<FunctionTemplate> postmessage_fun_template = + FunctionTemplate::New(isolate_, PostMessageOut, this_value); + + Local<Function> postmessage_fun; + if (postmessage_fun_template->GetFunction(context).ToLocal( + &postmessage_fun)) { + global + ->Set( + context, v8::String::NewFromUtf8Literal( isolate_, "postMessage", NewStringType::kInternalized), postmessage_fun) - .FromJust(); - } + .FromJust(); + } - // First run the script - Local<String> file_name = - String::NewFromUtf8Literal(isolate_, "unnamed"); - Local<String> source = - String::NewFromUtf8(isolate_, script_).ToLocalChecked(); - if (Shell::ExecuteString( - isolate_, source, file_name, Shell::kNoPrintResult, - Shell::kReportExceptions, Shell::kProcessMessageQueue)) { - // Check that there's a message handler - MaybeLocal<Value> maybe_onmessage = global->Get( - context, - String::NewFromUtf8Literal(isolate_, "onmessage", - NewStringType::kInternalized)); - Local<Value> onmessage; - if (maybe_onmessage.ToLocal(&onmessage) && onmessage->IsFunction()) { - // Now wait for messages. - ProcessMessages(); + // First run the script + Local<String> file_name = + String::NewFromUtf8Literal(isolate_, "unnamed"); + Local<String> source = + String::NewFromUtf8(isolate_, script_).ToLocalChecked(); + if (Shell::ExecuteString( + isolate_, source, file_name, Shell::kNoPrintResult, + Shell::kReportExceptions, Shell::kProcessMessageQueue)) { + // Check that there's a message handler + MaybeLocal<Value> maybe_onmessage = global->Get( + context, + String::NewFromUtf8Literal(isolate_, "onmessage", + NewStringType::kInternalized)); + Local<Value> onmessage; + if (maybe_onmessage.ToLocal(&onmessage) && + onmessage->IsFunction()) { + // Now wait for messages. + ProcessMessages(); + } } } } + Shell::CollectGarbage(isolate_); } - Shell::CollectGarbage(isolate_); - } - { - base::MutexGuard lock_guard(&worker_mutex_); - state_.store(State::kTerminated); - CHECK(!is_running()); - task_runner_.reset(); - task_manager_ = nullptr; + { + base::MutexGuard lock_guard(&worker_mutex_); + state_.store(State::kTerminated); + CHECK(!is_running()); + task_runner_.reset(); + task_manager_ = nullptr; + } + + Shell::ResetOnProfileEndListener(isolate_); + context_.Reset(); + platform::NotifyIsolateShutdown(g_default_platform, isolate_); } - context_.Reset(); - platform::NotifyIsolateShutdown(g_default_platform, isolate_); isolate_->Dispose(); isolate_ = nullptr; @@ -4725,7 +4688,7 @@ void Worker::PostMessageOut(const v8::FunctionCallbackInfo<v8::Value>& args) { HandleScope handle_scope(isolate); if (args.Length() < 1) { - isolate->ThrowError("Invalid argument"); + ThrowError(isolate, "Invalid argument"); return; } @@ -4940,15 +4903,6 @@ bool Shell::SetOptions(int argc, char* argv[]) { } else if (strcmp(argv[i], "--stress-deserialize") == 0) { options.stress_deserialize = true; argv[i] = nullptr; - } else if (strncmp(argv[i], "--web-snapshot-config=", 22) == 0) { - options.web_snapshot_config = argv[i] + 22; - argv[i] = nullptr; - } else if (strncmp(argv[i], "--web-snapshot-output=", 22) == 0) { - options.web_snapshot_output = 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; @@ -4960,8 +4914,8 @@ bool Shell::SetOptions(int argc, char* argv[]) { options.max_serializer_memory = atoi(argv[i] + 24) * i::MB; argv[i] = nullptr; #ifdef V8_FUZZILLI - } else if (strcmp(argv[i], "--no-fuzzilli-enable-builtins-coverage") == 0) { - options.fuzzilli_enable_builtins_coverage = false; + } else if (strcmp(argv[i], "--fuzzilli-enable-builtins-coverage") == 0) { + options.fuzzilli_enable_builtins_coverage = true; argv[i] = nullptr; } else if (strcmp(argv[i], "--fuzzilli-coverage-statistics") == 0) { options.fuzzilli_coverage_statistics = true; @@ -5027,12 +4981,11 @@ bool Shell::SetOptions(int argc, char* argv[]) { const char* usage = "Synopsis:\n" " shell [options] [--shell] [<file>...]\n" - " d8 [options] [-e <string>] [--shell] [[--module|--web-snapshot]" + " d8 [options] [-e <string>] [--shell] [--module|]" " <file>...]\n\n" " -e execute a string in V8\n" " --shell run an interactive JavaScript shell\n" - " --module execute a file as a JavaScript module\n" - " --web-snapshot execute a file as a web snapshot\n\n"; + " --module execute a file as a JavaScript module\n"; using HelpOptions = i::FlagList::HelpOptions; i::v8_flags.abort_on_contradictory_flags = true; i::FlagList::SetFlagsFromCommandLine(&argc, argv, true, @@ -5059,9 +5012,7 @@ bool Shell::SetOptions(int argc, char* argv[]) { current->End(i); current++; current->Begin(argv, i + 1); - } else if (strcmp(str, "--module") == 0 || - strcmp(str, "--web-snapshot") == 0 || - strcmp(str, "--json") == 0) { + } else if (strcmp(str, "--module") == 0 || strcmp(str, "--json") == 0) { // Pass on to SourceGroup, which understands these options. } else if (strncmp(str, "--", 2) == 0) { if (!i::v8_flags.correctness_fuzzer_suppressions) { @@ -5083,45 +5034,11 @@ bool Shell::SetOptions(int argc, char* argv[]) { return true; } -int Shell::RunMain(Isolate* isolate, bool last_run) { +int Shell::RunMain(v8::Isolate* isolate, bool last_run) { for (int i = 1; i < options.num_isolates; ++i) { options.isolate_sources[i].StartExecuteInThread(); } - bool success = true; - { - SetWaitUntilDone(isolate, false); - if (options.lcov_file) { - debug::Coverage::SelectMode(isolate, debug::CoverageMode::kBlockCount); - } - 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. - evaluation_context_.Reset(isolate, context); - } - { - Context::Scope cscope(context); - InspectorClient inspector_client(context, options.enable_inspector); - PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate)); - if (!options.isolate_sources[0].Execute(isolate)) success = false; - if (!CompleteMessageLoop(isolate)) success = false; - } - WriteLcovData(isolate, options.lcov_file); - if (last_run && i::v8_flags.stress_snapshot) { - static constexpr bool kClearRecompilableData = true; - i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); - i::Handle<i::Context> i_context = Utils::OpenHandle(*context); - // TODO(jgruber,v8:10500): Don't deoptimize once we support serialization - // of optimized code. - i::Deoptimizer::DeoptimizeAll(i_isolate); - i::Snapshot::ClearReconstructableDataForSerialization( - i_isolate, kClearRecompilableData); - i::Snapshot::SerializeDeserializeAndVerifyForTesting(i_isolate, - i_context); - } - } + bool success = RunMainIsolate(isolate, last_run); CollectGarbage(isolate); // Park the main thread here to prevent deadlocks in shared GCs when waiting @@ -5150,12 +5067,64 @@ int Shell::RunMain(Isolate* isolate, bool last_run) { return (success == Shell::options.expected_to_throw ? 1 : 0); } +bool Shell::RunMainIsolate(v8::Isolate* isolate, bool last_run) { + Shell::SetWaitUntilDone(isolate, false); + if (options.lcov_file) { + debug::Coverage::SelectMode(isolate, debug::CoverageMode::kBlockCount); + } + HandleScope scope(isolate); + Local<Context> context; + if (!CreateEvaluationContext(isolate).ToLocal(&context)) { + DCHECK(isolate->IsExecutionTerminating()); + // We must not exit early here in REPRL mode as that would cause the next + // testcase sent by Fuzzilli to be skipped, which will desynchronize the + // communication between d8 and Fuzzilli, leading to a crash. + DCHECK(!fuzzilli_reprl); + return false; + } + bool use_existing_context = last_run && use_interactive_shell(); + if (use_existing_context) { + // Keep using the same context in the interactive shell. + evaluation_context_.Reset(isolate, context); + } + bool success = true; + { + Context::Scope context_scope(context); + InspectorClient inspector_client(context, options.enable_inspector); + PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate)); + if (!options.isolate_sources[0].Execute(isolate)) success = false; + if (!CompleteMessageLoop(isolate)) success = false; + } + WriteLcovData(isolate, options.lcov_file); + if (last_run && i::v8_flags.stress_snapshot) { + { + // We can't run the serializer while workers are still active. Ideally, + // we'd terminate these properly (see WaitForRunningWorkers), but that's + // not easily possible due to ordering issues. It's not expected to be a + // common case, and it's unrelated to issues that stress_snapshot is + // intended to catch - simply bail out. + base::MutexGuard lock_guard(workers_mutex_.Pointer()); + if (!running_workers_.empty()) { + printf("Warning: stress_snapshot disabled due to active workers\n"); + return success; + } + } + + static constexpr bool kClearRecompilableData = true; + i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); + i::Handle<i::Context> i_context = Utils::OpenHandle(*context); + // TODO(jgruber,v8:10500): Don't deoptimize once we support serialization + // of optimized code. + i::Deoptimizer::DeoptimizeAll(i_isolate); + i::Snapshot::ClearReconstructableDataForSerialization( + i_isolate, kClearRecompilableData); + i::Snapshot::SerializeDeserializeAndVerifyForTesting(i_isolate, i_context); + } + return success; +} void Shell::CollectGarbage(Isolate* isolate) { if (options.send_idle_notification) { - const double kLongIdlePauseInSeconds = 1.0; isolate->ContextDisposedNotification(); - isolate->IdleNotificationDeadline( - g_platform->MonotonicallyIncreasingTime() + kLongIdlePauseInSeconds); } if (options.invoke_weak_callbacks) { // By sending a low memory notifications, we will try hard to collect all @@ -5210,8 +5179,10 @@ bool ProcessMessages( SealHandleScope shs(isolate); for (bool ran_tasks = true; ran_tasks;) { // Execute one foreground task (if one exists), then microtasks. + if (isolate->IsExecutionTerminating()) return false; ran_tasks = v8::platform::PumpMessageLoop(g_default_platform, isolate, behavior()); + if (isolate->IsExecutionTerminating()) return false; if (ran_tasks) MicrotasksScope::PerformCheckpoint(isolate); // In predictable mode we push all background tasks into the foreground @@ -5219,11 +5190,13 @@ bool ProcessMessages( // isolate. We execute all background tasks after running one foreground // task. if (i::v8_flags.verify_predictable) { + if (isolate->IsExecutionTerminating()) return false; while (v8::platform::PumpMessageLoop( g_default_platform, kProcessGlobalPredictablePlatformWorkerTaskQueue, platform::MessageLoopBehavior::kDoNotWait)) { ran_tasks = true; + if (isolate->IsExecutionTerminating()) return false; } } } @@ -5231,6 +5204,7 @@ bool ProcessMessages( v8::platform::RunIdleTasks(g_default_platform, isolate, 50.0 / base::Time::kMillisecondsPerSecond); } + if (isolate->IsExecutionTerminating()) return false; bool ran_set_timeout = false; if (!RunSetTimeoutCallback(isolate, &ran_set_timeout)) return false; if (!ran_set_timeout) return true; @@ -5559,7 +5533,6 @@ std::unique_ptr<SerializationData> Shell::SerializeValue( MaybeLocal<Value> Shell::DeserializeValue( Isolate* isolate, std::unique_ptr<SerializationData> data) { - Local<Value> value; Local<Context> context = isolate->GetCurrentContext(); Deserializer deserializer(isolate, std::move(data)); return deserializer.ReadValue(context); @@ -5790,11 +5763,37 @@ int Shell::Main(int argc, char* argv[]) { } #endif // V8_ENABLE_WEBASSEMBLY + if (i::v8_flags.experimental) { + // This message is printed to stderr so that it is also visible in + // Clusterfuzz reports. + fprintf(stderr, + "V8 is running with experimental features enabled. Stability and " + "security will suffer.\n"); + } + Isolate* isolate = Isolate::New(create_params); +#ifdef V8_FUZZILLI + // Let the parent process (Fuzzilli) know we are ready. + if (options.fuzzilli_enable_builtins_coverage) { + cov_init_builtins_edges(static_cast<uint32_t>( + i::BasicBlockProfiler::Get() + ->GetCoverageBitmap(reinterpret_cast<i::Isolate*>(isolate)) + .size())); + } + char helo[] = "HELO"; + if (write(REPRL_CWFD, helo, 4) != 4 || read(REPRL_CRFD, helo, 4) != 4) { + fuzzilli_reprl = false; + } + + if (memcmp(helo, "HELO", 4) != 0) { + FATAL("REPRL: Invalid response from parent"); + } +#endif // V8_FUZZILLI + { - D8Console console(isolate); Isolate::Scope scope(isolate); + D8Console console(isolate); Initialize(isolate, &console); PerIsolateData data(isolate); @@ -5805,8 +5804,7 @@ int Shell::Main(int argc, char* argv[]) { unsigned action = 0; ssize_t nread = read(REPRL_CRFD, &action, 4); if (nread != 4 || action != 'cexe') { - fprintf(stderr, "Unknown action: %u\n", action); - _exit(-1); + FATAL("REPRL: Unknown action: %u", action); } } #endif // V8_FUZZILLI @@ -5863,12 +5861,13 @@ int Shell::Main(int argc, char* argv[]) { // Restore old hash seed. i::v8_flags.hash_seed = i::v8_flags.hash_seed ^ 1337; { + Isolate::Scope isolate_scope(isolate2); D8Console console2(isolate2); Initialize(isolate2, &console2); PerIsolateData data2(isolate2); - Isolate::Scope isolate_scope(isolate2); result = RunMain(isolate2, false); + ResetOnProfileEndListener(isolate2); } isolate2->Dispose(); } @@ -5914,11 +5913,6 @@ int Shell::Main(int argc, char* argv[]) { cpu_profiler->Dispose(); } - // Shut down contexts and collect garbage. - cached_code_map_.clear(); - evaluation_context_.Reset(); - stringify_function_.Reset(); - CollectGarbage(isolate); #ifdef V8_FUZZILLI // Send result to parent (fuzzilli) and reset edge guards. if (fuzzilli_reprl) { @@ -5954,6 +5948,13 @@ int Shell::Main(int argc, char* argv[]) { } #endif // V8_FUZZILLI } while (fuzzilli_reprl); + + // Shut down contexts and collect garbage. + cached_code_map_.clear(); + evaluation_context_.Reset(); + stringify_function_.Reset(); + ResetOnProfileEndListener(isolate); + CollectGarbage(isolate); } OnExit(isolate, true); |