1 // Copyright 2017 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/builtins/builtins-async-gen.h" 6 #include "src/builtins/builtins-utils-gen.h" 7 #include "src/builtins/builtins.h" 8 #include "src/code-factory.h" 9 #include "src/code-stub-assembler.h" 10 #include "src/frames-inl.h" 11 12 namespace v8 { 13 namespace internal { 14 15 using compiler::Node; 16 17 namespace { 18 class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler { 19 public: 20 explicit AsyncFromSyncBuiltinsAssembler(compiler::CodeAssemblerState* state) 21 : AsyncBuiltinsAssembler(state) {} 22 23 void ThrowIfNotAsyncFromSyncIterator(Node* const context, Node* const object, 24 Label* if_exception, 25 Variable* var_exception, 26 const char* method_name); 27 28 typedef std::function<void(Node* const context, Node* const promise, 29 Label* if_exception)> 30 UndefinedMethodHandler; 31 typedef std::function<Node*(Node*)> SyncIteratorNodeGenerator; 32 void Generate_AsyncFromSyncIteratorMethod( 33 Node* const context, Node* const iterator, Node* const sent_value, 34 const SyncIteratorNodeGenerator& get_method, 35 const UndefinedMethodHandler& if_method_undefined, 36 const char* operation_name, 37 Label::Type reject_label_type = Label::kDeferred, 38 Node* const initial_exception_value = nullptr); 39 void Generate_AsyncFromSyncIteratorMethodOptimized( 40 Node* const context, Node* const iterator, Node* const sent_value, 41 const SyncIteratorNodeGenerator& get_method, 42 const UndefinedMethodHandler& if_method_undefined, 43 const char* operation_name, 44 Label::Type reject_label_type = Label::kDeferred, 45 Node* const initial_exception_value = nullptr); 46 47 void Generate_AsyncFromSyncIteratorMethod( 48 Node* const context, Node* const iterator, Node* const sent_value, 49 Handle<String> name, const UndefinedMethodHandler& if_method_undefined, 50 const char* operation_name, 51 Label::Type reject_label_type = Label::kDeferred, 52 Node* const initial_exception_value = nullptr) { 53 auto get_method = [=](Node* const sync_iterator) { 54 return GetProperty(context, sync_iterator, name); 55 }; 56 return Generate_AsyncFromSyncIteratorMethod( 57 context, iterator, sent_value, get_method, if_method_undefined, 58 operation_name, reject_label_type, initial_exception_value); 59 } 60 void Generate_AsyncFromSyncIteratorMethodOptimized( 61 Node* const context, Node* const iterator, Node* const sent_value, 62 Handle<String> name, const UndefinedMethodHandler& if_method_undefined, 63 const char* operation_name, 64 Label::Type reject_label_type = Label::kDeferred, 65 Node* const initial_exception_value = nullptr) { 66 auto get_method = [=](Node* const sync_iterator) { 67 return GetProperty(context, sync_iterator, name); 68 }; 69 return Generate_AsyncFromSyncIteratorMethodOptimized( 70 context, iterator, sent_value, get_method, if_method_undefined, 71 operation_name, reject_label_type, initial_exception_value); 72 } 73 74 // Load "value" and "done" from an iterator result object. If an exception 75 // is thrown at any point, jumps to te `if_exception` label with exception 76 // stored in `var_exception`. 77 // 78 // Returns a Pair of Nodes, whose first element is the value of the "value" 79 // property, and whose second element is the value of the "done" property, 80 // converted to a Boolean if needed. 81 std::pair<Node*, Node*> LoadIteratorResult(Node* const context, 82 Node* const native_context, 83 Node* const iter_result, 84 Label* if_exception, 85 Variable* var_exception); 86 }; 87 88 void AsyncFromSyncBuiltinsAssembler::ThrowIfNotAsyncFromSyncIterator( 89 Node* const context, Node* const object, Label* if_exception, 90 Variable* var_exception, const char* method_name) { 91 Label if_receiverisincompatible(this, Label::kDeferred), done(this); 92 93 GotoIf(TaggedIsSmi(object), &if_receiverisincompatible); 94 Branch(HasInstanceType(object, JS_ASYNC_FROM_SYNC_ITERATOR_TYPE), &done, 95 &if_receiverisincompatible); 96 97 BIND(&if_receiverisincompatible); 98 { 99 // If Type(O) is not Object, or if O does not have a [[SyncIterator]] 100 // internal slot, then 101 102 // Let badIteratorError be a new TypeError exception. 103 Node* const error = 104 MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver, context, 105 StringConstant(method_name), object); 106 107 // Perform ! Call(promiseCapability.[[Reject]], undefined, 108 // badIteratorError ). 109 var_exception->Bind(error); 110 Goto(if_exception); 111 } 112 113 BIND(&done); 114 } 115 116 void AsyncFromSyncBuiltinsAssembler::Generate_AsyncFromSyncIteratorMethod( 117 Node* const context, Node* const iterator, Node* const sent_value, 118 const SyncIteratorNodeGenerator& get_method, 119 const UndefinedMethodHandler& if_method_undefined, 120 const char* operation_name, Label::Type reject_label_type, 121 Node* const initial_exception_value) { 122 Node* const native_context = LoadNativeContext(context); 123 Node* const promise = AllocateAndInitJSPromise(context); 124 125 VARIABLE(var_exception, MachineRepresentation::kTagged, 126 initial_exception_value == nullptr ? UndefinedConstant() 127 : initial_exception_value); 128 Label reject_promise(this, reject_label_type); 129 130 ThrowIfNotAsyncFromSyncIterator(context, iterator, &reject_promise, 131 &var_exception, operation_name); 132 133 Node* const sync_iterator = 134 LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset); 135 136 Node* const method = get_method(sync_iterator); 137 138 if (if_method_undefined) { 139 Label if_isnotundefined(this); 140 141 GotoIfNot(IsUndefined(method), &if_isnotundefined); 142 if_method_undefined(native_context, promise, &reject_promise); 143 144 BIND(&if_isnotundefined); 145 } 146 147 Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context, 148 method, sync_iterator, sent_value); 149 GotoIfException(iter_result, &reject_promise, &var_exception); 150 151 Node* value; 152 Node* done; 153 std::tie(value, done) = LoadIteratorResult( 154 context, native_context, iter_result, &reject_promise, &var_exception); 155 Node* const wrapper = AllocateAndInitJSPromise(context); 156 157 // Perform ! Call(valueWrapperCapability.[[Resolve]], undefined, 158 // throwValue ). 159 CallBuiltin(Builtins::kResolvePromise, context, wrapper, value); 160 161 // Let onFulfilled be a new built-in function object as defined in 162 // Async Iterator Value Unwrap Functions. 163 // Set onFulfilled.[[Done]] to throwDone. 164 Node* const on_fulfilled = CreateUnwrapClosure(native_context, done); 165 166 // Perform ! PerformPromiseThen(valueWrapperCapability.[[Promise]], 167 // onFulfilled, undefined, promiseCapability). 168 Return(CallBuiltin(Builtins::kPerformPromiseThen, context, wrapper, 169 on_fulfilled, UndefinedConstant(), promise)); 170 171 BIND(&reject_promise); 172 { 173 Node* const exception = var_exception.value(); 174 CallBuiltin(Builtins::kRejectPromise, context, promise, exception, 175 TrueConstant()); 176 Return(promise); 177 } 178 } 179 180 void AsyncFromSyncBuiltinsAssembler:: 181 Generate_AsyncFromSyncIteratorMethodOptimized( 182 Node* const context, Node* const iterator, Node* const sent_value, 183 const SyncIteratorNodeGenerator& get_method, 184 const UndefinedMethodHandler& if_method_undefined, 185 const char* operation_name, Label::Type reject_label_type, 186 Node* const initial_exception_value) { 187 Node* const native_context = LoadNativeContext(context); 188 Node* const promise = AllocateAndInitJSPromise(context); 189 190 VARIABLE(var_exception, MachineRepresentation::kTagged, 191 initial_exception_value == nullptr ? UndefinedConstant() 192 : initial_exception_value); 193 Label reject_promise(this, reject_label_type); 194 195 ThrowIfNotAsyncFromSyncIterator(context, iterator, &reject_promise, 196 &var_exception, operation_name); 197 198 Node* const sync_iterator = 199 LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset); 200 201 Node* const method = get_method(sync_iterator); 202 203 if (if_method_undefined) { 204 Label if_isnotundefined(this); 205 206 GotoIfNot(IsUndefined(method), &if_isnotundefined); 207 if_method_undefined(native_context, promise, &reject_promise); 208 209 BIND(&if_isnotundefined); 210 } 211 212 Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context, 213 method, sync_iterator, sent_value); 214 GotoIfException(iter_result, &reject_promise, &var_exception); 215 216 Node* value; 217 Node* done; 218 std::tie(value, done) = LoadIteratorResult( 219 context, native_context, iter_result, &reject_promise, &var_exception); 220 221 Node* const promise_fun = 222 LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); 223 CSA_ASSERT(this, IsConstructor(promise_fun)); 224 225 // Let valueWrapper be ? PromiseResolve( value ). 226 Node* const valueWrapper = CallBuiltin(Builtins::kPromiseResolve, 227 native_context, promise_fun, value); 228 229 // Let onFulfilled be a new built-in function object as defined in 230 // Async Iterator Value Unwrap Functions. 231 // Set onFulfilled.[[Done]] to throwDone. 232 Node* const on_fulfilled = CreateUnwrapClosure(native_context, done); 233 234 // Perform ! PerformPromiseThen(valueWrapper, 235 // onFulfilled, undefined, promiseCapability). 236 Return(CallBuiltin(Builtins::kPerformPromiseThen, context, valueWrapper, 237 on_fulfilled, UndefinedConstant(), promise)); 238 239 BIND(&reject_promise); 240 { 241 Node* const exception = var_exception.value(); 242 CallBuiltin(Builtins::kRejectPromise, context, promise, exception, 243 TrueConstant()); 244 Return(promise); 245 } 246 } 247 std::pair<Node*, Node*> AsyncFromSyncBuiltinsAssembler::LoadIteratorResult( 248 Node* const context, Node* const native_context, Node* const iter_result, 249 Label* if_exception, Variable* var_exception) { 250 Label if_fastpath(this), if_slowpath(this), merge(this), to_boolean(this), 251 done(this), if_notanobject(this, Label::kDeferred); 252 GotoIf(TaggedIsSmi(iter_result), &if_notanobject); 253 254 Node* const iter_result_map = LoadMap(iter_result); 255 GotoIfNot(IsJSReceiverMap(iter_result_map), &if_notanobject); 256 257 Node* const fast_iter_result_map = 258 LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX); 259 260 VARIABLE(var_value, MachineRepresentation::kTagged); 261 VARIABLE(var_done, MachineRepresentation::kTagged); 262 Branch(WordEqual(iter_result_map, fast_iter_result_map), &if_fastpath, 263 &if_slowpath); 264 265 BIND(&if_fastpath); 266 { 267 var_done.Bind(LoadObjectField(iter_result, JSIteratorResult::kDoneOffset)); 268 var_value.Bind( 269 LoadObjectField(iter_result, JSIteratorResult::kValueOffset)); 270 Goto(&merge); 271 } 272 273 BIND(&if_slowpath); 274 { 275 // Let nextDone be IteratorComplete(nextResult). 276 // IfAbruptRejectPromise(nextDone, promiseCapability). 277 Node* const done = 278 GetProperty(context, iter_result, factory()->done_string()); 279 GotoIfException(done, if_exception, var_exception); 280 281 // Let nextValue be IteratorValue(nextResult). 282 // IfAbruptRejectPromise(nextValue, promiseCapability). 283 Node* const value = 284 GetProperty(context, iter_result, factory()->value_string()); 285 GotoIfException(value, if_exception, var_exception); 286 287 var_value.Bind(value); 288 var_done.Bind(done); 289 Goto(&merge); 290 } 291 292 BIND(&if_notanobject); 293 { 294 // Sync iterator result is not an object --- Produce a TypeError and jump 295 // to the `if_exception` path. 296 Node* const error = MakeTypeError( 297 MessageTemplate::kIteratorResultNotAnObject, context, iter_result); 298 var_exception->Bind(error); 299 Goto(if_exception); 300 } 301 302 BIND(&merge); 303 // Ensure `iterResult.done` is a Boolean. 304 GotoIf(TaggedIsSmi(var_done.value()), &to_boolean); 305 Branch(IsBoolean(var_done.value()), &done, &to_boolean); 306 307 BIND(&to_boolean); 308 { 309 Node* const result = 310 CallBuiltin(Builtins::kToBoolean, context, var_done.value()); 311 var_done.Bind(result); 312 Goto(&done); 313 } 314 315 BIND(&done); 316 return std::make_pair(var_value.value(), var_done.value()); 317 } 318 319 } // namespace 320 321 // https://tc39.github.io/proposal-async-iteration/ 322 // Section #sec-%asyncfromsynciteratorprototype%.next 323 TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext, AsyncFromSyncBuiltinsAssembler) { 324 Node* const iterator = Parameter(Descriptor::kReceiver); 325 Node* const value = Parameter(Descriptor::kValue); 326 Node* const context = Parameter(Descriptor::kContext); 327 328 auto get_method = [=](Node* const unused) { 329 return LoadObjectField(iterator, JSAsyncFromSyncIterator::kNextOffset); 330 }; 331 Generate_AsyncFromSyncIteratorMethod( 332 context, iterator, value, get_method, UndefinedMethodHandler(), 333 "[Async-from-Sync Iterator].prototype.next"); 334 } 335 336 TF_BUILTIN(AsyncFromSyncIteratorPrototypeNextOptimized, 337 AsyncFromSyncBuiltinsAssembler) { 338 Node* const iterator = Parameter(Descriptor::kReceiver); 339 Node* const value = Parameter(Descriptor::kValue); 340 Node* const context = Parameter(Descriptor::kContext); 341 342 auto get_method = [=](Node* const unused) { 343 return LoadObjectField(iterator, JSAsyncFromSyncIterator::kNextOffset); 344 }; 345 Generate_AsyncFromSyncIteratorMethodOptimized( 346 context, iterator, value, get_method, UndefinedMethodHandler(), 347 "[Async-from-Sync Iterator].prototype.next"); 348 } 349 350 // https://tc39.github.io/proposal-async-iteration/ 351 // Section #sec-%asyncfromsynciteratorprototype%.return 352 TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn, 353 AsyncFromSyncBuiltinsAssembler) { 354 Node* const iterator = Parameter(Descriptor::kReceiver); 355 Node* const value = Parameter(Descriptor::kValue); 356 Node* const context = Parameter(Descriptor::kContext); 357 358 auto if_return_undefined = [=](Node* const native_context, 359 Node* const promise, Label* if_exception) { 360 // If return is undefined, then 361 // Let iterResult be ! CreateIterResultObject(value, true) 362 Node* const iter_result = CallBuiltin(Builtins::kCreateIterResultObject, 363 context, value, TrueConstant()); 364 365 // Perform ! Call(promiseCapability.[[Resolve]], undefined, iterResult ). 366 // IfAbruptRejectPromise(nextDone, promiseCapability). 367 // Return promiseCapability.[[Promise]]. 368 CallBuiltin(Builtins::kResolvePromise, context, promise, iter_result); 369 Return(promise); 370 }; 371 372 Generate_AsyncFromSyncIteratorMethod( 373 context, iterator, value, factory()->return_string(), if_return_undefined, 374 "[Async-from-Sync Iterator].prototype.return"); 375 } 376 377 TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturnOptimized, 378 AsyncFromSyncBuiltinsAssembler) { 379 Node* const iterator = Parameter(Descriptor::kReceiver); 380 Node* const value = Parameter(Descriptor::kValue); 381 Node* const context = Parameter(Descriptor::kContext); 382 383 auto if_return_undefined = [=](Node* const native_context, 384 Node* const promise, Label* if_exception) { 385 // If return is undefined, then 386 // Let iterResult be ! CreateIterResultObject(value, true) 387 Node* const iter_result = CallBuiltin(Builtins::kCreateIterResultObject, 388 context, value, TrueConstant()); 389 390 // Perform ! Call(promiseCapability.[[Resolve]], undefined, iterResult ). 391 // IfAbruptRejectPromise(nextDone, promiseCapability). 392 // Return promiseCapability.[[Promise]]. 393 CallBuiltin(Builtins::kResolvePromise, context, promise, iter_result); 394 Return(promise); 395 }; 396 397 Generate_AsyncFromSyncIteratorMethodOptimized( 398 context, iterator, value, factory()->return_string(), if_return_undefined, 399 "[Async-from-Sync Iterator].prototype.return"); 400 } 401 402 // https://tc39.github.io/proposal-async-iteration/ 403 // Section #sec-%asyncfromsynciteratorprototype%.throw 404 TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow, 405 AsyncFromSyncBuiltinsAssembler) { 406 Node* const iterator = Parameter(Descriptor::kReceiver); 407 Node* const reason = Parameter(Descriptor::kReason); 408 Node* const context = Parameter(Descriptor::kContext); 409 410 auto if_throw_undefined = [=](Node* const native_context, Node* const promise, 411 Label* if_exception) { Goto(if_exception); }; 412 413 Generate_AsyncFromSyncIteratorMethod( 414 context, iterator, reason, factory()->throw_string(), if_throw_undefined, 415 "[Async-from-Sync Iterator].prototype.throw", Label::kNonDeferred, 416 reason); 417 } 418 419 TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrowOptimized, 420 AsyncFromSyncBuiltinsAssembler) { 421 Node* const iterator = Parameter(Descriptor::kReceiver); 422 Node* const reason = Parameter(Descriptor::kReason); 423 Node* const context = Parameter(Descriptor::kContext); 424 425 auto if_throw_undefined = [=](Node* const native_context, Node* const promise, 426 Label* if_exception) { Goto(if_exception); }; 427 428 Generate_AsyncFromSyncIteratorMethodOptimized( 429 context, iterator, reason, factory()->throw_string(), if_throw_undefined, 430 "[Async-from-Sync Iterator].prototype.throw", Label::kNonDeferred, 431 reason); 432 } 433 434 } // namespace internal 435 } // namespace v8 436