1 // Copyright 2014 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/runtime/runtime-utils.h"
6 
7 #include "src/arguments.h"
8 #include "src/factory.h"
9 #include "src/frames-inl.h"
10 #include "src/objects-inl.h"
11 
12 namespace v8 {
13 namespace internal {
14 
RUNTIME_FUNCTION(Runtime_CreateJSGeneratorObject)15 RUNTIME_FUNCTION(Runtime_CreateJSGeneratorObject) {
16   HandleScope scope(isolate);
17   DCHECK(args.length() == 0);
18 
19   JavaScriptFrameIterator it(isolate);
20   JavaScriptFrame* frame = it.frame();
21   Handle<JSFunction> function(frame->function());
22   RUNTIME_ASSERT(function->shared()->is_generator());
23 
24   Handle<JSGeneratorObject> generator;
25   if (frame->IsConstructor()) {
26     generator = handle(JSGeneratorObject::cast(frame->receiver()));
27   } else {
28     generator = isolate->factory()->NewJSGeneratorObject(function);
29   }
30   generator->set_function(*function);
31   generator->set_context(Context::cast(frame->context()));
32   generator->set_receiver(frame->receiver());
33   generator->set_continuation(0);
34   generator->set_operand_stack(isolate->heap()->empty_fixed_array());
35 
36   return *generator;
37 }
38 
39 
RUNTIME_FUNCTION(Runtime_SuspendJSGeneratorObject)40 RUNTIME_FUNCTION(Runtime_SuspendJSGeneratorObject) {
41   HandleScope handle_scope(isolate);
42   DCHECK(args.length() == 1 || args.length() == 2);
43   CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator_object, 0);
44 
45   JavaScriptFrameIterator stack_iterator(isolate);
46   JavaScriptFrame* frame = stack_iterator.frame();
47   RUNTIME_ASSERT(frame->function()->shared()->is_generator());
48   DCHECK_EQ(frame->function(), generator_object->function());
49 
50   // The caller should have saved the context and continuation already.
51   DCHECK_EQ(generator_object->context(), Context::cast(frame->context()));
52   DCHECK_LT(0, generator_object->continuation());
53 
54   // We expect there to be at least two values on the operand stack: the return
55   // value of the yield expression, and the arguments to this runtime call.
56   // Neither of those should be saved.
57   int operands_count = frame->ComputeOperandsCount();
58   DCHECK_GE(operands_count, 1 + args.length());
59   operands_count -= 1 + args.length();
60 
61   // Second argument indicates that we need to patch the handler table because
62   // a delegating yield introduced a try-catch statement at expression level,
63   // hence the operand count was off when we statically computed it.
64   // TODO(mstarzinger): This special case disappears with do-expressions.
65   if (args.length() == 2) {
66     CONVERT_SMI_ARG_CHECKED(handler_index, 1);
67     Handle<Code> code(frame->unchecked_code());
68     Handle<HandlerTable> table(HandlerTable::cast(code->handler_table()));
69     int handler_depth = operands_count - TryBlockConstant::kElementCount;
70     table->SetRangeDepth(handler_index, handler_depth);
71   }
72 
73   if (operands_count == 0) {
74     // Although it's semantically harmless to call this function with an
75     // operands_count of zero, it is also unnecessary.
76     DCHECK_EQ(generator_object->operand_stack(),
77               isolate->heap()->empty_fixed_array());
78   } else {
79     Handle<FixedArray> operand_stack =
80         isolate->factory()->NewFixedArray(operands_count);
81     frame->SaveOperandStack(*operand_stack);
82     generator_object->set_operand_stack(*operand_stack);
83   }
84 
85   return isolate->heap()->undefined_value();
86 }
87 
88 
89 // Note that this function is the slow path for resuming generators.  It is only
90 // called if the suspended activation had operands on the stack, stack handlers
91 // needing rewinding, or if the resume should throw an exception.  The fast path
92 // is handled directly in FullCodeGenerator::EmitGeneratorResume(), which is
93 // inlined into GeneratorNext and GeneratorThrow.  EmitGeneratorResumeResume is
94 // called in any case, as it needs to reconstruct the stack frame and make space
95 // for arguments and operands.
RUNTIME_FUNCTION(Runtime_ResumeJSGeneratorObject)96 RUNTIME_FUNCTION(Runtime_ResumeJSGeneratorObject) {
97   SealHandleScope shs(isolate);
98   DCHECK(args.length() == 3);
99   CONVERT_ARG_CHECKED(JSGeneratorObject, generator_object, 0);
100   CONVERT_ARG_CHECKED(Object, value, 1);
101   CONVERT_SMI_ARG_CHECKED(resume_mode_int, 2);
102   JavaScriptFrameIterator stack_iterator(isolate);
103   JavaScriptFrame* frame = stack_iterator.frame();
104 
105   DCHECK_EQ(frame->function(), generator_object->function());
106   DCHECK(frame->function()->is_compiled());
107 
108   STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting < 0);
109   STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed == 0);
110 
111   Address pc = generator_object->function()->code()->instruction_start();
112   int offset = generator_object->continuation();
113   DCHECK(offset > 0);
114   frame->set_pc(pc + offset);
115   if (FLAG_enable_embedded_constant_pool) {
116     frame->set_constant_pool(
117         generator_object->function()->code()->constant_pool());
118   }
119   generator_object->set_continuation(JSGeneratorObject::kGeneratorExecuting);
120 
121   FixedArray* operand_stack = generator_object->operand_stack();
122   int operands_count = operand_stack->length();
123   if (operands_count != 0) {
124     frame->RestoreOperandStack(operand_stack);
125     generator_object->set_operand_stack(isolate->heap()->empty_fixed_array());
126   }
127 
128   JSGeneratorObject::ResumeMode resume_mode =
129       static_cast<JSGeneratorObject::ResumeMode>(resume_mode_int);
130   switch (resume_mode) {
131     case JSGeneratorObject::NEXT:
132       return value;
133     case JSGeneratorObject::THROW:
134       return isolate->Throw(value);
135   }
136 
137   UNREACHABLE();
138   return isolate->ThrowIllegalOperation();
139 }
140 
141 
RUNTIME_FUNCTION(Runtime_GeneratorClose)142 RUNTIME_FUNCTION(Runtime_GeneratorClose) {
143   HandleScope scope(isolate);
144   DCHECK(args.length() == 1);
145   CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
146 
147   generator->set_continuation(JSGeneratorObject::kGeneratorClosed);
148 
149   return isolate->heap()->undefined_value();
150 }
151 
152 
153 // Returns function of generator activation.
RUNTIME_FUNCTION(Runtime_GeneratorGetFunction)154 RUNTIME_FUNCTION(Runtime_GeneratorGetFunction) {
155   HandleScope scope(isolate);
156   DCHECK(args.length() == 1);
157   CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
158 
159   return generator->function();
160 }
161 
162 
163 // Returns context of generator activation.
RUNTIME_FUNCTION(Runtime_GeneratorGetContext)164 RUNTIME_FUNCTION(Runtime_GeneratorGetContext) {
165   HandleScope scope(isolate);
166   DCHECK(args.length() == 1);
167   CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
168 
169   return generator->context();
170 }
171 
172 
173 // Returns receiver of generator activation.
RUNTIME_FUNCTION(Runtime_GeneratorGetReceiver)174 RUNTIME_FUNCTION(Runtime_GeneratorGetReceiver) {
175   HandleScope scope(isolate);
176   DCHECK(args.length() == 1);
177   CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
178 
179   return generator->receiver();
180 }
181 
182 
183 // Returns generator continuation as a PC offset, or the magic -1 or 0 values.
RUNTIME_FUNCTION(Runtime_GeneratorGetContinuation)184 RUNTIME_FUNCTION(Runtime_GeneratorGetContinuation) {
185   HandleScope scope(isolate);
186   DCHECK(args.length() == 1);
187   CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
188 
189   return Smi::FromInt(generator->continuation());
190 }
191 
192 
RUNTIME_FUNCTION(Runtime_GeneratorGetSourcePosition)193 RUNTIME_FUNCTION(Runtime_GeneratorGetSourcePosition) {
194   HandleScope scope(isolate);
195   DCHECK(args.length() == 1);
196   CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
197 
198   if (generator->is_suspended()) {
199     Handle<Code> code(generator->function()->code(), isolate);
200     int offset = generator->continuation();
201 
202     RUNTIME_ASSERT(0 <= offset && offset < code->Size());
203     Address pc = code->address() + offset;
204 
205     return Smi::FromInt(code->SourcePosition(pc));
206   }
207 
208   return isolate->heap()->undefined_value();
209 }
210 
211 
RUNTIME_FUNCTION(Runtime_GeneratorNext)212 RUNTIME_FUNCTION(Runtime_GeneratorNext) {
213   UNREACHABLE();  // Optimization disabled in SetUpGenerators().
214   return NULL;
215 }
216 
217 
RUNTIME_FUNCTION(Runtime_GeneratorThrow)218 RUNTIME_FUNCTION(Runtime_GeneratorThrow) {
219   UNREACHABLE();  // Optimization disabled in SetUpGenerators().
220   return NULL;
221 }
222 }  // namespace internal
223 }  // namespace v8
224