Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / js / src / vm / GeneratorObject.cpp
blob4f8d807df539d798e1c2d1f8f75fb1cf81a0a5bf
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"
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
21 #include "debugger/DebugAPI-inl.h"
22 #include "vm/Stack-inl.h"
24 using namespace js;
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);
34 } else {
35 genObj = AsyncFunctionGeneratorObject::create(cx, callee);
37 if (!genObj) {
38 return nullptr;
41 genObj->setCallee(*callee);
42 genObj->setEnvironmentChain(*environmentChain);
43 if (argsObject) {
44 genObj->setArgsObj(*argsObject.get());
47 ArrayObject* stack = NewDenseFullyAllocatedArray(cx, script->nslots());
48 if (!stack) {
49 return nullptr;
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.
56 return genObj;
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,
76 maybeArgs));
77 if (!genObj) {
78 return nullptr;
81 if (!DebugAPI::onNewGenerator(cx, frame, genObj)) {
82 return nullptr;
85 return 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);
93 if (!genObj) {
94 return nullptr;
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));
105 if (!handlerFun) {
106 return nullptr;
108 handlerFun->initScript(module->script());
110 genObj->setCallee(*handlerFun);
111 genObj->setEnvironmentChain(*frame.environmentChain());
113 ArrayObject* stack =
114 NewDenseFullyAllocatedArray(cx, module->script()->nslots());
115 if (!stack) {
116 return nullptr;
119 genObj->setStackStorage(*stack);
121 if (!DebugAPI::onNewGenerator(cx, frame, genObj)) {
122 return nullptr;
125 return 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());
143 if (nvalues > 0) {
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)) {
149 return false;
153 genObj->setResumeIndex(pc);
154 genObj->setEnvironmentChain(*frame.environmentChain());
155 return true;
158 #ifdef DEBUG
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());
164 if (hasArgsObj()) {
165 fprintf(stderr, " argsObj: Some((ArgumentsObject*) %p),\n",
166 (void*)&argsObj());
167 } else {
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");
185 } else {
186 fprintf(stderr, " stackStorage: None\n");
188 if (isSuspended()) {
189 fprintf(stderr, " resumeIndex: Some(%u),\n", resumeIndex());
190 } else {
191 fprintf(stderr, " resumeIndex: None, /* (not suspended) */\n");
193 fprintf(stderr, "}\n");
195 #endif
197 void AbstractGeneratorObject::finalSuspend(HandleObject obj) {
198 auto* genObj = &obj->as<AbstractGeneratorObject>();
199 MOZ_ASSERT(genObj->isRunning());
200 genObj->setClosed();
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()) {
209 return nullptr;
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>()
217 : nullptr;
220 AbstractGeneratorObject* js::GetGeneratorObjectForFrame(
221 JSContext* cx, AbstractFramePtr frame) {
222 cx->check(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>()
233 : nullptr;
235 if (!frame.hasInitialEnvironment()) {
236 return nullptr;
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,
250 HandleValue arg,
251 GeneratorResumeKind resumeKind) {
252 MOZ_ASSERT(genObj->isRunning());
253 if (resumeKind == GeneratorResumeKind::Throw) {
254 cx->setPendingException(arg, ShouldCaptureStack::Maybe);
255 } else {
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);
264 return false;
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)) {
276 return false;
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();
305 return true;
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)) {
315 return nullptr;
317 RootedObject proto(cx, pval.isObject() ? &pval.toObject() : nullptr);
318 if (!proto) {
319 proto = GlobalObject::getOrCreateGeneratorObjectPrototype(cx, cx->global());
320 if (!proto) {
321 return nullptr;
324 return NewObjectWithGivenProto<GeneratorObject>(cx, proto);
327 const JSClass GeneratorObject::class_ = {
328 "Generator",
329 JSCLASS_HAS_RESERVED_SLOTS(GeneratorObject::RESERVED_SLOTS),
330 &classOps_,
333 const JSClassOps GeneratorObject::classOps_ = {
334 nullptr, // addProperty
335 nullptr, // delProperty
336 nullptr, // enumerate
337 nullptr, // newEnumerate
338 nullptr, // resolve
339 nullptr, // mayResolve
340 nullptr, // finalize
341 nullptr, // call
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,
362 TenuredObject);
365 static JSObject* CreateGeneratorFunctionPrototype(JSContext* cx,
366 JSProtoKey key) {
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)) {
385 return false;
387 MOZ_ASSERT(!genFunctionProto->as<NativeObject>().inDictionaryMode());
389 RootedObject iteratorProto(
390 cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
391 if (!iteratorProto) {
392 return false;
395 RootedObject genObjectProto(cx, GlobalObject::createBlankPrototypeInheriting(
396 cx, &PlainObject::class_, iteratorProto));
397 if (!genObjectProto) {
398 return false;
400 if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr,
401 generator_methods) ||
402 !DefineToStringTag(cx, genObjectProto, cx->names().Generator)) {
403 return false;
406 if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto,
407 JSPROP_READONLY, JSPROP_READONLY) ||
408 !DefineToStringTag(cx, genFunctionProto, cx->names().GeneratorFunction)) {
409 return false;
412 global->setGeneratorObjectPrototype(genObjectProto);
414 return true;
417 static const ClassSpec GeneratorFunctionClassSpec = {
418 CreateGeneratorFunction,
419 CreateGeneratorFunctionPrototype,
420 nullptr,
421 nullptr,
422 nullptr,
423 nullptr,
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()) {
455 return false;
458 JSScript* script = callee().nonLazyScript();
459 jsbytecode* code = script->code();
460 uint32_t nextOffset = script->resumeOffsets()[resumeIndex()];
461 if (JSOp(code[nextOffset]) != JSOp::AfterYield) {
462 return false;
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);
475 return prevOp == op;
478 template <>
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) {
497 switch (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");