diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /js/src/vm/GeneratorObject.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/vm/GeneratorObject.cpp')
-rw-r--r-- | js/src/vm/GeneratorObject.cpp | 367 |
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 000000000..690c0bf48 --- /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; +} |