Provide a way to propagate contextual script information through promise resolution

This state can be set on the NativeContext by the embedder. When a
PromiseReaction/PromiseReactionJobTask is constructed, store this
contextual state if present, and restore it while the reaction job
is running.

Change-Id: I141cdbd9e36ea83ce4a6bf08440ae7eaa54523df
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2005849
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Sathya Gunasekaran  <gsathya@chromium.org>
Commit-Queue: Nate Chapin <japhet@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66617}
This commit is contained in:
Nate Chapin 2020-02-13 10:22:17 -08:00 committed by Commit Bot
parent 1333214417
commit 9423ede124
9 changed files with 86 additions and 3 deletions

View File

@ -10371,6 +10371,18 @@ class V8_EXPORT Context {
Local<Context> context);
void SetAbortScriptExecution(AbortScriptExecutionCallback callback);
/**
* Returns the value that was set or restored by
* SetContinuationPreservedEmbedderData(), if any.
*/
Local<Value> GetContinuationPreservedEmbedderData() const;
/**
* Sets a value that will be stored on continuations and reset while the
* continuation runs.
*/
void SetContinuationPreservedEmbedderData(Local<Value> context);
/**
* Stack-allocated class which sets the execution context for all
* operations executed within a local scope.

View File

@ -6148,6 +6148,24 @@ void Context::SetAbortScriptExecution(
}
}
Local<Value> Context::GetContinuationPreservedEmbedderData() const {
i::Handle<i::Context> context = Utils::OpenHandle(this);
i::Isolate* isolate = context->GetIsolate();
i::Handle<i::Object> data(
context->native_context().continuation_preserved_embedder_data(),
isolate);
return ToApiHandle<Object>(data);
}
void Context::SetContinuationPreservedEmbedderData(Local<Value> data) {
i::Handle<i::Context> context = Utils::OpenHandle(this);
i::Isolate* isolate = context->GetIsolate();
if (data.IsEmpty())
data = v8::Undefined(reinterpret_cast<v8::Isolate*>(isolate));
context->native_context().set_continuation_preserved_embedder_data(
*i::Handle<i::HeapObject>::cast(Utils::OpenHandle(*data)));
}
namespace {
i::Address* GetSerializedDataFromFixedArray(i::Isolate* isolate,
i::FixedArray list, size_t index) {

View File

@ -223,6 +223,13 @@ void MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask(
const TNode<HeapObject> promise_or_capability = CAST(LoadObjectField(
microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset));
TNode<Object> preserved_embedder_data = LoadObjectField(
microtask,
PromiseReactionJobTask::kContinuationPreservedEmbedderDataOffset);
StoreContextElement(native_context,
Context::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX,
preserved_embedder_data);
// Run the promise before/debug hook if enabled.
RunPromiseHook(Runtime::kPromiseHookBefore, microtask_context,
promise_or_capability);
@ -237,6 +244,10 @@ void MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask(
RunPromiseHook(Runtime::kPromiseHookAfter, microtask_context,
promise_or_capability);
StoreContextElement(native_context,
Context::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX,
UndefinedConstant());
RewindEnteredContext(saved_entered_context_count);
SetCurrentContext(current_context);
Goto(&done);
@ -257,6 +268,13 @@ void MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask(
const TNode<HeapObject> promise_or_capability = CAST(LoadObjectField(
microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset));
TNode<Object> preserved_embedder_data = LoadObjectField(
microtask,
PromiseReactionJobTask::kContinuationPreservedEmbedderDataOffset);
StoreContextElement(native_context,
Context::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX,
preserved_embedder_data);
// Run the promise before/debug hook if enabled.
RunPromiseHook(Runtime::kPromiseHookBefore, microtask_context,
promise_or_capability);
@ -271,6 +289,10 @@ void MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask(
RunPromiseHook(Runtime::kPromiseHookAfter, microtask_context,
promise_or_capability);
StoreContextElement(native_context,
Context::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX,
UndefinedConstant());
RewindEnteredContext(saved_entered_context_count);
SetCurrentContext(current_context);
Goto(&done);

View File

@ -429,10 +429,12 @@ namespace promise {
// PromiseReaction holding both the onFulfilled and onRejected callbacks.
// Once the {promise} is resolved we decide on the concrete handler to
// push onto the microtask queue.
const handlerContext = ExtractHandlerContext(onFulfilled, onRejected);
const promiseReactions =
UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result);
const reaction = NewPromiseReaction(
promiseReactions, resultPromiseOrCapability, onFulfilled, onRejected);
handlerContext, promiseReactions, resultPromiseOrCapability,
onFulfilled, onRejected);
promise.reactions_or_result = reaction;
} else {
let map: Map;

View File

@ -59,6 +59,7 @@ namespace promise {
handler: Callable|Undefined,
promiseOrCapability: JSPromise|PromiseCapability|
Undefined): PromiseReactionJobTask {
const nativeContext = LoadNativeContext(handlerContext);
const taskHeapObject =
promise_internal::AllocatePromiseReactionJobTask(context);
taskHeapObject.map = map;
@ -67,6 +68,9 @@ namespace promise {
jobTask.context = handlerContext;
jobTask.handler = handler;
jobTask.promise_or_capability = promiseOrCapability;
jobTask.continuation_preserved_embedder_data =
nativeContext[NativeContextSlot::
CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX];
return jobTask;
}
@ -113,16 +117,19 @@ namespace promise {
}
macro NewPromiseReaction(implicit context: Context)(
next: Zero|PromiseReaction,
handlerContext: Context, next: Zero|PromiseReaction,
promiseOrCapability: JSPromise|PromiseCapability|Undefined,
fulfillHandler: Callable|Undefined,
rejectHandler: Callable|Undefined): PromiseReaction {
const nativeContext = LoadNativeContext(handlerContext);
return new PromiseReaction{
map: PromiseReactionMapConstant(),
next: next,
reject_handler: rejectHandler,
fulfill_handler: fulfillHandler,
promise_or_capability: promiseOrCapability
promise_or_capability: promiseOrCapability,
continuation_preserved_embedder_data: nativeContext
[NativeContextSlot::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX]
};
}

View File

@ -63,6 +63,8 @@ enum ContextLookupFlags {
/* it's already UBSan-fiendly and doesn't require a star... So declare */ \
/* it as a HeapObject for now. */ \
V(EMBEDDER_DATA_INDEX, HeapObject, embedder_data) \
V(CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX, HeapObject, \
continuation_preserved_embedder_data) \
/* Below is alpha-sorted */ \
V(ACCESSOR_PROPERTY_DESCRIPTOR_MAP_INDEX, Map, \
accessor_property_descriptor_map) \

