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/builtins/builtins-utils-gen.h" 6#include "src/builtins/builtins.h" 7#include "src/codegen/code-factory.h" 8#include "src/codegen/code-stub-assembler.h" 9#include "src/execution/isolate.h" 10#include "src/objects/js-generator.h" 11#include "src/objects/objects-inl.h" 12 13namespace v8 { 14namespace internal { 15 16class GeneratorBuiltinsAssembler : public CodeStubAssembler { 17 public: 18 explicit GeneratorBuiltinsAssembler(compiler::CodeAssemblerState* state) 19 : CodeStubAssembler(state) {} 20 21 protected: 22 // Currently, AsyncModules in V8 are built on top of JSAsyncFunctionObjects 23 // with an initial yield. Thus, we need some way to 'resume' the 24 // underlying JSAsyncFunctionObject owned by an AsyncModule. To support this 25 // the body of resume is factored out below, and shared by JSGeneratorObject 26 // prototype methods as well as AsyncModuleEvaluate. The only difference 27 // between AsyncModuleEvaluate and JSGeneratorObject::PrototypeNext is 28 // the expected receiver. 29 void InnerResume(CodeStubArguments* args, TNode<JSGeneratorObject> receiver, 30 TNode<Object> value, TNode<Context> context, 31 JSGeneratorObject::ResumeMode resume_mode, 32 char const* const method_name); 33 void GeneratorPrototypeResume(CodeStubArguments* args, TNode<Object> receiver, 34 TNode<Object> value, TNode<Context> context, 35 JSGeneratorObject::ResumeMode resume_mode, 36 char const* const method_name); 37}; 38 39void GeneratorBuiltinsAssembler::InnerResume( 40 CodeStubArguments* args, TNode<JSGeneratorObject> receiver, 41 TNode<Object> value, TNode<Context> context, 42 JSGeneratorObject::ResumeMode resume_mode, char const* const method_name) { 43 // Check if the {receiver} is running or already closed. 44 TNode<Smi> receiver_continuation = 45 LoadObjectField<Smi>(receiver, JSGeneratorObject::kContinuationOffset); 46 Label if_receiverisclosed(this, Label::kDeferred), 47 if_receiverisrunning(this, Label::kDeferred); 48 TNode<Smi> closed = SmiConstant(JSGeneratorObject::kGeneratorClosed); 49 GotoIf(SmiEqual(receiver_continuation, closed), &if_receiverisclosed); 50 DCHECK_LT(JSGeneratorObject::kGeneratorExecuting, 51 JSGeneratorObject::kGeneratorClosed); 52 GotoIf(SmiLessThan(receiver_continuation, closed), &if_receiverisrunning); 53 54 // Remember the {resume_mode} for the {receiver}. 55 StoreObjectFieldNoWriteBarrier(receiver, JSGeneratorObject::kResumeModeOffset, 56 SmiConstant(resume_mode)); 57 58 // Resume the {receiver} using our trampoline. 59 // Close the generator if there was an exception. 60 TVARIABLE(Object, var_exception); 61 Label if_exception(this, Label::kDeferred), if_final_return(this); 62 TNode<Object> result; 63 { 64 compiler::ScopedExceptionHandler handler(this, &if_exception, 65 &var_exception); 66 result = CallStub(CodeFactory::ResumeGenerator(isolate()), context, value, 67 receiver); 68 } 69 70 // If the generator is not suspended (i.e., its state is 'executing'), 71 // close it and wrap the return value in IteratorResult. 72 TNode<Smi> result_continuation = 73 LoadObjectField<Smi>(receiver, JSGeneratorObject::kContinuationOffset); 74 75 // The generator function should not close the generator by itself, let's 76 // check it is indeed not closed yet. 77 CSA_DCHECK(this, SmiNotEqual(result_continuation, closed)); 78 79 TNode<Smi> executing = SmiConstant(JSGeneratorObject::kGeneratorExecuting); 80 GotoIf(SmiEqual(result_continuation, executing), &if_final_return); 81 82 args->PopAndReturn(result); 83 84 BIND(&if_final_return); 85 { 86 // Close the generator. 87 StoreObjectFieldNoWriteBarrier( 88 receiver, JSGeneratorObject::kContinuationOffset, closed); 89 // Return the wrapped result. 90 args->PopAndReturn(CallBuiltin(Builtin::kCreateIterResultObject, context, 91 result, TrueConstant())); 92 } 93 94 BIND(&if_receiverisclosed); 95 { 96 // The {receiver} is closed already. 97 TNode<Object> builtin_result; 98 switch (resume_mode) { 99 case JSGeneratorObject::kNext: 100 builtin_result = CallBuiltin(Builtin::kCreateIterResultObject, context, 101 UndefinedConstant(), TrueConstant()); 102 break; 103 case JSGeneratorObject::kReturn: 104 builtin_result = CallBuiltin(Builtin::kCreateIterResultObject, context, 105 value, TrueConstant()); 106 break; 107 case JSGeneratorObject::kThrow: 108 builtin_result = CallRuntime(Runtime::kThrow, context, value); 109 break; 110 } 111 args->PopAndReturn(builtin_result); 112 } 113 114 BIND(&if_receiverisrunning); 115 { ThrowTypeError(context, MessageTemplate::kGeneratorRunning); } 116 117 BIND(&if_exception); 118 { 119 StoreObjectFieldNoWriteBarrier( 120 receiver, JSGeneratorObject::kContinuationOffset, closed); 121 CallRuntime(Runtime::kReThrow, context, var_exception.value()); 122 Unreachable(); 123 } 124} 125 126void GeneratorBuiltinsAssembler::GeneratorPrototypeResume( 127 CodeStubArguments* args, TNode<Object> receiver, TNode<Object> value, 128 TNode<Context> context, JSGeneratorObject::ResumeMode resume_mode, 129 char const* const method_name) { 130 // Check if the {receiver} is actually a JSGeneratorObject. 131 ThrowIfNotInstanceType(context, receiver, JS_GENERATOR_OBJECT_TYPE, 132 method_name); 133 TNode<JSGeneratorObject> generator = CAST(receiver); 134 InnerResume(args, generator, value, context, resume_mode, method_name); 135} 136 137TF_BUILTIN(AsyncModuleEvaluate, GeneratorBuiltinsAssembler) { 138 const int kValueArg = 0; 139 140 auto argc = UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount); 141 CodeStubArguments args(this, argc); 142 143 TNode<Object> receiver = args.GetReceiver(); 144 TNode<Object> value = args.GetOptionalArgumentValue(kValueArg); 145 auto context = Parameter<Context>(Descriptor::kContext); 146 147 // AsyncModules act like JSAsyncFunctions. Thus we check here 148 // that the {receiver} is a JSAsyncFunction. 149 char const* const method_name = "[AsyncModule].evaluate"; 150 ThrowIfNotInstanceType(context, receiver, JS_ASYNC_FUNCTION_OBJECT_TYPE, 151 method_name); 152 TNode<JSAsyncFunctionObject> async_function = CAST(receiver); 153 InnerResume(&args, async_function, value, context, JSGeneratorObject::kNext, 154 method_name); 155} 156 157// ES6 #sec-generator.prototype.next 158TF_BUILTIN(GeneratorPrototypeNext, GeneratorBuiltinsAssembler) { 159 const int kValueArg = 0; 160 161 auto argc = UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount); 162 CodeStubArguments args(this, argc); 163 164 TNode<Object> receiver = args.GetReceiver(); 165 TNode<Object> value = args.GetOptionalArgumentValue(kValueArg); 166 auto context = Parameter<Context>(Descriptor::kContext); 167 168 GeneratorPrototypeResume(&args, receiver, value, context, 169 JSGeneratorObject::kNext, 170 "[Generator].prototype.next"); 171} 172 173// ES6 #sec-generator.prototype.return 174TF_BUILTIN(GeneratorPrototypeReturn, GeneratorBuiltinsAssembler) { 175 const int kValueArg = 0; 176 177 auto argc = UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount); 178 CodeStubArguments args(this, argc); 179 180 TNode<Object> receiver = args.GetReceiver(); 181 TNode<Object> value = args.GetOptionalArgumentValue(kValueArg); 182 auto context = Parameter<Context>(Descriptor::kContext); 183 184 GeneratorPrototypeResume(&args, receiver, value, context, 185 JSGeneratorObject::kReturn, 186 "[Generator].prototype.return"); 187} 188 189// ES6 #sec-generator.prototype.throw 190TF_BUILTIN(GeneratorPrototypeThrow, GeneratorBuiltinsAssembler) { 191 const int kExceptionArg = 0; 192 193 auto argc = UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount); 194 CodeStubArguments args(this, argc); 195 196 TNode<Object> receiver = args.GetReceiver(); 197 TNode<Object> exception = args.GetOptionalArgumentValue(kExceptionArg); 198 auto context = Parameter<Context>(Descriptor::kContext); 199 200 GeneratorPrototypeResume(&args, receiver, exception, context, 201 JSGeneratorObject::kThrow, 202 "[Generator].prototype.throw"); 203} 204 205// TODO(cbruni): Merge with corresponding bytecode handler. 206TF_BUILTIN(SuspendGeneratorBaseline, GeneratorBuiltinsAssembler) { 207 auto generator = Parameter<JSGeneratorObject>(Descriptor::kGeneratorObject); 208 auto context = LoadContextFromBaseline(); 209 StoreJSGeneratorObjectContext(generator, context); 210 auto suspend_id = SmiTag(UncheckedParameter<IntPtrT>(Descriptor::kSuspendId)); 211 StoreJSGeneratorObjectContinuation(generator, suspend_id); 212 // Store the bytecode offset in the [input_or_debug_pos] field, to be used by 213 // the inspector. 214 auto bytecode_offset = 215 SmiTag(UncheckedParameter<IntPtrT>(Descriptor::kBytecodeOffset)); 216 // Avoid the write barrier by using the generic helper. 217 StoreObjectFieldNoWriteBarrier( 218 generator, JSGeneratorObject::kInputOrDebugPosOffset, bytecode_offset); 219 220 TNode<JSFunction> closure = LoadJSGeneratorObjectFunction(generator); 221 auto sfi = LoadJSFunctionSharedFunctionInfo(closure); 222 CSA_DCHECK(this, 223 Word32BinaryNot(IsSharedFunctionInfoDontAdaptArguments(sfi))); 224 TNode<IntPtrT> formal_parameter_count = Signed(ChangeUint32ToWord( 225 LoadSharedFunctionInfoFormalParameterCountWithoutReceiver(sfi))); 226 227 TNode<FixedArray> parameters_and_registers = 228 LoadJSGeneratorObjectParametersAndRegisters(generator); 229 auto parameters_and_registers_length = 230 SmiUntag(LoadFixedArrayBaseLength(parameters_and_registers)); 231 232 // Copy over the function parameters 233 auto parameter_base_index = IntPtrConstant( 234 interpreter::Register::FromParameterIndex(0).ToOperand() + 1); 235 CSA_CHECK(this, UintPtrLessThan(formal_parameter_count, 236 parameters_and_registers_length)); 237 auto parent_frame_pointer = LoadParentFramePointer(); 238 BuildFastLoop<IntPtrT>( 239 IntPtrConstant(0), formal_parameter_count, 240 [=](TNode<IntPtrT> index) { 241 auto reg_index = IntPtrAdd(parameter_base_index, index); 242 TNode<Object> value = LoadFullTagged(parent_frame_pointer, 243 TimesSystemPointerSize(reg_index)); 244 UnsafeStoreFixedArrayElement(parameters_and_registers, index, value); 245 }, 246 1, IndexAdvanceMode::kPost); 247 248 // Iterate over register file and write values into array. 249 // The mapping of register to array index must match that used in 250 // BytecodeGraphBuilder::VisitResumeGenerator. 251 auto register_base_index = 252 IntPtrAdd(formal_parameter_count, 253 IntPtrConstant(interpreter::Register(0).ToOperand())); 254 auto register_count = UncheckedParameter<IntPtrT>(Descriptor::kRegisterCount); 255 auto end_index = IntPtrAdd(formal_parameter_count, register_count); 256 CSA_CHECK(this, UintPtrLessThan(end_index, parameters_and_registers_length)); 257 BuildFastLoop<IntPtrT>( 258 formal_parameter_count, end_index, 259 [=](TNode<IntPtrT> index) { 260 auto reg_index = IntPtrSub(register_base_index, index); 261 TNode<Object> value = LoadFullTagged(parent_frame_pointer, 262 TimesSystemPointerSize(reg_index)); 263 UnsafeStoreFixedArrayElement(parameters_and_registers, index, value); 264 }, 265 1, IndexAdvanceMode::kPost); 266 267 // The return value is unused, defaulting to undefined. 268 Return(UndefinedConstant()); 269} 270 271// TODO(cbruni): Merge with corresponding bytecode handler. 272TF_BUILTIN(ResumeGeneratorBaseline, GeneratorBuiltinsAssembler) { 273 auto generator = Parameter<JSGeneratorObject>(Descriptor::kGeneratorObject); 274 TNode<JSFunction> closure = LoadJSGeneratorObjectFunction(generator); 275 auto sfi = LoadJSFunctionSharedFunctionInfo(closure); 276 CSA_DCHECK(this, 277 Word32BinaryNot(IsSharedFunctionInfoDontAdaptArguments(sfi))); 278 TNode<IntPtrT> formal_parameter_count = Signed(ChangeUint32ToWord( 279 LoadSharedFunctionInfoFormalParameterCountWithoutReceiver(sfi))); 280 281 TNode<FixedArray> parameters_and_registers = 282 LoadJSGeneratorObjectParametersAndRegisters(generator); 283 284 // Iterate over array and write values into register file. Also erase the 285 // array contents to not keep them alive artificially. 286 auto register_base_index = 287 IntPtrAdd(formal_parameter_count, 288 IntPtrConstant(interpreter::Register(0).ToOperand())); 289 auto register_count = UncheckedParameter<IntPtrT>(Descriptor::kRegisterCount); 290 auto end_index = IntPtrAdd(formal_parameter_count, register_count); 291 auto parameters_and_registers_length = 292 SmiUntag(LoadFixedArrayBaseLength(parameters_and_registers)); 293 CSA_CHECK(this, UintPtrLessThan(end_index, parameters_and_registers_length)); 294 auto parent_frame_pointer = LoadParentFramePointer(); 295 BuildFastLoop<IntPtrT>( 296 formal_parameter_count, end_index, 297 [=](TNode<IntPtrT> index) { 298 TNode<Object> value = 299 UnsafeLoadFixedArrayElement(parameters_and_registers, index); 300 auto reg_index = IntPtrSub(register_base_index, index); 301 StoreFullTaggedNoWriteBarrier(parent_frame_pointer, 302 TimesSystemPointerSize(reg_index), value); 303 UnsafeStoreFixedArrayElement(parameters_and_registers, index, 304 StaleRegisterConstant(), 305 SKIP_WRITE_BARRIER); 306 }, 307 1, IndexAdvanceMode::kPost); 308 309 Return(LoadJSGeneratorObjectInputOrDebugPos(generator)); 310} 311 312} // namespace internal 313} // namespace v8 314