Commit 74e42008 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[wasm] Make an "incumbent context" available after async compilation

A Wasm module's start function might be imported from JavaScript, and
as such might contain calls to Blink. For such a case, we must make
sure that an "incumbent context" is available. Usually, having any
JavaScript function on the stack is enough to ensure that; but in the
special case of async compilation, the "success" task is executed
directly from the event loop, so we have to manually enter a context.

Additionally, we need to ensure that such a start function's own context
is properly entered: in addition to setting it as the current context
on the isolate (as the function call sequence is doing), we have to
register it in the list of entered contexts, as if v8::Context::Enter
had been used.

Bug: chromium:1096558
Change-Id: I12679ab49ee764572e3742da24889dcd55c29160
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2292248Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70175}
parent 12cd0359
......@@ -7,7 +7,7 @@
#include <algorithm>
#include <queue>
#include "src/api/api.h"
#include "src/api/api-inl.h"
#include "src/asmjs/asm-js.h"
#include "src/base/enum-set.h"
#include "src/base/optional.h"
......@@ -1774,7 +1774,7 @@ void RecompileNativeModule(NativeModule* native_module,
AsyncCompileJob::AsyncCompileJob(
Isolate* isolate, const WasmFeatures& enabled,
std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
const char* api_method_name,
Handle<Context> incumbent_context, const char* api_method_name,
std::shared_ptr<CompilationResultResolver> resolver)
: isolate_(isolate),
api_method_name_(api_method_name),
......@@ -1793,6 +1793,7 @@ AsyncCompileJob::AsyncCompileJob(
foreground_task_runner_ = platform->GetForegroundTaskRunner(v8_isolate);
native_context_ =
isolate->global_handles()->Create(context->native_context());
incumbent_context_ = isolate->global_handles()->Create(*incumbent_context);
DCHECK(native_context_->IsNativeContext());
context_id_ = isolate->GetOrRegisterRecorderContextId(native_context_);
metrics_event_.async = true;
......@@ -1886,6 +1887,7 @@ AsyncCompileJob::~AsyncCompileJob() {
if (stream_) stream_->NotifyCompilationEnded();
CancelPendingForegroundTask();
isolate_->global_handles()->Destroy(native_context_.location());
isolate_->global_handles()->Destroy(incumbent_context_.location());
if (!module_object_.is_null()) {
isolate_->global_handles()->Destroy(module_object_.location());
}
......@@ -2034,6 +2036,11 @@ void AsyncCompileJob::AsyncCompileFailed() {
void AsyncCompileJob::AsyncCompileSucceeded(Handle<WasmModuleObject> result) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
"wasm.OnCompilationSucceeded");
// We have to make sure that an "incumbent context" is available in case
// the module's start function calls out to Blink.
Local<v8::Context> backup_incumbent_context =
Utils::ToLocal(incumbent_context_);
v8::Context::BackupIncumbentScope incumbent(backup_incumbent_context);
resolver_->OnCompilationSucceeded(result);
}
......
......@@ -114,7 +114,8 @@ class AsyncCompileJob {
public:
AsyncCompileJob(Isolate* isolate, const WasmFeatures& enabled_features,
std::unique_ptr<byte[]> bytes_copy, size_t length,
Handle<Context> context, const char* api_method_name,
Handle<Context> context, Handle<Context> incumbent_context,
const char* api_method_name,
std::shared_ptr<CompilationResultResolver> resolver);
~AsyncCompileJob();
......@@ -212,6 +213,7 @@ class AsyncCompileJob {
// {native_module_}).
ModuleWireBytes wire_bytes_;
Handle<NativeContext> native_context_;
Handle<Context> incumbent_context_;
v8::metrics::Recorder::ContextId context_id_;
v8::metrics::WasmModuleDecoded metrics_event_;
const std::shared_ptr<CompilationResultResolver> resolver_;
......
......@@ -4,6 +4,7 @@
#include "src/wasm/module-instantiate.h"
#include "src/api/api.h"
#include "src/asmjs/asm-js.h"
#include "src/logging/counters.h"
#include "src/logging/metrics.h"
......@@ -722,6 +723,22 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
start_function_ = WasmExportedFunction::New(
isolate_, instance, start_index,
static_cast<int>(function.sig->parameter_count()), wrapper_code);
if (function.imported) {
ImportedFunctionEntry entry(instance, module_->start_function_index);
Object callable = entry.maybe_callable();
if (callable.IsJSFunction()) {
// If the start function was imported and calls into Blink, we have
// to pretend that the V8 API was used to enter its correct context.
// To get that context to {ExecuteStartFunction} below, we install it
// as the context of the wrapper we just compiled. That's a bit of a
// hack because it's not really the wrapper's context, only its wrapped
// target's context, but the end result is the same, and since the
// start function wrapper doesn't leak, neither does this
// implementation detail.
start_function_->set_context(JSFunction::cast(callable).context());
}
}
}
DCHECK(!isolate_->has_pending_exception());
......@@ -740,10 +757,18 @@ bool InstanceBuilder::ExecuteStartFunction() {
if (start_function_.is_null()) return true; // No start function.
HandleScope scope(isolate_);
// In case the start function calls out to Blink, we have to make sure that
// the correct "entered context" is available. This is the equivalent of
// v8::Context::Enter() and must happen in addition to the function call
// sequence doing the compiled version of "isolate->set_context(...)".
HandleScopeImplementer* hsi = isolate_->handle_scope_implementer();
hsi->EnterContext(start_function_->context());
// Call the JS function.
Handle<Object> undefined = isolate_->factory()->undefined_value();
MaybeHandle<Object> retval =
Execution::Call(isolate_, start_function_, undefined, 0, nullptr);
hsi->LeaveContext();
if (retval.is_null()) {
DCHECK(isolate_->has_pending_exception());
......
......@@ -859,9 +859,10 @@ AsyncCompileJob* WasmEngine::CreateAsyncCompileJob(
std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
const char* api_method_name,
std::shared_ptr<CompilationResultResolver> resolver) {
AsyncCompileJob* job =
new AsyncCompileJob(isolate, enabled, std::move(bytes_copy), length,
context, api_method_name, std::move(resolver));
Handle<Context> incumbent_context = isolate->GetIncumbentContext();
AsyncCompileJob* job = new AsyncCompileJob(
isolate, enabled, std::move(bytes_copy), length, context,
incumbent_context, api_method_name, std::move(resolver));
// Pass ownership to the unique_ptr in {async_compile_jobs_}.
base::MutexGuard guard(&mutex_);
async_compile_jobs_[job] = std::unique_ptr<AsyncCompileJob>(job);
......
......@@ -1141,7 +1141,7 @@ void ImportedFunctionEntry::SetWasmToWasm(WasmInstanceObject instance,
WasmInstanceObject ImportedFunctionEntry::instance() {
// The imported reference entry is either a target instance or a tuple
// of this instance and the target callable.
Object value = instance_->imported_function_refs().get(index_);
Object value = object_ref();
if (value.IsWasmInstanceObject()) {
return WasmInstanceObject::cast(value);
}
......@@ -1149,6 +1149,15 @@ WasmInstanceObject ImportedFunctionEntry::instance() {
return WasmInstanceObject::cast(tuple.value1());
}
// Returns an empty Object() if no callable is available, a JSReceiver
// otherwise.
Object ImportedFunctionEntry::maybe_callable() {
Object value = object_ref();
if (!value.IsTuple2()) return Object();
Tuple2 tuple = Tuple2::cast(value);
return JSReceiver::cast(tuple.value2());
}
JSReceiver ImportedFunctionEntry::callable() {
return JSReceiver::cast(Tuple2::cast(object_ref()).value2());
}
......
......@@ -108,6 +108,7 @@ class ImportedFunctionEntry {
WasmInstanceObject instance();
JSReceiver callable();
Object maybe_callable();
Object object_ref();
Address target();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment