Skip to content

Commit c381649

Browse files
committed
[GR-23234][GR-23272] Implement check for running generator and gi_running
PullRequest: graalpython/1033
2 parents ab7d4dd + 054d0ef commit c381649

File tree

5 files changed

+91
-12
lines changed

5 files changed

+91
-12
lines changed

graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_generators.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
*graalpython.lib-python.3.test.test_generators.ExceptionTest.test_except_gen_except
22
*graalpython.lib-python.3.test.test_generators.ExceptionTest.test_except_next
33
*graalpython.lib-python.3.test.test_generators.ExceptionTest.test_except_throw
4+
*graalpython.lib-python.3.test.test_generators.ExceptionTest.test_except_throw_exception_context
45
*graalpython.lib-python.3.test.test_generators.ExceptionTest.test_return_stopiteration
56
*graalpython.lib-python.3.test.test_generators.ExceptionTest.test_return_tuple
67
*graalpython.lib-python.3.test.test_generators.ExceptionTest.test_stopiteration_error
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_attempted_yield_from_loop
2+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_attempting_to_send_to_non_generator
3+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_catching_exception_from_subgen_and_returning
4+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_close_with_cleared_frame
5+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_conversion_of_sendNone_to_next
6+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_custom_iterator_return
7+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_delegating_throw
8+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_delegating_throw_to_non_generator
9+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_delegation_of_initial_next_to_subgenerator
10+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_delegation_of_next_call_to_subgenerator
11+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_delegation_of_next_to_non_generator
12+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_delegation_of_send
13+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_delegator_is_visible_to_debugger
14+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_exception_in_initial_next_call
15+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_exception_value_crash
16+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_generator_return_value
17+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_handling_exception_while_delegating_send
18+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_next_and_return_with_value
19+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_raising_exception_in_delegated_next_call
20+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_raising_exception_in_initial_next_call
21+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_returning_value_from_delegated_throw
22+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_send_and_return_with_value
23+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_send_tuple_with_custom_generator
24+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_throwing_GeneratorExit_into_subgen_that_returns
25+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_value_attribute_of_StopIteration_exception
26+
*graalpython.lib-python.3.test.test_yield_from.TestPEP380Operation.test_yield_from_empty

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/GeneratorBuiltins.java

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import static com.oracle.graal.python.nodes.SpecialMethodNames.__REPR__;
3131
import static com.oracle.graal.python.runtime.exception.PythonErrorType.StopIteration;
3232
import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError;
33+
import static com.oracle.graal.python.runtime.exception.PythonErrorType.ValueError;
3334

3435
import java.util.List;
3536

@@ -113,13 +114,16 @@ private static Object[] prepareArguments(PGenerator self) {
113114
return arguments;
114115
}
115116

