Skip to content

Commit ecd85ce

Browse files
committed
[GR-66558] Make eval/exec work on top-level in embedding
PullRequest: graalpython/3860
2 parents d1a6b55 + 28eb43a commit ecd85ce

File tree

2 files changed

+106
-13
lines changed

2 files changed

+106
-13
lines changed

graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/builtin/BuiltinFunctionTests.java

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2023, Oracle and/or its affiliates.
2+
* Copyright (c) 2017, 2025, Oracle and/or its affiliates.
33
* Copyright (c) 2013, Regents of the University of California
44
*
55
* All rights reserved.
@@ -28,6 +28,7 @@
2828
import static com.oracle.graal.python.test.integration.PythonTests.assertPrintContains;
2929
import static com.oracle.graal.python.test.integration.PythonTests.assertPrints;
3030
import static org.junit.Assert.assertEquals;
31+
import static org.junit.Assert.assertFalse;
3132
import static org.junit.Assert.assertTrue;
3233

3334
import org.graalvm.polyglot.Context;
@@ -382,4 +383,81 @@ public void cellType() {
382383
}
383384
}
384385

386+
@Test
387+
public void testExec() {
388+
Context context = PythonTests.enterContext();
389+
try {
390+
Value mainModule = context.getBindings("python");
391+
Value exec = context.eval("python", "exec");
392+
Value dict = context.eval("python", "dict");
393+
// Inherited globals and locals
394+
exec.execute("foo = 1");
395+
assertTrue(mainModule.hasMember("foo"));
396+
// Explicit globals
397+
Value globals = dict.newInstance();
398+
exec.execute("a = 1", globals);
399+
assertTrue(globals.hasHashEntry("a"));
400+
assertFalse(mainModule.hasMember("a"));
401+
// Explicit globals and locals
402+
Value locals = dict.newInstance();
403+
exec.execute("b = a", globals, locals);
404+
assertTrue(locals.hasHashEntry("b"));
405+
assertFalse(globals.hasHashEntry("b"));
406+
assertFalse(mainModule.hasMember("b"));
407+
// Inherited globals, explicit locals
408+
exec.execute("c = foo", mainModule.getMember("None"), locals);
409+
assertTrue(locals.hasHashEntry("c"));
410+
assertFalse(mainModule.hasMember("c"));
411+
} finally {
412+
PythonTests.closeContext();
413+
}
414+
}
415+
416+
@Test
417+
public void testEval() {
418+
Context context = PythonTests.enterContext();
419+
try {
420+
Value mainModule = context.getBindings("python");
421+
Value eval = context.eval("python", "eval");
422+
Value dict = context.eval("python", "dict");
423+
mainModule.putMember("foo", 1);
424+
// Inherited globals and locals
425+
Value result = eval.execute("foo");
426+
assertEquals(1, result.asInt());
427+
// Explicit globals
428+
Value globals = dict.newInstance();
429+
globals.putHashEntry("a", 2);
430+
result = eval.execute("a", globals);
431+
assertEquals(2, result.asInt());
432+
// Explicit globals and locals
433+
Value locals = dict.newInstance();
434+
locals.putHashEntry("b", 3);
435+
result = eval.execute("b", globals, locals);
436+
assertEquals(3, result.asInt());
437+
result = eval.execute("a", globals, locals);
438+
assertEquals(2, result.asInt());
439+
// Inherited globals, explicit locals
440+
Value none = mainModule.getMember("None");
441+
result = eval.execute("b", none, locals);
442+
assertEquals(3, result.asInt());
443+
result = eval.execute("foo", none, locals);
444+
assertEquals(1, result.asInt());
445+
} finally {
446+
PythonTests.closeContext();
447+
}
448+
}
449+
450+
@Test
451+
public void testCompile() {
452+
Context context = PythonTests.enterContext();
453+
try {
454+
Value compile = context.eval("python", "compile");
455+
Value eval = context.eval("python", "eval");
456+
Value code = compile.execute("1", "<input>", "eval");
457+
Value result = eval.execute(code);
458+
assertEquals(1, result.asInt());
459+
} finally {
460+
PythonTests.closeContext();
461+
}
462+
}
385463
}

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

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -788,16 +788,27 @@ abstract static class CreateEvalExecArgumentsNode extends Node {
788788
@Specialization
789789
static Object[] inheritGlobals(VirtualFrame frame, Node inliningTarget, @SuppressWarnings("unused") PNone globals, Object locals, TruffleString mode,
790790
@Exclusive @Cached ReadCallerFrameNode readCallerFrameNode,
791+
@Exclusive @Cached GetOrCreateDictNode getOrCreateDictNode,
792+
@Exclusive @Cached InlinedConditionProfile haveCallerFrameProfile,
791793
@Exclusive @Cached InlinedConditionProfile haveLocals,
792794
@Exclusive @Cached PyMappingCheckNode mappingCheckNode,
793795
@Exclusive @Cached GetFrameLocalsNode getFrameLocalsNode,
794796
@Exclusive @Cached PRaiseNode raiseNode) {
795797
PFrame callerFrame = readCallerFrameNode.executeWith(frame, 0);
796798
Object[] args = PArguments.create();
797-
PArguments.setGlobals(args, callerFrame.getGlobals());
799+
boolean haveCallerFrame = haveCallerFrameProfile.profile(inliningTarget, callerFrame != null);
800+
if (haveCallerFrame) {
801+
PArguments.setGlobals(args, callerFrame.getGlobals());
802+
} else {
803+
PArguments.setGlobals(args, getOrCreateDictNode.execute(inliningTarget, PythonContext.get(inliningTarget).getMainModule()));
804+
}
798805
if (haveLocals.profile(inliningTarget, locals instanceof PNone)) {
799-
Object callerLocals = getFrameLocalsNode.execute(inliningTarget, callerFrame);
800-
setCustomLocals(args, callerLocals);
806+
if (haveCallerFrame) {
807+
Object callerLocals = getFrameLocalsNode.execute(inliningTarget, callerFrame);
808+
setCustomLocals(args, callerLocals);
809+
} else {
810+
setCustomLocals(args, PArguments.getGlobals(args));
811+
}
801812
} else {
802813
if (!mappingCheckNode.execute(inliningTarget, locals)) {
803814
throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.LOCALS_MUST_BE_MAPPING, mode, locals);
@@ -951,15 +962,21 @@ public final PCode compile(VirtualFrame frame, Object source, TruffleString file
951962
protected abstract Object executeInternal(VirtualFrame frame, Object source, TruffleString filename, TruffleString mode, int flags, boolean dontInherit, int optimize,
952963
int featureVersion);
953964

965+
private int inheritFlags(VirtualFrame frame, int flags, ReadCallerFrameNode readCallerFrame) {
966+
PFrame fr = readCallerFrame.executeWith(frame, 0);
967+
if (fr != null) {
968+
PCode code = PFactory.createCode(PythonLanguage.get(this), fr.getTarget());
969+
flags |= code.getFlags() & PyCF_MASK;
970+
}
971+
return flags;
972+
}
973+
954974
@Specialization
955975
Object doCompile(VirtualFrame frame, TruffleString expression, TruffleString filename, TruffleString mode, int flags, boolean dontInherit, int optimize,
956976
int featureVersion,
957-
@Shared @Cached ReadCallerFrameNode readCallerFrame,
958-
@Bind PythonLanguage language) {
977+
@Shared @Cached ReadCallerFrameNode readCallerFrame) {
959978
if (!dontInherit) {
960-
PFrame fr = readCallerFrame.executeWith(frame, 0);
961-
PCode code = PFactory.createCode(language, fr.getTarget());
962-
flags |= code.getFlags() & PyCF_MASK;
979+
flags = inheritFlags(frame, flags, readCallerFrame);
963980
}
964981
EncapsulatingNodeReference encapsulating = EncapsulatingNodeReference.getCurrent();
965982
Node encapsulatingNode = encapsulating.set(this);
@@ -1037,7 +1054,7 @@ Object compile(TruffleString expression, TruffleString filename, TruffleString m
10371054

10381055
@Specialization(limit = "3")
10391056
@SuppressWarnings("truffle-static-method")
1040-
Object generic(VirtualFrame frame, Object wSource, Object wFilename, TruffleString mode, int flags, @SuppressWarnings("unused") boolean dontInherit, int optimize, int featureVersion,
1057+
Object generic(VirtualFrame frame, Object wSource, Object wFilename, TruffleString mode, int flags, boolean dontInherit, int optimize, int featureVersion,
10411058
@CachedLibrary(limit = "3") PythonBufferAcquireLibrary acquireLib,
10421059
@CachedLibrary(limit = "3") PythonBufferAccessLibrary bufferLib,
10431060
@Bind PythonContext context,
@@ -1056,9 +1073,7 @@ Object generic(VirtualFrame frame, Object wSource, Object wFilename, TruffleStri
10561073
TruffleString filename = asPath.execute(frame, wFilename);
10571074

10581075
if (!dontInherit) {
1059-
PFrame fr = readCallerFrame.executeWith(frame, 0);
1060-
PCode code = PFactory.createCode(PythonLanguage.get(inliningTarget), fr.getTarget());
1061-
flags |= code.getFlags() & PyCF_MASK;
1076+
flags = inheritFlags(frame, flags, readCallerFrame);
10621077
}
10631078

10641079
EncapsulatingNodeReference encapsulating = EncapsulatingNodeReference.getCurrent();

0 commit comments

Comments
 (0)