Skip to content

Commit 7ae9669

Browse files
committed
use nodes equivalent to LOAD_NAME, STORE_NAME, DELETE_NAME bytecodes in module root contexts
1 parent 155df21 commit 7ae9669

File tree

11 files changed

+352
-14
lines changed

11 files changed

+352
-14
lines changed

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

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
import com.oracle.graal.python.builtins.objects.type.TypeBuiltins;
117117
import com.oracle.graal.python.nodes.BuiltinNames;
118118
import com.oracle.graal.python.nodes.GraalPythonTranslationErrorNode;
119+
import com.oracle.graal.python.nodes.PClosureRootNode;
119120
import com.oracle.graal.python.nodes.PGuards;
120121
import com.oracle.graal.python.nodes.SpecialMethodNames;
121122
import com.oracle.graal.python.nodes.argument.ReadArgumentNode;
@@ -180,6 +181,7 @@
180181
import com.oracle.truffle.api.nodes.NodeVisitor;
181182
import com.oracle.truffle.api.nodes.RootNode;
182183
import com.oracle.truffle.api.nodes.UnexpectedResultException;
184+
import com.oracle.truffle.api.profiles.BranchProfile;
183185
import com.oracle.truffle.api.profiles.ConditionProfile;
184186
import com.oracle.truffle.api.profiles.ValueProfile;
185187
import com.oracle.truffle.api.source.Source;
@@ -607,29 +609,78 @@ private static Object evalNode(RootCallTarget callTarget, PythonObject globals,
607609
@Builtin(name = EXEC, fixedNumOfPositionalArgs = 1, parameterNames = {"source"}, keywordArguments = {"globals", "locals"})
608610
@GenerateNodeFactory
609611
abstract static class ExecNode extends PythonBuiltinNode {
612+
private final BranchProfile hasFreeVars = BranchProfile.create();
610613
@Child CompileNode compileNode = CompileNode.create();
611614
@Child IndirectCallNode indirectCallNode = IndirectCallNode.create();
612615

613-
@Specialization(guards = {"isNoValue(globals)", "isNoValue(locals)"})
614-
PNone execDefault(VirtualFrame frame, Object source, @SuppressWarnings("unused") PNone globals, @SuppressWarnings("unused") PNone locals,
616+
private void assertNoFreeVars(PCode code) {
617+
RootNode rootNode = code.getRootNode();
618+
if (rootNode instanceof PClosureRootNode && ((PClosureRootNode) rootNode).hasFreeVars()) {
619+
hasFreeVars.enter();
620+
throw raise(PythonBuiltinClassType.TypeError, "code object passed to exec() may not contain free variables");
621+
}
622+
}
623+
624+
@Specialization(guards = {"isNoValue(globals) || isNone(globals)", "isNoValue(locals) || isNone(locals)"})
625+
PNone execInheritGlobalsInheritLocals(VirtualFrame frame, Object source, @SuppressWarnings("unused") PNone globals, @SuppressWarnings("unused") PNone locals,
615626
@Cached("create()") ReadCallerFrameNode readCallerFrameNode) {
616-
PCode code = compileNode.execute(source, "exec", "exec", 0, false, -1);
627+
PCode code = createAndCheckCode(source);
617628
Frame callerFrame = readCallerFrameNode.executeWith(frame);
618629
Object[] args = PArguments.create();
619-
PArguments.setGlobals(args, PArguments.getGlobals(callerFrame));
620-
PArguments.setClosure(args, PArguments.getClosure(callerFrame));
630+
inheritGlobals(callerFrame, args);
631+
inheritLocals(callerFrame, args);
621632
indirectCallNode.call(code.getRootCallTarget(), args);
622633
return PNone.NO_VALUE;
623634
}
624635

625-
@Specialization(guards = {"isNoValue(locals)"})
626-
PNone execDefault(Object source, PDict globals, @SuppressWarnings("unused") PNone locals) {
636+
private PCode createAndCheckCode(Object source) {
627637
PCode code = compileNode.execute(source, "exec", "exec", 0, false, -1);
638+
assertNoFreeVars(code);
639+
return code;
640+
}
641+
642+
private static void inheritGlobals(Frame callerFrame, Object[] args) {
643+
PArguments.setGlobals(args, PArguments.getGlobals(callerFrame));
644+
}
645+
646+
private void inheritLocals(Frame callerFrame, Object[] args) {
647+
PFrame pFrame = PArguments.getPFrame(callerFrame);
648+
if (pFrame == null) {
649+
pFrame = factory().createPFrame(callerFrame);
650+
PArguments.setPFrame(callerFrame, pFrame);
651+
}
652+
PDict callerLocals = pFrame.getLocals(factory());
653+
PArguments.setSpecialArgument(args, callerLocals);
654+
}
655+
656+
@Specialization(guards = {"isNoValue(locals) || isNone(locals)"})
657+
PNone execCustomGlobalsGlobalLocals(Object source, PDict globals, @SuppressWarnings("unused") PNone locals) {
658+
PCode code = createAndCheckCode(source);
659+
Object[] args = PArguments.create();
660+
PArguments.setGlobals(args, globals);
661+
PArguments.setSpecialArgument(args, globals);
662+
indirectCallNode.call(code.getRootCallTarget(), args);
663+
return PNone.NO_VALUE;
664+
}
665+
666+
@Specialization(guards = {"isNone(globals)", "!isNoValue(locals)", "!isNone(locals)"})
667+
PNone execInheritGlobalsCustomLocals(VirtualFrame frame, Object source, @SuppressWarnings("unused") PNone globals, Object locals,
668+
@Cached("create()") ReadCallerFrameNode readCallerFrameNode) {
669+
PCode code = createAndCheckCode(source);
670+
Frame callerFrame = readCallerFrameNode.executeWith(frame);
671+
Object[] args = PArguments.create();
672+
inheritGlobals(callerFrame, args);
673+
PArguments.setSpecialArgument(args, locals);
674+
indirectCallNode.call(code.getRootCallTarget(), args);
675+
return PNone.NO_VALUE;
676+
}
677+
678+
@Specialization(guards = {"!isNoValue(locals)", "!isNone(locals)"})
679+
PNone execCustomGlobalsCustomLocals(Object source, PDict globals, Object locals) {
680+
PCode code = createAndCheckCode(source);
628681
Object[] args = PArguments.create();
629682
PArguments.setGlobals(args, globals);
630-
PArguments.setPFrame(args, factory().createPFrame(globals));
631-
// If locals are not given, they default to the globals, so we don't need the caller
632-
// frame's closure at all
683+
PArguments.setSpecialArgument(args, locals);
633684
indirectCallNode.call(code.getRootCallTarget(), args);
634685
return PNone.NO_VALUE;
635686
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PArguments.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.oracle.graal.python.builtins.objects.frame.PFrame;
3030
import com.oracle.graal.python.builtins.objects.generator.GeneratorControlData;
3131
import com.oracle.graal.python.builtins.objects.object.PythonObject;
32+
import com.oracle.graal.python.nodes.function.ClassBodyRootNode;
3233
import com.oracle.truffle.api.frame.Frame;
3334
import com.oracle.truffle.api.frame.MaterializedFrame;
3435
import com.oracle.truffle.api.frame.VirtualFrame;
@@ -127,8 +128,22 @@ public static void setSpecialArgument(Object[] arguments, Object value) {
127128
arguments[INDEX_SPECIAL_ARGUMENT] = value;
128129
}
129130

131+
/**
132+
* The special argument is used for various purposes, none of which can occur at the same time:
133+
* <ul>
134+
* <li>The value sent to a generator via <code>send</code></li>
135+
* <li>An exception thrown through a generator via <code>throw</code></li>
136+
* <li>The {@link ClassBodyRootNode} when we are executing a class body</li>
137+
* <li>The custom locals mapping when executing through <code>exec</code> with a
138+
* <code>locals</code> keyword argument</li>
139+
* </ul>
140+
*/
130141
public static Object getSpecialArgument(Frame frame) {
131-
return frame.getArguments()[INDEX_SPECIAL_ARGUMENT];
142+
return getSpecialArgument(frame.getArguments());
143+
}
144+
145+
public static Object getSpecialArgument(Object[] arguments) {
146+
return arguments[INDEX_SPECIAL_ARGUMENT];
132147
}
133148

134149
public static void setGlobals(Object[] arguments, PythonObject globals) {

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,11 @@
6767
import com.oracle.graal.python.nodes.expression.TernaryIfNode;
6868
import com.oracle.graal.python.nodes.expression.UnaryArithmetic;
6969
import com.oracle.graal.python.nodes.frame.DeleteGlobalNode;
70+
import com.oracle.graal.python.nodes.frame.DeleteNameNode;
7071
import com.oracle.graal.python.nodes.frame.DestructuringAssignmentNode;
7172
import com.oracle.graal.python.nodes.frame.ReadGlobalOrBuiltinNode;
7273
import com.oracle.graal.python.nodes.frame.ReadLocalVariableNode;
74+
import com.oracle.graal.python.nodes.frame.ReadNameNode;
7375
import com.oracle.graal.python.nodes.frame.ReadNode;
7476
import com.oracle.graal.python.nodes.frame.WriteLocalVariableNode;
7577
import com.oracle.graal.python.nodes.frame.WriteNode;
@@ -422,6 +424,10 @@ public StatementNode createDeleteGlobal(String attributeId) {
422424
return DeleteGlobalNode.create(attributeId);
423425
}
424426

427+
public StatementNode createDeleteName(String attributeId) {
428+
return DeleteNameNode.create(attributeId);
429+
}
430+
425431
public ExpressionNode createSlice(ExpressionNode lower, ExpressionNode upper, ExpressionNode step) {
426432
return SliceLiteralNode.create(lower, upper, step);
427433
}
@@ -462,6 +468,10 @@ public ExpressionNode createReadGlobalOrBuiltinScope(String attributeId) {
462468
return ReadGlobalOrBuiltinNode.create(attributeId);
463469
}
464470

471+
public ReadNode createLoadName(String name) {
472+
return ReadNameNode.create(name);
473+
}
474+
465475
public ExpressionNode createBooleanLiteral(boolean value) {
466476
return new BooleanLiteralNode(value);
467477
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ protected void addClosureCellsToLocalsExploded(Frame frame, PCell[] closure) {
8585
}
8686
}
8787

88+
public boolean hasFreeVars() {
89+
return freeVarSlots != null && freeVarSlots.length > 0;
90+
}
91+
8892
public String[] getFreeVars() {
8993
String[] freeVars = new String[freeVarSlots.length];
9094
for (int i = 0; i < freeVarSlots.length; i++) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/InvokeNode.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ protected final MaterializedFrame getCallerFrame(VirtualFrame frame, CallTarget
106106
protected final void optionallySetClassBodySpecial(Object[] arguments, CallTarget callTarget) {
107107
RootNode rootNode = ((RootCallTarget) callTarget).getRootNode();
108108
if (isClassBodyProfile.profile(rootNode instanceof ClassBodyRootNode)) {
109+
assert PArguments.getSpecialArgument(arguments) == null : "there cannot be a special argument in a class body";
109110
PArguments.setSpecialArgument(arguments, rootNode);
110111
}
111112
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package com.oracle.graal.python.nodes.frame;
2+
3+
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
4+
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes;
5+
import com.oracle.graal.python.builtins.objects.dict.PDict;
6+
import com.oracle.graal.python.builtins.objects.function.PArguments;
7+
import com.oracle.graal.python.nodes.object.IsBuiltinClassProfile;
8+
import com.oracle.graal.python.nodes.statement.StatementNode;
9+
import com.oracle.graal.python.nodes.subscript.DeleteItemNode;
10+
import com.oracle.graal.python.runtime.exception.PException;
11+
import com.oracle.truffle.api.CompilerDirectives;
12+
import com.oracle.truffle.api.dsl.Cached;
13+
import com.oracle.truffle.api.dsl.Specialization;
14+
import com.oracle.truffle.api.frame.VirtualFrame;
15+
16+
public abstract class DeleteNameNode extends StatementNode {
17+
@Child private DeleteGlobalNode deleteGlobalNode;
18+
protected final IsBuiltinClassProfile keyError = IsBuiltinClassProfile.create();
19+
protected final String attributeId;
20+
21+
protected DeleteNameNode(String attributeId) {
22+
this.attributeId = attributeId;
23+
}
24+
25+
public static DeleteNameNode create(String attributeId) {
26+
return DeleteNameNodeGen.create(attributeId);
27+
}
28+
29+
protected boolean hasLocals(VirtualFrame frame) {
30+
// (tfel): This node will only ever be generated in a module scope
31+
// where neither generator special args nor a ClassBodyRootNode can
32+
// occur
33+
return PArguments.getSpecialArgument(frame) != null;
34+
}
35+
36+
protected boolean hasLocalsDict(VirtualFrame frame) {
37+
return PArguments.getSpecialArgument(frame) instanceof PDict;
38+
}
39+
40+
private DeleteGlobalNode getDeleteGlobalNode() {
41+
if (deleteGlobalNode == null) {
42+
CompilerDirectives.transferToInterpreterAndInvalidate();
43+
deleteGlobalNode = insert(DeleteGlobalNode.create(attributeId));
44+
}
45+
return deleteGlobalNode;
46+
}
47+
48+
private void deleteGlobalIfKeyError(VirtualFrame frame, PException e) {
49+
e.expect(PythonBuiltinClassType.KeyError, keyError);
50+
getDeleteGlobalNode().executeVoid(frame);
51+
}
52+
53+
@Specialization(guards = "hasLocalsDict(frame)")
54+
protected void readFromLocalsDict(VirtualFrame frame,
55+
@Cached("create()") HashingStorageNodes.DelItemNode delItem) {
56+
PDict frameLocals = (PDict) PArguments.getSpecialArgument(frame);
57+
if (!delItem.execute(frameLocals, frameLocals.getDictStorage(), attributeId)) {
58+
getDeleteGlobalNode().executeVoid(frame);
59+
}
60+
}
61+
62+
@Specialization(guards = "hasLocals(frame)", replaces = "readFromLocalsDict")
63+
protected void readFromLocals(VirtualFrame frame,
64+
@Cached("create()") DeleteItemNode delItem) {
65+
Object frameLocals = PArguments.getSpecialArgument(frame);
66+
try {
67+
delItem.executeWith(frameLocals, attributeId);
68+
} catch (PException e) {
69+
deleteGlobalIfKeyError(frame, e);
70+
}
71+
}
72+
73+
@Specialization(guards = "!hasLocals(frame)")
74+
protected void readFromLocals(VirtualFrame frame) {
75+
getDeleteGlobalNode().executeVoid(frame);
76+
}
77+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package com.oracle.graal.python.nodes.frame;
2+
3+
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
4+
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes;
5+
import com.oracle.graal.python.builtins.objects.dict.PDict;
6+
import com.oracle.graal.python.builtins.objects.function.PArguments;
7+
import com.oracle.graal.python.nodes.expression.ExpressionNode;
8+
import com.oracle.graal.python.nodes.object.IsBuiltinClassProfile;
9+
import com.oracle.graal.python.nodes.statement.StatementNode;
10+
import com.oracle.graal.python.nodes.subscript.GetItemNode;
11+
import com.oracle.graal.python.runtime.exception.PException;
12+
import com.oracle.truffle.api.CompilerDirectives;
13+
import com.oracle.truffle.api.dsl.Cached;
14+
import com.oracle.truffle.api.dsl.Specialization;
15+
import com.oracle.truffle.api.frame.VirtualFrame;
16+
17+
public abstract class ReadNameNode extends ExpressionNode implements ReadNode {
18+
@Child private ReadGlobalOrBuiltinNode readGlobalNode;
19+
protected final IsBuiltinClassProfile keyError = IsBuiltinClassProfile.create();
20+
protected final String attributeId;
21+
22+
protected ReadNameNode(String attributeId) {
23+
this.attributeId = attributeId;
24+
}
25+
26+
public static ReadNameNode create(String attributeId) {
27+
return ReadNameNodeGen.create(attributeId);
28+
}
29+
30+
protected boolean hasLocals(VirtualFrame frame) {
31+
// (tfel): This node will only ever be generated in a module scope
32+
// where neither generator special args nor a ClassBodyRootNode can
33+
// occur
34+
return PArguments.getSpecialArgument(frame) != null;
35+
}
36+
37+
protected boolean hasLocalsDict(VirtualFrame frame) {
38+
return PArguments.getSpecialArgument(frame) instanceof PDict;
39+
}
40+
41+
private ReadGlobalOrBuiltinNode getReadGlobalNode() {
42+
if (readGlobalNode == null) {
43+
CompilerDirectives.transferToInterpreterAndInvalidate();
44+
readGlobalNode = insert(ReadGlobalOrBuiltinNode.create(attributeId));
45+
}
46+
return readGlobalNode;
47+
}
48+
49+
private Object readGlobalsIfKeyError(VirtualFrame frame, PException e) {
50+
e.expect(PythonBuiltinClassType.KeyError, keyError);
51+
return getReadGlobalNode().execute(frame);
52+
}
53+
54+
@Specialization(guards = "hasLocalsDict(frame)")
55+
protected Object readFromLocalsDict(VirtualFrame frame,
56+
@Cached("create()") HashingStorageNodes.GetItemNode getItem) {
57+
PDict frameLocals = (PDict) PArguments.getSpecialArgument(frame);
58+
Object result = getItem.execute(frameLocals.getDictStorage(), attributeId);
59+
if (result == null) {
60+
return getReadGlobalNode().execute(frame);
61+
} else {
62+
return result;
63+
}
64+
}
65+
66+
@Specialization(guards = "hasLocals(frame)", replaces = "readFromLocalsDict")
67+
protected Object readFromLocals(VirtualFrame frame,
68+
@Cached("create()") GetItemNode getItem) {
69+
Object frameLocals = PArguments.getSpecialArgument(frame);
70+
try {
71+
return getItem.execute(frameLocals, attributeId);
72+
} catch (PException e) {
73+
return readGlobalsIfKeyError(frame, e);
74+
}
75+
}
76+
77+
@Specialization(guards = "!hasLocals(frame)")
78+
protected Object readFromLocals(VirtualFrame frame) {
79+
return getReadGlobalNode().execute(frame);
80+
}
81+
82+
public StatementNode makeWriteNode(ExpressionNode rhs) {
83+
return WriteNameNode.create(attributeId, rhs);
84+
}
85+
86+
public String getAttributeId() {
87+
return attributeId;
88+
}
89+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ public abstract class WriteGlobalNode extends StatementNode implements GlobalNod
6060
this.attributeId = attributeId;
6161
}
6262

63-
public static WriteGlobalNode create() {
64-
return create(null, null);
63+
public static WriteGlobalNode create(String attributeId) {
64+
return create(attributeId, null);
6565
}
6666

6767
public static WriteGlobalNode create(String attributeId, ExpressionNode rhs) {

0 commit comments

Comments
 (0)