View File

@ -59,6 +59,8 @@ extern enum NativeContextSlot extends intptr constexpr 'Context::Field' {
PROMISE_THEN_FINALLY_SHARED_FUN,
PROMISE_VALUE_THUNK_FINALLY_SHARED_FUN,
STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX,
CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX,
...
}

View File

@ -6135,6 +6135,11 @@ Handle<Object> JSPromise::TriggerPromiseReactions(Isolate* isolate,
static_cast<int>(PromiseReaction::kPromiseOrCapabilityOffset) ==
static_cast<int>(
PromiseFulfillReactionJobTask::kPromiseOrCapabilityOffset));
STATIC_ASSERT(
static_cast<int>(
PromiseReaction::kContinuationPreservedEmbedderDataOffset) ==
static_cast<int>(PromiseFulfillReactionJobTask::
kContinuationPreservedEmbedderDataOffset));
} else {
DisallowHeapAllocation no_gc;
task->synchronized_set_map(
@ -6148,6 +6153,11 @@ Handle<Object> JSPromise::TriggerPromiseReactions(Isolate* isolate,
static_cast<int>(PromiseReaction::kPromiseOrCapabilityOffset) ==
static_cast<int>(
PromiseRejectReactionJobTask::kPromiseOrCapabilityOffset));
STATIC_ASSERT(
static_cast<int>(
PromiseReaction::kContinuationPreservedEmbedderDataOffset) ==
static_cast<int>(PromiseRejectReactionJobTask::
kContinuationPreservedEmbedderDataOffset));
}
MicrotaskQueue* microtask_queue = handler_context->microtask_queue();

View File

@ -22,6 +22,8 @@ const kPromiseReactionFulfillHandlerOffset: constexpr int31
generates 'PromiseReaction::kFulfillHandlerOffset';
const kPromiseReactionPromiseOrCapabilityOffset: constexpr int31
generates 'PromiseReaction::kPromiseOrCapabilityOffset';
const kPromiseReactionContinuationPreservedEmbedderDataOffset: constexpr int31
generates 'PromiseReaction::kContinuationPreservedEmbedderDataOffset';
@generateCppClass
extern class PromiseReaction extends Struct {
@ -31,6 +33,7 @@ extern class PromiseReaction extends Struct {
// Either a JSPromise (in case of native promises), a PromiseCapability
// (general case), or undefined (in case of await).
promise_or_capability: JSPromise|PromiseCapability|Undefined;
continuation_preserved_embedder_data: Object|Undefined;
}
// PromiseReactionJobTask constants
@ -40,6 +43,10 @@ const kPromiseReactionJobTaskHandlerOffset: constexpr int31
generates 'PromiseReactionJobTask::kHandlerOffset';
const kPromiseReactionJobTaskPromiseOrCapabilityOffset: constexpr int31
generates 'PromiseReactionJobTask::kPromiseOrCapabilityOffset';
const kPromiseReactionJobTaskContinuationPreservedEmbedderDataOffset:
constexpr int31
generates 'PromiseReactionJobTask::kContinuationPreservedEmbedderDataOffset'
;
@abstract
@generateCppClass
@ -50,6 +57,7 @@ extern class PromiseReactionJobTask extends Microtask {
// Either a JSPromise (in case of native promises), a PromiseCapability
// (general case), or undefined (in case of await).
promise_or_capability: JSPromise|PromiseCapability|Undefined;
continuation_preserved_embedder_data: Object|Undefined;
}
@generateCppClass