Skip to content

Commit c0ed366

Browse files
committed
Fix: correctly support inherited locals.
1 parent 1a86bd7 commit c0ed366

File tree

1 file changed

+39
-8
lines changed

1 file changed

+39
-8
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/MaterializeFrameNode.java

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
4444
import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes.SetItemNode;
45+
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
4546
import com.oracle.graal.python.builtins.objects.common.LocalsStorage;
4647
import com.oracle.graal.python.builtins.objects.dict.PDict;
4748
import com.oracle.graal.python.builtins.objects.frame.PFrame;
@@ -128,7 +129,7 @@ static PFrame freshPFrameInClassBody(VirtualFrame frame, Node location, boolean
128129
static PFrame incompleteFrame(VirtualFrame frame, Node location, boolean markAsEscaped, @SuppressWarnings("unused") boolean forceSync, Frame frameToMaterialize,
129130
@Shared("factory") @Cached PythonObjectFactory factory,
130131
@Shared("syncValuesNode") @Cached SyncFrameValuesNode syncValuesNode) {
131-
PDict locals = factory.createDictLocals(frameToMaterialize.getFrameDescriptor());
132+
Object locals = getPFrame(frameToMaterialize).getLocalsDict();
132133
PFrame escapedFrame = factory.createPFrame(PArguments.getCurrentFrameInfo(frameToMaterialize), location, locals, inClassBody(frameToMaterialize));
133134
return doEscapeFrame(frame, frameToMaterialize, escapedFrame, markAsEscaped, forceSync, syncValuesNode);
134135
}
@@ -206,12 +207,30 @@ protected static PFrame getPFrame(Frame frame) {
206207
return PArguments.getCurrentFrameInfo(frame).getPyFrame();
207208
}
208209

210+
/**
211+
* When refreshing the frame values in the locals dict, there are 4 cases:
212+
* <ol>
213+
* <li>The locals object is a {@code dict} with a {@code LocalsStorage} having the same frame
214+
* descriptor as the frame to synchronize. This then represents the unmodified set of frame
215+
* variables and need to be refreshed.</li>
216+
* <li>The locals object is a {@code dict} with a storage <b>different</b> to
217+
* {@code LocalsStorage}. This may happen if someone retrieves the locals and manually assigns
218+
* to the dict. Then the storage changes. In this case we must also refresh the frame
219+
* values.</li>
220+
* <li>The locals object is some arbitrary custom mapping object (excluding the above 2 cases).
221+
* In this case, all code was already working on the custom object and we don't need to do
222+
* anything.</li>
223+
* <li>The locals object is a {@code dict} with a {@code LocalsStorage} having a different frame
224+
* descriptor as the frame to synchronize. This is common when {@code exec} / {@code eval} and
225+
* inheriting the locals of the caller. In this case, we don't need to do anything.</li>
226+
* </ol>
227+
*/
209228
@ImportStatic(SpecialMethodNames.class)
210229
public abstract static class SyncFrameValuesNode extends Node {
211230

212231
public abstract void execute(VirtualFrame frame, PFrame pyframe, Frame frameToSync);
213232

214-
@Specialization(guards = {"hasLocalsStorage(pyFrame)", "frameToSync.getFrameDescriptor() == cachedFd"}, //
233+
@Specialization(guards = {"hasLocalsStorage(pyFrame, frameToSync)", "frameToSync.getFrameDescriptor() == cachedFd"}, //
215234
assumptions = "cachedFd.getVersion()", //
216235
limit = "1")
217236
@ExplodeLoop
@@ -247,7 +266,7 @@ static void doLocalsStorageCached(PFrame pyFrame, Frame frameToSync,
247266
}
248267
}
249268

250-
@Specialization(guards = "hasLocalsStorage(pyFrame)", replaces = "doLocalsStorageCached")
269+
@Specialization(guards = "hasLocalsStorage(pyFrame, frameToSync)", replaces = "doLocalsStorageCached")
251270
static void doLocalsStorageUncached(PFrame pyFrame, Frame frameToSync) {
252271
FrameDescriptor fd = frameToSync.getFrameDescriptor();
253272
FrameSlot[] cachedSlots = getSlots(fd);
@@ -324,7 +343,7 @@ static void doGenericDict(VirtualFrame frame, PFrame pyFrame, Frame frameToSync,
324343
}
325344
}
326345

327-
@Specialization(guards = "isCustomLocalsObject(pyFrame)")
346+
@Specialization(guards = "isCustomLocalsObject(pyFrame, frameToSync)")
328347
@SuppressWarnings("unused")
329348
static void doCustomLocalsObject(PFrame pyFrame, Frame frameToSync) {
330349
// nothing to do; we already worked on the custom object
@@ -334,9 +353,14 @@ protected static FrameSlot[] getSlots(FrameDescriptor fd) {
334353
return fd.getSlots().toArray(new FrameSlot[0]);
335354
}
336355

337-
protected static boolean hasLocalsStorage(PFrame pyFrame) {
356+
/**
357+
* Guard that tests if the locals object is a dict having a {@link LocalsStorage} using the
358+
* same frame descriptor as the frame to synchronize. That means, the dict represents
359+
* unmodified set of frame values of the frame to sync.
360+
*/
361+
protected static boolean hasLocalsStorage(PFrame pyFrame, Frame frameToSync) {
338362
Object localsObject = pyFrame.getLocalsDict();
339-
return localsObject instanceof PDict && ((PDict) localsObject).getDictStorage() instanceof LocalsStorage;
363+
return localsObject instanceof PDict && getFd(((PDict) localsObject).getDictStorage()) == frameToSync.getFrameDescriptor();
340364
}
341365

342366
protected static boolean isDictWithCustomStorage(PFrame pyFrame) {
@@ -345,12 +369,19 @@ protected static boolean isDictWithCustomStorage(PFrame pyFrame) {
345369
return localsObject instanceof PDict && ((PDict) localsObject).getLazyPythonClass() == PythonBuiltinClassType.PDict;
346370
}
347371

348-
protected static boolean isCustomLocalsObject(PFrame pyFrame) {
349-
return !(pyFrame.getLocalsDict() instanceof PDict);
372+
protected static boolean isCustomLocalsObject(PFrame pyFrame, Frame frameToSync) {
373+
return !hasLocalsStorage(pyFrame, frameToSync);
350374
}
351375

352376
protected static LocalsStorage getLocalsStorage(PFrame pyFrame) {
353377
return (LocalsStorage) ((PDict) pyFrame.getLocalsDict()).getDictStorage();
354378
}
379+
380+
private static FrameDescriptor getFd(HashingStorage storage) {
381+
if (storage instanceof LocalsStorage) {
382+
return ((LocalsStorage) storage).getFrame().getFrameDescriptor();
383+
}
384+
return null;
385+
}
355386
}
356387
}

0 commit comments

Comments
 (0)