Skip to content

Commit 52fdc80

Browse files
committed
Handle StopIteration from a generator
1 parent 5d54794 commit 52fdc80

File tree

5 files changed

+53
-32
lines changed

5 files changed

+53
-32
lines changed

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

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,7 @@ abstract static class ResumeGeneratorNode extends Node {
143143

144144
@Specialization(guards = "sameCallTarget(self.getCurrentCallTarget(), call.getCallTarget())", limit = "getCallSiteInlineCacheMaxDepth()")
145145
Object cached(VirtualFrame frame, PGenerator self, Object sendValue,
146-
@Cached("createDirectCall(self.getCurrentCallTarget())") CallTargetInvokeNode call,
147-
@Cached PRaiseNode raiseNode) {
146+
@Cached("createDirectCall(self.getCurrentCallTarget())") CallTargetInvokeNode call) {
148147
self.setRunning(true);
149148
Object[] arguments = prepareArguments(self);
150149
if (sendValue != null) {
@@ -153,9 +152,6 @@ Object cached(VirtualFrame frame, PGenerator self, Object sendValue,
153152
Object result;
154153
try {
155154
result = call.execute(frame, null, null, null, arguments);
156-
if (result == null) {
157-
self.markAsFinished();
158-
}
159155
} catch (PException e) {
160156
self.markAsFinished();
161157
throw e;
@@ -165,16 +161,14 @@ Object cached(VirtualFrame frame, PGenerator self, Object sendValue,
165161
self.setNextCallTarget(PythonLanguage.get(this));
166162
}
167163
}
168-
handleResult(self, result, raiseNode);
169164
return result;
170165
}
171166

172167
@Specialization(replaces = "cached")
173168
@Megamorphic
174169
Object generic(VirtualFrame frame, PGenerator self, Object sendValue,
175170
@Cached ConditionProfile hasFrameProfile,
176-
@Cached GenericInvokeNode call,
177-
@Cached PRaiseNode raiseNode) {
171+
@Cached GenericInvokeNode call) {
178172
self.setRunning(true);
179173
Object[] arguments = prepareArguments(self);
180174
if (sendValue != null) {
@@ -187,9 +181,6 @@ Object generic(VirtualFrame frame, PGenerator self, Object sendValue,
187181
} else {
188182
result = call.execute(self.getCurrentCallTarget(), arguments);
189183
}
190-
if (result == null) {
191-
self.markAsFinished();
192-
}
193184
} catch (PException e) {
194185
self.markAsFinished();
195186
throw e;
@@ -199,21 +190,9 @@ Object generic(VirtualFrame frame, PGenerator self, Object sendValue,
199190
self.setNextCallTarget(PythonLanguage.get(this));
200191
}
201192
}
202-
handleResult(self, result, raiseNode);
203193
return result;
204194
}
205195

206-
private static void handleResult(PGenerator self, Object result, PRaiseNode raiseNode) {
207-
if (result == null) {
208-
Object returnValue = self.getReturnValue();
209-
if (returnValue != PNone.NONE) {
210-
throw raiseNode.raise(StopIteration, returnValue);
211-
} else {
212-
throw raiseNode.raise(StopIteration);
213-
}
214-
}
215-
}
216-
217196
protected static CallTargetInvokeNode createDirectCall(CallTarget target) {
218197
return CallTargetInvokeNode.create(target, false, true);
219198
}

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ public void setNextCallTarget(PythonLanguage language) {
182182
/**
183183
* Returns the call target that should be used the next time the generator is called. Each time
184184
* a generator call target returns through a yield, the generator should be updated with the
185-
* next yield index to use via {@link #setNextCallTarget()}
185+
* next yield index to use via {@link #setNextCallTarget}
186186
*/
187187
public RootCallTarget getCurrentCallTarget() {
188188
return callTargets[currentCallTarget];
@@ -230,10 +230,6 @@ public int getStackTop() {
230230
return frameInfo.getGeneratorStackTop(PArguments.getGeneratorFrame(arguments));
231231
}
232232

233-
public Object getReturnValue() {
234-
return frameInfo.getGeneratorReturnValue(PArguments.getGeneratorFrame(arguments));
235-
}
236-
237233
public Object[] getArguments() {
238234
return arguments;
239235
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/PBytecodeGeneratorFunctionRootNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public PBytecodeGeneratorFunctionRootNode(PythonLanguage language, FrameDescript
6969
this.originalName = originalName;
7070
// TODO compress somehow
7171
this.callTargets = new RootCallTarget[rootNode.getCodeUnit().code.length];
72-
this.callTargets[0] = rootNode.getCallTarget();
72+
this.callTargets[0] = new PBytecodeGeneratorRootNode(language, rootNode, 0, rootNode.getInitialStackTop()).getCallTarget();
7373
}
7474

7575
@Override

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/PBytecodeGeneratorRootNode.java

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,25 @@
4040
*/
4141
package com.oracle.graal.python.nodes.bytecode;
4242

43+
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.RuntimeError;
44+
import static com.oracle.graal.python.runtime.exception.PythonErrorType.StopIteration;
45+
4346
import com.oracle.graal.python.PythonLanguage;
47+
import com.oracle.graal.python.builtins.objects.PNone;
48+
import com.oracle.graal.python.builtins.objects.function.PArguments;
4449
import com.oracle.graal.python.builtins.objects.function.Signature;
50+
import com.oracle.graal.python.nodes.ErrorMessages;
51+
import com.oracle.graal.python.nodes.PRaiseNode;
4552
import com.oracle.graal.python.nodes.PRootNode;
53+
import com.oracle.graal.python.nodes.object.IsBuiltinClassProfile;
4654
import com.oracle.graal.python.runtime.ExecutionContext;
55+
import com.oracle.graal.python.runtime.exception.PException;
4756
import com.oracle.truffle.api.CompilerAsserts;
57+
import com.oracle.truffle.api.CompilerDirectives;
4858
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
59+
import com.oracle.truffle.api.frame.MaterializedFrame;
4960
import com.oracle.truffle.api.frame.VirtualFrame;
61+
import com.oracle.truffle.api.profiles.ConditionProfile;
5062
import com.oracle.truffle.api.source.SourceSection;
5163

5264
public class PBytecodeGeneratorRootNode extends PRootNode {
@@ -55,6 +67,10 @@ public class PBytecodeGeneratorRootNode extends PRootNode {
5567
private final int resumeStackTop;
5668

5769
@Child private ExecutionContext.CalleeContext calleeContext = ExecutionContext.CalleeContext.create();
70+
@Child private IsBuiltinClassProfile errorProfile;
71+
@Child private PRaiseNode raise = PRaiseNode.create();
72+
73+
private final ConditionProfile returnProfile = ConditionProfile.create();
5874

5975
@TruffleBoundary
6076
public PBytecodeGeneratorRootNode(PythonLanguage language, PBytecodeRootNode rootNode, int resumeBci, int resumeStackTop) {
@@ -67,11 +83,29 @@ public PBytecodeGeneratorRootNode(PythonLanguage language, PBytecodeRootNode roo
6783
@Override
6884
public Object execute(VirtualFrame frame) {
6985
calleeContext.enter(frame);
86+
Object result;
7087
try {
71-
return rootNode.executeFromBci(frame, resumeBci, resumeStackTop);
88+
result = rootNode.executeFromBci(frame, resumeBci, resumeStackTop);
89+
} catch (PException pe) {
90+
// PEP 479 - StopIteration raised from generator body needs to be wrapped in
91+
// RuntimeError
92+
pe.expectStopIteration(getErrorProfile());
93+
throw raise.raise(RuntimeError, pe.setCatchingFrameAndGetEscapedException(frame, this), ErrorMessages.GENERATOR_RAISED_STOPITER);
7294
} finally {
7395
calleeContext.exit(frame, this);
7496
}
97+
if (returnProfile.profile(result == null)) {
98+
// Null result indicates a generator return
99+
MaterializedFrame generatorFrame = PArguments.getGeneratorFrame(frame);
100+
PBytecodeRootNode.FrameInfo info = (PBytecodeRootNode.FrameInfo) generatorFrame.getFrameDescriptor().getInfo();
101+
Object returnValue = info.getGeneratorReturnValue(generatorFrame);
102+
if (returnValue != PNone.NONE) {
103+
throw raise.raise(StopIteration, returnValue);
104+
} else {
105+
throw raise.raise(StopIteration);
106+
}
107+
}
108+
return result;
75109
}
76110

77111
@Override
@@ -99,4 +133,12 @@ public boolean isPythonInternal() {
99133
public SourceSection getSourceSection() {
100134
return rootNode.getSourceSection();
101135
}
136+
137+
private IsBuiltinClassProfile getErrorProfile() {
138+
if (errorProfile == null) {
139+
CompilerDirectives.transferToInterpreterAndInvalidate();
140+
errorProfile = insert(IsBuiltinClassProfile.create());
141+
}
142+
return errorProfile;
143+
}
102144
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/PBytecodeRootNode.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ public void createGeneratorFrame(Object[] arguments) {
770770
PArguments.setGeneratorFrameLocals(generatorFrameArguments, factory.createDictLocals(generatorFrame));
771771
// This is just for inspection, unstarted generators should have bci == -1
772772
generatorFrame.setInt(bcioffset, -1);
773-
generatorFrame.setInt(generatorStackTopOffset, stackoffset - 1);
773+
generatorFrame.setInt(generatorStackTopOffset, -1);
774774
copyArgsAndCells(generatorFrame, arguments);
775775
}
776776

@@ -787,6 +787,10 @@ private void copyArgsAndCells(Frame localFrame, Object[] arguments) {
787787
initFreeVars(localFrame, arguments);
788788
}
789789

790+
int getInitialStackTop() {
791+
return stackoffset - 1;
792+
}
793+
790794
@Override
791795
public Object execute(VirtualFrame virtualFrame) {
792796
calleeContext.enter(virtualFrame);
@@ -795,7 +799,7 @@ public Object execute(VirtualFrame virtualFrame) {
795799
copyArgsAndCells(virtualFrame, virtualFrame.getArguments());
796800
}
797801

798-
return executeFromBci(virtualFrame, 0, stackoffset - 1);
802+
return executeFromBci(virtualFrame, 0, getInitialStackTop());
799803
} finally {
800804
calleeContext.exit(virtualFrame, this);
801805
}

0 commit comments

Comments
 (0)