[promises] Port remaining promise code to Torque.

Bug: v8:9838
Change-Id: Idc6bda122354a54dd24e39b0356f35b0f54ef089
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2012596
Commit-Queue: Joshua Litt <joshualitt@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66031}
This commit is contained in:
Joshua Litt 2020-01-23 08:50:05 -08:00 committed by Commit Bot
parent 01646bc89c
commit f22c213304
15 changed files with 297 additions and 391 deletions

View File

@ -987,6 +987,7 @@ torque_files = [
"src/builtins/promise-all-element-closure.tq",
"src/builtins/promise-constructor.tq",
"src/builtins/promise-finally.tq",
"src/builtins/promise-misc.tq",
"src/builtins/promise-race.tq",
"src/builtins/promise-reaction-job.tq",
"src/builtins/promise-resolve.tq",

View File

@ -148,7 +148,7 @@ void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorEnqueue(
// presently executing, then this method will loop through, processing each
// request from front to back.
// This loop resides in AsyncGeneratorResumeNext.
TNode<JSPromise> promise = AllocateAndInitJSPromise(context);
TNode<JSPromise> promise = NewJSPromise(context);
Label if_receiverisincompatible(this, Label::kDeferred);
GotoIf(TaggedIsSmi(receiver), &if_receiverisincompatible);

View File

@ -105,7 +105,7 @@ void AsyncFromSyncBuiltinsAssembler::Generate_AsyncFromSyncIteratorMethod(
const char* operation_name, Label::Type reject_label_type,
base::Optional<TNode<Object>> initial_exception_value) {
const TNode<NativeContext> native_context = LoadNativeContext(context);
const TNode<JSPromise> promise = AllocateAndInitJSPromise(context);
const TNode<JSPromise> promise = NewJSPromise(context);
TVARIABLE(
Object, var_exception,

View File

@ -20,234 +20,22 @@
namespace v8 {
namespace internal {
using Node = compiler::Node;
using IteratorRecord = TorqueStructIteratorRecord;
using PromiseResolvingFunctions = TorqueStructPromiseResolvingFunctions;
TNode<JSPromise> PromiseBuiltinsAssembler::AllocateJSPromise(
TNode<Context> context) {
const TNode<NativeContext> native_context = LoadNativeContext(context);
const TNode<JSFunction> promise_fun =
CAST(LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX));
CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
const TNode<Map> promise_map = LoadObjectField<Map>(
promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
const TNode<HeapObject> promise =
Allocate(JSPromise::kSizeWithEmbedderFields);
StoreMapNoWriteBarrier(promise, promise_map);
StoreObjectFieldRoot(promise, JSPromise::kPropertiesOrHashOffset,
RootIndex::kEmptyFixedArray);
StoreObjectFieldRoot(promise, JSPromise::kElementsOffset,
RootIndex::kEmptyFixedArray);
return CAST(promise);
}
void PromiseBuiltinsAssembler::PromiseInit(TNode<JSPromise> promise) {
STATIC_ASSERT(v8::Promise::kPending == 0);
StoreObjectFieldNoWriteBarrier(promise, JSPromise::kReactionsOrResultOffset,
SmiConstant(Smi::zero()));
StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset,
SmiConstant(Smi::zero()));
void PromiseBuiltinsAssembler::ZeroOutEmbedderOffsets(
TNode<JSPromise> promise) {
for (int offset = JSPromise::kHeaderSize;
offset < JSPromise::kSizeWithEmbedderFields; offset += kTaggedSize) {
StoreObjectFieldNoWriteBarrier(promise, offset, SmiConstant(Smi::zero()));
}
}
TNode<JSPromise> PromiseBuiltinsAssembler::AllocateAndInitJSPromise(
TNode<HeapObject> PromiseBuiltinsAssembler::AllocatePromiseReactionJobTask(
TNode<Context> context) {
return AllocateAndInitJSPromise(context, UndefinedConstant());
return Allocate(PromiseReactionJobTask::kSizeOfAllPromiseReactionJobTasks);
}
TNode<JSPromise> PromiseBuiltinsAssembler::AllocateAndInitJSPromise(
TNode<Context> context, TNode<Object> parent) {
const TNode<JSPromise> instance = AllocateJSPromise(context);
PromiseInit(instance);
Label out(this);
GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &out);
CallRuntime(Runtime::kPromiseHookInit, context, instance, parent);
Goto(&out);
BIND(&out);
return instance;
}
TNode<JSPromise> PromiseBuiltinsAssembler::AllocateAndSetJSPromise(
TNode<Context> context, v8::Promise::PromiseState status,
TNode<Object> result) {
DCHECK_NE(Promise::kPending, status);
const TNode<JSPromise> instance = AllocateJSPromise(context);
StoreObjectFieldNoWriteBarrier(instance, JSPromise::kReactionsOrResultOffset,
result);
STATIC_ASSERT(JSPromise::kStatusShift == 0);
StoreObjectFieldNoWriteBarrier(instance, JSPromise::kFlagsOffset,
SmiConstant(status));
for (int offset = JSPromise::kHeaderSize;
offset < JSPromise::kSizeWithEmbedderFields; offset += kTaggedSize) {
StoreObjectFieldNoWriteBarrier(instance, offset, SmiConstant(0));
}
Label out(this);
GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &out);
CallRuntime(Runtime::kPromiseHookInit, context, instance,
UndefinedConstant());
Goto(&out);
BIND(&out);
return instance;
}
TNode<BoolT> PromiseBuiltinsAssembler::PromiseHasHandler(
TNode<JSPromise> promise) {
const TNode<Smi> flags =
LoadObjectField<Smi>(promise, JSPromise::kFlagsOffset);
return IsSetWord(SmiUntag(flags), 1 << JSPromise::kHasHandlerBit);
}
TNode<PromiseReaction> PromiseBuiltinsAssembler::AllocatePromiseReaction(
TNode<Object> next, TNode<HeapObject> promise_or_capability,
TNode<HeapObject> fulfill_handler, TNode<HeapObject> reject_handler) {
const TNode<HeapObject> reaction = Allocate(PromiseReaction::kSize);
StoreMapNoWriteBarrier(reaction, RootIndex::kPromiseReactionMap);
StoreObjectFieldNoWriteBarrier(reaction, PromiseReaction::kNextOffset, next);
StoreObjectFieldNoWriteBarrier(reaction,
PromiseReaction::kPromiseOrCapabilityOffset,
promise_or_capability);
StoreObjectFieldNoWriteBarrier(
reaction, PromiseReaction::kFulfillHandlerOffset, fulfill_handler);
StoreObjectFieldNoWriteBarrier(
reaction, PromiseReaction::kRejectHandlerOffset, reject_handler);
return CAST(reaction);
}
TNode<PromiseReactionJobTask>
PromiseBuiltinsAssembler::AllocatePromiseReactionJobTask(
TNode<Map> map, TNode<Context> context, TNode<Object> argument,
TNode<HeapObject> handler, TNode<HeapObject> promise_or_capability) {
const TNode<HeapObject> microtask =
Allocate(PromiseReactionJobTask::kSizeOfAllPromiseReactionJobTasks);
StoreMapNoWriteBarrier(microtask, map);
StoreObjectFieldNoWriteBarrier(
microtask, PromiseReactionJobTask::kArgumentOffset, argument);
StoreObjectFieldNoWriteBarrier(
microtask, PromiseReactionJobTask::kContextOffset, context);
StoreObjectFieldNoWriteBarrier(
microtask, PromiseReactionJobTask::kHandlerOffset, handler);
StoreObjectFieldNoWriteBarrier(
microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset,
promise_or_capability);
return CAST(microtask);
}
TNode<PromiseResolveThenableJobTask>
PromiseBuiltinsAssembler::AllocatePromiseResolveThenableJobTask(
TNode<JSPromise> promise_to_resolve, TNode<JSReceiver> then,
TNode<JSReceiver> thenable, TNode<Context> context) {
const TNode<HeapObject> microtask =
Allocate(PromiseResolveThenableJobTask::kSize);
StoreMapNoWriteBarrier(microtask,
RootIndex::kPromiseResolveThenableJobTaskMap);
StoreObjectFieldNoWriteBarrier(
microtask, PromiseResolveThenableJobTask::kContextOffset, context);
StoreObjectFieldNoWriteBarrier(
microtask, PromiseResolveThenableJobTask::kPromiseToResolveOffset,
promise_to_resolve);
StoreObjectFieldNoWriteBarrier(
microtask, PromiseResolveThenableJobTask::kThenOffset, then);
StoreObjectFieldNoWriteBarrier(
microtask, PromiseResolveThenableJobTask::kThenableOffset, thenable);
return CAST(microtask);
}
void PromiseBuiltinsAssembler::BranchIfPromiseResolveLookupChainIntact(
TNode<NativeContext> native_context, TNode<Object> constructor,
Label* if_fast, Label* if_slow) {
GotoIfForceSlowPath(if_slow);
TNode<Object> promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
GotoIfNot(TaggedEqual(promise_fun, constructor), if_slow);
Branch(IsPromiseResolveProtectorCellInvalid(), if_slow, if_fast);
}
void PromiseBuiltinsAssembler::GotoIfNotPromiseResolveLookupChainIntact(
TNode<NativeContext> native_context, TNode<Object> constructor,
Label* if_slow) {
Label if_fast(this);
BranchIfPromiseResolveLookupChainIntact(native_context, constructor, &if_fast,
if_slow);
BIND(&if_fast);
}
void PromiseBuiltinsAssembler::BranchIfPromiseSpeciesLookupChainIntact(
TNode<NativeContext> native_context, TNode<Map> promise_map, Label* if_fast,
Label* if_slow) {
TNode<Object> promise_prototype =
LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
GotoIfForceSlowPath(if_slow);
GotoIfNot(TaggedEqual(LoadMapPrototype(promise_map), promise_prototype),
if_slow);
Branch(IsPromiseSpeciesProtectorCellInvalid(), if_slow, if_fast);
}
void PromiseBuiltinsAssembler::BranchIfPromiseThenLookupChainIntact(
TNode<NativeContext> native_context, TNode<Map> receiver_map,
Label* if_fast, Label* if_slow) {
GotoIfForceSlowPath(if_slow);
GotoIfNot(IsJSPromiseMap(receiver_map), if_slow);
const TNode<Object> promise_prototype =
LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
GotoIfNot(TaggedEqual(LoadMapPrototype(receiver_map), promise_prototype),
if_slow);
Branch(IsPromiseThenProtectorCellInvalid(), if_slow, if_fast);
}
void PromiseBuiltinsAssembler::BranchIfAccessCheckFailed(
TNode<Context> context, TNode<Context> native_context,
TNode<Object> promise_constructor, TNode<Object> executor,
Label* if_noaccess) {
TVARIABLE(HeapObject, var_executor);
var_executor = CAST(executor);
Label has_access(this), call_runtime(this, Label::kDeferred);
// If executor is a bound function, load the bound function until we've
// reached an actual function.
Label found_function(this), loop_over_bound_function(this, &var_executor);
Goto(&loop_over_bound_function);
BIND(&loop_over_bound_function);
{
TNode<Uint16T> executor_type = LoadInstanceType(var_executor.value());
GotoIf(InstanceTypeEqual(executor_type, JS_FUNCTION_TYPE), &found_function);
GotoIfNot(InstanceTypeEqual(executor_type, JS_BOUND_FUNCTION_TYPE),
&call_runtime);
var_executor = LoadObjectField<HeapObject>(
var_executor.value(), JSBoundFunction::kBoundTargetFunctionOffset);
Goto(&loop_over_bound_function);
}
// Load the context from the function and compare it to the Promise
// constructor's context. If they match, everything is fine, otherwise, bail
// out to the runtime.
BIND(&found_function);
{
TNode<Context> function_context = LoadObjectField<Context>(
var_executor.value(), JSFunction::kContextOffset);
TNode<NativeContext> native_function_context =
LoadNativeContext(function_context);
Branch(TaggedEqual(native_context, native_function_context), &has_access,
&call_runtime);
}
BIND(&call_runtime);
{
Branch(TaggedEqual(CallRuntime(Runtime::kAllowDynamicFunction, context,
promise_constructor),
TrueConstant()),
&has_access, if_noaccess);
}
BIND(&has_access);
TNode<HeapObject> PromiseBuiltinsAssembler::AllocateJSPromise(
TNode<Context> context) {
return Allocate(JSPromise::kSizeWithEmbedderFields);
}
} // namespace internal

View File

@ -17,112 +17,11 @@ class V8_EXPORT_PRIVATE PromiseBuiltinsAssembler : public CodeStubAssembler {
public:
explicit PromiseBuiltinsAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
// These allocate and initialize a promise with pending state and
// undefined fields.
//
// This uses undefined as the parent promise for the promise init
// hook.
TNode<JSPromise> AllocateAndInitJSPromise(TNode<Context> context);
// This uses the given parent as the parent promise for the promise
// init hook.
TNode<JSPromise> AllocateAndInitJSPromise(TNode<Context> context,
TNode<Object> parent);
void ZeroOutEmbedderOffsets(TNode<JSPromise> promise);
// This allocates and initializes a promise with the given state and
// fields.
TNode<JSPromise> AllocateAndSetJSPromise(TNode<Context> context,
v8::Promise::PromiseState status,
TNode<Object> result);
TNode<HeapObject> AllocateJSPromise(TNode<Context> context);
TNode<PromiseReaction> AllocatePromiseReaction(
TNode<Object> next, TNode<HeapObject> promise_or_capability,
TNode<HeapObject> fulfill_handler, TNode<HeapObject> reject_handler);
TNode<PromiseReactionJobTask> AllocatePromiseReactionJobTask(
TNode<Map> map, TNode<Context> context, TNode<Object> argument,
TNode<HeapObject> handler, TNode<HeapObject> promise_or_capability);
TNode<PromiseResolveThenableJobTask> AllocatePromiseResolveThenableJobTask(
TNode<JSPromise> promise_to_resolve, TNode<JSReceiver> then,
TNode<JSReceiver> thenable, TNode<Context> context);
TNode<BoolT> PromiseHasHandler(TNode<JSPromise> promise);
void BranchIfAccessCheckFailed(TNode<Context> context,
TNode<Context> native_context,
TNode<Object> promise_constructor,
TNode<Object> executor, Label* if_noaccess);
void PromiseInit(TNode<JSPromise> promise);
// We can shortcut the SpeciesConstructor on {promise_map} if it's
// [[Prototype]] is the (initial) Promise.prototype and the @@species
// protector is intact, as that guards the lookup path for the "constructor"
// property on JSPromise instances which have the %PromisePrototype%.
void BranchIfPromiseSpeciesLookupChainIntact(
TNode<NativeContext> native_context, TNode<Map> promise_map,
Label* if_fast, Label* if_slow);
template <typename... TArgs>
TNode<Object> InvokeThen(TNode<NativeContext> native_context,
TNode<Object> receiver, TArgs... args) {
TVARIABLE(Object, var_result);
Label if_fast(this), if_slow(this, Label::kDeferred),
done(this, &var_result);
GotoIf(TaggedIsSmi(receiver), &if_slow);
const TNode<Map> receiver_map = LoadMap(CAST(receiver));
// We can skip the "then" lookup on {receiver} if it's [[Prototype]]
// is the (initial) Promise.prototype and the Promise#then protector
// is intact, as that guards the lookup path for the "then" property
// on JSPromise instances which have the (initial) %PromisePrototype%.
BranchIfPromiseThenLookupChainIntact(native_context, receiver_map, &if_fast,
&if_slow);
BIND(&if_fast);
{
const TNode<Object> then =
LoadContextElement(native_context, Context::PROMISE_THEN_INDEX);
var_result =
CallJS(CodeFactory::CallFunction(
isolate(), ConvertReceiverMode::kNotNullOrUndefined),
native_context, then, receiver, args...);
Goto(&done);
}
BIND(&if_slow);
{
const TNode<Object> then = GetProperty(
native_context, receiver, isolate()->factory()->then_string());
var_result =
CallJS(CodeFactory::Call(isolate(),
ConvertReceiverMode::kNotNullOrUndefined),
native_context, then, receiver, args...);
Goto(&done);
}
BIND(&done);
return var_result.value();
}
protected:
// We can skip the "resolve" lookup on {constructor} if it's the (initial)
// Promise constructor and the Promise.resolve() protector is intact, as
// that guards the lookup path for the "resolve" property on the %Promise%
// intrinsic object.
void BranchIfPromiseResolveLookupChainIntact(
TNode<NativeContext> native_context, TNode<Object> constructor,
Label* if_fast, Label* if_slow);
void GotoIfNotPromiseResolveLookupChainIntact(
TNode<NativeContext> native_context, TNode<Object> constructor,
Label* if_slow);
// We can skip the "then" lookup on {receiver_map} if it's [[Prototype]]
// is the (initial) Promise.prototype and the Promise#then() protector
// is intact, as that guards the lookup path for the "then" property
// on JSPromise instances which have the (initial) %PromisePrototype%.
void BranchIfPromiseThenLookupChainIntact(TNode<NativeContext> native_context,
TNode<Map> receiver_map,
Label* if_fast, Label* if_slow);
TNode<JSPromise> AllocateJSPromise(TNode<Context> context);
TNode<HeapObject> AllocatePromiseReactionJobTask(TNode<Context> context);
};
} // namespace internal

View File

@ -36,6 +36,7 @@ extern macro IsExtensibleMap(Map): bool;
extern macro IsJSPrimitiveWrapper(HeapObject): bool;
extern macro IsPromiseCapability(HeapObject): bool;
extern macro IsPromiseReaction(HeapObject): bool;
extern macro IsPromiseReactionJobTask(HeapObject): bool;
extern macro IsPromiseRejectReactionJobTask(HeapObject): bool;
extern macro IsPromiseFulfillReactionJobTask(HeapObject): bool;
extern macro IsSharedFunctionInfo(HeapObject): bool;
@ -643,6 +644,14 @@ Cast<JSReceiver|Null>(o: HeapObject): JSReceiver|Null
}
}
Cast<PromiseReactionJobTask>(o: HeapObject):
PromiseReactionJobTask labels CastError {
if (IsPromiseReactionJobTask(o)) {
return %RawDownCast<PromiseReactionJobTask>(o);
}
goto CastError;
}
Cast<PromiseFulfillReactionJobTask>(o: HeapObject):
PromiseFulfillReactionJobTask labels CastError {
if (IsPromiseFulfillReactionJobTask(o)) {

View File

@ -246,9 +246,6 @@ namespace promise {
const kPromiseBuiltinsDebugEventSlot: constexpr ContextSlot
generates 'PromiseBuiltins::kDebugEventSlot';
extern macro
PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Context): JSPromise;
@export
macro CreatePromiseCapabilitiesExecutorContext(
nativeContext: NativeContext, capability: PromiseCapability): Context {
@ -306,7 +303,7 @@ namespace promise {
if (TaggedEqual(
constructor,
nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX])) {
const promise = AllocateAndInitJSPromise(nativeContext);
const promise = NewJSPromise();
const pair =
CreatePromiseResolvingFunctions(promise, debugEvent, nativeContext);
@ -409,14 +406,6 @@ namespace promise {
return ResolvePromise(context, promise, resolution);
}
extern macro
PromiseBuiltinsAssembler::AllocatePromiseReaction(
Object, HeapObject, HeapObject, HeapObject): PromiseReaction;
extern macro
PromiseBuiltinsAssembler::AllocatePromiseReactionJobTask(
Map, Context, Object, HeapObject, HeapObject): PromiseReactionJobTask;
@export
transitioning macro PerformPromiseThenImpl(implicit context: Context)(
promise: JSPromise, onFulfilled: Callable|Undefined,
@ -427,13 +416,14 @@ 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 promiseReactions = promise.reactions_or_result;
const reaction = AllocatePromiseReaction(
const promiseReactions =
UnsafeCast<(Zero | PromiseReaction)>(promise.reactions_or_result);
const reaction = NewPromiseReaction(
promiseReactions, resultPromiseOrCapability, onFulfilled, onRejected);
promise.reactions_or_result = reaction;
} else {
let map: Map;
let handler: HeapObject;
let handler: Callable|Undefined = Undefined;
let handlerContext: Context;
if (promise.Status() == PromiseState::kFulfilled) {
map = PromiseFulfillReactionJobTaskMapConstant();
@ -451,7 +441,7 @@ namespace promise {
}
const reactionsOrResult = promise.reactions_or_result;
const microtask = AllocatePromiseReactionJobTask(
const microtask = NewPromiseReactionJobTask(
map, handlerContext, reactionsOrResult, handler,
resultPromiseOrCapability);
EnqueueMicrotask(handlerContext, microtask);
@ -469,10 +459,6 @@ namespace promise {
return resultPromise;
}
extern macro
PromiseBuiltinsAssembler::AllocateAndSetJSPromise(
Context, constexpr PromiseState, JSAny): JSPromise;
// https://tc39.es/ecma262/#sec-promise-reject-functions
transitioning javascript builtin
PromiseReject(js-implicit context: NativeContext, receiver: JSAny)(
@ -484,8 +470,7 @@ namespace promise {
const promiseFun = context[NativeContextSlot::PROMISE_FUNCTION_INDEX];
if (promiseFun == receiver) {
const promise =
AllocateAndSetJSPromise(context, PromiseState::kRejected, reason);
const promise = NewJSPromise(PromiseState::kRejected, reason);
runtime::PromiseRejectEventFromStack(promise, reason);
return promise;
} else {

View File

@ -25,18 +25,13 @@ namespace promise {
const kPromiseConstructorReturnedUndefined: constexpr UseCounterFeature
generates 'v8::Isolate::kPromiseConstructorReturnedUndefined';
extern macro
PromiseBuiltinsAssembler::BranchIfAccessCheckFailed(
Context, Context, Object, Object): void labels NoAccess;
extern macro
IsDebugActive(): bool;
transitioning macro
HasAccessCheckFailed(
context: Context, nativeContext: Context, promiseFun: Object,
executor: Object): bool {
BranchIfAccessCheckFailed(context, nativeContext, promiseFun, executor)
HasAccessCheckFailed(implicit context: Context)(
nativeContext: NativeContext, promiseFun: JSAny, executor: JSAny): bool {
BranchIfAccessCheckFailed(nativeContext, promiseFun, executor)
otherwise return true;
return false;
}
@ -44,8 +39,6 @@ namespace promise {
extern macro ConstructorBuiltinsAssembler::EmitFastNewObject(
Context, JSFunction, JSReceiver): JSObject;
extern macro PromiseBuiltinsAssembler::PromiseInit(JSPromise): void;
extern macro
PromiseBuiltinsAssembler::IsPromiseHookEnabledOrHasAsyncEventDelegate(): bool;
@ -68,7 +61,7 @@ namespace promise {
context[NativeContextSlot::PROMISE_FUNCTION_INDEX]);
// Silently fail if the stack looks fishy.
if (HasAccessCheckFailed(context, context, promiseFun, executor)) {
if (HasAccessCheckFailed(context, promiseFun, executor)) {
IncrementUseCounter(
context, SmiConstant(kPromiseConstructorReturnedUndefined));
return Undefined;
@ -76,7 +69,7 @@ namespace promise {
let result: JSPromise;
if (promiseFun == newTarget) {
result = AllocateAndInitJSPromise(context);
result = NewJSPromise();
} else {
result = UnsafeCast<JSPromise>(EmitFastNewObject(
context, promiseFun, UnsafeCast<JSReceiver>(newTarget)));

View File

@ -48,11 +48,6 @@ namespace promise {
return AllocateFunctionWithMapAndContext(map, throwerInfo, throwerContext);
}
extern transitioning macro
PromiseBuiltinsAssembler::InvokeThen(NativeContext, JSAny, JSAny): JSAny;
extern transitioning macro PromiseBuiltinsAssembler::InvokeThen(
NativeContext, JSAny, JSAny, JSAny): JSAny;
transitioning javascript builtin
PromiseCatchFinally(js-implicit context: Context, receiver: JSAny)(
reason: JSAny): JSAny {

View File

@ -0,0 +1,236 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include 'src/builtins/builtins-promise.h'
#include 'src/builtins/builtins-promise-gen.h'
namespace runtime {
extern transitioning runtime
AllowDynamicFunction(implicit context: Context)(JSAny): JSAny;
}
// Unsafe functions that should be used very carefully.
namespace promise_internal {
extern macro PromiseBuiltinsAssembler::ZeroOutEmbedderOffsets(JSPromise):
void;
extern macro PromiseBuiltinsAssembler::AllocateJSPromise(Context): HeapObject;
extern macro PromiseBuiltinsAssembler::AllocatePromiseReactionJobTask(
Context): HeapObject;
}
namespace promise {
extern macro IsFunctionWithPrototypeSlotMap(Map): bool;
@export
macro PromiseHasHandler(promise: JSPromise): bool {
return promise.HasHandler();
}
@export
macro PromiseInit(promise: JSPromise): void {
assert(PromiseState::kPending == 0);
promise.reactions_or_result = kZero;
promise.flags = 0;
promise_internal::ZeroOutEmbedderOffsets(promise);
}
macro InnerNewJSPromise(implicit context: Context)(): JSPromise {
const nativeContext = LoadNativeContext(context);
const promiseFun = UnsafeCast<JSFunction>(
nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]);
assert(IsFunctionWithPrototypeSlotMap(promiseFun.map));
const promiseMap = UnsafeCast<Map>(promiseFun.prototype_or_initial_map);
const promiseHeapObject = promise_internal::AllocateJSPromise(context);
promiseHeapObject.map = promiseMap;
const promise = UnsafeCast<JSPromise>(promiseHeapObject);
promise.properties_or_hash = kEmptyFixedArray;
promise.elements = kEmptyFixedArray;
promise.reactions_or_result = kZero;
promise.flags = 0;
return promise;
}
macro NewPromiseReactionJobTask(implicit context: Context)(
map: Map, handlerContext: Context, argument: Object,
handler: Callable|Undefined,
promiseOrCapability: JSPromise|PromiseCapability|
Undefined): PromiseReactionJobTask {
const taskHeapObject =
promise_internal::AllocatePromiseReactionJobTask(context);
taskHeapObject.map = map;
const jobTask = UnsafeCast<PromiseReactionJobTask>(taskHeapObject);
jobTask.argument = argument;
jobTask.context = handlerContext;
jobTask.handler = handler;
jobTask.promise_or_capability = promiseOrCapability;
return jobTask;
}
// These allocate and initialize a promise with pending state and
// undefined fields.
//
// This uses the given parent as the parent promise for the promise
// init hook.
@export
transitioning macro NewJSPromise(implicit context: Context)(parent: Object):
JSPromise {
const instance = InnerNewJSPromise();
PromiseInit(instance);
if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) {
runtime::PromiseHookInit(instance, parent);
}
return instance;
}
// This uses undefined as the parent promise for the promise init
// hook.
@export
transitioning macro NewJSPromise(implicit context: Context)(): JSPromise {
return NewJSPromise(Undefined);
}
// This allocates and initializes a promise with the given state and
// fields.
@export
transitioning macro NewJSPromise(implicit context: Context)(
status: constexpr PromiseState, result: JSAny): JSPromise {
assert(status != PromiseState::kPending);
assert(kJSPromiseStatusShift == 0);
const instance = InnerNewJSPromise();
instance.reactions_or_result = result;
instance.SetStatus(status);
promise_internal::ZeroOutEmbedderOffsets(instance);
if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) {
runtime::PromiseHookInit(instance, Undefined);
}
return instance;
}
macro NewPromiseReaction(implicit context: Context)(
next: Zero|PromiseReaction,
promiseOrCapability: JSPromise|PromiseCapability|Undefined,
fulfillHandler: Callable|Undefined,
rejectHandler: Callable|Undefined): PromiseReaction {
return new PromiseReaction{
map: PromiseReactionMapConstant(),
next: next,
reject_handler: rejectHandler,
fulfill_handler: fulfillHandler,
promise_or_capability: promiseOrCapability
};
}
extern macro PromiseResolveThenableJobTaskMapConstant(): Map;
macro NewPromiseResolveThenableJobTask(implicit context: Context)(
promiseToResolve: JSPromise, then: JSReceiver, thenable: JSReceiver,
thenableContext: Context): PromiseResolveThenableJobTask {
return new PromiseResolveThenableJobTask{
map: PromiseResolveThenableJobTaskMapConstant(),
context: thenableContext,
promise_to_resolve: promiseToResolve,
then: then,
thenable: thenable
};
}
struct InvokeThenOneArgFunctor {
transitioning
macro Call(
nativeContext: NativeContext, then: JSAny, receiver: JSAny, arg1: JSAny,
_arg2: JSAny): JSAny {
return Call(nativeContext, then, receiver, arg1);
}
}
struct InvokeThenTwoArgFunctor {
transitioning
macro Call(
nativeContext: NativeContext, then: JSAny, receiver: JSAny, arg1: JSAny,
arg2: JSAny): JSAny {
return Call(nativeContext, then, receiver, arg1, arg2);
}
}
transitioning
macro InvokeThen<F: type>(implicit context: Context)(
nativeContext: NativeContext, receiver: JSAny, arg1: JSAny, arg2: JSAny,
callFunctor: F): JSAny {
// We can skip the "then" lookup on {receiver} if it's [[Prototype]]
// is the (initial) Promise.prototype and the Promise#then protector
// is intact, as that guards the lookup path for the "then" property
// on JSPromise instances which have the (initial) %PromisePrototype%.
if (!Is<Smi>(receiver) &&
IsPromiseThenLookupChainIntact(
nativeContext, UnsafeCast<HeapObject>(receiver).map)) {
const then = UnsafeCast<JSAny>(
nativeContext[NativeContextSlot::PROMISE_THEN_INDEX]);
return callFunctor.Call(nativeContext, then, receiver, arg1, arg2);
} else
deferred {
const then = UnsafeCast<JSAny>(GetProperty(receiver, kThenString));
return callFunctor.Call(nativeContext, then, receiver, arg1, arg2);
}
}
transitioning
macro InvokeThen(implicit context: Context)(
nativeContext: NativeContext, receiver: JSAny, arg: JSAny): JSAny {
return InvokeThen(
nativeContext, receiver, arg, Undefined, InvokeThenOneArgFunctor{});
}
transitioning
macro InvokeThen(implicit context: Context)(
nativeContext: NativeContext, receiver: JSAny, arg1: JSAny,
arg2: JSAny): JSAny {
return InvokeThen(
nativeContext, receiver, arg1, arg2, InvokeThenTwoArgFunctor{});
}
transitioning
macro BranchIfAccessCheckFailed(implicit context: Context)(
nativeContext: NativeContext, promiseConstructor: JSAny,
executor: JSAny): void labels IfNoAccess {
try {
// If executor is a bound function, load the bound function until we've
// reached an actual function.
let foundExecutor = executor;
while (true) {
typeswitch (foundExecutor) {
case (f: JSFunction): {
// Load the context from the function and compare it to the Promise
// constructor's context. If they match, everything is fine,
// otherwise, bail out to the runtime.
const functionContext = f.context;
const nativeFunctionContext = LoadNativeContext(functionContext);
if (TaggedEqual(nativeContext, nativeFunctionContext)) {
goto HasAccess;
} else {
goto CallRuntime;
}
}
case (b: JSBoundFunction): {
foundExecutor = b.bound_target_function;
}
case (Object): {
goto CallRuntime;
}
}
}
}
label CallRuntime deferred {
const result = runtime::AllowDynamicFunction(promiseConstructor);
if (result != True) {
goto IfNoAccess;
}
}
label HasAccess {}
}
}

View File

@ -64,7 +64,7 @@ namespace promise {
if (promiseFun == constructor) {
// This adds a fast path for native promises that don't need to
// create NewPromiseCapability.
const result = AllocateAndInitJSPromise(context);
const result = NewJSPromise();
ResolvePromise(context, result, value);
return result;
} else
@ -85,10 +85,6 @@ namespace promise {
const kThenString: String = ThenStringConstant();
extern macro PromiseBuiltinsAssembler::AllocatePromiseResolveThenableJobTask(
JSPromise, JSReceiver, JSReceiver,
Context): PromiseResolveThenableJobTask;
transitioning builtin
ResolvePromise(implicit context:
Context)(promise: JSPromise, resolution: JSAny): JSAny {
@ -182,7 +178,7 @@ namespace promise {
// 12. Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob,
// «promise, resolution, thenAction»).
const nativeContext = LoadNativeContext(context);
const task = AllocatePromiseResolveThenableJobTask(
const task = NewPromiseResolveThenableJobTask(
promise, UnsafeCast<JSReceiver>(then),
UnsafeCast<JSReceiver>(resolution), nativeContext);
return EnqueueMicrotask(nativeContext, task);

View File

@ -6,22 +6,16 @@
namespace promise {
extern macro
PromiseBuiltinsAssembler::BranchIfPromiseSpeciesLookupChainIntact(
NativeContext, Map): never labels IsIntact,
IsNotIntact;
macro
IsPromiseSpeciesLookupChainIntact(
nativeContext: NativeContext, promiseMap: Map): bool {
BranchIfPromiseSpeciesLookupChainIntact(nativeContext, promiseMap)
otherwise return true, return false;
const promisePrototype =
nativeContext[NativeContextSlot::PROMISE_PROTOTYPE_INDEX];
if (IsForceSlowPath()) return false;
if (promiseMap.prototype != promisePrototype) return false;
return !IsPromiseSpeciesProtectorCellInvalid();
}
extern macro
PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Context, Object):
JSPromise;
// https://tc39.es/ecma262/#sec-promise.prototype.then
transitioning javascript builtin
PromisePrototypeThen(js-implicit context: NativeContext, receiver: JSAny)(
@ -54,7 +48,7 @@ namespace promise {
}
}
label AllocateAndInit {
const resultJSPromise = AllocateAndInitJSPromise(context, promise);
const resultJSPromise = NewJSPromise(promise);
resultPromiseOrCapability = resultJSPromise;
resultPromise = resultJSPromise;
}

View File

@ -6010,6 +6010,13 @@ TNode<BoolT> CodeStubAssembler::IsPromiseReaction(
return HasInstanceType(object, PROMISE_REACTION_TYPE);
}
TNode<BoolT> CodeStubAssembler::IsPromiseReactionJobTask(
TNode<HeapObject> object) {
TNode<Uint16T> instance_type = LoadInstanceType(object);
return IsInRange(instance_type, FIRST_PROMISE_REACTION_JOB_TASK_TYPE,
LAST_PROMISE_REACTION_JOB_TASK_TYPE);
}
TNode<BoolT> CodeStubAssembler::IsPromiseRejectReactionJobTask(
SloppyTNode<HeapObject> object) {
return HasInstanceType(object, PROMISE_REJECT_REACTION_JOB_TASK_TYPE);

View File

@ -129,6 +129,8 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
V(PromiseReactionMap, promise_reaction_map, PromiseReactionMap) \
V(PromiseRejectReactionJobTaskMap, promise_reject_reaction_job_task_map, \
PromiseRejectReactionJobTaskMap) \
V(PromiseResolveThenableJobTaskMap, promise_resolve_thenable_job_task_map, \
PromiseResolveThenableJobTaskMap) \
V(prototype_string, prototype_string, PrototypeString) \
V(PrototypeInfoMap, prototype_info_map, PrototypeInfoMap) \
V(replace_symbol, replace_symbol, ReplaceSymbol) \
@ -2548,6 +2550,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<BoolT> IsPropertyArray(SloppyTNode<HeapObject> object);
TNode<BoolT> IsPropertyCell(SloppyTNode<HeapObject> object);
TNode<BoolT> IsPromiseReaction(SloppyTNode<HeapObject> object);
TNode<BoolT> IsPromiseReactionJobTask(TNode<HeapObject> object);
TNode<BoolT> IsPromiseRejectReactionJobTask(SloppyTNode<HeapObject> object);
TNode<BoolT> IsPromiseFulfillReactionJobTask(SloppyTNode<HeapObject> object);
TNode<BoolT> IsPrototypeInitialArrayPrototype(SloppyTNode<Context> context,

View File

@ -2449,7 +2449,7 @@ TEST(IsPromiseHookEnabled) {
CHECK_EQ(ReadOnlyRoots(isolate).false_value(), *result);
}
TEST(AllocateAndInitJSPromise) {
TEST(NewJSPromise) {
Isolate* isolate(CcTest::InitIsolateOnce());
const int kNumParams = 1;
@ -2457,7 +2457,7 @@ TEST(AllocateAndInitJSPromise) {
PromiseBuiltinsAssembler m(asm_tester.state());
Node* const context = m.Parameter(kNumParams + 2);
const TNode<JSPromise> promise = m.AllocateAndInitJSPromise(m.CAST(context));
const TNode<JSPromise> promise = m.NewJSPromise(m.CAST(context));
m.Return(promise);
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
@ -2466,7 +2466,7 @@ TEST(AllocateAndInitJSPromise) {
CHECK(result->IsJSPromise());
}
TEST(AllocateAndSetJSPromise) {
TEST(NewJSPromise2) {
Isolate* isolate(CcTest::InitIsolateOnce());
const int kNumParams = 1;
@ -2474,8 +2474,8 @@ TEST(AllocateAndSetJSPromise) {
PromiseBuiltinsAssembler m(asm_tester.state());
Node* const context = m.Parameter(kNumParams + 2);
const TNode<JSPromise> promise = m.AllocateAndSetJSPromise(
m.CAST(context), v8::Promise::kRejected, m.SmiConstant(1));
const TNode<JSPromise> promise =
m.NewJSPromise(m.CAST(context), v8::Promise::kRejected, m.SmiConstant(1));
m.Return(promise);
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
@ -2538,7 +2538,7 @@ TEST(PromiseHasHandler) {
Node* const context = m.Parameter(kNumParams + 2);
const TNode<JSPromise> promise =
m.AllocateAndInitJSPromise(m.CAST(context), m.UndefinedConstant());
m.NewJSPromise(m.CAST(context), m.UndefinedConstant());
m.Return(m.SelectBooleanConstant(m.PromiseHasHandler(promise)));
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
@ -2557,7 +2557,7 @@ TEST(CreatePromiseResolvingFunctionsContext) {
const TNode<Context> context = m.CAST(m.Parameter(kNumParams + 2));
const TNode<NativeContext> native_context = m.LoadNativeContext(context);
const TNode<JSPromise> promise =
m.AllocateAndInitJSPromise(context, m.UndefinedConstant());
m.NewJSPromise(context, m.UndefinedConstant());
const TNode<Context> promise_context =
m.CreatePromiseResolvingFunctionsContext(
context, promise, m.BooleanConstant(false), native_context);
@ -2585,7 +2585,7 @@ TEST(CreatePromiseResolvingFunctions) {
Node* const context = m.Parameter(kNumParams + 2);
const TNode<NativeContext> native_context = m.LoadNativeContext(context);
const TNode<JSPromise> promise =
m.AllocateAndInitJSPromise(m.CAST(context), m.UndefinedConstant());
m.NewJSPromise(m.CAST(context), m.UndefinedConstant());
PromiseResolvingFunctions funcs = m.CreatePromiseResolvingFunctions(
m.CAST(context), promise, m.BooleanConstant(false), native_context);
Node *resolve = funcs.resolve, *reject = funcs.reject;
@ -2675,7 +2675,7 @@ TEST(AllocateFunctionWithMapAndContext) {
const TNode<Context> context = m.CAST(m.Parameter(kNumParams + 2));
const TNode<NativeContext> native_context = m.LoadNativeContext(context);
const TNode<JSPromise> promise =
m.AllocateAndInitJSPromise(context, m.UndefinedConstant());
m.NewJSPromise(context, m.UndefinedConstant());
TNode<Context> promise_context = m.CreatePromiseResolvingFunctionsContext(
context, promise, m.BooleanConstant(false), native_context);
TNode<Object> resolve_info = m.LoadContextElement(