Skip to content

Commit 99ef6ad

Browse files
committed
eval and exec are pretty much the same safe for the compilation
1 parent 9bc98cf commit 99ef6ad

File tree

2 files changed

+49
-161
lines changed

2 files changed

+49
-161
lines changed

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -250,11 +250,6 @@ def test_exec_unicode(self):
250250
assert ord(x[4]) == 0x0435
251251
assert ord(x[5]) == 0x043d
252252

253-
def test_eval_unicode(self):
254-
u = "'%s'" % chr(0x1234)
255-
v = eval(u)
256-
assert v == chr(0x1234)
257-
258253
def test_compile_bytes(self):
259254
s = b"x = '\xd0\xb9\xd1\x86\xd1\x83\xd0\xba\xd0\xb5\xd0\xbd'"
260255
c = compile(s, '<input>', 'exec')

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

Lines changed: 49 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,6 @@
8989
import com.oracle.graal.python.builtins.objects.bytes.BytesNodes;
9090
import com.oracle.graal.python.builtins.objects.bytes.OpaqueBytes;
9191
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
92-
import com.oracle.graal.python.builtins.objects.bytes.PIBytesLike;
93-
import com.oracle.graal.python.builtins.objects.cell.PCell;
9492
import com.oracle.graal.python.builtins.objects.code.PCode;
9593
import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes;
9694
import com.oracle.graal.python.builtins.objects.common.PHashingCollection;
@@ -123,6 +121,7 @@
123121
import com.oracle.graal.python.nodes.argument.ReadIndexedArgumentNode;
124122
import com.oracle.graal.python.nodes.argument.ReadVarArgsNode;
125123
import com.oracle.graal.python.nodes.attributes.DeleteAttributeNode;
124+
import com.oracle.graal.python.nodes.attributes.HasInheritedAttributeNode;
126125
import com.oracle.graal.python.nodes.attributes.LookupInheritedAttributeNode;
127126
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
128127
import com.oracle.graal.python.nodes.attributes.SetAttributeNode;
@@ -136,7 +135,6 @@
136135
import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
137136
import com.oracle.graal.python.nodes.control.GetIteratorNode;
138137
import com.oracle.graal.python.nodes.control.GetNextNode;
139-
import com.oracle.graal.python.nodes.datamodel.IsMappingNode;
140138
import com.oracle.graal.python.nodes.expression.BinaryArithmetic;
141139
import com.oracle.graal.python.nodes.expression.BinaryComparisonNode;
142140
import com.oracle.graal.python.nodes.expression.CastToBooleanNode;
@@ -156,15 +154,13 @@
156154
import com.oracle.graal.python.runtime.PythonContext;
157155
import com.oracle.graal.python.runtime.PythonCore;
158156
import com.oracle.graal.python.runtime.PythonOptions;
159-
import com.oracle.graal.python.runtime.PythonParser;
160157
import com.oracle.graal.python.runtime.PythonParser.ParserMode;
161158
import com.oracle.graal.python.runtime.exception.PException;
162159
import com.oracle.graal.python.runtime.exception.PythonErrorType;
163160
import com.oracle.graal.python.runtime.sequence.PSequence;
164161
import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage;
165162
import com.oracle.truffle.api.CompilerDirectives;
166163
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
167-
import com.oracle.truffle.api.RootCallTarget;
168164
import com.oracle.truffle.api.Truffle;
169165
import com.oracle.truffle.api.dsl.Cached;
170166
import com.oracle.truffle.api.dsl.Fallback;
@@ -184,7 +180,6 @@
184180
import com.oracle.truffle.api.nodes.UnexpectedResultException;
185181
import com.oracle.truffle.api.profiles.BranchProfile;
186182
import com.oracle.truffle.api.profiles.ConditionProfile;
187-
import com.oracle.truffle.api.profiles.ValueProfile;
188183
import com.oracle.truffle.api.source.Source;
189184

