From 7851623770d18a09615e33208e70885166ddf30f Mon Sep 17 00:00:00 2001 From: Jeongseop Lim Date: Tue, 20 Oct 2026 02:33:14 +0900 Subject: [PATCH] Validate the compile() mode before the source compile() resolved mode with getParserInputType only inside the string compile path, after sourceAsString had converted the source and checkSource had looked for null bytes, and checked that func_type comes with PyCF_ONLY_AST later still. So compile(None, 'f', 'x') raised TypeError, a source with a null byte or bytes that fail to decode raised SyntaxError, and an AST source with mode 'func_type' but no PyCF_ONLY_AST reached obj2sst and hit ShouldNotReachHere. CPython reports all of these as ValueError. CPython's builtin_compile_impl validates mode, including the func_type flag requirement, right after the flags and optimize checks and before it touches the source. getParserInputType now does the func_type check, generic resolves the input type at that point and reuses it for the AST path, and compile() resolves it before checkSource. Signed-off-by: Jeongseop Lim --- .../src/tests/test_parser.py | 19 +++++++++++++++++++ .../builtins/modules/BuiltinFunctions.java | 13 ++++++------- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_parser.py b/graalpython/com.oracle.graal.python.test/src/tests/test_parser.py index 5941b6668d..bfe1049875 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_parser.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_parser.py @@ -42,6 +42,15 @@ import types +def assert_raises(err, fn, *args, **kwargs): + raised = False + try: + fn(*args, **kwargs) + except err: + raised = True + assert raised + + def test_incomplete_input_error(): flags = 0x4000 | 0x200 # PyCF_ALLOW_INCOMPLETE_INPUT | PyCF_DONT_IMPLY_DEDENT @@ -152,6 +161,16 @@ def newhook(value): sys.displayhook = oldhook +def test_compile_checks_mode_before_source(): + assert_raises(ValueError, compile, None, "", "x") + assert_raises(ValueError, compile, "\x00", "", "x") + assert_raises(ValueError, compile, b"\xff", "", "x") + assert_raises(ValueError, compile, b"\x00", "", "x") + assert_raises(ValueError, compile, None, "", "x", ast.PyCF_ONLY_AST) + assert_raises(ValueError, compile, None, "", "func_type") + assert_raises(ValueError, compile, ast.parse("1"), "", "func_type") + + def test_underscore_in_numbers(): assert eval('1_0') == 10 assert eval('0b1_1') == 0b11 diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java index cd369906d2..d55b2dc268 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java @@ -1011,16 +1011,11 @@ Object doCompile(VirtualFrame frame, TruffleString expression, TruffleString fil Object compile(TruffleString expression, TruffleString filename, TruffleString mode, int flags, @SuppressWarnings("unused") boolean dontInherit, int optimize, int featureVersion) { checkFlags(flags); checkOptimize(optimize, optimize); + InputType type = getParserInputType(mode, flags); checkSource(expression); TruffleString code = expression; PythonContext context = getContext(); - InputType type = getParserInputType(mode, flags); - if (type == InputType.FUNCTION_TYPE) { - if ((flags & PyCF_ONLY_AST) == 0) { - throw PRaiseNode.raiseStatic(this, ValueError, ErrorMessages.COMPILE_MODE_FUNC_TYPE_REQUIED_FLAG_ONLY_AST); - } - } if (lstrip && !code.isEmpty()) { int c = code.codePointAtIndexUncached(0, TS_ENCODING); if (c == ' ' || c == '\t') { @@ -1087,11 +1082,12 @@ Object generic(VirtualFrame frame, Object wSource, Object wFilename, TruffleStri } checkFlags(flags); checkOptimize(optimize, optimize); + InputType type = getParserInputType(mode, flags); Object saved = BoundaryCallContext.enter(frame, boundaryCallData); try { if (AstModuleBuiltins.isAst(context, wSource)) { - ModTy mod = AstModuleBuiltins.obj2sst(inliningTarget, context, wSource, getParserInputType(mode, flags)); + ModTy mod = AstModuleBuiltins.obj2sst(inliningTarget, context, wSource, type); if ((flags & PyCF_ONLY_AST) != 0) { if ((flags & PyCF_OPTIMIZED_AST) != PyCF_OPTIMIZED_AST) { return wSource; @@ -1156,6 +1152,9 @@ private InputType getParserInputType(TruffleString mode, int flags) { } else if (mode.equalsUncached(StringLiterals.T_SINGLE, TS_ENCODING)) { return InputType.SINGLE; } else if (mode.equalsUncached(StringLiterals.T_FUNC_TYPE, TS_ENCODING)) { + if ((flags & PyCF_ONLY_AST) == 0) { + throw PRaiseNode.raiseStatic(this, ValueError, ErrorMessages.COMPILE_MODE_FUNC_TYPE_REQUIED_FLAG_ONLY_AST); + } return InputType.FUNCTION_TYPE; } else { if ((flags & PyCF_ONLY_AST) != 0) {