• 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/code-factory.h"
8 #include "src/code-stub-assembler.h"
9 #include "src/isolate.h"
10 #include "src/objects-inl.h"
11 #include "src/objects/js-generator.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   void GeneratorPrototypeResume(CodeStubArguments* args, Node* receiver,
23                                 Node* value, Node* context,
24                                 JSGeneratorObject::ResumeMode resume_mode,
25                                 char const* const method_name);
26 };
27 
GeneratorPrototypeResume(CodeStubArguments * args,Node * receiver,Node * value,Node * context,JSGeneratorObject::ResumeMode resume_mode,char const * const method_name)28 void GeneratorBuiltinsAssembler::GeneratorPrototypeResume(
29     CodeStubArguments* args, Node* receiver, Node* value, Node* context,
30     JSGeneratorObject::ResumeMode resume_mode, char const* const method_name) {
31   // Check if the {receiver} is actually a JSGeneratorObject.
32   ThrowIfNotInstanceType(context, receiver, JS_GENERATOR_OBJECT_TYPE,
33                          method_name);
34 
35   // Check if the {receiver} is running or already closed.
36   TNode<Smi> receiver_continuation =
37       CAST(LoadObjectField(receiver, JSGeneratorObject::kContinuationOffset));
38   Label if_receiverisclosed(this, Label::kDeferred),
39       if_receiverisrunning(this, Label::kDeferred);
40   TNode<Smi> closed = SmiConstant(JSGeneratorObject::kGeneratorClosed);
41   GotoIf(SmiEqual(receiver_continuation, closed), &if_receiverisclosed);
42   DCHECK_LT(JSGeneratorObject::kGeneratorExecuting,
43             JSGeneratorObject::kGeneratorClosed);
44   GotoIf(SmiLessThan(receiver_continuation, closed), &if_receiverisrunning);
45 
46   // Remember the {resume_mode} for the {receiver}.
47   StoreObjectFieldNoWriteBarrier(receiver, JSGeneratorObject::kResumeModeOffset,
48                                  SmiConstant(resume_mode));
49 
50   // Resume the {receiver} using our trampoline.
51   VARIABLE(var_exception, MachineRepresentation::kTagged, UndefinedConstant());
52   Label if_exception(this, Label::kDeferred), if_final_return(this);
53   Node* result = CallStub(CodeFactory::ResumeGenerator(isolate()), context,
54                           value, receiver);
55   // Make sure we close the generator if there was an exception.
56   GotoIfException(result, &if_exception, &var_exception);
57 
58   // If the generator is not suspended (i.e., its state is 'executing'),
59   // close it and wrap the return value in IteratorResult.
60   TNode<Smi> result_continuation =
61       CAST(LoadObjectField(receiver, JSGeneratorObject::kContinuationOffset));
62 
63   // The generator function should not close the generator by itself, let's
64   // check it is indeed not closed yet.
65   CSA_ASSERT(this, SmiNotEqual(result_continuation, closed));
66 
67   TNode<Smi> executing = SmiConstant(JSGeneratorObject::kGeneratorExecuting);
68   GotoIf(SmiEqual(result_continuation, executing), &if_final_return);
69 
70   args->PopAndReturn(result);
71 
72   BIND(&if_final_return);
73   {
74     // Close the generator.
75     StoreObjectFieldNoWriteBarrier(
76         receiver, JSGeneratorObject::kContinuationOffset, closed);
77     // Return the wrapped result.
78     args->PopAndReturn(CallBuiltin(Builtins::kCreateIterResultObject, context,
79                                    result, TrueConstant()));
80   }
81 
82   BIND(&if_receiverisclosed);
83   {
84     // The {receiver} is closed already.
85     Node* result = nullptr;
86     switch (resume_mode) {
87       case JSGeneratorObject::kNext:
88         result = CallBuiltin(Builtins::kCreateIterResultObject, context,
89                              UndefinedConstant(), TrueConstant());
90         break;
91       case JSGeneratorObject::kReturn:
92         result = CallBuiltin(Builtins::kCreateIterResultObject, context, value,
93                              TrueConstant());
94         break;
95       case JSGeneratorObject::kThrow:
96         result = CallRuntime(Runtime::kThrow, context, value);
97         break;
98     }
99     args->PopAndReturn(result);
100   }
101 
102   BIND(&if_receiverisrunning);
103   { ThrowTypeError(context, MessageTemplate::kGeneratorRunning); }
104 
105   BIND(&if_exception);
106   {
107     StoreObjectFieldNoWriteBarrier(
108         receiver, JSGeneratorObject::kContinuationOffset, closed);
109     CallRuntime(Runtime::kReThrow, context, var_exception.value());
110     Unreachable();
111   }
112 }
113 
114 // ES6 #sec-generator.prototype.next
TF_BUILTIN(GeneratorPrototypeNext,GeneratorBuiltinsAssembler)115 TF_BUILTIN(GeneratorPrototypeNext, GeneratorBuiltinsAssembler) {
116   const int kValueArg = 0;
117 
118   Node* argc =
119       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
120   CodeStubArguments args(this, argc);
121 
122   Node* receiver = args.GetReceiver();
123   Node* value = args.GetOptionalArgumentValue(kValueArg);
124   Node* context = Parameter(Descriptor::kContext);
125 
126   GeneratorPrototypeResume(&args, receiver, value, context,
127                            JSGeneratorObject::kNext,
128                            "[Generator].prototype.next");
129 }
130 
131 // ES6 #sec-generator.prototype.return
TF_BUILTIN(GeneratorPrototypeReturn,GeneratorBuiltinsAssembler)132 TF_BUILTIN(GeneratorPrototypeReturn, GeneratorBuiltinsAssembler) {
133   const int kValueArg = 0;
134 
135   Node* argc =
136       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
137   CodeStubArguments args(this, argc);
138 
139   Node* receiver = args.GetReceiver();
140   Node* value = args.GetOptionalArgumentValue(kValueArg);
141   Node* context = Parameter(Descriptor::kContext);
142 
143   GeneratorPrototypeResume(&args, receiver, value, context,
144                            JSGeneratorObject::kReturn,
145                            "[Generator].prototype.return");
146 }
147 
148 // ES6 #sec-generator.prototype.throw
TF_BUILTIN(GeneratorPrototypeThrow,GeneratorBuiltinsAssembler)149 TF_BUILTIN(GeneratorPrototypeThrow, GeneratorBuiltinsAssembler) {
150   const int kExceptionArg = 0;
151 
152   Node* argc =
153       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
154   CodeStubArguments args(this, argc);
155 
156   Node* receiver = args.GetReceiver();
157   Node* exception = args.GetOptionalArgumentValue(kExceptionArg);
158   Node* context = Parameter(Descriptor::kContext);
159 
160   GeneratorPrototypeResume(&args, receiver, exception, context,
161                            JSGeneratorObject::kThrow,
162                            "[Generator].prototype.throw");
163 }
164 
165 }  // namespace internal
166 }  // namespace v8
167