• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
13 namespace v8 {
14 namespace internal {
15 
16 class GeneratorBuiltinsAssembler : public CodeStubAssembler {
17  public:
GeneratorBuiltinsAssembler(compiler::CodeAssemblerState * state)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 
InnerResume(CodeStubArguments * args,TNode<JSGeneratorObject> receiver,TNode<Object> value,TNode<Context> context,JSGeneratorObject::ResumeMode resume_mode,char const * const method_name)39 void 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 
GeneratorPrototypeResume(CodeStubArguments * args,TNode<Object> receiver,TNode<Object> value,TNode<Context> context,JSGeneratorObject::ResumeMode resume_mode,char const * const method_name)126 void 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 
TF_BUILTIN(AsyncModuleEvaluate,GeneratorBuiltinsAssembler)137 TF_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
TF_BUILTIN(GeneratorPrototypeNext,GeneratorBuiltinsAssembler)158 TF_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
TF_BUILTIN(GeneratorPrototypeReturn,GeneratorBuiltinsAssembler)174 TF_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
TF_BUILTIN(GeneratorPrototypeThrow,GeneratorBuiltinsAssembler)190 TF_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.
TF_BUILTIN(SuspendGeneratorBaseline,GeneratorBuiltinsAssembler)206 TF_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.
TF_BUILTIN(ResumeGeneratorBaseline,GeneratorBuiltinsAssembler)272 TF_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