[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:
gsathya 2016-11-10 08:04:15 -08:00 committed by Commit bot
parent 45b9f15f44
commit cb6c8e48cc
11 changed files with 186 additions and 42 deletions

View File

@ -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",

View File

@ -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();

View 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

View File

@ -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) \

View File

@ -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) \

View File

@ -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(

View File

@ -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).

View File

@ -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() {

View File

@ -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,

View File

@ -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',

View File

@ -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: [