1// Copyright 2016 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/runtime/runtime-utils.h" 6 7#include "src/debug/debug.h" 8#include "src/elements.h" 9#include "src/promise-utils.h" 10 11namespace v8 { 12namespace internal { 13 14namespace { 15 16void PromiseRejectEvent(Isolate* isolate, Handle<JSReceiver> promise, 17 Handle<Object> rejected_promise, Handle<Object> value, 18 bool debug_event) { 19 if (isolate->debug()->is_active() && debug_event) { 20 isolate->debug()->OnPromiseReject(rejected_promise, value); 21 } 22 Handle<Symbol> key = isolate->factory()->promise_has_handler_symbol(); 23 // Do not report if we actually have a handler. 24 if (JSReceiver::GetDataProperty(promise, key)->IsUndefined(isolate)) { 25 isolate->ReportPromiseReject(Handle<JSObject>::cast(promise), value, 26 v8::kPromiseRejectWithNoHandler); 27 } 28} 29 30} // namespace 31 32RUNTIME_FUNCTION(Runtime_PromiseRejectEventFromStack) { 33 DCHECK(args.length() == 2); 34 HandleScope scope(isolate); 35 CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0); 36 CONVERT_ARG_HANDLE_CHECKED(Object, value, 1); 37 38 Handle<Object> rejected_promise = promise; 39 if (isolate->debug()->is_active()) { 40 // If the Promise.reject call is caught, then this will return 41 // undefined, which will be interpreted by PromiseRejectEvent 42 // as being a caught exception event. 43 rejected_promise = isolate->GetPromiseOnStackOnThrow(); 44 } 45 PromiseRejectEvent(isolate, promise, rejected_promise, value, true); 46 return isolate->heap()->undefined_value(); 47} 48 49RUNTIME_FUNCTION(Runtime_PromiseRevokeReject) { 50 DCHECK(args.length() == 1); 51 HandleScope scope(isolate); 52 CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0); 53 Handle<Symbol> key = isolate->factory()->promise_has_handler_symbol(); 54 // At this point, no revocation has been issued before 55 CHECK(JSReceiver::GetDataProperty(promise, key)->IsUndefined(isolate)); 56 isolate->ReportPromiseReject(promise, Handle<Object>(), 57 v8::kPromiseHandlerAddedAfterReject); 58 return isolate->heap()->undefined_value(); 59} 60 61namespace { 62void EnqueuePromiseReactionJob(Isolate* isolate, Handle<Object> value, 63 Handle<Object> tasks, Handle<Object> deferred, 64 Handle<Object> status) { 65 Handle<Object> debug_id = isolate->factory()->undefined_value(); 66 Handle<Object> debug_name = isolate->factory()->undefined_value(); 67 if (isolate->debug()->is_active()) { 68 MaybeHandle<Object> maybe_result; 69 Handle<Object> argv[] = {deferred, status}; 70 maybe_result = Execution::TryCall( 71 isolate, isolate->promise_debug_get_info(), 72 isolate->factory()->undefined_value(), arraysize(argv), argv); 73 Handle<Object> result; 74 if ((maybe_result).ToHandle(&result)) { 75 CHECK(result->IsJSArray()); 76 Handle<JSArray> array = Handle<JSArray>::cast(result); 77 ElementsAccessor* accessor = array->GetElementsAccessor(); 78 DCHECK(accessor->HasElement(array, 0)); 79 DCHECK(accessor->HasElement(array, 1)); 80 debug_id = accessor->Get(array, 0); 81 debug_name = accessor->Get(array, 1); 82 } 83 } 84 Handle<PromiseReactionJobInfo> info = 85 isolate->factory()->NewPromiseReactionJobInfo(value, tasks, deferred, 86 debug_id, debug_name, 87 isolate->native_context()); 88 isolate->EnqueueMicrotask(info); 89} 90 91void PromiseFulfill(Isolate* isolate, Handle<JSReceiver> promise, 92 Handle<Smi> status, Handle<Object> value, 93 Handle<Symbol> reaction) { 94 Handle<Object> tasks = JSReceiver::GetDataProperty(promise, reaction); 95 if (!tasks->IsUndefined(isolate)) { 96 Handle<Object> deferred = JSReceiver::GetDataProperty( 97 promise, isolate->factory()->promise_deferred_reaction_symbol()); 98 EnqueuePromiseReactionJob(isolate, value, tasks, deferred, status); 99 } 100} 101} // namespace 102 103RUNTIME_FUNCTION(Runtime_PromiseReject) { 104 DCHECK(args.length() == 3); 105 HandleScope scope(isolate); 106 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, promise, 0); 107 CONVERT_ARG_HANDLE_CHECKED(Object, reason, 1); 108 CONVERT_BOOLEAN_ARG_CHECKED(debug_event, 2); 109 110 PromiseRejectEvent(isolate, promise, promise, reason, debug_event); 111 112 Handle<Smi> status = handle(Smi::FromInt(kPromiseRejected), isolate); 113 Handle<Symbol> reaction = 114 isolate->factory()->promise_reject_reactions_symbol(); 115 PromiseFulfill(isolate, promise, status, reason, reaction); 116 return isolate->heap()->undefined_value(); 117} 118 119RUNTIME_FUNCTION(Runtime_PromiseFulfill) { 120 DCHECK(args.length() == 4); 121 HandleScope scope(isolate); 122 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, promise, 0); 123 CONVERT_ARG_HANDLE_CHECKED(Smi, status, 1); 124 CONVERT_ARG_HANDLE_CHECKED(Object, value, 2); 125 CONVERT_ARG_HANDLE_CHECKED(Symbol, reaction, 3); 126 PromiseFulfill(isolate, promise, status, value, reaction); 127 return isolate->heap()->undefined_value(); 128} 129 130RUNTIME_FUNCTION(Runtime_EnqueuePromiseReactionJob) { 131 HandleScope scope(isolate); 132 DCHECK(args.length() == 4); 133 CONVERT_ARG_HANDLE_CHECKED(Object, value, 0); 134 CONVERT_ARG_HANDLE_CHECKED(Object, tasks, 1); 135 CONVERT_ARG_HANDLE_CHECKED(Object, deferred, 2); 136 CONVERT_ARG_HANDLE_CHECKED(Object, status, 3); 137 EnqueuePromiseReactionJob(isolate, value, tasks, deferred, status); 138 return isolate->heap()->undefined_value(); 139} 140 141RUNTIME_FUNCTION(Runtime_EnqueuePromiseResolveThenableJob) { 142 HandleScope scope(isolate); 143 DCHECK(args.length() == 3); 144 CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0); 145 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, resolution, 1); 146 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, then, 2); 147 148 // TODO(gsathya): Add fast path for native promises with unmodified 149 // PromiseThen (which don't need these resolving functions, but 150 // instead can just call resolve/reject directly). 151 Handle<JSFunction> resolve, reject; 152 PromiseUtils::CreateResolvingFunctions( 153 isolate, promise, isolate->factory()->false_value(), &resolve, &reject); 154 155 Handle<Object> debug_id, debug_name; 156 if (isolate->debug()->is_active()) { 157 debug_id = 158 handle(Smi::FromInt(isolate->GetNextDebugMicrotaskId()), isolate); 159 debug_name = isolate->factory()->PromiseResolveThenableJob_string(); 160 isolate->debug()->OnAsyncTaskEvent(isolate->factory()->enqueue_string(), 161 debug_id, 162 Handle<String>::cast(debug_name)); 163 } else { 164 debug_id = isolate->factory()->undefined_value(); 165 debug_name = isolate->factory()->undefined_value(); 166 } 167 168 Handle<PromiseResolveThenableJobInfo> info = 169 isolate->factory()->NewPromiseResolveThenableJobInfo( 170 resolution, then, resolve, reject, debug_id, debug_name, 171 isolate->native_context()); 172 isolate->EnqueueMicrotask(info); 173 174 return isolate->heap()->undefined_value(); 175} 176 177RUNTIME_FUNCTION(Runtime_EnqueueMicrotask) { 178 HandleScope scope(isolate); 179 DCHECK(args.length() == 1); 180 CONVERT_ARG_HANDLE_CHECKED(JSFunction, microtask, 0); 181 isolate->EnqueueMicrotask(microtask); 182 return isolate->heap()->undefined_value(); 183} 184 185RUNTIME_FUNCTION(Runtime_RunMicrotasks) { 186 HandleScope scope(isolate); 187 DCHECK(args.length() == 0); 188 isolate->RunMicrotasks(); 189 return isolate->heap()->undefined_value(); 190} 191 192} // namespace internal 193} // namespace v8 194