1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "vm/GeneratorObject.h"
9 #include "frontend/ParserAtom.h"
11 # include "js/friend/DumpFunctions.h" // js::DumpObject, js::DumpValue
13 #include "js/PropertySpec.h"
14 #include "vm/AsyncFunction.h"
15 #include "vm/AsyncIteration.h"
16 #include "vm/FunctionFlags.h" // js::FunctionFlags
17 #include "vm/GlobalObject.h"
18 #include "vm/JSObject.h"
19 #include "vm/PlainObject.h" // js::PlainObject
21 #include "debugger/DebugAPI-inl.h"
22 #include "vm/Stack-inl.h"
26 AbstractGeneratorObject
* AbstractGeneratorObject::create(
27 JSContext
* cx
, HandleFunction callee
, HandleScript script
,
28 HandleObject environmentChain
, Handle
<ArgumentsObject
*> argsObject
) {
29 Rooted
<AbstractGeneratorObject
*> genObj(cx
);
30 if (!callee
->isAsync()) {
31 genObj
= GeneratorObject::create(cx
, callee
);
32 } else if (callee
->isGenerator()) {
33 genObj
= AsyncGeneratorObject::create(cx
, callee
);
35 genObj
= AsyncFunctionGeneratorObject::create(cx
, callee
);
41 genObj
->setCallee(*callee
);
42 genObj
->setEnvironmentChain(*environmentChain
);
44 genObj
->setArgsObj(*argsObject
.get());
47 ArrayObject
* stack
= NewDenseFullyAllocatedArray(cx
, script
->nslots());
52 genObj
->setStackStorage(*stack
);
54 // Note: This assumes that a Warp frame cannot be the target of
55 // the debugger, as we do not call OnNewGenerator.
59 JSObject
* AbstractGeneratorObject::createFromFrame(JSContext
* cx
,
60 AbstractFramePtr frame
) {
61 MOZ_ASSERT(frame
.isGeneratorFrame());
62 MOZ_ASSERT(!frame
.isConstructing());
64 if (frame
.isModuleFrame()) {
65 return createModuleGenerator(cx
, frame
);
68 RootedFunction
fun(cx
, frame
.callee());
69 Rooted
<ArgumentsObject
*> maybeArgs(
70 cx
, frame
.script()->needsArgsObj() ? &frame
.argsObj() : nullptr);
71 RootedObject
environmentChain(cx
, frame
.environmentChain());
73 RootedScript
script(cx
, frame
.script());
74 Rooted
<AbstractGeneratorObject
*> genObj(
75 cx
, AbstractGeneratorObject::create(cx
, fun
, script
, environmentChain
,
81 if (!DebugAPI::onNewGenerator(cx
, frame
, genObj
)) {
88 JSObject
* AbstractGeneratorObject::createModuleGenerator(
89 JSContext
* cx
, AbstractFramePtr frame
) {
90 Rooted
<ModuleObject
*> module(cx
, frame
.script()->module());
91 Rooted
<AbstractGeneratorObject
*> genObj(cx
);
92 genObj
= AsyncFunctionGeneratorObject::create(cx
, module
);
97 // Create a handler function to wrap the module's script. This way
98 // we can access it later and restore the state.
99 Handle
<PropertyName
*> funName
= cx
->names().empty_
;
100 RootedFunction
handlerFun(
101 cx
, NewFunctionWithProto(cx
, nullptr, 0,
102 FunctionFlags::INTERPRETED_GENERATOR_OR_ASYNC
,
103 nullptr, funName
, nullptr,
104 gc::AllocKind::FUNCTION
, GenericObject
));
108 handlerFun
->initScript(module
->script());
110 genObj
->setCallee(*handlerFun
);
111 genObj
->setEnvironmentChain(*frame
.environmentChain());
114 NewDenseFullyAllocatedArray(cx
, module
->script()->nslots());
119 genObj
->setStackStorage(*stack
);
121 if (!DebugAPI::onNewGenerator(cx
, frame
, genObj
)) {
128 void AbstractGeneratorObject::trace(JSTracer
* trc
) {
129 DebugAPI::traceGeneratorFrame(trc
, this);
132 bool AbstractGeneratorObject::suspend(JSContext
* cx
, HandleObject obj
,
133 AbstractFramePtr frame
,
134 const jsbytecode
* pc
, unsigned nvalues
) {
135 MOZ_ASSERT(JSOp(*pc
) == JSOp::InitialYield
|| JSOp(*pc
) == JSOp::Yield
||
136 JSOp(*pc
) == JSOp::Await
);
138 auto genObj
= obj
.as
<AbstractGeneratorObject
>();
139 MOZ_ASSERT(!genObj
->hasStackStorage() || genObj
->isStackStorageEmpty());
140 MOZ_ASSERT_IF(JSOp(*pc
) == JSOp::Await
, genObj
->callee().isAsync());
141 MOZ_ASSERT_IF(JSOp(*pc
) == JSOp::Yield
, genObj
->callee().isGenerator());
144 ArrayObject
* stack
= nullptr;
145 MOZ_ASSERT(genObj
->hasStackStorage());
146 stack
= &genObj
->stackStorage();
147 MOZ_ASSERT(stack
->getDenseCapacity() >= nvalues
);
148 if (!frame
.saveGeneratorSlots(cx
, nvalues
, stack
)) {
153 genObj
->setResumeIndex(pc
);
154 genObj
->setEnvironmentChain(*frame
.environmentChain());
159 void AbstractGeneratorObject::dump() const {
160 fprintf(stderr
, "(AbstractGeneratorObject*) %p {\n", (void*)this);
161 fprintf(stderr
, " callee: (JSFunction*) %p,\n", (void*)&callee());
162 fprintf(stderr
, " environmentChain: (JSObject*) %p,\n",
163 (void*)&environmentChain());
165 fprintf(stderr
, " argsObj: Some((ArgumentsObject*) %p),\n",
168 fprintf(stderr
, " argsObj: None,\n");
170 if (hasStackStorage()) {
171 fprintf(stderr
, " stackStorage: Some(ArrayObject {\n");
172 ArrayObject
& stack
= stackStorage();
173 uint32_t denseLen
= uint32_t(stack
.getDenseInitializedLength());
174 fprintf(stderr
, " denseInitializedLength: %u\n,", denseLen
);
175 uint32_t len
= stack
.length();
176 fprintf(stderr
, " length: %u\n,", len
);
177 fprintf(stderr
, " data: [\n");
178 const Value
* elements
= getDenseElements();
179 for (uint32_t i
= 0; i
< std::max(len
, denseLen
); i
++) {
180 fprintf(stderr
, " [%u]: ", i
);
181 js::DumpValue(elements
[i
]);
183 fprintf(stderr
, " ],\n");
184 fprintf(stderr
, " }),\n");
186 fprintf(stderr
, " stackStorage: None\n");
189 fprintf(stderr
, " resumeIndex: Some(%u),\n", resumeIndex());
191 fprintf(stderr
, " resumeIndex: None, /* (not suspended) */\n");
193 fprintf(stderr
, "}\n");
197 void AbstractGeneratorObject::finalSuspend(HandleObject obj
) {
198 auto* genObj
= &obj
->as
<AbstractGeneratorObject
>();
199 MOZ_ASSERT(genObj
->isRunning());
203 static AbstractGeneratorObject
* GetGeneratorObjectForCall(JSContext
* cx
,
204 CallObject
& callObj
) {
205 // The ".generator" binding is always present and always "aliased".
206 mozilla::Maybe
<PropertyInfo
> prop
=
207 callObj
.lookup(cx
, cx
->names().dot_generator_
);
208 if (prop
.isNothing()) {
211 Value genValue
= callObj
.getSlot(prop
->slot());
213 // If the `Generator; SetAliasedVar ".generator"; InitialYield` bytecode
214 // sequence has not run yet, genValue is undefined.
215 return genValue
.isObject()
216 ? &genValue
.toObject().as
<AbstractGeneratorObject
>()
220 AbstractGeneratorObject
* js::GetGeneratorObjectForFrame(
221 JSContext
* cx
, AbstractFramePtr frame
) {
223 MOZ_ASSERT(frame
.isGeneratorFrame());
225 if (frame
.isModuleFrame()) {
226 ModuleEnvironmentObject
* moduleEnv
=
227 frame
.script()->module()->environment();
228 mozilla::Maybe
<PropertyInfo
> prop
=
229 moduleEnv
->lookup(cx
, cx
->names().dot_generator_
);
230 Value genValue
= moduleEnv
->getSlot(prop
->slot());
231 return genValue
.isObject()
232 ? &genValue
.toObject().as
<AbstractGeneratorObject
>()
235 if (!frame
.hasInitialEnvironment()) {
239 return GetGeneratorObjectForCall(cx
, frame
.callObj());
242 AbstractGeneratorObject
* js::GetGeneratorObjectForEnvironment(
243 JSContext
* cx
, HandleObject env
) {
244 auto* call
= CallObject::find(env
);
245 return call
? GetGeneratorObjectForCall(cx
, *call
) : nullptr;
248 bool js::GeneratorThrowOrReturn(JSContext
* cx
, AbstractFramePtr frame
,
249 Handle
<AbstractGeneratorObject
*> genObj
,
251 GeneratorResumeKind resumeKind
) {
252 MOZ_ASSERT(genObj
->isRunning());
253 if (resumeKind
== GeneratorResumeKind::Throw
) {
254 cx
->setPendingException(arg
, ShouldCaptureStack::Maybe
);
256 MOZ_ASSERT(resumeKind
== GeneratorResumeKind::Return
);
258 MOZ_ASSERT_IF(genObj
->is
<GeneratorObject
>(), arg
.isObject());
259 frame
.setReturnValue(arg
);
261 RootedValue
closing(cx
, MagicValue(JS_GENERATOR_CLOSING
));
262 cx
->setPendingException(closing
, nullptr);
267 bool AbstractGeneratorObject::resume(JSContext
* cx
,
268 InterpreterActivation
& activation
,
269 Handle
<AbstractGeneratorObject
*> genObj
,
270 HandleValue arg
, HandleValue resumeKind
) {
271 MOZ_ASSERT(genObj
->isSuspended());
273 RootedFunction
callee(cx
, &genObj
->callee());
274 RootedObject
envChain(cx
, &genObj
->environmentChain());
275 if (!activation
.resumeGeneratorFrame(callee
, envChain
)) {
278 activation
.regs().fp()->setResumedGenerator();
280 if (genObj
->hasArgsObj()) {
281 activation
.regs().fp()->initArgsObj(genObj
->argsObj());
284 if (genObj
->hasStackStorage() && !genObj
->isStackStorageEmpty()) {
285 JSScript
* script
= activation
.regs().fp()->script();
286 ArrayObject
* storage
= &genObj
->stackStorage();
287 uint32_t len
= storage
->getDenseInitializedLength();
288 activation
.regs().fp()->restoreGeneratorSlots(storage
);
289 activation
.regs().sp
+= len
- script
->nfixed();
290 storage
->setDenseInitializedLength(0);
293 JSScript
* script
= callee
->nonLazyScript();
294 uint32_t offset
= script
->resumeOffsets()[genObj
->resumeIndex()];
295 activation
.regs().pc
= script
->offsetToPC(offset
);
297 // Push arg, generator, resumeKind Values on the generator's stack.
298 activation
.regs().sp
+= 3;
299 MOZ_ASSERT(activation
.regs().spForStackDepth(activation
.regs().stackDepth()));
300 activation
.regs().sp
[-3] = arg
;
301 activation
.regs().sp
[-2] = ObjectValue(*genObj
);
302 activation
.regs().sp
[-1] = resumeKind
;
304 genObj
->setRunning();
308 GeneratorObject
* GeneratorObject::create(JSContext
* cx
, HandleFunction fun
) {
309 MOZ_ASSERT(fun
->isGenerator() && !fun
->isAsync());
311 // FIXME: This would be faster if we could avoid doing a lookup to get
312 // the prototype for the instance. Bug 906600.
313 RootedValue
pval(cx
);
314 if (!GetProperty(cx
, fun
, fun
, cx
->names().prototype
, &pval
)) {
317 RootedObject
proto(cx
, pval
.isObject() ? &pval
.toObject() : nullptr);
319 proto
= GlobalObject::getOrCreateGeneratorObjectPrototype(cx
, cx
->global());
324 return NewObjectWithGivenProto
<GeneratorObject
>(cx
, proto
);
327 const JSClass
GeneratorObject::class_
= {
329 JSCLASS_HAS_RESERVED_SLOTS(GeneratorObject::RESERVED_SLOTS
),
333 const JSClassOps
GeneratorObject::classOps_
= {
334 nullptr, // addProperty
335 nullptr, // delProperty
336 nullptr, // enumerate
337 nullptr, // newEnumerate
339 nullptr, // mayResolve
342 nullptr, // construct
343 CallTraceMethod
<AbstractGeneratorObject
>, // trace
346 static const JSFunctionSpec generator_methods
[] = {
347 JS_SELF_HOSTED_FN("next", "GeneratorNext", 1, 0),
348 JS_SELF_HOSTED_FN("throw", "GeneratorThrow", 1, 0),
349 JS_SELF_HOSTED_FN("return", "GeneratorReturn", 1, 0), JS_FS_END
};
351 JSObject
* js::NewTenuredObjectWithFunctionPrototype(
352 JSContext
* cx
, Handle
<GlobalObject
*> global
) {
353 RootedObject
proto(cx
, &cx
->global()->getFunctionPrototype());
354 return NewPlainObjectWithProto(cx
, proto
, TenuredObject
);
357 static JSObject
* CreateGeneratorFunction(JSContext
* cx
, JSProtoKey key
) {
358 RootedObject
proto(cx
, &cx
->global()->getFunctionConstructor());
359 Handle
<PropertyName
*> name
= cx
->names().GeneratorFunction
;
360 return NewFunctionWithProto(cx
, Generator
, 1, FunctionFlags::NATIVE_CTOR
,
361 nullptr, name
, proto
, gc::AllocKind::FUNCTION
,
365 static JSObject
* CreateGeneratorFunctionPrototype(JSContext
* cx
,
367 return NewTenuredObjectWithFunctionPrototype(cx
, cx
->global());
370 static bool GeneratorFunctionClassFinish(JSContext
* cx
,
371 HandleObject genFunction
,
372 HandleObject genFunctionProto
) {
373 Handle
<GlobalObject
*> global
= cx
->global();
375 // Change the "constructor" property to non-writable before adding any other
376 // properties, so it's still the last property and can be modified without a
377 // dictionary-mode transition.
378 MOZ_ASSERT(genFunctionProto
->as
<NativeObject
>().getLastProperty().key() ==
379 NameToId(cx
->names().constructor
));
380 MOZ_ASSERT(!genFunctionProto
->as
<NativeObject
>().inDictionaryMode());
382 RootedValue
genFunctionVal(cx
, ObjectValue(*genFunction
));
383 if (!DefineDataProperty(cx
, genFunctionProto
, cx
->names().constructor
,
384 genFunctionVal
, JSPROP_READONLY
)) {
387 MOZ_ASSERT(!genFunctionProto
->as
<NativeObject
>().inDictionaryMode());
389 RootedObject
iteratorProto(
390 cx
, GlobalObject::getOrCreateIteratorPrototype(cx
, global
));
391 if (!iteratorProto
) {
395 RootedObject
genObjectProto(cx
, GlobalObject::createBlankPrototypeInheriting(
396 cx
, &PlainObject::class_
, iteratorProto
));
397 if (!genObjectProto
) {
400 if (!DefinePropertiesAndFunctions(cx
, genObjectProto
, nullptr,
401 generator_methods
) ||
402 !DefineToStringTag(cx
, genObjectProto
, cx
->names().Generator
)) {
406 if (!LinkConstructorAndPrototype(cx
, genFunctionProto
, genObjectProto
,
407 JSPROP_READONLY
, JSPROP_READONLY
) ||
408 !DefineToStringTag(cx
, genFunctionProto
, cx
->names().GeneratorFunction
)) {
412 global
->setGeneratorObjectPrototype(genObjectProto
);
417 static const ClassSpec GeneratorFunctionClassSpec
= {
418 CreateGeneratorFunction
,
419 CreateGeneratorFunctionPrototype
,
424 GeneratorFunctionClassFinish
,
425 ClassSpec::DontDefineConstructor
};
427 const JSClass
js::GeneratorFunctionClass
= {
428 "GeneratorFunction", 0, JS_NULL_CLASS_OPS
, &GeneratorFunctionClassSpec
};
430 const Value
& AbstractGeneratorObject::getUnaliasedLocal(uint32_t slot
) const {
431 MOZ_ASSERT(isSuspended());
432 MOZ_ASSERT(hasStackStorage());
433 MOZ_ASSERT(slot
< callee().nonLazyScript()->nfixed());
434 return stackStorage().getDenseElement(slot
);
437 void AbstractGeneratorObject::setUnaliasedLocal(uint32_t slot
,
438 const Value
& value
) {
439 MOZ_ASSERT(isSuspended());
440 MOZ_ASSERT(hasStackStorage());
441 MOZ_ASSERT(slot
< callee().nonLazyScript()->nfixed());
442 return stackStorage().setDenseElement(slot
, value
);
445 bool AbstractGeneratorObject::isAfterYield() {
446 return isAfterYieldOrAwait(JSOp::Yield
);
449 bool AbstractGeneratorObject::isAfterAwait() {
450 return isAfterYieldOrAwait(JSOp::Await
);
453 bool AbstractGeneratorObject::isAfterYieldOrAwait(JSOp op
) {
454 if (isClosed() || isRunning()) {
458 JSScript
* script
= callee().nonLazyScript();
459 jsbytecode
* code
= script
->code();
460 uint32_t nextOffset
= script
->resumeOffsets()[resumeIndex()];
461 if (JSOp(code
[nextOffset
]) != JSOp::AfterYield
) {
465 static_assert(JSOpLength_Yield
== JSOpLength_InitialYield
,
466 "JSOp::Yield and JSOp::InitialYield must have the same length");
467 static_assert(JSOpLength_Yield
== JSOpLength_Await
,
468 "JSOp::Yield and JSOp::Await must have the same length");
470 uint32_t offset
= nextOffset
- JSOpLength_Yield
;
471 JSOp prevOp
= JSOp(code
[offset
]);
472 MOZ_ASSERT(prevOp
== JSOp::InitialYield
|| prevOp
== JSOp::Yield
||
473 prevOp
== JSOp::Await
);
479 bool JSObject::is
<js::AbstractGeneratorObject
>() const {
480 return is
<GeneratorObject
>() || is
<AsyncFunctionGeneratorObject
>() ||
481 is
<AsyncGeneratorObject
>();
484 GeneratorResumeKind
js::ParserAtomToResumeKind(
485 frontend::TaggedParserAtomIndex atom
) {
486 if (atom
== frontend::TaggedParserAtomIndex::WellKnown::next()) {
487 return GeneratorResumeKind::Next
;
489 if (atom
== frontend::TaggedParserAtomIndex::WellKnown::throw_()) {
490 return GeneratorResumeKind::Throw
;
492 MOZ_ASSERT(atom
== frontend::TaggedParserAtomIndex::WellKnown::return_());
493 return GeneratorResumeKind::Return
;
496 JSAtom
* js::ResumeKindToAtom(JSContext
* cx
, GeneratorResumeKind kind
) {
498 case GeneratorResumeKind::Next
:
499 return cx
->names().next
;
501 case GeneratorResumeKind::Throw
:
502 return cx
->names().throw_
;
504 case GeneratorResumeKind::Return
:
505 return cx
->names().return_
;
507 MOZ_CRASH("Invalid resume kind");