[promises] Move CreateResolvingFunctions to c++
- A new runtime function (%create_resolving_functions) is installed to call the CreateResolvingFunctions builtin from JS. - Three new builtins are created - resolve and reject functions and a third function that creates a new JSFunctions from these resolve/reject builtins. - The promise reject function is installed on the context temporarily as internal_promise_reject. This should go away once we remove PromiseSet. BUG=v8:5343 Review-Url: https://codereview.chromium.org/2459283004 Cr-Commit-Position: refs/heads/master@{#40903}
This commit is contained in:
parent
45b9f15f44
commit
cb6c8e48cc
1
BUILD.gn
1
BUILD.gn
@ -952,6 +952,7 @@ v8_source_set("v8_base") {
|
||||
"src/builtins/builtins-math.cc",
|
||||
"src/builtins/builtins-number.cc",
|
||||
"src/builtins/builtins-object.cc",
|
||||
"src/builtins/builtins-promise.cc",
|
||||
"src/builtins/builtins-proxy.cc",
|
||||
"src/builtins/builtins-reflect.cc",
|
||||
"src/builtins/builtins-regexp.cc",
|
||||
|
@ -3582,6 +3582,33 @@ bool Genesis::InstallNatives(GlobalContextType context_type) {
|
||||
*isolate()->builtins()->JSBuiltinsConstructStub());
|
||||
InstallWithIntrinsicDefaultProto(isolate(), function,
|
||||
Context::PROMISE_FUNCTION_INDEX);
|
||||
|
||||
{
|
||||
Handle<Code> code = handle(
|
||||
isolate()->builtins()->builtin(Builtins::kPromiseResolveClosure),
|
||||
isolate());
|
||||
Handle<SharedFunctionInfo> info =
|
||||
isolate()->factory()->NewSharedFunctionInfo(factory()->empty_string(),
|
||||
code, false);
|
||||
info->set_internal_formal_parameter_count(1);
|
||||
info->set_length(1);
|
||||
native_context()->set_promise_resolve_shared_fun(*info);
|
||||
|
||||
code = handle(
|
||||
isolate()->builtins()->builtin(Builtins::kPromiseRejectClosure),
|
||||
isolate());
|
||||
info = isolate()->factory()->NewSharedFunctionInfo(
|
||||
factory()->empty_string(), code, false);
|
||||
info->set_internal_formal_parameter_count(2);
|
||||
info->set_length(1);
|
||||
native_context()->set_promise_reject_shared_fun(*info);
|
||||
}
|
||||
|
||||
Handle<JSFunction> create_resolving_functions =
|
||||
SimpleCreateFunction(isolate(), factory()->empty_string(),
|
||||
Builtins::kCreateResolvingFunctions, 2, false);
|
||||
native_context()->set_create_resolving_functions(
|
||||
*create_resolving_functions);
|
||||
}
|
||||
|
||||
InstallBuiltinFunctionIds();
|
||||
|
113
src/builtins/builtins-promise.cc
Normal file
113
src/builtins/builtins-promise.cc
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright 2016 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-utils.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
enum PromiseResolvingFunctionContextSlot {
|
||||
kAlreadyVisitedSlot = Context::MIN_CONTEXT_SLOTS,
|
||||
kPromiseSlot,
|
||||
kDebugEventSlot,
|
||||
kPromiseContextLength,
|
||||
};
|
||||
|
||||
// ES#sec-promise-resolve-functions
|
||||
// Promise Resolve Functions
|
||||
BUILTIN(PromiseResolveClosure) {
|
||||
HandleScope scope(isolate);
|
||||
|
||||
Handle<Context> context(isolate->context(), isolate);
|
||||
Handle<Smi> already_visited(Smi::cast(context->get(kAlreadyVisitedSlot)),
|
||||
isolate);
|
||||
|
||||
if (already_visited->value() != 0) {
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
context->set(kAlreadyVisitedSlot, Smi::FromInt(1));
|
||||
Handle<JSObject> promise(JSObject::cast(context->get(kPromiseSlot)), isolate);
|
||||
Handle<Object> value = args.atOrUndefined(isolate, 1);
|
||||
|
||||
MaybeHandle<Object> maybe_result;
|
||||
Handle<Object> argv[] = {promise, value};
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, Execution::Call(isolate, isolate->promise_resolve(),
|
||||
isolate->factory()->undefined_value(),
|
||||
arraysize(argv), argv));
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
// ES#sec-promise-reject-functions
|
||||
// Promise Reject Functions
|
||||
BUILTIN(PromiseRejectClosure) {
|
||||
HandleScope scope(isolate);
|
||||
|
||||
Handle<Context> context(isolate->context(), isolate);
|
||||
Handle<Smi> already_visited(Smi::cast(context->get(kAlreadyVisitedSlot)),
|
||||
isolate);
|
||||
|
||||
if (already_visited->value() != 0) {
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
context->set(kAlreadyVisitedSlot, Smi::FromInt(1));
|
||||
|
||||
Handle<Object> value = args.atOrUndefined(isolate, 1);
|
||||
Handle<JSObject> promise(JSObject::cast(context->get(kPromiseSlot)), isolate);
|
||||
Handle<Object> debug_event(context->get(kDebugEventSlot), isolate);
|
||||
MaybeHandle<Object> maybe_result;
|
||||
Handle<Object> argv[] = {promise, value, debug_event};
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, Execution::Call(isolate, isolate->promise_internal_reject(),
|
||||
isolate->factory()->undefined_value(),
|
||||
arraysize(argv), argv));
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
// ES#sec-createresolvingfunctions
|
||||
// CreateResolvingFunctions ( promise )
|
||||
BUILTIN(CreateResolvingFunctions) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(3, args.length());
|
||||
|
||||
Handle<JSObject> promise = args.at<JSObject>(1);
|
||||
Handle<Object> debug_event = args.at<Object>(2);
|
||||
|
||||
Handle<Context> context =
|
||||
isolate->factory()->NewPromiseResolvingFunctionContext(
|
||||
kPromiseContextLength);
|
||||
context->set_native_context(*isolate->native_context());
|
||||
context->set(kAlreadyVisitedSlot, Smi::kZero);
|
||||
context->set(kPromiseSlot, *promise);
|
||||
context->set(kDebugEventSlot, *debug_event);
|
||||
|
||||
Handle<SharedFunctionInfo> resolve_shared_fun(
|
||||
isolate->native_context()->promise_resolve_shared_fun(), isolate);
|
||||
Handle<JSFunction> resolve =
|
||||
isolate->factory()->NewFunctionFromSharedFunctionInfo(
|
||||
isolate->sloppy_function_without_prototype_map(), resolve_shared_fun,
|
||||
isolate->native_context(), TENURED);
|
||||
|
||||
Handle<SharedFunctionInfo> reject_shared_fun(
|
||||
isolate->native_context()->promise_reject_shared_fun(), isolate);
|
||||
Handle<JSFunction> reject =
|
||||
isolate->factory()->NewFunctionFromSharedFunctionInfo(
|
||||
isolate->sloppy_function_without_prototype_map(), reject_shared_fun,
|
||||
isolate->native_context(), TENURED);
|
||||
|
||||
resolve->set_context(*context);
|
||||
reject->set_context(*context);
|
||||
|
||||
Handle<FixedArray> result = isolate->factory()->NewFixedArray(2);
|
||||
result->set(0, *resolve);
|
||||
result->set(1, *reject);
|
||||
|
||||
return *isolate->factory()->NewJSArrayWithElements(result, FAST_ELEMENTS, 2,
|
||||
NOT_TENURED);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -561,6 +561,11 @@ namespace internal {
|
||||
TFS(InstanceOf, BUILTIN, kNoExtraICState, Compare) \
|
||||
TFS(ForInFilter, BUILTIN, kNoExtraICState, ForInFilter) \
|
||||
\
|
||||
/* Promise */ \
|
||||
CPP(CreateResolvingFunctions) \
|
||||
CPP(PromiseResolveClosure) \
|
||||
CPP(PromiseRejectClosure) \
|
||||
\
|
||||
/* Proxy */ \
|
||||
CPP(ProxyConstructor) \
|
||||
CPP(ProxyConstructor_ConstructStub) \
|
||||
|
@ -59,7 +59,8 @@ enum ContextLookupFlags {
|
||||
V(SPREAD_ARGUMENTS_INDEX, JSFunction, spread_arguments) \
|
||||
V(SPREAD_ITERABLE_INDEX, JSFunction, spread_iterable) \
|
||||
V(MATH_FLOOR_INDEX, JSFunction, math_floor) \
|
||||
V(MATH_POW_INDEX, JSFunction, math_pow)
|
||||
V(MATH_POW_INDEX, JSFunction, math_pow) \
|
||||
V(CREATE_RESOLVING_FUNCTION_INDEX, JSFunction, create_resolving_functions)
|
||||
|
||||
#define NATIVE_CONTEXT_IMPORTED_FIELDS(V) \
|
||||
V(ARRAY_CONCAT_INDEX, JSFunction, array_concat) \
|
||||
@ -97,6 +98,7 @@ enum ContextLookupFlags {
|
||||
promise_has_user_defined_reject_handler) \
|
||||
V(PROMISE_DEBUG_GET_INFO_INDEX, JSFunction, promise_debug_get_info) \
|
||||
V(PROMISE_REJECT_INDEX, JSFunction, promise_reject) \
|
||||
V(PROMISE_INTERNAL_REJECT_INDEX, JSFunction, promise_internal_reject) \
|
||||
V(PROMISE_RESOLVE_INDEX, JSFunction, promise_resolve) \
|
||||
V(PROMISE_THEN_INDEX, JSFunction, promise_then) \
|
||||
V(RANGE_ERROR_FUNCTION_INDEX, JSFunction, range_error_function) \
|
||||
@ -273,6 +275,9 @@ enum ContextLookupFlags {
|
||||
V(PROXY_FUNCTION_INDEX, JSFunction, proxy_function) \
|
||||
V(PROXY_FUNCTION_MAP_INDEX, Map, proxy_function_map) \
|
||||
V(PROXY_MAP_INDEX, Map, proxy_map) \
|
||||
V(PROMISE_RESOLVE_SHARED_FUN, SharedFunctionInfo, \
|
||||
promise_resolve_shared_fun) \
|
||||
V(PROMISE_REJECT_SHARED_FUN, SharedFunctionInfo, promise_reject_shared_fun) \
|
||||
V(REGEXP_EXEC_FUNCTION_INDEX, JSFunction, regexp_exec_function) \
|
||||
V(REGEXP_FUNCTION_INDEX, JSFunction, regexp_function) \
|
||||
V(REGEXP_LAST_MATCH_INFO_INDEX, RegExpMatchInfo, regexp_last_match_info) \
|
||||
|
@ -962,6 +962,14 @@ Handle<Context> Factory::NewBlockContext(Handle<JSFunction> function,
|
||||
return context;
|
||||
}
|
||||
|
||||
Handle<Context> Factory::NewPromiseResolvingFunctionContext(int length) {
|
||||
DCHECK_GE(length, Context::MIN_CONTEXT_SLOTS);
|
||||
Handle<FixedArray> array = NewFixedArray(length);
|
||||
array->set_map_no_write_barrier(*function_context_map());
|
||||
Handle<Context> context = Handle<Context>::cast(array);
|
||||
context->set_extension(*the_hole_value());
|
||||
return context;
|
||||
}
|
||||
|
||||
Handle<Struct> Factory::NewStruct(InstanceType type) {
|
||||
CALL_HEAP_FUNCTION(
|
||||
|
@ -310,6 +310,8 @@ class V8_EXPORT_PRIVATE Factory final {
|
||||
Handle<Context> NewBlockContext(Handle<JSFunction> function,
|
||||
Handle<Context> previous,
|
||||
Handle<ScopeInfo> scope_info);
|
||||
// Create a promise context.
|
||||
Handle<Context> NewPromiseResolvingFunctionContext(int length);
|
||||
|
||||
// Allocate a new struct. The struct is pretenured (allocated directly in
|
||||
// the old generation).
|
||||
|
@ -130,7 +130,7 @@ function AsyncFunctionAwaitCaught(generator, awaited, outerPromise) {
|
||||
|
||||
// How the parser rejects promises from async/await desugaring
|
||||
function RejectPromiseNoDebugEvent(promise, reason) {
|
||||
return RejectPromise(promise, reason);
|
||||
return RejectPromise(promise, reason, false);
|
||||
}
|
||||
|
||||
function AsyncFunctionPromiseCreate() {
|
||||
|
@ -49,35 +49,8 @@ const kPending = 0;
|
||||
const kFulfilled = +1;
|
||||
const kRejected = +2;
|
||||
|
||||
// ES#sec-createresolvingfunctions
|
||||
// CreateResolvingFunctions ( promise )
|
||||
function CreateResolvingFunctions(promise, debugEvent) {
|
||||
var alreadyResolved = false;
|
||||
|
||||
// ES#sec-promise-resolve-functions
|
||||
// Promise Resolve Functions
|
||||
var resolve = value => {
|
||||
if (alreadyResolved === true) return;
|
||||
alreadyResolved = true;
|
||||
ResolvePromise(promise, value);
|
||||
};
|
||||
|
||||
// ES#sec-promise-reject-functions
|
||||
// Promise Reject Functions
|
||||
var reject = reason => {
|
||||
if (alreadyResolved === true) return;
|
||||
alreadyResolved = true;
|
||||
%PromiseReject(promise, reason, debugEvent);
|
||||
PromiseSet(promise, kRejected, reason);
|
||||
};
|
||||
|
||||
return {
|
||||
__proto__: null,
|
||||
resolve: resolve,
|
||||
reject: reject
|
||||
};
|
||||
}
|
||||
|
||||
const kResolveCallback = 0;
|
||||
const kRejectCallback = 1;
|
||||
|
||||
// ES#sec-promise-executor
|
||||
// Promise ( executor )
|
||||
@ -92,13 +65,15 @@ var GlobalPromise = function Promise(executor) {
|
||||
|
||||
var promise = PromiseInit(%_NewObject(GlobalPromise, new.target));
|
||||
// Calling the reject function would be a new exception, so debugEvent = true
|
||||
var callbacks = CreateResolvingFunctions(promise, true);
|
||||
// TODO(gsathya): Remove container for callbacks when this is moved
|
||||
// to CPP/TF.
|
||||
var callbacks = %create_resolving_functions(promise, true);
|
||||
var debug_is_active = DEBUG_IS_ACTIVE;
|
||||
try {
|
||||
if (debug_is_active) %DebugPushPromise(promise);
|
||||
executor(callbacks.resolve, callbacks.reject);
|
||||
executor(callbacks[kResolveCallback], callbacks[kRejectCallback]);
|
||||
} %catch (e) { // Natives syntax to mark this catch block.
|
||||
%_Call(callbacks.reject, UNDEFINED, e);
|
||||
%_Call(callbacks[kRejectCallback], UNDEFINED, e);
|
||||
} finally {
|
||||
if (debug_is_active) %DebugPopPromise();
|
||||
}
|
||||
@ -291,13 +266,16 @@ function ResolvePromise(promise, resolution) {
|
||||
}
|
||||
|
||||
if (IS_CALLABLE(then)) {
|
||||
var callbacks = CreateResolvingFunctions(promise, false);
|
||||
// TODO(gsathya): Remove container for callbacks when this is
|
||||
// moved to CPP/TF.
|
||||
var callbacks = %create_resolving_functions(promise, false);
|
||||
if (DEBUG_IS_ACTIVE && IsPromise(resolution)) {
|
||||
// Mark the dependency of the new promise on the resolution
|
||||
SET_PRIVATE(resolution, promiseHandledBySymbol, promise);
|
||||
}
|
||||
%EnqueuePromiseResolveThenableJob(
|
||||
resolution, then, callbacks.resolve, callbacks.reject);
|
||||
resolution, then, callbacks[kResolveCallback],
|
||||
callbacks[kRejectCallback]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -307,8 +285,8 @@ function ResolvePromise(promise, resolution) {
|
||||
}
|
||||
|
||||
// Only used by async-await.js
|
||||
function RejectPromise(promise, reason) {
|
||||
%PromiseReject(promise, reason, false);
|
||||
function RejectPromise(promise, reason, debugEvent) {
|
||||
%PromiseReject(promise, reason, debugEvent);
|
||||
PromiseSet(promise, kRejected, reason);
|
||||
}
|
||||
|
||||
@ -324,11 +302,13 @@ function NewPromiseCapability(C, debugEvent) {
|
||||
if (C === GlobalPromise) {
|
||||
// Optimized case, avoid extra closure.
|
||||
var promise = PromiseCreate();
|
||||
var callbacks = CreateResolvingFunctions(promise, debugEvent);
|
||||
// TODO(gsathya): Remove container for callbacks when this is
|
||||
// moved to CPP/TF.
|
||||
var callbacks = %create_resolving_functions(promise, debugEvent);
|
||||
return {
|
||||
promise: promise,
|
||||
resolve: callbacks.resolve,
|
||||
reject: callbacks.reject
|
||||
resolve: callbacks[kResolveCallback],
|
||||
reject: callbacks[kRejectCallback]
|
||||
};
|
||||
}
|
||||
|
||||
@ -643,6 +623,8 @@ utils.InstallFunctions(GlobalPromise.prototype, DONT_ENUM, [
|
||||
"promise_create", PromiseCreate,
|
||||
"promise_has_user_defined_reject_handler", PromiseHasUserDefinedRejectHandler,
|
||||
"promise_reject", DoRejectPromise,
|
||||
// TODO(gsathya): Remove this once we update the promise builtin.
|
||||
"promise_internal_reject", RejectPromise,
|
||||
"promise_resolve", ResolvePromise,
|
||||
"promise_then", PromiseThen,
|
||||
"promise_handle", PromiseHandle,
|
||||
|
@ -492,6 +492,7 @@
|
||||
'builtins/builtins-math.cc',
|
||||
'builtins/builtins-number.cc',
|
||||
'builtins/builtins-object.cc',
|
||||
'builtins/builtins-promise.cc',
|
||||
'builtins/builtins-proxy.cc',
|
||||
'builtins/builtins-reflect.cc',
|
||||
'builtins/builtins-regexp.cc',
|
||||
|
@ -79,7 +79,7 @@ bytecodes: [
|
||||
B(Star), R(0),
|
||||
B(CreateArrayLiteral), U8(0), U8(0), U8(9),
|
||||
B(Star), R(1),
|
||||
B(CallJSRuntime), U8(150), R(0), U8(2),
|
||||
B(CallJSRuntime), U8(152), R(0), U8(2),
|
||||
/* 44 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
|
Loading…
Reference in New Issue
Block a user