116-
private static Object resumeGenerator(PGenerator self) {
117+
private static Object resumeGenerator(PGenerator self, Object sendValue) {
117118
try {
119+
self.setRunning(true);
120+
PArguments.setSpecialArgument(self.getArguments(), sendValue);
118121
return self.getCurrentCallTarget().call(prepareArguments(self));
119122
} catch (PException e) {
120123
self.markAsFinished();
121124
throw e;
122125
} finally {
126+
self.setRunning(false);
123127
self.setNextCallTarget();
124128
PArguments.setSpecialArgument(self.getArguments(), null);
125129
}
@@ -159,32 +163,46 @@ protected static boolean sameCallTarget(RootCallTarget target1, CallTarget targe
159163

160164
@Specialization(guards = "sameCallTarget(self.getCurrentCallTarget(), call.getCallTarget())", limit = "getCallSiteInlineCacheMaxDepth()")
161165
public Object nextCached(VirtualFrame frame, PGenerator self,
162-
@Cached("createDirectCall(self.getCurrentCallTarget())") CallTargetInvokeNode call) {
166+
@Cached("createDirectCall(self.getCurrentCallTarget())") CallTargetInvokeNode call,
167+
@Cached BranchProfile alreadyRunning) {
163168
if (self.isFinished()) {
164169
throw raise(StopIteration);
165170
}
171+
if (self.isRunning()) {
172+
alreadyRunning.enter();
173+
throw raise(ValueError, ErrorMessages.GENERATOR_ALREADY_EXECUTING);
174+
}
166175
try {
176+
self.setRunning(true);
167177
return call.execute(frame, null, null, prepareArguments(self));
168178
} catch (PException e) {
169179
self.markAsFinished();
170180
throw e;
171181
} finally {
182+
self.setRunning(false);
172183
self.setNextCallTarget();
173184
}
174185
}
175186

176187
@Specialization(replaces = "nextCached")
177188
public Object next(VirtualFrame frame, PGenerator self,
178-
@Cached("createIndirectCall()") GenericInvokeNode call) {
189+
@Cached("createIndirectCall()") GenericInvokeNode call,
190+
@Cached BranchProfile alreadyRunning) {
179191
if (self.isFinished()) {
180192
throw raise(StopIteration);
181193
}
194+
if (self.isRunning()) {
195+
alreadyRunning.enter();
196+
throw raise(ValueError, ErrorMessages.GENERATOR_ALREADY_EXECUTING);
197+
}
182198
try {
199+
self.setRunning(true);
183200
return call.execute(frame, self.getCurrentCallTarget(), prepareArguments(self));
184201
} catch (PException e) {
185202
self.markAsFinished();
186203
throw e;
187204
} finally {
205+
self.setRunning(false);
188206
self.setNextCallTarget();
189207
}
190208
}
@@ -195,9 +213,13 @@ public Object next(VirtualFrame frame, PGenerator self,
195213
public abstract static class SendNode extends PythonBuiltinNode {
196214

197215
@Specialization
198-
public Object send(PGenerator self, Object value) {
199-
PArguments.setSpecialArgument(self.getArguments(), value);
200-
return resumeGenerator(self);
216+
public Object send(PGenerator self, Object value,
217+
@Cached BranchProfile alreadyRunning) {
218+
if (self.isRunning()) {
219+
alreadyRunning.enter();
220+
throw raise(ValueError, ErrorMessages.GENERATOR_ALREADY_EXECUTING);
221+
}
222+
return resumeGenerator(self, value);
201223
}
202224
}
203225

@@ -308,14 +330,24 @@ private void checkExceptionClass(LazyPythonClass type) {
308330

309331
@Specialization
310332
Object sendThrow(VirtualFrame frame, PGenerator self, Object typ, Object val, @SuppressWarnings("unused") PNone tb,
311-
@Cached PrepareExceptionNode prepareExceptionNode) {
333+
@Cached PrepareExceptionNode prepareExceptionNode,
334+
@Cached BranchProfile alreadyRunning) {
335+
if (self.isRunning()) {
336+
alreadyRunning.enter();
337+
throw raise(ValueError, ErrorMessages.GENERATOR_ALREADY_EXECUTING);
338+
}
312339
PBaseException instance = prepareExceptionNode.execute(frame, typ, val);
313340
return doThrow(self, instance);
314341
}
315342

316343
@Specialization
317344
Object sendThrow(VirtualFrame frame, PGenerator self, Object typ, Object val, PTraceback tb,
318-
@Cached PrepareExceptionNode prepareExceptionNode) {
345+
@Cached PrepareExceptionNode prepareExceptionNode,
346+
@Cached BranchProfile alreadyRunning) {
347+
if (self.isRunning()) {
348+
alreadyRunning.enter();
349+
throw raise(ValueError, ErrorMessages.GENERATOR_ALREADY_EXECUTING);
350+
}
319351
PBaseException instance = prepareExceptionNode.execute(frame, typ, val);
320352
instance.setTraceback(tb);
321353
return doThrow(self, instance);
@@ -328,8 +360,7 @@ private Object doThrow(PGenerator self, PBaseException instance) {
328360
// Pass it to the generator where it will be thrown by the last yield, the location
329361
// will be filled there
330362
PException pException = PException.fromObject(instance, null);
331-
PArguments.setSpecialArgument(self.getArguments(), pException);
332-
return resumeGenerator(self);
363+
return resumeGenerator(self, pException);
333364
} else {
334365
// Unstarted generator, we cannot pass the exception into the generator as there is
335366
// nothing that would handle it.
@@ -373,7 +404,7 @@ private GetTracebackNode ensureGetTracebackNode() {
373404

374405
@Builtin(name = "gi_code", minNumOfPositionalArgs = 1, isGetter = true)
375406
@GenerateNodeFactory
376-
public abstract static class GetCodeNode extends PythonBuiltinNode {
407+
public abstract static class GetCodeNode extends PythonUnaryBuiltinNode {
377408
@Specialization
378409
Object getCode(PGenerator self,
379410
@Cached("createBinaryProfile()") ConditionProfile hasCodeProfile) {
@@ -386,6 +417,15 @@ Object getCode(PGenerator self,
386417
}
387418
}
388419

420+
@Builtin(name = "gi_running", minNumOfPositionalArgs = 1, isGetter = true)
421+
@GenerateNodeFactory
422+
public abstract static class GetRunningNode extends PythonUnaryBuiltinNode {
423+
@Specialization
424+
static Object getRunning(PGenerator self) {
425+
return self.isRunning();
426+
}
427+
}
428+
389429
@Builtin(name = __REPR__, minNumOfPositionalArgs = 1)
390430
@GenerateNodeFactory
391431
abstract static class ReprNode extends PythonUnaryBuiltinNode {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/PGenerator.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ public final class PGenerator extends PythonBuiltinObject {
6464
private int currentCallTarget;
6565
private final Object iterator;
6666
private final boolean isPRangeIterator;
67+
// running means it is currently on the stack, not just started
68+
private boolean running;
6769

6870
public static PGenerator create(String name, RootCallTarget[] callTargets, FrameDescriptor frameDescriptor, Object[] arguments, PCell[] closure,
6971
ExecutionCellSlots cellSlots, int numOfActiveFlags, int numOfGeneratorBlockNode, int numOfGeneratorForNode, int numOfGeneratorTryNode, PythonObjectFactory factory,
@@ -143,7 +145,7 @@ public RootCallTarget getCurrentCallTarget() {
143145
}
144146

145147
public boolean isStarted() {
146-
return currentCallTarget != 0;
148+
return currentCallTarget != 0 && !running;
147149
}
148150

149151
public Object[] getArguments() {
@@ -190,4 +192,13 @@ public PCode getCode() {
190192
public void setCode(PCode code) {
191193
this.code = code;
192194
}
195+
196+
public boolean isRunning() {
197+
return running;
198+
}
199+
200+
public void setRunning(boolean running) {
201+
assert !running || !this.running : "Attempted to set an already running generator as running";
202+
this.running = running;
203+
}
193204
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ public abstract class ErrorMessages {
213213
public static final String FUNC_TAKES_AT_LEAST_D_ARGS = "function takes at least %d arguments (%d given)";
214214
public static final String FUNC_TAKES_EXACTLY_D_ARGS = "function takes exaclty %d arguments (%d given)";
215215
public static final String GENERATOR_RAISED_STOPITER = "generator raised StopIteration";
216+
public static final String GENERATOR_ALREADY_EXECUTING = "generator already executing";
216217
public static final String GETTING_THER_SOURCE_NOT_SUPPORTED_FOR_P = "getting the source is not supported for '%p'";
217218
public static final String GLOBALS_MUST_BE_DICT = "%s() globals must be a dict, not %p";
218219
public static final String GOT_INVALID_CODESTRING = "got an invalid codestring trying to create a function code object";

0 commit comments

Comments
 (0)