runtime/generator-builtins.cpp (840 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
#include "generator-builtins.h"
#include <string>
#include "builtins.h"
#include "exception-builtins.h"
#include "frame.h"
#include "modules.h"
#include "objects.h"
#include "type-builtins.h"
namespace py {
RawGeneratorBase generatorFromStackFrame(Frame* frame) {
// For now, we have the invariant that GeneratorBase bodies are only invoked
// by __next__() or send(), which have the GeneratorBase as their first local.
return GeneratorBase::cast(frame->previousFrame()->local(0));
}
template <SymbolId name, LayoutId type>
static RawObject sendImpl(Thread* thread, RawObject raw_self,
RawObject raw_value) {
HandleScope scope(thread);
Object self(&scope, raw_self);
Object value(&scope, raw_value);
if (self.layoutId() != type) return thread->raiseRequiresType(self, name);
GeneratorBase gen(&scope, *self);
return Interpreter::resumeGenerator(thread, gen, value);
}
// Validate the given exception and send it to gen.
static RawObject throwDoRaise(Thread* thread, const GeneratorBase& gen,
const Object& exc_in, const Object& value_in,
const Object& tb_in) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
Object exc(&scope, *exc_in);
Object value(&scope, value_in.isUnbound() ? NoneType::object() : *value_in);
Object tb(&scope, tb_in.isUnbound() ? NoneType::object() : *tb_in);
if (!tb.isNoneType() && !tb.isTraceback()) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"throw() third argument must be a traceback object");
}
if (runtime->isInstanceOfType(*exc) &&
Type(&scope, *exc).isBaseExceptionSubclass()) {
normalizeException(thread, &exc, &value, &tb);
} else if (runtime->isInstanceOfBaseException(*exc)) {
if (!value.isNoneType()) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"instance exception may not have a separate value");
}
value = *exc;
exc = runtime->typeOf(*exc);
if (tb.isNoneType()) {
BaseException base_exc(&scope, *value);
tb = base_exc.traceback();
}
} else {
return thread->raiseWithFmt(LayoutId::kTypeError,
"exceptions must be classes or instances "
"deriving from BaseException, not %T",
&exc);
}
return Interpreter::resumeGeneratorWithRaise(thread, gen, exc, value, tb);
}
// Delegate the given exception to yf.throw(). If yf does not have a throw
// attribute, send the exception to gen like normal.
static RawObject throwYieldFrom(Thread* thread, const GeneratorBase& gen,
const Object& yf, const Object& exc,
const Object& value, const Object& tb) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
// TODO(bsimmers): If exc == GeneratorExit, close the subiterator. See
// _gen_throw() in CPython.
Object throw_obj(&scope, runtime->attributeAtById(thread, yf, ID(throw)));
if (throw_obj.isError()) {
// If the call failed with an AttributeError, ignore it and proceed with
// the throw. Otherwise, forward the exception.
if (throw_obj.isErrorNotFound() ||
thread->pendingExceptionMatches(LayoutId::kAttributeError)) {
thread->clearPendingException();
return throwDoRaise(thread, gen, exc, value, tb);
}
return *throw_obj;
}
Object result(&scope, NoneType::object());
gen.setRunning(Bool::trueObj());
// This is awkward but necessary to maintain compatibility with how CPython
// calls yf.throw(): it forwards exaclty as many arguments as it was given.
if (value.isUnbound()) {
result = Interpreter::call1(thread, throw_obj, exc);
} else if (tb.isUnbound()) {
result = Interpreter::call2(thread, throw_obj, exc, value);
} else {
result = Interpreter::call3(thread, throw_obj, exc, value, tb);
}
gen.setRunning(Bool::falseObj());
if (result.isError()) {
// The subiterator raised, so finish the YIELD_FROM in the parent. If the
// exception is a StopIteration, continue in the parent like usually;
// otherwise, propagate the exception at the YIELD_FROM.
// findYieldFrom() returns None when gen is currently executing, so we
// don't have to worry about messing with the GeneratorFrame of a generator
// that's running.
DCHECK(gen.running() == Bool::falseObj(), "Generator shouldn't be running");
GeneratorFrame gf(&scope, gen.generatorFrame());
Object subiter(&scope, gf.popValue());
DCHECK(*subiter == *yf, "Unexpected subiter on generator stack");
gf.setVirtualPC(gf.virtualPC() + kCodeUnitSize);
if (thread->hasPendingStopIteration()) {
Object subiter_value(&scope, thread->pendingStopIterationValue());
thread->clearPendingException();
return Interpreter::resumeGenerator(thread, gen, subiter_value);
}
Object exc_type(&scope, thread->pendingExceptionType());
Object exc_value(&scope, thread->pendingExceptionValue());
Object exc_traceback(&scope, thread->pendingExceptionTraceback());
thread->clearPendingException();
return Interpreter::resumeGeneratorWithRaise(thread, gen, exc_type,
exc_value, exc_traceback);
}
return *result;
}
static RawObject throwImpl(Thread* thread, const GeneratorBase& gen,
const Object& exc, const Object& value,
const Object& tb) {
HandleScope scope(thread);
Object yf(&scope, Interpreter::findYieldFrom(*gen));
if (!yf.isNoneType()) {
return throwYieldFrom(thread, gen, yf, exc, value, tb);
}
return throwDoRaise(thread, gen, exc, value, tb);
}
template <SymbolId name, LayoutId type>
static RawObject throwImpl(Thread* thread, RawObject raw_self,
RawObject raw_exc, RawObject raw_value,
RawObject raw_tb) {
HandleScope scope(thread);
Object self(&scope, raw_self);
Object exc(&scope, raw_exc);
Object value(&scope, raw_value);
Object tb(&scope, raw_tb);
if (self.layoutId() != type) return thread->raiseRequiresType(self, name);
GeneratorBase gen(&scope, *self);
return throwImpl(thread, gen, exc, value, tb);
}
static RawObject closeImpl(Thread* thread, const GeneratorBase& gen) {
HandleScope scope(thread);
GeneratorFrame generator_frame(&scope, gen.generatorFrame());
if (generator_frame.virtualPC() == Frame::kFinishedGeneratorPC) {
return NoneType::object();
}
Runtime* runtime = thread->runtime();
Object gen_exit_exc(&scope, runtime->typeAt(LayoutId::kGeneratorExit));
Object none(&scope, NoneType::object());
Object result(&scope, throwImpl(thread, gen, gen_exit_exc, none, none));
if (!result.isError()) {
return thread->raiseWithFmt(LayoutId::kRuntimeError,
"ignored GeneratorExit");
}
if (thread->pendingExceptionMatches(LayoutId::kGeneratorExit) ||
thread->pendingExceptionMatches(LayoutId::kStopIteration)) {
thread->clearPendingException();
return NoneType::object();
}
return *result;
}
static const BuiltinAttribute kGeneratorAttributes[] = {
{ID(_generator__frame), RawGenerator::kFrameOffset,
AttributeFlags::kHidden},
{ID(_generator__exception_state), RawGenerator::kExceptionStateOffset,
AttributeFlags::kHidden},
{ID(__name__), RawAsyncGenerator::kNameOffset, AttributeFlags::kReadOnly},
{ID(__qualname__), RawGenerator::kQualnameOffset},
{ID(gi_running), RawGenerator::kRunningOffset, AttributeFlags::kReadOnly},
{ID(_generator__yield_from), RawGenerator::kYieldFromOffset,
AttributeFlags::kHidden},
};
static const BuiltinAttribute kCoroutineAttributes[] = {
{ID(_coroutine__frame), RawCoroutine::kFrameOffset,
AttributeFlags::kHidden},
{ID(_coroutine__exception_state), RawCoroutine::kExceptionStateOffset,
AttributeFlags::kHidden},
{ID(__name__), RawAsyncGenerator::kNameOffset, AttributeFlags::kReadOnly},
{ID(__qualname__), RawCoroutine::kQualnameOffset},
{ID(cr_running), RawCoroutine::kRunningOffset, AttributeFlags::kReadOnly},
{ID(_coroutine__await), RawCoroutine::kAwaitOffset,
AttributeFlags::kHidden},
{ID(_coroutine__origin), RawCoroutine::kOriginOffset,
AttributeFlags::kHidden},
};
static const BuiltinAttribute kCoroutineWrapperAttributes[] = {
{ID(_coroutine_wrapper__cw_coroutine),
RawCoroutineWrapper::kCoroutineOffset, AttributeFlags::kHidden},
};
static const BuiltinAttribute kAsyncGeneratorAttributes[] = {
{ID(_async_generator__frame), RawAsyncGenerator::kFrameOffset,
AttributeFlags::kHidden},
{ID(_async_generator__exception_state),
RawAsyncGenerator::kExceptionStateOffset, AttributeFlags::kHidden},
// TODO(T70191611) Make __name__ and __qualname__ writable.
{ID(__name__), RawAsyncGenerator::kNameOffset, AttributeFlags::kReadOnly},
{ID(__qualname__), RawAsyncGenerator::kQualnameOffset,
AttributeFlags::kReadOnly},
{ID(_async_generator__running), RawAsyncGenerator::kRunningOffset,
AttributeFlags::kHidden},
{ID(_async_generator__finalizer), RawAsyncGenerator::kFinalizerOffset,
AttributeFlags::kHidden},
{ID(_async_generator__hooks_inited), RawAsyncGenerator::kHooksInitedOffset,
AttributeFlags::kHidden},
};
static const BuiltinAttribute kAsyncGeneratorAcloseAttributes[] = {
{ID(_async_generator_aclose__generator),
RawAsyncGeneratorAclose::kGeneratorOffset, AttributeFlags::kHidden},
{ID(_async_generator_aclose__state), RawAsyncGeneratorAclose::kStateOffset,
AttributeFlags::kHidden},
};
static const BuiltinAttribute kAsyncGeneratorAsendAttributes[] = {
{ID(_async_generator_asend__generator),
RawAsyncGeneratorAsend::kGeneratorOffset, AttributeFlags::kHidden},
{ID(_async_generator_asend__state), RawAsyncGeneratorAsend::kStateOffset,
AttributeFlags::kHidden},
{ID(_async_generator_asend__value), RawAsyncGeneratorAsend::kValueOffset,
AttributeFlags::kHidden},
};
static const BuiltinAttribute kAsyncGeneratorAthrowAttributes[] = {
{ID(_async_generator_athrow__generator),
RawAsyncGeneratorAthrow::kGeneratorOffset, AttributeFlags::kHidden},
{ID(_async_generator_athrow__state), RawAsyncGeneratorAthrow::kStateOffset,
AttributeFlags::kHidden},
{ID(_async_generator_athrow__exception_traceback),
RawAsyncGeneratorAthrow::kExceptionTracebackOffset,
AttributeFlags::kHidden},
{ID(_async_generator_athrow__exception_type),
RawAsyncGeneratorAthrow::kExceptionTypeOffset, AttributeFlags::kHidden},
{ID(_async_generator_athrow__exception_value),
RawAsyncGeneratorAthrow::kExceptionValueOffset, AttributeFlags::kHidden},
};
static const BuiltinAttribute kAsyncGeneratorWrappedValueAttributes[] = {
{ID(_async_generator_wrapped_value__value),
RawAsyncGeneratorWrappedValue::kValueOffset},
};
void initializeGeneratorTypes(Thread* thread) {
addBuiltinType(thread, ID(generator), LayoutId::kGenerator,
/*superclass_id=*/LayoutId::kObject, kGeneratorAttributes,
Generator::kSize, /*basetype=*/false);
addBuiltinType(thread, ID(frame), LayoutId::kGeneratorFrame,
LayoutId::kObject, kNoAttributes, GeneratorFrame::kSize,
/*basetype=*/false);
addBuiltinType(thread, ID(coroutine), LayoutId::kCoroutine,
/*superclass_id=*/LayoutId::kObject, kCoroutineAttributes,
Coroutine::kSize, /*basetype=*/false);
addBuiltinType(thread, ID(coroutine_wrapper), LayoutId::kCoroutineWrapper,
/*superclass_id=*/LayoutId::kObject,
kCoroutineWrapperAttributes, CoroutineWrapper::kSize,
/*basetype=*/false);
addBuiltinType(thread, ID(async_generator), LayoutId::kAsyncGenerator,
/*superclass_id=*/LayoutId::kObject, kAsyncGeneratorAttributes,
AsyncGenerator::kSize, /*basetype=*/false);
addBuiltinType(
thread, ID(async_generator_aclose), LayoutId::kAsyncGeneratorAclose,
/*superclass_id=*/LayoutId::kObject, kAsyncGeneratorAcloseAttributes,
RawAsyncGeneratorAclose::kSize, /*basetype=*/false);
addBuiltinType(
thread, ID(async_generator_asend), LayoutId::kAsyncGeneratorAsend,
/*superclass_id=*/LayoutId::kObject, kAsyncGeneratorAsendAttributes,
RawAsyncGeneratorAsend::kSize, /*basetype=*/false);
addBuiltinType(
thread, ID(async_generator_athrow), LayoutId::kAsyncGeneratorAthrow,
/*superclass_id=*/LayoutId::kObject, kAsyncGeneratorAthrowAttributes,
RawAsyncGeneratorAthrow::kSize, /*basetype=*/false);
addBuiltinType(thread, ID(async_generator_wrapped_value),
LayoutId::kAsyncGeneratorWrappedValue,
/*superclass_id=*/LayoutId::kObject,
kAsyncGeneratorWrappedValueAttributes,
AsyncGeneratorWrappedValue::kSize,
/*basetype=*/false);
}
RawObject METH(async_generator, __aiter__)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
if (!self.isAsyncGenerator()) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"__aiter__() must be called with an "
"async_generator instance as the first "
"argument, not %T",
&self);
}
return *self;
}
static RawObject initAsyncGenHooksOnInstance(Thread* thread,
const AsyncGenerator& gen) {
if (gen.hooksInited()) {
return NoneType::object();
}
HandleScope scope(thread);
gen.setHooksInited(true);
gen.setFinalizer(thread->asyncgenHooksFinalizer());
Object first_iter(&scope, thread->asyncgenHooksFirstIter());
if (!first_iter.isNoneType()) {
Object first_iter_res(&scope, Interpreter::call1(thread, first_iter, gen));
if (first_iter_res.isErrorException()) {
return *first_iter_res;
}
}
return NoneType::object();
}
static RawObject setupAsyncGenOpIter(HandleScope& scope, Thread* thread,
RawObject raw_self_obj,
LayoutId op_layout) {
Object self_obj(&scope, raw_self_obj);
if (!self_obj.isAsyncGenerator()) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"Must be called with an async_generator "
"instance as the first argument, not %T",
&self_obj);
}
AsyncGenerator self(&scope, *self_obj);
Object init_res(&scope, initAsyncGenHooksOnInstance(thread, self));
if (init_res.isErrorException()) {
return *init_res;
}
Runtime* runtime = thread->runtime();
Layout layout(&scope, runtime->layoutAt(op_layout));
Object op_iter_obj(&scope, runtime->newInstance(layout));
AsyncGeneratorOpIterBase op_iter(&scope, *op_iter_obj);
op_iter.setGenerator(*self);
op_iter.setState(AsyncGeneratorOpIterBase::State::Init);
return *op_iter;
}
static RawObject setupAsyncGenASend(Thread* thread, RawObject raw_self_obj,
RawObject initial_send_value) {
HandleScope scope(thread);
Object asend_obj(&scope, setupAsyncGenOpIter(scope, thread, raw_self_obj,
LayoutId::kAsyncGeneratorAsend));
if (asend_obj.isErrorException()) return *asend_obj;
AsyncGeneratorAsend asend(&scope, *asend_obj);
asend.setValue(initial_send_value);
return *asend;
}
RawObject METH(async_generator, __anext__)(Thread* thread, Arguments args) {
return setupAsyncGenASend(thread, args.get(0), NoneType::object());
}
RawObject METH(async_generator, aclose)(Thread* thread, Arguments args) {
HandleScope scope(thread);
return setupAsyncGenOpIter(scope, thread, args.get(0),
LayoutId::kAsyncGeneratorAclose);
}
RawObject METH(async_generator, asend)(Thread* thread, Arguments args) {
return setupAsyncGenASend(thread, args.get(0), args.get(1));
}
RawObject METH(async_generator, athrow)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object athrow_obj(&scope,
setupAsyncGenOpIter(scope, thread, args.get(0),
LayoutId::kAsyncGeneratorAthrow));
if (athrow_obj.isErrorException()) return *athrow_obj;
AsyncGeneratorAthrow athrow(&scope, *athrow_obj);
athrow.setExceptionType(args.get(1));
athrow.setExceptionValue(args.get(2));
athrow.setExceptionTraceback(args.get(3));
return *athrow;
}
static RawObject asyncOpIterReturnSelf(Thread* thread, RawObject raw_self_obj,
LayoutId op_layout, SymbolId method,
SymbolId op_type) {
HandleScope scope(thread);
Object self(&scope, raw_self_obj);
if (!self.isHeapObjectWithLayout(op_layout)) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"%Y be called with an %Y instance as the first argument, not %T",
method, op_type, &self);
}
return *self;
}
RawObject METH(async_generator_aclose, __await__)(Thread* thread,
Arguments args) {
return asyncOpIterReturnSelf(thread, args.get(0),
LayoutId::kAsyncGeneratorAclose, ID(__await__),
ID(async_generator_aclose));
}
RawObject METH(async_generator_aclose, __iter__)(Thread* thread,
Arguments args) {
return asyncOpIterReturnSelf(thread, args.get(0),
LayoutId::kAsyncGeneratorAclose, ID(__iter__),
ID(async_generator_aclose));
}
static RawObject asyncGenAcloseSend(Thread* thread, RawObject raw_self_obj,
RawObject send_value_raw) {
HandleScope scope(thread);
Object self_obj(&scope, raw_self_obj);
Object send_value(&scope, send_value_raw);
if (!self_obj.isAsyncGeneratorAclose()) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"Must be called with an async_generator_aclose "
"instance as the first argument, not %T",
&self_obj);
}
AsyncGeneratorAclose self(&scope, *self_obj);
AsyncGeneratorOpIterBase::State state = self.state();
if (state == AsyncGeneratorOpIterBase::State::Closed) {
return thread->raiseWithFmt(
LayoutId::kRuntimeError,
"cannot reuse already awaited aclose()/athrow()");
}
// Depending on whether the close operation has been applied yet either
// throw GeneratorExit into the generator, or just send into the iterator
// to make progress through async-like yields.
Object res(&scope, NoneType::object());
AsyncGenerator generator(&scope, self.generator());
if (state == AsyncGeneratorOpIterBase::State::Init) {
if (!send_value.isNoneType()) {
return thread->raiseWithFmt(
LayoutId::kRuntimeError,
"Cannot send non-None value to async generator aclose iterator on "
"first iteration");
}
self.setState(AsyncGeneratorOpIterBase::State::Iter);
Object exception_type(&scope,
thread->runtime()->typeAt(LayoutId::kGeneratorExit));
Object none(&scope, NoneType::object());
res = throwImpl(thread, generator, exception_type, none, none);
} else {
DCHECK(state == AsyncGeneratorOpIterBase::State::Iter, "Unexpected state");
res = Interpreter::resumeGenerator(thread, generator, send_value);
}
if (res.isErrorException()) {
// If the exceptions are GeneratorExit or StopAsyncIteration, these are
// correct and expected ways for the overall async generator to stop. So
// clear the pending exceptions, mark this iterator as closed, and
// propagate a StopIteration indicating a clean shutdown. As this is a
// "close" operation the StopIteration value is always None.
if (thread->pendingExceptionMatches(LayoutId::kGeneratorExit) ||
thread->pendingExceptionMatches(LayoutId::kStopAsyncIteration)) {
self.setState(AsyncGeneratorOpIterBase::State::Closed);
thread->clearPendingException();
return thread->raiseStopIteration();
}
// Propagate all other exceptions/errors.
return *res;
}
// Producing a generator-like yield indcates the generator has ignored the
// close request.
if (res.isAsyncGeneratorWrappedValue()) {
return thread->raiseWithFmt(LayoutId::kRuntimeError,
"async generator ignored GeneratorExit");
}
// Pass along async-like yield to caller for propagation up to the event
// loop.
return *res;
}
RawObject METH(async_generator_aclose, __next__)(Thread* thread,
Arguments args) {
return asyncGenAcloseSend(thread, args.get(0), NoneType::object());
}
static RawObject closeAsyncGenOpIter(Thread* thread, RawObject raw_self_obj,
LayoutId op_layout, SymbolId op_type) {
HandleScope scope(thread);
Object self_obj(&scope, raw_self_obj);
if (!self_obj.isHeapObjectWithLayout(op_layout)) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"close() must be called with an %Y instance as "
"the first argument, not %T",
op_type, &self_obj);
}
AsyncGeneratorOpIterBase self(&scope, *self_obj);
self.setState(AsyncGeneratorOpIterBase::State::Closed);
return NoneType::object();
}
RawObject METH(async_generator_aclose, close)(Thread* thread, Arguments args) {
return closeAsyncGenOpIter(thread, args.get(0),
LayoutId::kAsyncGeneratorAclose,
ID(async_generator_aclose));
}
RawObject METH(async_generator_aclose, send)(Thread* thread, Arguments args) {
return asyncGenAcloseSend(thread, args.get(0), args.get(1));
}
RawObject METH(async_generator_aclose, throw)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isAsyncGeneratorAclose()) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"Must be called with an async_generator_aclose "
"instance as the first argument, not %T",
&self_obj);
}
AsyncGeneratorAclose self(&scope, *self_obj);
AsyncGeneratorOpIterBase::State state = self.state();
if (state == AsyncGeneratorOpIterBase::State::Closed) {
return thread->raiseWithFmt(
LayoutId::kRuntimeError,
"cannot reuse already awaited aclose()/athrow()");
}
// Throw into generator
AsyncGenerator generator(&scope, self.generator());
Object exception_type(&scope, args.get(1));
Object exception_value(&scope, args.get(2));
Object exception_traceback(&scope, args.get(3));
Object res(&scope, throwImpl(thread, generator, exception_type,
exception_value, exception_traceback));
// Getting a generator-like yield means the generator ignored the close
// operation.
if (res.isAsyncGeneratorWrappedValue()) {
return thread->raiseWithFmt(LayoutId::kRuntimeError,
"async generator ignored GeneratorExit");
}
if (res.isErrorException() &&
(thread->pendingExceptionMatches(LayoutId::kStopAsyncIteration) ||
thread->pendingExceptionMatches(LayoutId::kGeneratorExit))) {
thread->clearPendingException();
return thread->raiseStopIteration();
}
// Propagate async-like yield.
return *res;
}
RawObject METH(async_generator_asend, __await__)(Thread* thread,
Arguments args) {
return asyncOpIterReturnSelf(thread, args.get(0),
LayoutId::kAsyncGeneratorAsend, ID(__await__),
ID(async_generator_asend));
}
RawObject METH(async_generator_asend, __iter__)(Thread* thread,
Arguments args) {
return asyncOpIterReturnSelf(thread, args.get(0),
LayoutId::kAsyncGeneratorAsend, ID(__iter__),
ID(async_generator_asend));
}
static RawObject asyncGenAsendSend(Thread* thread, RawObject raw_self_obj,
RawObject send_value_raw) {
HandleScope scope(thread);
Object self_obj(&scope, raw_self_obj);
Object send_value(&scope, send_value_raw);
if (!self_obj.isAsyncGeneratorAsend()) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"Must be called with an async_generator_asend "
"instance as the first argument, not %T",
&self_obj);
}
AsyncGeneratorAsend self(&scope, *self_obj);
AsyncGeneratorOpIterBase::State state = self.state();
if (state == AsyncGeneratorOpIterBase::State::Closed) {
return thread->raiseWithFmt(
LayoutId::kRuntimeError,
"cannot reuse already awaited __anext__()/asend()");
}
// Only use primed value for initial send, and only if no other specific value
// is provided.
if (state == AsyncGeneratorOpIterBase::State::Init) {
if (send_value.isNoneType()) {
send_value = self.value();
}
}
// "Send" value into generator
AsyncGenerator generator(&scope, self.generator());
Object send_res(&scope,
Interpreter::resumeGenerator(thread, generator, send_value));
// Send raises: mark this ASend operation as closed and propagate.
if (send_res.isErrorException()) {
self.setState(AsyncGeneratorOpIterBase::State::Closed);
return *send_res;
}
// Send produces a generator-like yield: mark this ASend operation as closed
// and return the value via a StopIteration raise.
if (send_res.isAsyncGeneratorWrappedValue()) {
self.setState(AsyncGeneratorOpIterBase::State::Closed);
AsyncGeneratorWrappedValue res_wrapped(&scope, *send_res);
Object res_value(&scope, res_wrapped.value());
return thread->raiseStopIterationWithValue(res_value);
}
// Send produces an async-like yield: pass this along to caller to propagate
// up to the event loop.
self.setState(AsyncGeneratorOpIterBase::State::Iter);
return *send_res;
}
RawObject METH(async_generator_asend, __next__)(Thread* thread,
Arguments args) {
return asyncGenAsendSend(thread, args.get(0), NoneType::object());
}
RawObject METH(async_generator_asend, close)(Thread* thread, Arguments args) {
return closeAsyncGenOpIter(thread, args.get(0),
LayoutId::kAsyncGeneratorAsend,
ID(async_generator_asend));
}
RawObject METH(async_generator_asend, send)(Thread* thread, Arguments args) {
return asyncGenAsendSend(thread, args.get(0), args.get(1));
}
RawObject METH(async_generator_asend, throw)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isAsyncGeneratorAsend()) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"Must be called with an async_generator_asend "
"instance as the first argument, not %T",
&self_obj);
}
AsyncGeneratorAsend self(&scope, *self_obj);
AsyncGeneratorOpIterBase::State state = self.state();
if (state == AsyncGeneratorOpIterBase::State::Closed) {
return thread->raiseWithFmt(
LayoutId::kRuntimeError,
"cannot reuse already awaited __anext__()/asend()");
}
// Throw into generator
AsyncGenerator generator(&scope, self.generator());
Object exception_type(&scope, args.get(1));
Object exception_value(&scope, args.get(2));
Object exception_traceback(&scope, args.get(3));
Object res(&scope, throwImpl(thread, generator, exception_type,
exception_value, exception_traceback));
// Propagate any uncaught exceptions and mark this send operation as closed.
if (res.isError()) {
self.setState(AsyncGeneratorOpIterBase::State::Closed);
return *res;
}
// Generator-like yield: raise this in a StopIteration and mark this iterator
// as closed.
if (res.isAsyncGeneratorWrappedValue()) {
self.setState(AsyncGeneratorOpIterBase::State::Closed);
AsyncGeneratorWrappedValue wrapped_value(&scope, *res);
Object value(&scope, wrapped_value.value());
return thread->raiseStopIterationWithValue(value);
}
// Async-like yield: mark this iterator as being in the iteration state and
// pass the result to the caller for propagation to the event-loop.
self.setState(AsyncGeneratorOpIterBase::State::Iter);
return *res;
}
RawObject METH(async_generator_athrow, __await__)(Thread* thread,
Arguments args) {
return asyncOpIterReturnSelf(thread, args.get(0),
LayoutId::kAsyncGeneratorAthrow, ID(__await__),
ID(async_generator_athrow));
}
RawObject METH(async_generator_athrow, __iter__)(Thread* thread,
Arguments args) {
return asyncOpIterReturnSelf(thread, args.get(0),
LayoutId::kAsyncGeneratorAthrow, ID(__iter__),
ID(async_generator_athrow));
}
static RawObject asyncGenAthrowSend(Thread* thread, RawObject raw_self_obj,
RawObject send_value_raw) {
HandleScope scope(thread);
Object self_obj(&scope, raw_self_obj);
Object send_value(&scope, send_value_raw);
if (!self_obj.isAsyncGeneratorAthrow()) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"Must be called with an async_generator_athrow "
"instance as the first argument, not %T",
&self_obj);
}
AsyncGeneratorAthrow self(&scope, *self_obj);
AsyncGeneratorOpIterBase::State state = self.state();
if (state == AsyncGeneratorOpIterBase::State::Closed) {
return thread->raiseWithFmt(
LayoutId::kRuntimeError,
"cannot reuse already awaited aclose()/athrow()");
}
// Depending on whether the throw operation has been applied yet either
// implement the throw, or just send into the iterator to make progress
// through async-like yields.
Object res(&scope, NoneType::object());
AsyncGenerator generator(&scope, self.generator());
if (state == AsyncGeneratorOpIterBase::State::Init) {
if (!send_value.isNoneType()) {
return thread->raiseWithFmt(
LayoutId::kRuntimeError,
"Cannot send non-None value to async generator athrow iterator on "
"first iteration");
}
self.setState(AsyncGeneratorOpIterBase::State::Iter);
Object exception_type(&scope, self.exceptionType());
Object exception_value(&scope, self.exceptionValue());
Object exception_traceback(&scope, self.exceptionTraceback());
res = throwImpl(thread, generator, exception_type, exception_value,
exception_traceback);
// Handle StopAsyncIteration and GeneratorExit exceptions raised here.
// Other exceptions are handled further down.
if (res.isErrorException()) {
if (thread->pendingExceptionMatches(LayoutId::kStopAsyncIteration) ||
thread->pendingExceptionMatches(LayoutId::kGeneratorExit)) {
self.setState(AsyncGeneratorOpIterBase::State::Closed);
return *res;
}
}
} else {
DCHECK(state == AsyncGeneratorOpIterBase::State::Iter, "Unexpected state");
res = Interpreter::resumeGenerator(thread, generator, send_value);
}
// Propagate all unhandled exceptions from send or throw operation.
if (res.isErrorException()) return *res;
// Generator-like yield: raise this in a StopIteration and mark this iterator
// as closed.
if (res.isAsyncGeneratorWrappedValue()) {
// Note we don't move into the "closed" state here as we would in an asend
// iterator. I'm not sure why, but this is the CPython behavior.
AsyncGeneratorWrappedValue wrapped_value(&scope, *res);
Object value(&scope, wrapped_value.value());
return thread->raiseStopIterationWithValue(value);
}
// Async-like yield: pass the result to the caller for propagation to the
// event-loop.
return *res;
}
RawObject METH(async_generator_athrow, __next__)(Thread* thread,
Arguments args) {
return asyncGenAthrowSend(thread, args.get(0), NoneType::object());
}
RawObject METH(async_generator_athrow, close)(Thread* thread, Arguments args) {
return closeAsyncGenOpIter(thread, args.get(0),
LayoutId::kAsyncGeneratorAthrow,
ID(async_generator_athrow));
}
RawObject METH(async_generator_athrow, send)(Thread* thread, Arguments args) {
return asyncGenAthrowSend(thread, args.get(0), args.get(1));
}
RawObject METH(async_generator_athrow, throw)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isAsyncGeneratorAthrow()) {
return thread->raiseWithFmt(LayoutId::kTypeError,
"Must be called with an async_generator_athrow "
"instance as the first argument, not %T",
&self_obj);
}
AsyncGeneratorAthrow self(&scope, *self_obj);
AsyncGeneratorOpIterBase::State state = self.state();
if (state == AsyncGeneratorOpIterBase::State::Closed) {
return thread->raiseWithFmt(
LayoutId::kRuntimeError,
"cannot reuse already awaited aclose()/athrow()");
}
// Throw into generator
AsyncGenerator generator(&scope, self.generator());
Object exception_type(&scope, args.get(1));
Object exception_value(&scope, args.get(2));
Object exception_traceback(&scope, args.get(3));
Object res(&scope, throwImpl(thread, generator, exception_type,
exception_value, exception_traceback));
// Propagate any uncaught exceptions.
if (res.isError()) return *res;
// Generator-like yield: raise this in a StopIteration
if (res.isAsyncGeneratorWrappedValue()) {
AsyncGeneratorWrappedValue wrapped_value(&scope, *res);
Object value(&scope, wrapped_value.value());
return thread->raiseStopIterationWithValue(value);
}
// Async-like yield: pass result to the caller for propagation to the
// event-loop.
return *res;
}
RawObject METH(generator, __iter__)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
if (!self.isGenerator()) {
return thread->raiseWithFmt(
LayoutId::kAttributeError,
"__iter__() must be called with a generator instance as the first "
"argument");
}
return *self;
}
RawObject METH(generator, __next__)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
if (!self.isGenerator()) {
return thread->raiseRequiresType(self, ID(generator));
}
Generator gen(&scope, *self);
Object value(&scope, NoneType::object());
return Interpreter::resumeGenerator(thread, gen, value);
}
RawObject METH(generator, close)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
if (!self.isGenerator()) {
return thread->raiseRequiresType(self, ID(generator));
}
GeneratorBase gen(&scope, *self);
return closeImpl(thread, gen);
}
RawObject generatorSend(Thread* thread, const Object& self_obj,
const Object& value) {
return sendImpl<ID(generator), LayoutId::kGenerator>(thread, *self_obj,
*value);
}
RawObject METH(generator, send)(Thread* thread, Arguments args) {
return sendImpl<ID(generator), LayoutId::kGenerator>(thread, args.get(0),
args.get(1));
}
RawObject METH(generator, throw)(Thread* thread, Arguments args) {
return throwImpl<ID(generator), LayoutId::kGenerator>(
thread, args.get(0), args.get(1), args.get(2), args.get(3));
}
RawObject METH(coroutine, __await__)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isCoroutine()) {
return thread->raiseRequiresType(self_obj, ID(coroutine));
}
Coroutine self(&scope, *self_obj);
Runtime* runtime = thread->runtime();
Layout coro_wrap_layout(&scope,
runtime->layoutAt(LayoutId::kCoroutineWrapper));
CoroutineWrapper coro_wrap(&scope, runtime->newInstance(coro_wrap_layout));
coro_wrap.setCoroutine(*self);
return *coro_wrap;
}
RawObject METH(coroutine, close)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self(&scope, args.get(0));
if (!self.isCoroutine()) {
return thread->raiseRequiresType(self, ID(coroutine));
}
GeneratorBase gen(&scope, *self);
return closeImpl(thread, gen);
}
RawObject coroutineSend(Thread* thread, const Object& self,
const Object& value) {
return sendImpl<ID(coroutine), LayoutId::kCoroutine>(thread, *self, *value);
}
RawObject METH(coroutine, send)(Thread* thread, Arguments args) {
return sendImpl<ID(coroutine), LayoutId::kCoroutine>(thread, args.get(0),
args.get(1));
}
RawObject METH(coroutine, throw)(Thread* thread, Arguments args) {
return throwImpl<ID(coroutine), LayoutId::kCoroutine>(
thread, args.get(0), args.get(1), args.get(2), args.get(3));
}
RawObject METH(coroutine_wrapper, __iter__)(Thread* thread, Arguments args) {
RawObject self = args.get(0);
if (self.isCoroutineWrapper()) return self;
HandleScope scope(thread);
Object self_obj(&scope, self);
return thread->raiseRequiresType(self_obj, ID(coroutine));
}
RawObject METH(coroutine_wrapper, __next__)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isCoroutineWrapper()) {
return thread->raiseRequiresType(self_obj, ID(coroutine));
}
CoroutineWrapper self(&scope, *self_obj);
GeneratorBase gen(&scope, self.coroutine());
Object none(&scope, NoneType::object());
return Interpreter::resumeGenerator(thread, gen, none);
}
RawObject METH(coroutine_wrapper, close)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isCoroutineWrapper()) {
return thread->raiseRequiresType(self_obj, ID(coroutine));
}
CoroutineWrapper self(&scope, *self_obj);
GeneratorBase gen(&scope, self.coroutine());
return closeImpl(thread, gen);
}
RawObject METH(coroutine_wrapper, send)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isCoroutineWrapper()) {
return thread->raiseRequiresType(self_obj, ID(coroutine));
}
CoroutineWrapper self(&scope, *self_obj);
GeneratorBase gen(&scope, self.coroutine());
Object val(&scope, args.get(1));
return Interpreter::resumeGenerator(thread, gen, val);
}
RawObject METH(coroutine_wrapper, throw)(Thread* thread, Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isCoroutineWrapper()) {
return thread->raiseRequiresType(self_obj, ID(coroutine));
}
CoroutineWrapper self(&scope, *self_obj);
GeneratorBase gen(&scope, self.coroutine());
Object exc(&scope, args.get(1));
Object value(&scope, args.get(2));
Object tb(&scope, args.get(3));
return throwImpl(thread, gen, exc, value, tb);
}
// Intended for tests only
RawObject FUNC(_builtins, _async_generator_finalizer)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isAsyncGenerator()) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"_async_generator_finalizer() must be called with an "
"async_generator instance as the first argument, not %T",
&self_obj);
}
AsyncGenerator self(&scope, *self_obj);
return self.finalizer();
}
// Intended for tests only
RawObject FUNC(_builtins, _async_generator_op_iter_get_state)(Thread* thread,
Arguments args) {
HandleScope scope(thread);
Object self_obj(&scope, args.get(0));
if (!self_obj.isAsyncGeneratorOpIterBase()) {
return thread->raiseWithFmt(
LayoutId::kTypeError,
"_async_generator_op_iter_get_state() must be called with an "
"async_generator_op_iter_base instance as the first argument, not %T",
&self_obj);
}
AsyncGeneratorOpIterBase self(&scope, *self_obj);
return RawSmallInt::fromWord(static_cast<word>(self.state()));
}
} // namespace py