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/. */
6 
7 #include "vm/GeneratorObject.h"
8 
9 #include "frontend/ParserAtom.h"
10 #ifdef DEBUG
11 #  include "js/friend/DumpFunctions.h"  // js::DumpObject, js::DumpValue
12 #endif
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
20 
21 #include "debugger/DebugAPI-inl.h"
22 #include "vm/ArrayObject-inl.h"
23 #include "vm/JSAtom-inl.h"
24 #include "vm/JSScript-inl.h"
25 #include "vm/NativeObject-inl.h"
26 #include "vm/Stack-inl.h"
27 
28 using namespace js;
29 
create(JSContext * cx,HandleFunction callee,HandleScript script,HandleObject environmentChain,Handle<ArgumentsObject * > argsObject)30 AbstractGeneratorObject* AbstractGeneratorObject::create(
31     JSContext* cx, HandleFunction callee, HandleScript script,
32     HandleObject environmentChain, Handle<ArgumentsObject*> argsObject) {
33   Rooted<AbstractGeneratorObject*> genObj(cx);
34   if (!callee->isAsync()) {
35     genObj = GeneratorObject::create(cx, callee);
36   } else if (callee->isGenerator()) {
37     genObj = AsyncGeneratorObject::create(cx, callee);
38   } else {
39     genObj = AsyncFunctionGeneratorObject::create(cx, callee);
40   }
41   if (!genObj) {
42     return nullptr;
43   }
44 
45   genObj->setCallee(*callee);
46   genObj->setEnvironmentChain(*environmentChain);
47   if (argsObject) {
48     genObj->setArgsObj(*argsObject.get());
49   }
50 
51   ArrayObject* stack = NewDenseFullyAllocatedArray(cx, script->nslots());
52   if (!stack) {
53     return nullptr;
54   }
55 
56   genObj->setStackStorage(*stack);
57 
58   // Note: This assumes that a Warp frame cannot be the target of
59   //       the debugger, as we do not call OnNewGenerator.
60   return genObj;
61 }
62 
createFromFrame(JSContext * cx,AbstractFramePtr frame)63 JSObject* AbstractGeneratorObject::createFromFrame(JSContext* cx,
64                                                    AbstractFramePtr frame) {
65   MOZ_ASSERT(frame.isGeneratorFrame());
66   MOZ_ASSERT(!frame.isConstructing());
67 
68   if (frame.isModuleFrame()) {
69     return createModuleGenerator(cx, frame);
70   }
71 
72   RootedFunction fun(cx, frame.callee());
73   Rooted<ArgumentsObject*> maybeArgs(
74       cx, frame.script()->needsArgsObj() ? &frame.argsObj() : nullptr);
75   RootedObject environmentChain(cx, frame.environmentChain());
76 
77   RootedScript script(cx, frame.script());
78   Rooted<AbstractGeneratorObject*> genObj(
79       cx, AbstractGeneratorObject::create(cx, fun, script, environmentChain,
80                                           maybeArgs));
81   if (!genObj) {
82     return nullptr;
83   }
84 
85   if (!DebugAPI::onNewGenerator(cx, frame, genObj)) {
86     return nullptr;
87   }
88 
89   return genObj;
90 }
91 
createModuleGenerator(JSContext * cx,AbstractFramePtr frame)92 JSObject* AbstractGeneratorObject::createModuleGenerator(
93     JSContext* cx, AbstractFramePtr frame) {
94   Rooted<ModuleObject*> module(cx, frame.script()->module());
95   Rooted<AbstractGeneratorObject*> genObj(cx);
96   genObj = AsyncFunctionGeneratorObject::create(cx, module);
97   if (!genObj) {
98     return nullptr;
99   }
100 
101   // Create a handler function to wrap the module's script. This way
102   // we can access it later and restore the state.
103   HandlePropertyName funName = cx->names().empty;
104   RootedFunction handlerFun(
105       cx, NewFunctionWithProto(cx, nullptr, 0,
106                                FunctionFlags::INTERPRETED_GENERATOR_OR_ASYNC,
107                                nullptr, funName, nullptr,
108                                gc::AllocKind::FUNCTION, GenericObject));
109   if (!handlerFun) {
110     return nullptr;
111   }
112   handlerFun->initScript(module->script());
113 
114   genObj->setCallee(*handlerFun);
115   genObj->setEnvironmentChain(*frame.environmentChain());
116 
117   ArrayObject* stack =
118       NewDenseFullyAllocatedArray(cx, module->script()->nslots());
119   if (!stack) {
120     return nullptr;
121   }
122 
123   genObj->setStackStorage(*stack);
124 
125   if (!DebugAPI::onNewGenerator(cx, frame, genObj)) {
126     return nullptr;
127   }
128 
129   return genObj;
130 }
131 
trace(JSTracer * trc)132 void AbstractGeneratorObject::trace(JSTracer* trc) {
133   DebugAPI::traceGeneratorFrame(trc, this);
134 }
135 
suspend(JSContext * cx,HandleObject obj,AbstractFramePtr frame,jsbytecode * pc,unsigned nvalues)136 bool AbstractGeneratorObject::suspend(JSContext* cx, HandleObject obj,
137                                       AbstractFramePtr frame, jsbytecode* pc,
138                                       unsigned nvalues) {
139   MOZ_ASSERT(JSOp(*pc) == JSOp::InitialYield || JSOp(*pc) == JSOp::Yield ||
140              JSOp(*pc) == JSOp::Await);
141 
142   auto genObj = obj.as<AbstractGeneratorObject>();
143   MOZ_ASSERT(!genObj->hasStackStorage() || genObj->isStackStorageEmpty());
144   MOZ_ASSERT_IF(JSOp(*pc) == JSOp::Await, genObj->callee().isAsync());
145   MOZ_ASSERT_IF(JSOp(*pc) == JSOp::Yield, genObj->callee().isGenerator());
146 
147   if (nvalues > 0) {
148     ArrayObject* stack = nullptr;
149     MOZ_ASSERT(genObj->hasStackStorage());
150     stack = &genObj->stackStorage();
151     MOZ_ASSERT(stack->getDenseCapacity() >= nvalues);
152     if (!frame.saveGeneratorSlots(cx, nvalues, stack)) {
153       return false;
154     }
155   }
156 
157   genObj->setResumeIndex(pc);
158   genObj->setEnvironmentChain(*frame.environmentChain());
159   return true;
160 }
161 
162 #ifdef DEBUG
dump() const163 void AbstractGeneratorObject::dump() const {
164   fprintf(stderr, "(AbstractGeneratorObject*) %p {\n", (void*)this);
165   fprintf(stderr, "  callee: (JSFunction*) %p,\n", (void*)&callee());
166   fprintf(stderr, "  environmentChain: (JSObject*) %p,\n",
167           (void*)&environmentChain());
168   if (hasArgsObj()) {
169     fprintf(stderr, "  argsObj: Some((ArgumentsObject*) %p),\n",
170             (void*)&argsObj());
171   } else {
172     fprintf(stderr, "  argsObj: None,\n");
173   }
174   if (hasStackStorage()) {
175     fprintf(stderr, "  stackStorage: Some(ArrayObject {\n");
176     ArrayObject& stack = stackStorage();
177     uint32_t denseLen = uint32_t(stack.getDenseInitializedLength());
178     fprintf(stderr, "    denseInitializedLength: %u\n,", denseLen);
179     uint32_t len = stack.length();
180     fprintf(stderr, "    length: %u\n,", len);
181     fprintf(stderr, "    data: [\n");
182     const Value* elements = getDenseElements();
183     for (uint32_t i = 0; i < std::max(len, denseLen); i++) {
184       fprintf(stderr, "      [%u]: ", i);
185       js::DumpValue(elements[i]);
186     }
187     fprintf(stderr, "    ],\n");
188     fprintf(stderr, "  }),\n");
189   } else {
190     fprintf(stderr, "  stackStorage: None\n");
191   }
192   if (isSuspended()) {
193     fprintf(stderr, "  resumeIndex: Some(%u),\n", resumeIndex());
194   } else {
195     fprintf(stderr, "  resumeIndex: None, /* (not suspended) */\n");
196   }
197   fprintf(stderr, "}\n");
198 }
199 #endif
200 
finalSuspend(HandleObject obj)201 void AbstractGeneratorObject::finalSuspend(HandleObject obj) {
202   auto* genObj = &obj->as<AbstractGeneratorObject>();
203   MOZ_ASSERT(genObj->isRunning());
204   genObj->setClosed();
205 }
206 
GetGeneratorObjectForCall(JSContext * cx,CallObject & callObj)207 static AbstractGeneratorObject* GetGeneratorObjectForCall(JSContext* cx,
208                                                           CallObject& callObj) {
209   // The ".generator" binding is always present and always "aliased".
210   mozilla::Maybe<PropertyInfo> prop =
211       callObj.lookup(cx, cx->names().dotGenerator);
212   if (prop.isNothing()) {
213     return nullptr;
214   }
215   Value genValue = callObj.getSlot(prop->slot());
216 
217   // If the `Generator; SetAliasedVar ".generator"; InitialYield` bytecode
218   // sequence has not run yet, genValue is undefined.
219   return genValue.isObject()
220              ? &genValue.toObject().as<AbstractGeneratorObject>()
221              : nullptr;
222 }
223 
GetGeneratorObjectForFrame(JSContext * cx,AbstractFramePtr frame)224 AbstractGeneratorObject* js::GetGeneratorObjectForFrame(
225     JSContext* cx, AbstractFramePtr frame) {
226   cx->check(frame);
227   MOZ_ASSERT(frame.isGeneratorFrame());
228 
229   if (frame.isModuleFrame()) {
230     ModuleEnvironmentObject* moduleEnv =
231         frame.script()->module()->environment();
232     mozilla::Maybe<PropertyInfo> prop =
233         moduleEnv->lookup(cx, cx->names().dotGenerator);
234     Value genValue = moduleEnv->getSlot(prop->slot());
235     return genValue.isObject()
236                ? &genValue.toObject().as<AbstractGeneratorObject>()
237                : nullptr;
238   }
239   if (!frame.hasInitialEnvironment()) {
240     return nullptr;
241   }
242 
243   return GetGeneratorObjectForCall(cx, frame.callObj());
244 }
245 
GetGeneratorObjectForEnvironment(JSContext * cx,HandleObject env)246 AbstractGeneratorObject* js::GetGeneratorObjectForEnvironment(
247     JSContext* cx, HandleObject env) {
248   auto* call = CallObject::find(env);
249   return call ? GetGeneratorObjectForCall(cx, *call) : nullptr;
250 }
251 
GeneratorThrowOrReturn(JSContext * cx,AbstractFramePtr frame,Handle<AbstractGeneratorObject * > genObj,HandleValue arg,GeneratorResumeKind resumeKind)252 bool js::GeneratorThrowOrReturn(JSContext* cx, AbstractFramePtr frame,
253                                 Handle<AbstractGeneratorObject*> genObj,
254                                 HandleValue arg,
255                                 GeneratorResumeKind resumeKind) {
256   MOZ_ASSERT(genObj->isRunning());
257   if (resumeKind == GeneratorResumeKind::Throw) {
258     cx->setPendingExceptionAndCaptureStack(arg);
259   } else {
260     MOZ_ASSERT(resumeKind == GeneratorResumeKind::Return);
261 
262     MOZ_ASSERT_IF(genObj->is<GeneratorObject>(), arg.isObject());
263     frame.setReturnValue(arg);
264 
265     RootedValue closing(cx, MagicValue(JS_GENERATOR_CLOSING));
266     cx->setPendingException(closing, nullptr);
267   }
268   return false;
269 }
270 
resume(JSContext * cx,InterpreterActivation & activation,Handle<AbstractGeneratorObject * > genObj,HandleValue arg,HandleValue resumeKind)271 bool AbstractGeneratorObject::resume(JSContext* cx,
272                                      InterpreterActivation& activation,
273                                      Handle<AbstractGeneratorObject*> genObj,
274                                      HandleValue arg, HandleValue resumeKind) {
275   MOZ_ASSERT(genObj->isSuspended());
276 
277   RootedFunction callee(cx, &genObj->callee());
278   RootedObject envChain(cx, &genObj->environmentChain());
279   if (!activation.resumeGeneratorFrame(callee, envChain)) {
280     return false;
281   }
282   activation.regs().fp()->setResumedGenerator();
283 
284   if (genObj->hasArgsObj()) {
285     activation.regs().fp()->initArgsObj(genObj->argsObj());
286   }
287 
288   if (genObj->hasStackStorage() && !genObj->isStackStorageEmpty()) {
289     JSScript* script = activation.regs().fp()->script();
290     ArrayObject* storage = &genObj->stackStorage();
291     uint32_t len = storage->getDenseInitializedLength();
292     activation.regs().fp()->restoreGeneratorSlots(storage);
293     activation.regs().sp += len - script->nfixed();
294     storage->setDenseInitializedLength(0);
295   }
296 
297   JSScript* script = callee->nonLazyScript();
298   uint32_t offset = script->resumeOffsets()[genObj->resumeIndex()];
299   activation.regs().pc = script->offsetToPC(offset);
300 
301   // Push arg, generator, resumeKind Values on the generator's stack.
302   activation.regs().sp += 3;
303   MOZ_ASSERT(activation.regs().spForStackDepth(activation.regs().stackDepth()));
304   activation.regs().sp[-3] = arg;
305   activation.regs().sp[-2] = ObjectValue(*genObj);
306   activation.regs().sp[-1] = resumeKind;
307 
308   genObj->setRunning();
309   return true;
310 }
311 
create(JSContext * cx,HandleFunction fun)312 GeneratorObject* GeneratorObject::create(JSContext* cx, HandleFunction fun) {
313   MOZ_ASSERT(fun->isGenerator() && !fun->isAsync());
314 
315   // FIXME: This would be faster if we could avoid doing a lookup to get
316   // the prototype for the instance.  Bug 906600.
317   RootedValue pval(cx);
318   if (!GetProperty(cx, fun, fun, cx->names().prototype, &pval)) {
319     return nullptr;
320   }
321   RootedObject proto(cx, pval.isObject() ? &pval.toObject() : nullptr);
322   if (!proto) {
323     proto = GlobalObject::getOrCreateGeneratorObjectPrototype(cx, cx->global());
324     if (!proto) {
325       return nullptr;
326     }
327   }
328   return NewObjectWithGivenProto<GeneratorObject>(cx, proto);
329 }
330 
331 const JSClass GeneratorObject::class_ = {
332     "Generator",
333     JSCLASS_HAS_RESERVED_SLOTS(GeneratorObject::RESERVED_SLOTS),
334     &classOps_,
335 };
336 
337 const JSClassOps GeneratorObject::classOps_ = {
338     nullptr,                                   // addProperty
339     nullptr,                                   // delProperty
340     nullptr,                                   // enumerate
341     nullptr,                                   // newEnumerate
342     nullptr,                                   // resolve
343     nullptr,                                   // mayResolve
344     nullptr,                                   // finalize
345     nullptr,                                   // call
346     nullptr,                                   // hasInstance
347     nullptr,                                   // construct
348     CallTraceMethod<AbstractGeneratorObject>,  // trace
349 };
350 
351 static const JSFunctionSpec generator_methods[] = {
352     JS_SELF_HOSTED_FN("next", "GeneratorNext", 1, 0),
353     JS_SELF_HOSTED_FN("throw", "GeneratorThrow", 1, 0),
354     JS_SELF_HOSTED_FN("return", "GeneratorReturn", 1, 0), JS_FS_END};
355 
NewTenuredObjectWithFunctionPrototype(JSContext * cx,Handle<GlobalObject * > global)356 JSObject* js::NewTenuredObjectWithFunctionPrototype(
357     JSContext* cx, Handle<GlobalObject*> global) {
358   RootedObject proto(cx,
359                      GlobalObject::getOrCreateFunctionPrototype(cx, global));
360   if (!proto) {
361     return nullptr;
362   }
363   return NewTenuredObjectWithGivenProto<PlainObject>(cx, proto);
364 }
365 
CreateGeneratorFunction(JSContext * cx,JSProtoKey key)366 static JSObject* CreateGeneratorFunction(JSContext* cx, JSProtoKey key) {
367   RootedObject proto(
368       cx, GlobalObject::getOrCreateFunctionConstructor(cx, cx->global()));
369   if (!proto) {
370     return nullptr;
371   }
372 
373   HandlePropertyName name = cx->names().GeneratorFunction;
374   return NewFunctionWithProto(cx, Generator, 1, FunctionFlags::NATIVE_CTOR,
375                               nullptr, name, proto, gc::AllocKind::FUNCTION,
376                               TenuredObject);
377 }
378 
CreateGeneratorFunctionPrototype(JSContext * cx,JSProtoKey key)379 static JSObject* CreateGeneratorFunctionPrototype(JSContext* cx,
380                                                   JSProtoKey key) {
381   return NewTenuredObjectWithFunctionPrototype(cx, cx->global());
382 }
383 
GeneratorFunctionClassFinish(JSContext * cx,HandleObject genFunction,HandleObject genFunctionProto)384 static bool GeneratorFunctionClassFinish(JSContext* cx,
385                                          HandleObject genFunction,
386                                          HandleObject genFunctionProto) {
387   Handle<GlobalObject*> global = cx->global();
388 
389   // Change the "constructor" property to non-writable before adding any other
390   // properties, so it's still the last property and can be modified without a
391   // dictionary-mode transition.
392   MOZ_ASSERT(genFunctionProto->as<NativeObject>().getLastProperty().key() ==
393              NameToId(cx->names().constructor));
394   MOZ_ASSERT(!genFunctionProto->as<NativeObject>().inDictionaryMode());
395 
396   RootedValue genFunctionVal(cx, ObjectValue(*genFunction));
397   if (!DefineDataProperty(cx, genFunctionProto, cx->names().constructor,
398                           genFunctionVal, JSPROP_READONLY)) {
399     return false;
400   }
401   MOZ_ASSERT(!genFunctionProto->as<NativeObject>().inDictionaryMode());
402 
403   RootedObject iteratorProto(
404       cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
405   if (!iteratorProto) {
406     return false;
407   }
408 
409   RootedObject genObjectProto(cx, GlobalObject::createBlankPrototypeInheriting(
410                                       cx, &PlainObject::class_, iteratorProto));
411   if (!genObjectProto) {
412     return false;
413   }
414   if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr,
415                                     generator_methods) ||
416       !DefineToStringTag(cx, genObjectProto, cx->names().Generator)) {
417     return false;
418   }
419 
420   if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto,
421                                    JSPROP_READONLY, JSPROP_READONLY) ||
422       !DefineToStringTag(cx, genFunctionProto, cx->names().GeneratorFunction)) {
423     return false;
424   }
425 
426   global->setGeneratorObjectPrototype(genObjectProto);
427 
428   return true;
429 }
430 
431 static const ClassSpec GeneratorFunctionClassSpec = {
432     CreateGeneratorFunction,
433     CreateGeneratorFunctionPrototype,
434     nullptr,
435     nullptr,
436     nullptr,
437     nullptr,
438     GeneratorFunctionClassFinish,
439     ClassSpec::DontDefineConstructor};
440 
441 const JSClass js::GeneratorFunctionClass = {
442     "GeneratorFunction", 0, JS_NULL_CLASS_OPS, &GeneratorFunctionClassSpec};
443 
getUnaliasedLocal(uint32_t slot) const444 const Value& AbstractGeneratorObject::getUnaliasedLocal(uint32_t slot) const {
445   MOZ_ASSERT(isSuspended());
446   MOZ_ASSERT(hasStackStorage());
447   MOZ_ASSERT(slot < callee().nonLazyScript()->nfixed());
448   return stackStorage().getDenseElement(slot);
449 }
450 
setUnaliasedLocal(uint32_t slot,const Value & value)451 void AbstractGeneratorObject::setUnaliasedLocal(uint32_t slot,
452                                                 const Value& value) {
453   MOZ_ASSERT(isSuspended());
454   MOZ_ASSERT(hasStackStorage());
455   MOZ_ASSERT(slot < callee().nonLazyScript()->nfixed());
456   return stackStorage().setDenseElement(slot, value);
457 }
458 
isAfterYield()459 bool AbstractGeneratorObject::isAfterYield() {
460   return isAfterYieldOrAwait(JSOp::Yield);
461 }
462 
isAfterAwait()463 bool AbstractGeneratorObject::isAfterAwait() {
464   return isAfterYieldOrAwait(JSOp::Await);
465 }
466 
isAfterYieldOrAwait(JSOp op)467 bool AbstractGeneratorObject::isAfterYieldOrAwait(JSOp op) {
468   if (isClosed() || isRunning()) {
469     return false;
470   }
471 
472   JSScript* script = callee().nonLazyScript();
473   jsbytecode* code = script->code();
474   uint32_t nextOffset = script->resumeOffsets()[resumeIndex()];
475   if (JSOp(code[nextOffset]) != JSOp::AfterYield) {
476     return false;
477   }
478 
479   static_assert(JSOpLength_Yield == JSOpLength_InitialYield,
480                 "JSOp::Yield and JSOp::InitialYield must have the same length");
481   static_assert(JSOpLength_Yield == JSOpLength_Await,
482                 "JSOp::Yield and JSOp::Await must have the same length");
483 
484   uint32_t offset = nextOffset - JSOpLength_Yield;
485   JSOp prevOp = JSOp(code[offset]);
486   MOZ_ASSERT(prevOp == JSOp::InitialYield || prevOp == JSOp::Yield ||
487              prevOp == JSOp::Await);
488 
489   return prevOp == op;
490 }
491 
492 template <>
is() const493 bool JSObject::is<js::AbstractGeneratorObject>() const {
494   return is<GeneratorObject>() || is<AsyncFunctionGeneratorObject>() ||
495          is<AsyncGeneratorObject>();
496 }
497 
ParserAtomToResumeKind(JSContext * cx,frontend::TaggedParserAtomIndex atom)498 GeneratorResumeKind js::ParserAtomToResumeKind(
499     JSContext* cx, frontend::TaggedParserAtomIndex atom) {
500   if (atom == frontend::TaggedParserAtomIndex::WellKnown::next()) {
501     return GeneratorResumeKind::Next;
502   }
503   if (atom == frontend::TaggedParserAtomIndex::WellKnown::throw_()) {
504     return GeneratorResumeKind::Throw;
505   }
506   MOZ_ASSERT(atom == frontend::TaggedParserAtomIndex::WellKnown::return_());
507   return GeneratorResumeKind::Return;
508 }
509 
ResumeKindToAtom(JSContext * cx,GeneratorResumeKind kind)510 JSAtom* js::ResumeKindToAtom(JSContext* cx, GeneratorResumeKind kind) {
511   switch (kind) {
512     case GeneratorResumeKind::Next:
513       return cx->names().next;
514 
515     case GeneratorResumeKind::Throw:
516       return cx->names().throw_;
517 
518     case GeneratorResumeKind::Return:
519       return cx->names().return_;
520   }
521   MOZ_CRASH("Invalid resume kind");
522 }
523