summaryrefslogtreecommitdiff
path: root/js/src/vm/GeneratorObject.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /js/src/vm/GeneratorObject.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloaduxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/vm/GeneratorObject.cpp')
-rw-r--r--js/src/vm/GeneratorObject.cpp367
1 files changed, 367 insertions, 0 deletions
diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp
new file mode 100644
index 0000000000..690c0bf485
--- /dev/null
+++ b/js/src/vm/GeneratorObject.cpp
@@ -0,0 +1,367 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "vm/GeneratorObject.h"
+
+#include "jsobj.h"
+
+#include "jsatominlines.h"
+#include "jsscriptinlines.h"
+
+#include "vm/NativeObject-inl.h"
+#include "vm/Stack-inl.h"
+
+using namespace js;
+
+JSObject*
+GeneratorObject::create(JSContext* cx, AbstractFramePtr frame)
+{
+ MOZ_ASSERT(frame.script()->isGenerator());
+ MOZ_ASSERT(frame.script()->nfixed() == 0);
+
+ Rooted<GlobalObject*> global(cx, cx->global());
+ RootedNativeObject obj(cx);
+ if (frame.script()->isStarGenerator()) {
+ RootedValue pval(cx);
+ RootedObject fun(cx, frame.callee());
+ // FIXME: This would be faster if we could avoid doing a lookup to get
+ // the prototype for the instance. Bug 906600.
+ if (!GetProperty(cx, fun, fun, cx->names().prototype, &pval))
+ return nullptr;
+ RootedObject proto(cx, pval.isObject() ? &pval.toObject() : nullptr);
+ if (!proto) {
+ proto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global);
+ if (!proto)
+ return nullptr;
+ }
+ obj = NewNativeObjectWithGivenProto(cx, &StarGeneratorObject::class_, proto);
+ } else {
+ MOZ_ASSERT(frame.script()->isLegacyGenerator());
+ RootedObject proto(cx, GlobalObject::getOrCreateLegacyGeneratorObjectPrototype(cx, global));
+ if (!proto)
+ return nullptr;
+ obj = NewNativeObjectWithGivenProto(cx, &LegacyGeneratorObject::class_, proto);
+ }
+ if (!obj)
+ return nullptr;
+
+ GeneratorObject* genObj = &obj->as<GeneratorObject>();
+ genObj->setCallee(*frame.callee());
+ genObj->setNewTarget(frame.newTarget());
+ genObj->setEnvironmentChain(*frame.environmentChain());
+ if (frame.script()->needsArgsObj())
+ genObj->setArgsObj(frame.argsObj());
+ genObj->clearExpressionStack();
+
+ return obj;
+}
+
+bool
+GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame, jsbytecode* pc,
+ Value* vp, unsigned nvalues)
+{
+ MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD);
+
+ Rooted<GeneratorObject*> genObj(cx, &obj->as<GeneratorObject>());
+ MOZ_ASSERT(!genObj->hasExpressionStack());
+
+ if (*pc == JSOP_YIELD && genObj->isClosing() && genObj->is<LegacyGeneratorObject>()) {
+ RootedValue val(cx, ObjectValue(*frame.callee()));
+ ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD, JSDVG_IGNORE_STACK, val, nullptr);
+ return false;
+ }
+
+ uint32_t yieldIndex = GET_UINT24(pc);
+ genObj->setYieldIndex(yieldIndex);
+ genObj->setEnvironmentChain(*frame.environmentChain());
+
+ if (nvalues) {
+ ArrayObject* stack = NewDenseCopiedArray(cx, nvalues, vp);
+ if (!stack)
+ return false;
+ genObj->setExpressionStack(*stack);
+ }
+
+ return true;
+}
+
+bool
+GeneratorObject::finalSuspend(JSContext* cx, HandleObject obj)
+{
+ Rooted<GeneratorObject*> genObj(cx, &obj->as<GeneratorObject>());
+ MOZ_ASSERT(genObj->isRunning() || genObj->isClosing());
+
+ bool closing = genObj->isClosing();
+ genObj->setClosed();
+
+ if (genObj->is<LegacyGeneratorObject>() && !closing)
+ return ThrowStopIteration(cx);
+
+ return true;
+}
+
+void
+js::SetReturnValueForClosingGenerator(JSContext* cx, AbstractFramePtr frame)
+{
+ CallObject& callObj = frame.callObj();
+
+ // Get the generator object stored on the scope chain and close it.
+ Shape* shape = callObj.lookup(cx, cx->names().dotGenerator);
+ GeneratorObject& genObj = callObj.getSlot(shape->slot()).toObject().as<GeneratorObject>();
+ genObj.setClosed();
+
+ // Return value is already set in GeneratorThrowOrClose.
+ if (genObj.is<StarGeneratorObject>())
+ return;
+
+ // Legacy generator .close() always returns |undefined|.
+ MOZ_ASSERT(genObj.is<LegacyGeneratorObject>());
+ frame.setReturnValue(UndefinedValue());
+}
+
+bool
+js::GeneratorThrowOrClose(JSContext* cx, AbstractFramePtr frame, Handle<GeneratorObject*> genObj,
+ HandleValue arg, uint32_t resumeKind)
+{
+ if (resumeKind == GeneratorObject::THROW) {
+ cx->setPendingException(arg);
+ genObj->setRunning();
+ } else {
+ MOZ_ASSERT(resumeKind == GeneratorObject::CLOSE);
+
+ if (genObj->is<StarGeneratorObject>()) {
+ MOZ_ASSERT(arg.isObject());
+ frame.setReturnValue(arg);
+ } else {
+ MOZ_ASSERT(arg.isUndefined());
+ }
+
+ cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING));
+ genObj->setClosing();
+ }
+ return false;
+}
+
+bool
+GeneratorObject::resume(JSContext* cx, InterpreterActivation& activation,
+ HandleObject obj, HandleValue arg, GeneratorObject::ResumeKind resumeKind)
+{
+ Rooted<GeneratorObject*> genObj(cx, &obj->as<GeneratorObject>());
+ MOZ_ASSERT(genObj->isSuspended());
+
+ RootedFunction callee(cx, &genObj->callee());
+ RootedValue newTarget(cx, genObj->newTarget());
+ RootedObject envChain(cx, &genObj->environmentChain());
+ if (!activation.resumeGeneratorFrame(callee, newTarget, envChain))
+ return false;
+ activation.regs().fp()->setResumedGenerator();
+
+ if (genObj->hasArgsObj())
+ activation.regs().fp()->initArgsObj(genObj->argsObj());
+
+ if (genObj->hasExpressionStack()) {
+ uint32_t len = genObj->expressionStack().length();
+ MOZ_ASSERT(activation.regs().spForStackDepth(len));
+ const Value* src = genObj->expressionStack().getDenseElements();
+ mozilla::PodCopy(activation.regs().sp, src, len);
+ activation.regs().sp += len;
+ genObj->clearExpressionStack();
+ }
+
+ JSScript* script = callee->nonLazyScript();
+ uint32_t offset = script->yieldOffsets()[genObj->yieldIndex()];
+ activation.regs().pc = script->offsetToPC(offset);
+
+ // Always push on a value, even if we are raising an exception. In the
+ // exception case, the stack needs to have something on it so that exception
+ // handling doesn't skip the catch blocks. See TryNoteIter::settle.
+ activation.regs().sp++;
+ MOZ_ASSERT(activation.regs().spForStackDepth(activation.regs().stackDepth()));
+ activation.regs().sp[-1] = arg;
+
+ switch (resumeKind) {
+ case NEXT:
+ genObj->setRunning();
+ return true;
+
+ case THROW:
+ case CLOSE:
+ return GeneratorThrowOrClose(cx, activation.regs().fp(), genObj, arg, resumeKind);
+
+ default:
+ MOZ_CRASH("bad resumeKind");
+ }
+}
+
+bool
+LegacyGeneratorObject::close(JSContext* cx, HandleObject obj)
+{
+ Rooted<LegacyGeneratorObject*> genObj(cx, &obj->as<LegacyGeneratorObject>());
+
+ // Avoid calling back into JS unless it is necessary.
+ if (genObj->isClosed())
+ return true;
+
+ RootedValue rval(cx);
+
+ RootedValue closeValue(cx);
+ if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().LegacyGeneratorCloseInternal,
+ &closeValue))
+ {
+ return false;
+ }
+ MOZ_ASSERT(closeValue.isObject());
+ MOZ_ASSERT(closeValue.toObject().is<JSFunction>());
+
+ FixedInvokeArgs<0> args(cx);
+
+ RootedValue v(cx, ObjectValue(*genObj));
+ return Call(cx, closeValue, v, args, &v);
+}
+
+const Class LegacyGeneratorObject::class_ = {
+ "Generator",
+ JSCLASS_HAS_RESERVED_SLOTS(GeneratorObject::RESERVED_SLOTS)
+};
+
+const Class StarGeneratorObject::class_ = {
+ "Generator",
+ JSCLASS_HAS_RESERVED_SLOTS(GeneratorObject::RESERVED_SLOTS)
+};
+
+static const JSFunctionSpec star_generator_methods[] = {
+ JS_SELF_HOSTED_FN("next", "StarGeneratorNext", 1, 0),
+ JS_SELF_HOSTED_FN("throw", "StarGeneratorThrow", 1, 0),
+ JS_SELF_HOSTED_FN("return", "StarGeneratorReturn", 1, 0),
+ JS_FS_END
+};
+
+#define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
+
+static const JSFunctionSpec legacy_generator_methods[] = {
+ JS_SELF_HOSTED_SYM_FN(iterator, "LegacyGeneratorIteratorShim", 0, 0),
+ // "send" is an alias for "next".
+ JS_SELF_HOSTED_FN("next", "LegacyGeneratorNext", 1, JSPROP_ROPERM),
+ JS_SELF_HOSTED_FN("send", "LegacyGeneratorNext", 1, JSPROP_ROPERM),
+ JS_SELF_HOSTED_FN("throw", "LegacyGeneratorThrow", 1, JSPROP_ROPERM),
+ JS_SELF_HOSTED_FN("close", "LegacyGeneratorClose", 0, JSPROP_ROPERM),
+ JS_FS_END
+};
+
+#undef JSPROP_ROPERM
+
+static JSObject*
+NewSingletonObjectWithObjectPrototype(JSContext* cx, Handle<GlobalObject*> global)
+{
+ RootedObject proto(cx, global->getOrCreateObjectPrototype(cx));
+ if (!proto)
+ return nullptr;
+ return NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject);
+}
+
+JSObject*
+js::NewSingletonObjectWithFunctionPrototype(JSContext* cx, Handle<GlobalObject*> global)
+{
+ RootedObject proto(cx, global->getOrCreateFunctionPrototype(cx));
+ if (!proto)
+ return nullptr;
+ return NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject);
+}
+
+/* static */ bool
+GlobalObject::initLegacyGeneratorProto(JSContext* cx, Handle<GlobalObject*> global)
+{
+ if (global->getReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO).isObject())
+ return true;
+
+ RootedObject proto(cx, NewSingletonObjectWithObjectPrototype(cx, global));
+ if (!proto || !proto->setDelegate(cx))
+ return false;
+ if (!DefinePropertiesAndFunctions(cx, proto, nullptr, legacy_generator_methods))
+ return false;
+
+ global->setReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO, ObjectValue(*proto));
+ return true;
+}
+
+/* static */ bool
+GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global)
+{
+ if (global->getReservedSlot(STAR_GENERATOR_OBJECT_PROTO).isObject())
+ return true;
+
+ RootedObject iteratorProto(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
+ if (!iteratorProto)
+ return false;
+
+ RootedObject genObjectProto(cx, global->createBlankPrototypeInheriting(cx,
+ &PlainObject::class_,
+ iteratorProto));
+ if (!genObjectProto)
+ return false;
+ if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr, star_generator_methods) ||
+ !DefineToStringTag(cx, genObjectProto, cx->names().Generator))
+ {
+ return false;
+ }
+
+ RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
+ if (!genFunctionProto || !genFunctionProto->setDelegate(cx))
+ return false;
+ if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto) ||
+ !DefineToStringTag(cx, genFunctionProto, cx->names().GeneratorFunction))
+ {
+ return false;
+ }
+
+ RootedValue function(cx, global->getConstructor(JSProto_Function));
+ if (!function.toObjectOrNull())
+ return false;
+ RootedObject proto(cx, &function.toObject());
+ RootedAtom name(cx, cx->names().GeneratorFunction);
+ RootedObject genFunction(cx, NewFunctionWithProto(cx, Generator, 1,
+ JSFunction::NATIVE_CTOR, nullptr, name,
+ proto, gc::AllocKind::FUNCTION,
+ SingletonObject));
+ if (!genFunction)
+ return false;
+ if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto))
+ return false;
+
+ global->setReservedSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto));
+ global->setReservedSlot(STAR_GENERATOR_FUNCTION, ObjectValue(*genFunction));
+ global->setReservedSlot(STAR_GENERATOR_FUNCTION_PROTO, ObjectValue(*genFunctionProto));
+ return true;
+}
+
+MOZ_MUST_USE bool
+js::CheckStarGeneratorResumptionValue(JSContext* cx, HandleValue v)
+{
+ // yield/return value should be an Object.
+ if (!v.isObject())
+ return false;
+
+ JSObject* obj = &v.toObject();
+
+ // It should have `done` data property with boolean value.
+ Value doneVal;
+ if (!GetPropertyPure(cx, obj, NameToId(cx->names().done), &doneVal))
+ return false;
+ if (!doneVal.isBoolean())
+ return false;
+
+ // It should have `value` data property, but the type doesn't matter
+ JSObject* ignored;
+ Shape* shape;
+ if (!LookupPropertyPure(cx, obj, NameToId(cx->names().value), &ignored, &shape))
+ return false;
+ if (!shape)
+ return false;
+ if (!shape->hasDefaultGetter())
+ return false;
+
+ return true;
+}