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