Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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.h" | |
| 6 #include "src/builtins/builtins-utils.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 namespace { | |
| 16 | |
| 17 // Describe fields of Context associated with the AsyncIterator unwrap closure. | |
| 18 class ValueUnwrapContext { | |
| 19 public: | |
| 20 enum Fields { kDoneSlot = Context::MIN_CONTEXT_SLOTS, kLength }; | |
| 21 }; | |
| 22 | |
| 23 class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler { | |
| 24 public: | |
| 25 explicit AsyncFromSyncBuiltinsAssembler(CodeAssemblerState* state) | |
| 26 : AsyncBuiltinsAssembler(state) {} | |
| 27 | |
| 28 void ThrowIfNotAsyncFromSyncIterator(Node* const context, Node* const object, | |
| 29 Label* if_exception, | |
| 30 Variable* var_exception, | |
| 31 const char* method_name); | |
| 32 | |
| 33 typedef std::function<void(Node* const context, Node* const promise, | |
| 34 Label* if_exception)> | |
| 35 UndefinedMethodHandler; | |
| 36 void Generate_AsyncFromSyncIteratorMethod( | |
| 37 Node* const context, Node* const iterator, Node* const sent_value, | |
| 38 Handle<Name> method_name, UndefinedMethodHandler&& if_method_undefined, | |
| 39 const char* operation_name, | |
| 40 Label::Type reject_label_type = Label::kDeferred, | |
| 41 Node* const initial_exception_value = nullptr); | |
| 42 | |
| 43 V8_INLINE void Generate_AsyncFromSyncIteratorMethod( | |
| 44 Node* const context, Node* const iterator, Node* const sent_value, | |
| 45 Handle<Name> method_name, const char* operation_name, | |
| 46 Label::Type reject_label_type = Label::kDeferred, | |
| 47 Node* const initial_exception_value = nullptr) { | |
| 48 return Generate_AsyncFromSyncIteratorMethod( | |
| 49 context, iterator, sent_value, method_name, UndefinedMethodHandler(), | |
| 50 operation_name, reject_label_type, initial_exception_value); | |
| 51 } | |
|
neis
2017/02/21 12:48:08
I think it's clearer if you remove the second one
caitp
2017/02/21 14:36:52
Done.
| |
| 52 | |
| 53 Node* AllocateAsyncIteratorValueUnwrapContext(Node* native_context, | |
| 54 Node* done); | |
| 55 | |
| 56 // Load "value" and "done" from an iterator result object. If an exception | |
| 57 // is thrown at any point, jumps to te `if_exception` label with exception | |
| 58 // stored in `var_exception`. | |
| 59 // | |
| 60 // Returns a Pair of Nodes, whose first element is the value of the "value" | |
| 61 // property, and whose second element is the value of the "done" property, | |
| 62 // converted to a Boolean if needed. | |
| 63 std::pair<Node*, Node*> LoadIteratorResult(Node* const context, | |
| 64 Node* const native_context, | |
| 65 Node* const iter_result, | |
| 66 Label* if_exception, | |
| 67 Variable* var_exception); | |
| 68 | |
| 69 Node* CreateUnwrapClosure(Node* const native_context, Node* const done); | |
| 70 }; | |
| 71 | |
| 72 void AsyncFromSyncBuiltinsAssembler::ThrowIfNotAsyncFromSyncIterator( | |
| 73 Node* const context, Node* const object, Label* if_exception, | |
| 74 Variable* var_exception, const char* method_name) { | |
| 75 Label if_receiverisincompatible(this, Label::kDeferred), done(this); | |
| 76 | |
| 77 GotoIf(TaggedIsSmi(object), &if_receiverisincompatible); | |
| 78 Branch(HasInstanceType(object, JS_ASYNC_FROM_SYNC_ITERATOR_TYPE), &done, | |
| 79 &if_receiverisincompatible); | |
| 80 | |
| 81 Bind(&if_receiverisincompatible); | |
| 82 { | |
| 83 // If Type(O) is not Object, or if O does not have a [[SyncIterator]] | |
| 84 // internal slot, then | |
| 85 | |
| 86 // Let badIteratorError be a new TypeError exception. | |
| 87 Node* const error = | |
| 88 MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver, context, | |
| 89 CStringConstant(method_name), object); | |
| 90 | |
| 91 // Perform ! Call(promiseCapability.[[Reject]], undefined, | |
| 92 // « badIteratorError »). | |
| 93 var_exception->Bind(error); | |
| 94 Goto(if_exception); | |
| 95 } | |
| 96 | |
| 97 Bind(&done); | |
| 98 } | |
| 99 | |
| 100 void AsyncFromSyncBuiltinsAssembler::Generate_AsyncFromSyncIteratorMethod( | |
| 101 Node* const context, Node* const iterator, Node* const sent_value, | |
| 102 Handle<Name> method_name, UndefinedMethodHandler&& if_method_undefined, | |
| 103 const char* operation_name, Label::Type reject_label_type, | |
| 104 Node* const initial_exception_value) { | |
| 105 Node* const native_context = LoadNativeContext(context); | |
| 106 Node* const promise = AllocateAndInitJSPromise(context); | |
| 107 | |
| 108 Variable var_exception(this, MachineRepresentation::kTagged, | |
| 109 initial_exception_value == nullptr | |
| 110 ? UndefinedConstant() | |
| 111 : initial_exception_value); | |
| 112 Label reject_promise(this, reject_label_type); | |
| 113 | |
| 114 ThrowIfNotAsyncFromSyncIterator(context, iterator, &reject_promise, | |
| 115 &var_exception, operation_name); | |
| 116 | |
| 117 Node* const sync_iterator = | |
| 118 LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset); | |
| 119 | |
| 120 Node* const method = GetProperty(context, sync_iterator, method_name); | |
| 121 | |
| 122 if (if_method_undefined) { | |
| 123 Label if_isnotundefined(this); | |
| 124 | |
| 125 GotoUnless(IsUndefined(method), &if_isnotundefined); | |
| 126 if_method_undefined(native_context, promise, &reject_promise); | |
| 127 | |
| 128 Bind(&if_isnotundefined); | |
| 129 } | |
| 130 | |
| 131 Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context, | |
| 132 method, sync_iterator, sent_value); | |
| 133 GotoIfException(iter_result, &reject_promise, &var_exception); | |
| 134 | |
| 135 Node* value; | |
| 136 Node* done; | |
| 137 std::tie(value, done) = LoadIteratorResult( | |
| 138 context, native_context, iter_result, &reject_promise, &var_exception); | |
| 139 Node* const wrapper = AllocateAndInitJSPromise(context); | |
| 140 | |
| 141 // Perform ! Call(valueWrapperCapability.[[Resolve]], undefined, « | |
| 142 // throwValue »). | |
| 143 InternalResolvePromise(context, wrapper, value); | |
| 144 | |
| 145 // Let onFulfilled be a new built-in function object as defined in | |
| 146 // Async Iterator Value Unwrap Functions. | |
| 147 // Set onFulfilled.[[Done]] to throwDone. | |
| 148 Node* const on_fulfilled = CreateUnwrapClosure(native_context, done); | |
| 149 | |
| 150 // Perform ! PerformPromiseThen(valueWrapperCapability.[[Promise]], | |
| 151 // onFulfilled, undefined, promiseCapability). | |
| 152 Node* const undefined = UndefinedConstant(); | |
| 153 InternalPerformPromiseThen(context, wrapper, on_fulfilled, undefined, promise, | |
| 154 undefined, undefined); | |
| 155 Return(promise); | |
| 156 | |
| 157 Bind(&reject_promise); | |
| 158 { | |
| 159 Node* const exception = var_exception.value(); | |
| 160 InternalPromiseReject(context, promise, exception, TrueConstant()); | |
| 161 | |
| 162 Return(promise); | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 std::pair<Node*, Node*> AsyncFromSyncBuiltinsAssembler::LoadIteratorResult( | |
| 167 Node* const context, Node* const native_context, Node* const iter_result, | |
| 168 Label* if_exception, Variable* var_exception) { | |
| 169 Label if_fastpath(this), if_slowpath(this), merge(this), to_boolean(this), | |
| 170 done(this), if_notanobject(this, Label::kDeferred); | |
| 171 GotoIf(TaggedIsSmi(iter_result), &if_notanobject); | |
| 172 | |
| 173 Node* const iter_result_map = LoadMap(iter_result); | |
| 174 GotoUnless(IsJSReceiverMap(iter_result_map), &if_notanobject); | |
| 175 | |
| 176 Node* const fast_iter_result_map = | |
| 177 LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX); | |
| 178 | |
| 179 Variable var_value(this, MachineRepresentation::kTagged); | |
| 180 Variable var_done(this, MachineRepresentation::kTagged); | |
| 181 Branch(WordEqual(iter_result_map, fast_iter_result_map), &if_fastpath, | |
| 182 &if_slowpath); | |
| 183 | |
| 184 Bind(&if_fastpath); | |
| 185 { | |
| 186 var_value.Bind( | |
| 187 LoadObjectField(iter_result, JSIteratorResult::kValueOffset)); | |
| 188 var_done.Bind(LoadObjectField(iter_result, JSIteratorResult::kDoneOffset)); | |
| 189 Goto(&merge); | |
| 190 } | |
| 191 | |
| 192 Bind(&if_slowpath); | |
| 193 { | |
| 194 // Let nextValue be IteratorValue(nextResult). | |
| 195 // IfAbruptRejectPromise(nextValue, promiseCapability). | |
| 196 Node* const value = | |
| 197 GetProperty(context, iter_result, factory()->value_string()); | |
| 198 GotoIfException(value, if_exception, var_exception); | |
| 199 | |
| 200 // Let nextDone be IteratorComplete(nextResult). | |
| 201 // IfAbruptRejectPromise(nextDone, promiseCapability). | |
| 202 Node* const done = | |
| 203 GetProperty(context, iter_result, factory()->done_string()); | |
| 204 GotoIfException(done, if_exception, var_exception); | |
| 205 | |
| 206 var_value.Bind(value); | |
| 207 var_done.Bind(done); | |
| 208 Goto(&merge); | |
| 209 } | |
| 210 | |
| 211 Bind(&if_notanobject); | |
| 212 { | |
| 213 // Sync iterator result is not an object --- Produce a TypeError and jump | |
| 214 // to the `if_exception` path. | |
| 215 Node* const error = MakeTypeError( | |
| 216 MessageTemplate::kIteratorResultNotAnObject, context, iter_result); | |
| 217 var_exception->Bind(error); | |
| 218 Goto(if_exception); | |
| 219 } | |
| 220 | |
| 221 Bind(&merge); | |
| 222 // Ensure `iterResult.done` is a Boolean. | |
| 223 GotoIf(TaggedIsSmi(var_done.value()), &to_boolean); | |
| 224 Branch(IsBoolean(var_done.value()), &done, &to_boolean); | |
| 225 | |
| 226 Bind(&to_boolean); | |
| 227 { | |
| 228 Node* const result = | |
| 229 CallStub(CodeFactory::ToBoolean(isolate()), context, var_done.value()); | |
| 230 var_done.Bind(result); | |
| 231 Goto(&done); | |
| 232 } | |
| 233 | |
| 234 Bind(&done); | |
| 235 return std::make_pair(var_value.value(), var_done.value()); | |
| 236 } | |
| 237 | |
| 238 Node* AsyncFromSyncBuiltinsAssembler::CreateUnwrapClosure(Node* native_context, | |
| 239 Node* done) { | |
| 240 Node* const map = LoadContextElement( | |
| 241 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); | |
| 242 Node* const on_fulfilled_shared = LoadContextElement( | |
| 243 native_context, Context::ASYNC_ITERATOR_VALUE_UNWRAP_SHARED_FUN); | |
| 244 CSA_ASSERT(this, | |
| 245 HasInstanceType(on_fulfilled_shared, SHARED_FUNCTION_INFO_TYPE)); | |
| 246 Node* const closure_context = | |
| 247 AllocateAsyncIteratorValueUnwrapContext(native_context, done); | |
| 248 return AllocateFunctionWithMapAndContext(map, on_fulfilled_shared, | |
| 249 closure_context); | |
| 250 } | |
| 251 | |
| 252 Node* AsyncFromSyncBuiltinsAssembler::AllocateAsyncIteratorValueUnwrapContext( | |
| 253 Node* native_context, Node* done) { | |
| 254 CSA_ASSERT(this, IsNativeContext(native_context)); | |
| 255 CSA_ASSERT(this, IsBoolean(done)); | |
| 256 | |
| 257 Node* const context = | |
| 258 CreatePromiseContext(native_context, ValueUnwrapContext::kLength); | |
| 259 StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot, | |
| 260 done); | |
| 261 return context; | |
| 262 } | |
| 263 } // namespace | |
| 264 | |
| 265 // https://tc39.github.io/proposal-async-iteration/ | |
| 266 // Section #sec-%asyncfromsynciteratorprototype%.next | |
| 267 TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext, AsyncFromSyncBuiltinsAssembler) { | |
| 268 Node* const iterator = Parameter(0); | |
| 269 Node* const value = Parameter(1); | |
| 270 Node* const context = Parameter(4); | |
| 271 | |
| 272 Generate_AsyncFromSyncIteratorMethod( | |
| 273 context, iterator, value, factory()->next_string(), | |
| 274 "[Async-from-Sync Iterator].prototype.next"); | |
| 275 } | |
| 276 | |
| 277 // https://tc39.github.io/proposal-async-iteration/ | |
| 278 // Section #sec-%asyncfromsynciteratorprototype%.return | |
| 279 TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn, | |
| 280 AsyncFromSyncBuiltinsAssembler) { | |
| 281 Node* const iterator = Parameter(0); | |
| 282 Node* const value = Parameter(1); | |
| 283 Node* const context = Parameter(4); | |
| 284 | |
| 285 auto if_return_undefined = [=](Node* const native_context, | |
| 286 Node* const promise, Label* if_exception) { | |
| 287 // If return is undefined, then | |
| 288 // Let iterResult be ! CreateIterResultObject(value, true) | |
| 289 Node* const iter_result = | |
| 290 CallStub(CodeFactory::CreateIterResultObject(isolate()), context, value, | |
| 291 TrueConstant()); | |
| 292 | |
| 293 // Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »). | |
| 294 // IfAbruptRejectPromise(nextDone, promiseCapability). | |
| 295 // Return promiseCapability.[[Promise]]. | |
| 296 PromiseFulfill(context, promise, iter_result, v8::Promise::kFulfilled); | |
| 297 Return(promise); | |
| 298 }; | |
| 299 | |
| 300 Generate_AsyncFromSyncIteratorMethod( | |
| 301 context, iterator, value, factory()->return_string(), if_return_undefined, | |
| 302 "[Async-from-Sync Iterator].prototype.return"); | |
|
neis
2017/02/21 12:48:08
Nice!
| |
| 303 } | |
| 304 | |
| 305 // https://tc39.github.io/proposal-async-iteration/ | |
| 306 // Section #sec-%asyncfromsynciteratorprototype%.throw | |
| 307 TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow, | |
| 308 AsyncFromSyncBuiltinsAssembler) { | |
| 309 Node* const iterator = Parameter(0); | |
| 310 Node* const reason = Parameter(1); | |
| 311 Node* const context = Parameter(4); | |
| 312 | |
| 313 auto if_throw_undefined = [=](Node* const native_context, Node* const promise, | |
| 314 Label* if_exception) { Goto(if_exception); }; | |
| 315 | |
| 316 Generate_AsyncFromSyncIteratorMethod( | |
| 317 context, iterator, reason, factory()->throw_string(), if_throw_undefined, | |
| 318 "[Async-from-Sync Iterator].prototype.throw", Label::kNonDeferred, | |
| 319 reason); | |
| 320 } | |
| 321 | |
| 322 TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncFromSyncBuiltinsAssembler) { | |
| 323 Node* const value = Parameter(1); | |
| 324 Node* const context = Parameter(4); | |
| 325 | |
| 326 Node* const done = LoadContextElement(context, ValueUnwrapContext::kDoneSlot); | |
| 327 CSA_ASSERT(this, IsBoolean(done)); | |
| 328 | |
| 329 Node* const unwrapped_value = CallStub( | |
| 330 CodeFactory::CreateIterResultObject(isolate()), context, value, done); | |
| 331 | |
| 332 Return(unwrapped_value); | |
| 333 } | |
| 334 | |
| 335 } // namespace internal | |
| 336 } // namespace v8 | |
| OLD | NEW |