Skip to content

Commit 6f40aa5

Browse files
committed
implement that the locals dict can be any object
1 parent da602a3 commit 6f40aa5

File tree

7 files changed

+124
-51
lines changed

7 files changed

+124
-51
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_exec.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,3 +277,24 @@ def f(a):
277277
x = f(4)\n""")
278278
assert eval("locals() is l")
279279
assert l["x"] == 4
280+
281+
def test_custom_locals(self):
282+
class M(object):
283+
def __getitem__(self, key):
284+
return self.result[key]
285+
def __setitem__(self, key, value):
286+
self.result[key] = value
287+
m = M()
288+
m.result = {"m": m, "M": M}
289+
exec("""if 1:
290+
assert locals() is m
291+
def f(a):
292+
exec('a=3')
293+
return a
294+
x = f(4)
295+
assert locals()["x"] == 4
296+
x = 12
297+
assert isinstance(locals(), M)
298+
assert locals()["x"] == 12\n""", None, m)
299+
assert eval("locals() is m", None, m)
300+
assert m["x"] == 12

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
9797
import com.oracle.graal.python.builtins.objects.dict.PDict;
9898
import com.oracle.graal.python.builtins.objects.frame.PFrame;
99+
import com.oracle.graal.python.builtins.objects.frame.FrameBuiltins.GetLocalsNode;
99100
import com.oracle.graal.python.builtins.objects.function.Arity;
100101
import com.oracle.graal.python.builtins.objects.function.PArguments;
101102
import com.oracle.graal.python.builtins.objects.function.PFunction;
@@ -516,15 +517,19 @@ private static void inheritGlobals(Frame callerFrame, Object[] args) {
516517
PArguments.setGlobals(args, PArguments.getGlobals(callerFrame));
517518
}
518519

519-
private void inheritLocals(Frame callerFrame, Object[] args) {
520+
private void inheritLocals(Frame callerFrame, Object[] args, GetLocalsNode getLocalsNode) {
520521
PFrame pFrame = PArguments.getPFrame(callerFrame);
521522
if (pFrame == null) {
522523
pFrame = factory().createPFrame(callerFrame);
523524
PArguments.setPFrame(callerFrame, pFrame);
524525
}
525-
PDict callerLocals = pFrame.getLocals(factory());
526-
PArguments.setSpecialArgument(args, callerLocals);
527-
PArguments.setPFrame(args, factory().createPFrame(callerLocals));
526+
Object callerLocals = getLocalsNode.execute(pFrame);
527+
setCustomLocals(args, callerLocals);
528+
}
529+
530+
private void setCustomLocals(Object[] args, Object locals) {
531+
PArguments.setSpecialArgument(args, locals);
532+
PArguments.setPFrame(args, factory().createPFrame(locals));
528533
}
529534

530535
private void setBuiltinsInGlobals(PDict globals, HashingCollectionNodes.SetItemNode setBuiltins, PythonModule builtins) {
@@ -549,12 +554,13 @@ private void setCustomGlobals(PDict globals, HashingCollectionNodes.SetItemNode
549554

550555
@Specialization
551556
Object execInheritGlobalsInheritLocals(VirtualFrame frame, Object source, @SuppressWarnings("unused") PNone globals, @SuppressWarnings("unused") PNone locals,
552-
@Cached("create()") ReadCallerFrameNode readCallerFrameNode) {
557+
@Cached("create()") ReadCallerFrameNode readCallerFrameNode,
558+
@Cached("create()") GetLocalsNode getLocalsNode) {
553559
PCode code = createAndCheckCode(source);
554560
Frame callerFrame = readCallerFrameNode.executeWith(frame);
555561
Object[] args = PArguments.create();
556562
inheritGlobals(callerFrame, args);
557-
inheritLocals(callerFrame, args);
563+
inheritLocals(callerFrame, args, getLocalsNode);
558564
return indirectCallNode.call(code.getRootCallTarget(), args);
559565
}
560566

@@ -564,7 +570,7 @@ Object execCustomGlobalsGlobalLocals(Object source, PDict globals, @SuppressWarn
564570
PCode code = createAndCheckCode(source);
565571
Object[] args = PArguments.create();
566572
setCustomGlobals(globals, setBuiltins, args);
567-
PArguments.setSpecialArgument(args, globals);
573+
setCustomLocals(args, globals);
568574
return indirectCallNode.call(code.getRootCallTarget(), args);
569575
}
570576

@@ -575,7 +581,7 @@ Object execInheritGlobalsCustomLocals(VirtualFrame frame, Object source, @Suppre
575581
Frame callerFrame = readCallerFrameNode.executeWith(frame);
576582
Object[] args = PArguments.create();
577583
inheritGlobals(callerFrame, args);
578-
PArguments.setSpecialArgument(args, locals);
584+
setCustomLocals(args, locals);
579585
return indirectCallNode.call(code.getRootCallTarget(), args);
580586
}
581587

@@ -585,7 +591,7 @@ Object execCustomGlobalsCustomLocals(Object source, PDict globals, Object locals
585591
PCode code = createAndCheckCode(source);
586592
Object[] args = PArguments.create();
587593
setCustomGlobals(globals, setBuiltins, args);
588-
PArguments.setSpecialArgument(args, locals);
594+
setCustomLocals(args, locals);
589595
return indirectCallNode.call(code.getRootCallTarget(), args);
590596
}
591597

@@ -1650,17 +1656,22 @@ public Object globals(VirtualFrame frame) {
16501656
@GenerateNodeFactory
16511657
abstract static class LocalsNode extends PythonBuiltinNode {
16521658
@Child ReadCallerFrameNode readCallerFrameNode = ReadCallerFrameNode.create();
1653-
private final ConditionProfile condProfile = ConditionProfile.createBinaryProfile();
1659+
@Child GetLocalsNode getLocalsNode = GetLocalsNode.create();
1660+
private final ConditionProfile hasFrame = ConditionProfile.createBinaryProfile();
1661+
private final ConditionProfile hasPFrame = ConditionProfile.createBinaryProfile();
16541662

16551663
@Specialization
16561664
public Object locals(VirtualFrame frame) {
16571665
Frame callerFrame = readCallerFrameNode.executeWith(frame);
16581666
PFrame pFrame = PArguments.getPFrame(callerFrame);
1659-
if (condProfile.profile(pFrame == null)) {
1667+
if (hasPFrame.profile(pFrame == null)) {
16601668
pFrame = factory().createPFrame(callerFrame);
16611669
PArguments.setPFrame(callerFrame, pFrame);
1670+
} else if (hasFrame.profile(!pFrame.hasFrame())) {
1671+
pFrame = factory().createPFrame(callerFrame, pFrame.getLocalsDict());
1672+
PArguments.setPFrame(callerFrame, pFrame);
16621673
}
1663-
return pFrame.getLocals(factory());
1674+
return getLocalsNode.execute(pFrame);
16641675
}
16651676
}
16661677
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/FrameBuiltins.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,18 @@
3232
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
3333
import com.oracle.graal.python.builtins.PythonBuiltins;
3434
import com.oracle.graal.python.builtins.objects.PNone;
35+
import com.oracle.graal.python.builtins.objects.common.HashingStorage.DictEntry;
3536
import com.oracle.graal.python.builtins.objects.dict.PDict;
37+
import com.oracle.graal.python.builtins.objects.frame.FrameBuiltinsFactory.GetLocalsNodeFactory;
3638
import com.oracle.graal.python.builtins.objects.function.PArguments;
3739
import com.oracle.graal.python.builtins.objects.module.PythonModule;
3840
import com.oracle.graal.python.builtins.objects.object.PythonObject;
3941
import com.oracle.graal.python.builtins.objects.traceback.PTraceback;
4042
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
4143
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
44+
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
45+
import com.oracle.graal.python.nodes.subscript.SetItemNode;
46+
import com.oracle.truffle.api.CompilerDirectives;
4247
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
4348
import com.oracle.truffle.api.dsl.NodeFactory;
4449
import com.oracle.truffle.api.dsl.Specialization;
@@ -132,10 +137,28 @@ Object get(PFrame self) {
132137

133138
@Builtin(name = "f_locals", fixedNumOfPositionalArgs = 1, isGetter = true)
134139
@GenerateNodeFactory
135-
public abstract static class GetLocalsNode extends PythonBuiltinNode {
140+
public abstract static class GetLocalsNode extends PythonUnaryBuiltinNode {
141+
@Child SetItemNode setItemNode;
142+
136143
@Specialization
137-
PDict get(PFrame self) {
138-
return self.getLocals(factory());
144+
Object get(PFrame self) {
145+
Frame frame = self.getFrame();
146+
Object locals = self.getLocals(factory());
147+
if (!self.inClassScope()) {
148+
if (setItemNode == null) {
149+
CompilerDirectives.transferToInterpreterAndInvalidate();
150+
setItemNode = insert(SetItemNode.create());
151+
}
152+
PDict currentDictLocals = factory().createDictLocals(frame, false);
153+
for (DictEntry entry : currentDictLocals.entries()) {
154+
setItemNode.executeWith(locals, entry.getKey(), entry.getValue());
155+
}
156+
}
157+
return locals;
158+
}
159+
160+
public static GetLocalsNode create() {
161+
return GetLocalsNodeFactory.create();
139162
}
140163
}
141164

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/PFrame.java

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
package com.oracle.graal.python.builtins.objects.frame;
4242

4343
import com.oracle.graal.python.builtins.objects.code.PCode;
44-
import com.oracle.graal.python.builtins.objects.dict.PDict;
4544
import com.oracle.graal.python.builtins.objects.exception.PBaseException;
45+
import com.oracle.graal.python.builtins.objects.frame.FrameBuiltins.GetLocalsNode;
4646
import com.oracle.graal.python.builtins.objects.function.PArguments;
4747
import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
4848
import com.oracle.graal.python.builtins.objects.object.PythonObject;
@@ -60,7 +60,7 @@ public final class PFrame extends PythonBuiltinObject {
6060

6161
private final PBaseException exception;
6262
private final int index;
63-
private PDict localsDict;
63+
private Object localsDict;
6464

6565
private final boolean inClassScope;
6666
private final Frame frame;
@@ -76,7 +76,17 @@ public PFrame(LazyPythonClass cls, Frame frame) {
7676
this.inClassScope = PArguments.getSpecialArgument(frame) instanceof ClassBodyRootNode;
7777
}
7878

79-
public PFrame(LazyPythonClass cls, PDict locals) {
79+
public PFrame(LazyPythonClass cls, Frame frame, Object locals) {
80+
super(cls);
81+
this.exception = null;
82+
this.index = -1;
83+
this.frame = frame;
84+
this.localsDict = locals;
85+
this.location = null;
86+
this.inClassScope = PArguments.getSpecialArgument(frame) instanceof ClassBodyRootNode;
87+
}
88+
89+
public PFrame(LazyPythonClass cls, Object locals) {
8090
super(cls);
8191
this.exception = null;
8292
this.index = -1;
@@ -109,9 +119,7 @@ public PFrame(LazyPythonClass cls, @SuppressWarnings("unused") Object threadStat
109119
this.inClassScope = code.getRootNode() instanceof ClassBodyRootNode;
110120
this.line = code.getRootNode() == null ? code.getFirstLineNo() : -2;
111121

112-
if (locals instanceof PDict) {
113-
localsDict = (PDict) locals;
114-
}
122+
localsDict = locals;
115123
}
116124

117125
public PBaseException getException() {
@@ -126,7 +134,7 @@ public Frame getFrame() {
126134
return frame;
127135
}
128136

129-
public PDict getLocalsDict() {
137+
public Object getLocalsDict() {
130138
return localsDict;
131139
}
132140

@@ -151,20 +159,20 @@ public Node getCallNode() {
151159
return location;
152160
}
153161

154-
public PDict getLocals(PythonObjectFactory factory) {
155-
if (frame != null) {
156-
if (localsDict == null) {
157-
localsDict = factory.createDictLocals(frame, inClassScope);
158-
} else {
159-
if (!inClassScope) {
160-
localsDict.update(factory.createDictLocals(frame, false));
161-
}
162-
}
163-
return localsDict;
164-
} else if (localsDict != null) {
165-
return localsDict;
162+
/**
163+
* Prefer to use the {@link GetLocalsNode}.<br/>
164+
* <br/>
165+
*
166+
* Returns a dictionary with the locals, possibly creating it from the frame. Note that the
167+
* dictionary may have been modified and should then be updated with the current frame locals.
168+
* To that end, use the {@link GetLocalsNode} instead of calling this method directly.
169+
*/
170+
public Object getLocals(PythonObjectFactory factory) {
171+
if (localsDict == null) {
172+
assert frame != null;
173+
return localsDict = factory.createDictLocals(frame, inClassScope);
166174
} else {
167-
return factory.createDict();
175+
return localsDict;
168176
}
169177
}
170178

@@ -176,4 +184,12 @@ public PDict getLocals(PythonObjectFactory factory) {
176184
public boolean isIncomplete() {
177185
return location == null;
178186
}
187+
188+
public boolean hasFrame() {
189+
return frame != null;
190+
}
191+
192+
public boolean inClassScope() {
193+
return inClassScope;
194+
}
179195
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/classes/DeleteClassAttributeNode.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,12 @@
4040
*/
4141
package com.oracle.graal.python.nodes.classes;
4242

43-
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes;
44-
import com.oracle.graal.python.builtins.objects.dict.PDict;
4543
import com.oracle.graal.python.builtins.objects.frame.PFrame;
4644
import com.oracle.graal.python.builtins.objects.function.PArguments;
4745
import com.oracle.graal.python.nodes.NodeFactory;
4846
import com.oracle.graal.python.nodes.argument.ReadIndexedArgumentNode;
4947
import com.oracle.graal.python.nodes.statement.StatementNode;
48+
import com.oracle.graal.python.nodes.subscript.DeleteItemNode;
5049
import com.oracle.truffle.api.dsl.Cached;
5150
import com.oracle.truffle.api.dsl.Specialization;
5251
import com.oracle.truffle.api.frame.VirtualFrame;
@@ -71,7 +70,7 @@ public static DeleteClassAttributeNode create(String name) {
7170
return DeleteClassAttributeNodeGen.create(name);
7271
}
7372

74-
PDict getLocalsDict(VirtualFrame frame) {
73+
Object getLocalsDict(VirtualFrame frame) {
7574
PFrame pFrame = PArguments.getPFrame(frame);
7675
if (pFrame != null) {
7776
return pFrame.getLocalsDict();
@@ -81,13 +80,10 @@ PDict getLocalsDict(VirtualFrame frame) {
8180

8281
@Specialization(guards = "localsDict != null")
8382
void deleteFromLocals(@SuppressWarnings("unused") VirtualFrame frame,
84-
@Cached("getLocalsDict(frame)") PDict localsDict,
85-
@Cached("create()") HashingStorageNodes.ContainsKeyNode hasKey,
86-
@Cached("create()") HashingStorageNodes.DelItemNode delItem) {
83+
@Cached("getLocalsDict(frame)") Object localsDict,
84+
@Cached("create()") DeleteItemNode delItemNode) {
8785
// class namespace overrides closure
88-
if (hasKey.execute(localsDict.getDictStorage(), identifier)) {
89-
delItem.execute(localsDict, localsDict.getDictStorage(), identifier);
90-
}
86+
delItemNode.executeWith(localsDict, identifier);
9187
}
9288

9389
@Specialization

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/classes/ReadClassAttributeNode.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@
4040
*/
4141
package com.oracle.graal.python.nodes.classes;
4242

43-
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes;
44-
import com.oracle.graal.python.builtins.objects.dict.PDict;
4543
import com.oracle.graal.python.builtins.objects.frame.PFrame;
4644
import com.oracle.graal.python.builtins.objects.function.PArguments;
4745
import com.oracle.graal.python.nodes.NodeFactory;
@@ -50,6 +48,7 @@
5048
import com.oracle.graal.python.nodes.frame.ReadLocalNode;
5149
import com.oracle.graal.python.nodes.frame.ReadNode;
5250
import com.oracle.graal.python.nodes.statement.StatementNode;
51+
import com.oracle.graal.python.nodes.subscript.GetItemNode;
5352
import com.oracle.graal.python.runtime.exception.PException;
5453
import com.oracle.truffle.api.dsl.Cached;
5554
import com.oracle.truffle.api.dsl.Specialization;
@@ -108,16 +107,19 @@ public StatementNode makeWriteNode(ExpressionNode rhs) {
108107

109108
@Specialization
110109
Object read(VirtualFrame frame,
111-
@Cached("create()") HashingStorageNodes.ContainsKeyNode hasKey) {
110+
@Cached("create()") GetItemNode getItemNode) {
112111
try {
113112
return getNsItem.execute(frame);
114113
} catch (PException pe) {
115114
// class namespace overrides closure
116115
PFrame pFrame = PArguments.getPFrame(frame);
117116
if (pFrame != null) {
118-
PDict localsDict = pFrame.getLocalsDict();
119-
if (localsDict != null && hasKey.execute(localsDict.getDictStorage(), identifier)) {
120-
return localsDict.getItem(identifier);
117+
Object localsDict = pFrame.getLocalsDict();
118+
if (localsDict != null) {
119+
try {
120+
return getItemNode.execute(localsDict, identifier);
121+
} catch (PException e) {
122+
}
121123
}
122124
}
123125

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PythonObjectFactory.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,14 +560,18 @@ public PReferenceType createReferenceType(PythonObject object, PFunction callbac
560560
* Frames, traces and exceptions
561561
*/
562562

563-
public PFrame createPFrame(PDict locals) {
563+
public PFrame createPFrame(Object locals) {
564564
return trace(new PFrame(PythonBuiltinClassType.PFrame, locals));
565565
}
566566

567567
public PFrame createPFrame(Frame frame) {
568568
return trace(new PFrame(PythonBuiltinClassType.PFrame, frame));
569569
}
570570

571+
public PFrame createPFrame(Frame frame, Object locals) {
572+
return trace(new PFrame(PythonBuiltinClassType.PFrame, frame, locals));
573+
}
574+
571575
public PFrame createPFrame(PBaseException exception, int index) {
572576
return trace(new PFrame(PythonBuiltinClassType.PFrame, exception, index));
573577
}

0 commit comments

Comments
 (0)