190185
@CoreFunctions(defineModule = "builtins")
@@ -475,169 +470,46 @@ private static BigInteger[] divideAndRemainder(PInt a, PInt b) {
475470
@Builtin(name = EVAL, fixedNumOfPositionalArgs = 1, keywordArguments = {"globals", "locals"})
476471
@GenerateNodeFactory
477472
public abstract static class EvalNode extends PythonBuiltinNode {
478-
@Child private GetItemNode getNameNode = GetItemNode.create();
479-
@Child private ReadCallerFrameNode readCallerFrameNode = ReadCallerFrameNode.create();
480-
@Child private SequenceStorageNodes.ToByteArrayNode toByteArrayNode;
481-
482-
@Specialization
483-
public Object eval(VirtualFrame frame, String expression, @SuppressWarnings("unused") PNone globals, @SuppressWarnings("unused") PNone locals) {
484-
Frame callerFrame = readCallerFrameNode.executeWith(frame);
485-
PythonObject callerGlobals = PArguments.getGlobals(callerFrame);
486-
PCell[] callerClosure = PArguments.getClosure(callerFrame);
487-
return evalExpression(expression, callerGlobals, callerGlobals, callerClosure, callerFrame, "<string>");
488-
}
489-
490-
@Specialization
491-
public Object eval(VirtualFrame frame, String expression, PythonObject globals, @SuppressWarnings("unused") PNone locals) {
492-
Frame callerFrame = readCallerFrameNode.executeWith(frame);
493-
return evalExpression(expression, globals, globals, null, callerFrame, null);
494-
}
495-
496-
@Specialization
497-
public Object eval(VirtualFrame frame, String expression, PythonObject globals, PythonObject locals) {
498-
Frame callerFrame = readCallerFrameNode.executeWith(frame);
499-
return evalExpression(expression, globals, locals, null, callerFrame, null);
500-
}
501-
502-
@Specialization
503-
public Object eval(VirtualFrame frame, String expression, @SuppressWarnings("unused") PNone globals, PythonObject locals) {
504-
Frame callerFrame = readCallerFrameNode.executeWith(frame);
505-
PythonObject callerGlobals = PArguments.getGlobals(callerFrame);
506-
PCell[] callerClosure = PArguments.getClosure(callerFrame);
507-
return evalExpression(expression, callerGlobals, locals, callerClosure, callerFrame, null);
508-
}
509-
510-
@Specialization
511-
public Object eval(VirtualFrame frame, PCode code, @SuppressWarnings("unused") PNone globals, @SuppressWarnings("unused") PNone locals,
512-
@Cached("createIdentityProfile()") ValueProfile constantCt) {
513-
Frame callerFrame = readCallerFrameNode.executeWith(frame);
514-
PythonObject callerGlobals = PArguments.getGlobals(callerFrame);
515-
PCell[] callerClosure = PArguments.getClosure(callerFrame);
516-
return evalExpression(constantCt.profile(code.getRootCallTarget()), callerGlobals, callerGlobals, callerClosure);
517-
}
518-
519-
@Specialization
520-
public Object eval(VirtualFrame frame, PCode code, PythonObject globals, @SuppressWarnings("unused") PNone locals,
521-
@Cached("createIdentityProfile()") ValueProfile constantCt) {
522-
Frame callerFrame = readCallerFrameNode.executeWith(frame);
523-
PCell[] callerClosure = PArguments.getClosure(callerFrame);
524-
return evalExpression(constantCt.profile(code.getRootCallTarget()), globals, globals, callerClosure);
525-
}
526-
527-
@Specialization
528-
public Object eval(VirtualFrame frame, PCode code, PythonObject globals, PythonObject locals,
529-
@Cached("createIdentityProfile()") ValueProfile constantCt) {
530-
Frame callerFrame = readCallerFrameNode.executeWith(frame);
531-
PCell[] callerClosure = PArguments.getClosure(callerFrame);
532-
return evalExpression(constantCt.profile(code.getRootCallTarget()), globals, locals, callerClosure);
533-
}
534-
535-
@Specialization
536-
public Object eval(VirtualFrame frame, PCode code, @SuppressWarnings("unused") PNone globals, PythonObject locals,
537-
@Cached("createIdentityProfile()") ValueProfile constantCt) {
538-
Frame callerFrame = readCallerFrameNode.executeWith(frame);
539-
PythonObject callerGlobals = PArguments.getGlobals(callerFrame);
540-
PCell[] callerClosure = PArguments.getClosure(callerFrame);
541-
return evalExpression(constantCt.profile(code.getRootCallTarget()), callerGlobals, locals, callerClosure);
542-
}
543-
544-
@Specialization
545-
public Object eval(VirtualFrame frame, PBytes expression, PNone globals, PNone locals) {
546-
return eval(frame, getString(expression), globals, locals);
547-
}
548-
549-
@Specialization
550-
public Object eval(VirtualFrame frame, PBytes expression, PythonObject globals, PNone locals) {
551-
return eval(frame, getString(expression), globals, locals);
552-
}
553-
554-
@Specialization
555-
public Object eval(VirtualFrame frame, PBytes expression, PythonObject globals, PythonObject locals) {
556-
return eval(frame, getString(expression), globals, locals);
557-
}
558-
559-
@Specialization
560-
public Object eval(VirtualFrame frame, PBytes expression, PNone globals, PythonObject locals) {
561-
return eval(frame, getString(expression), globals, locals);
562-
}
563-
564-
@TruffleBoundary
565-
private String getString(PBytes expression) {
566-
return new String(getByteArray(expression));
567-
}
473+
protected final String funcname = "eval";
474+
@Child protected CompileNode compileNode = CompileNode.create();
475+
@Child private IndirectCallNode indirectCallNode = IndirectCallNode.create();
476+
@Child private HasInheritedAttributeNode hasGetItemNode;
477+
@Child private HasInheritedAttributeNode hasSetItemNode;
568478

569-
private byte[] getByteArray(PIBytesLike pByteArray) {
570-
if (toByteArrayNode == null) {
479+
private HasInheritedAttributeNode getHasGetItemNode() {
480+
if (hasGetItemNode == null) {
571481
CompilerDirectives.transferToInterpreterAndInvalidate();
572-
toByteArrayNode = insert(SequenceStorageNodes.ToByteArrayNode.create());
482+
hasGetItemNode = insert(HasInheritedAttributeNode.create(SpecialMethodNames.__GETITEM__));
573483
}
574-
return toByteArrayNode.execute(pByteArray.getSequenceStorage());
575-
}
576-
577-
@TruffleBoundary
578-
private static Object evalExpression(RootCallTarget ct, PythonObject globals, PythonObject locals, PCell[] closure) {
579-
return evalNode(ct, globals, locals, closure);
484+
return hasGetItemNode;
580485
}
581486

582-
@TruffleBoundary
583-
private Object evalExpression(String expression, PythonObject globals, PythonObject locals, PCell[] closure, Frame callerFrame, String desiredName) {
584-
String name = desiredName;
585-
if (name == null) {
586-
name = "<eval>";
587-
if (globals instanceof PDict) {
588-
Object nameObject = getNameNode.execute(globals, __NAME__);
589-
if (nameObject instanceof String) {
590-
name = (String) nameObject;
591-
}
592-
}
487+
private HasInheritedAttributeNode getHasSetItemNode() {
488+
if (hasSetItemNode == null) {
489+
CompilerDirectives.transferToInterpreterAndInvalidate();
490+
hasSetItemNode = insert(HasInheritedAttributeNode.create(SpecialMethodNames.__SETITEM__));
593491
}
594-
PythonParser parser = getCore().getParser();
595-
Source source = PythonLanguage.newSource(getContext(), expression, name);
596-
RootCallTarget parsed = Truffle.getRuntime().createCallTarget((RootNode) parser.parse(ParserMode.Eval, getCore(), source, callerFrame));
597-
return evalNode(parsed, globals, locals, closure);
492+
return hasSetItemNode;
598493
}
599494

600-
/**
601-
* @param locals TODO: support the locals dictionary in execution
602-
*/
603-
@TruffleBoundary
604-
private static Object evalNode(RootCallTarget callTarget, PythonObject globals, PythonObject locals, PCell[] closure) {
605-
Object[] args = PArguments.create();
606-
PArguments.setGlobals(args, globals);
607-
PArguments.setClosure(args, closure);
608-
// TODO: cache code and CallTargets and use Direct/IndirectCallNode
609-
return callTarget.call(args);
610-
}
611-
}
612-
613-
@Builtin(name = EXEC, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 3, parameterNames = {"source"})
614-
@GenerateNodeFactory
615-
abstract static class ExecNode extends PythonBuiltinNode {
616-
private final BranchProfile hasFreeVars = BranchProfile.create();
617-
@Child private CompileNode compileNode = CompileNode.create();
618-
@Child private IndirectCallNode indirectCallNode = IndirectCallNode.create();
619-
@Child private IsMappingNode isMapping = IsMappingNode.create();
620-
621495
protected boolean isMapping(Object object) {
622-
return isMapping.execute(object);
496+
// tfel: it seems that CPython only checks that there is __getitem__ and __setitem__
497+
if (object instanceof PDict) {
498+
return true;
499+
} else {
500+
if (getHasGetItemNode().execute(object)) {
501+
return getHasSetItemNode().execute(object);
502+
}
503+
}
504+
return false;
623505
}
624506

625507
protected boolean isAnyNone(Object object) {
626508
return object instanceof PNone;
627509
}
628510

629-
private void assertNoFreeVars(PCode code) {
630-
RootNode rootNode = code.getRootNode();
631-
if (rootNode instanceof PClosureRootNode && ((PClosureRootNode) rootNode).hasFreeVars()) {
632-
hasFreeVars.enter();
633-
throw raise(PythonBuiltinClassType.TypeError, "code object passed to exec() may not contain free variables");
634-
}
635-
}
636-
637-
private PCode createAndCheckCode(Object source) {
638-
PCode code = compileNode.execute(source, "<string>", "exec", 0, false, -1);
639-
assertNoFreeVars(code);
640-
return code;
511+
protected PCode createAndCheckCode(Object source) {
512+
return compileNode.execute(source, "<string>", "eval", 0, false, -1);
641513
}
642514

643515
private static void inheritGlobals(Frame callerFrame, Object[] args) {
@@ -722,12 +594,33 @@ PNone execCustomGlobalsCustomLocals(Object source, PDict globals, Object locals,
722594

723595
@Specialization(guards = {"!isAnyNone(globals)", "!isDict(globals)"})
724596
PNone badGlobals(@SuppressWarnings("unused") Object source, Object globals, @SuppressWarnings("unused") Object locals) {
725-
throw raise(TypeError, "exec() globals must be a dict, not %p", globals);
597+
throw raise(TypeError, "%s() globals must be a dict, not %p", funcname, globals);
726598
}
727599

728600
@Specialization(guards = {"isAnyNone(globals) || isDict(globals)", "!isAnyNone(locals)", "!isMapping(locals)"})
729601
PNone badLocals(@SuppressWarnings("unused") Object source, @SuppressWarnings("unused") PDict globals, Object locals) {
730-
throw raise(TypeError, "exec() locals must be a mapping or None, not %p", locals);
602+
throw raise(TypeError, "%s() locals must be a mapping or None, not %p", funcname, locals);
603+
}
604+
}
605+
606+
@Builtin(name = EXEC, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 3, parameterNames = {"source"})
607+
@GenerateNodeFactory
608+
abstract static class ExecNode extends EvalNode {
609+
private final BranchProfile hasFreeVars = BranchProfile.create();
610+
611+
private void assertNoFreeVars(PCode code) {
612+
RootNode rootNode = code.getRootNode();
613+
if (rootNode instanceof PClosureRootNode && ((PClosureRootNode) rootNode).hasFreeVars()) {
614+
hasFreeVars.enter();
615+
throw raise(PythonBuiltinClassType.TypeError, "code object passed to exec() may not contain free variables");
616+
}
617+
}
618+
619+
@Override
620+
protected PCode createAndCheckCode(Object source) {
621+
PCode code = compileNode.execute(source, "<string>", "exec", 0, false, -1);
622+
assertNoFreeVars(code);
623+
return code;
731624
}
732625
}
733626

0 commit comments

Comments
 (0)