3939import java .lang .invoke .VarHandle ;
4040import java .nio .charset .StandardCharsets ;
4141import java .nio .file .InvalidPathException ;
42- import java .util .ArrayList ;
4342import java .util .Arrays ;
4443import java .util .Collections ;
4544import java .util .EnumSet ;
46- import java .util .HashSet ;
4745import java .util .List ;
4846import java .util .Map ;
4947import java .util .WeakHashMap ;
108106import com .oracle .graal .python .runtime .PythonContext .PythonThreadState ;
109107import com .oracle .graal .python .runtime .PythonImageBuildOptions ;
110108import com .oracle .graal .python .runtime .PythonOptions ;
109+ import com .oracle .graal .python .runtime .PythonSourceOptions ;
111110import com .oracle .graal .python .runtime .exception .PException ;
112111import com .oracle .graal .python .runtime .object .PFactory ;
113112import com .oracle .graal .python .util .Function ;
155154 sandbox = SandboxPolicy .UNTRUSTED , //
156155 implementationName = PythonLanguage .IMPLEMENTATION_NAME , //
157156 version = PythonLanguage .VERSION , //
158- characterMimeTypes = {PythonLanguage .MIME_TYPE ,
159- "text/x-python-\0 \u0000 -eval" , "text/x-python-\0 \u0000 -compile" , "text/x-python-\1 \u0000 -eval" , "text/x-python-\1 \u0000 -compile" , "text/x-python-\2 \u0000 -eval" ,
160- "text/x-python-\2 \u0000 -compile" , "text/x-python-\0 \u0100 -eval" , "text/x-python-\0 \u0100 -compile" , "text/x-python-\1 \u0100 -eval" , "text/x-python-\1 \u0100 -compile" ,
161- "text/x-python-\2 \u0100 -eval" , "text/x-python-\2 \u0100 -compile" , "text/x-python-\0 \u0040 -eval" , "text/x-python-\0 \u0040 -compile" , "text/x-python-\1 \u0040 -eval" ,
162- "text/x-python-\1 \u0040 -compile" , "text/x-python-\2 \u0040 -eval" , "text/x-python-\2 \u0040 -compile" , "text/x-python-\0 \u0140 -eval" , "text/x-python-\0 \u0140 -compile" ,
163- "text/x-python-\1 \u0140 -eval" , "text/x-python-\1 \u0140 -compile" , "text/x-python-\2 \u0140 -eval" , "text/x-python-\2 \u0140 -compile" }, //
157+ characterMimeTypes = {PythonLanguage .MIME_TYPE }, //
164158 defaultMimeType = PythonLanguage .MIME_TYPE , //
165159 dependentLanguages = {"nfi" , "llvm" }, //
166160 interactive = true , internal = false , //
@@ -274,48 +268,6 @@ public final class PythonLanguage extends TruffleLanguage<PythonContext> {
274268
275269 public static final String MIME_TYPE = "text/x-python" ;
276270
277- // the syntax for mime types is as follows
278- // <mime> ::= "text/x-python-" <optlevel> <flags> "-" kind
279- // <kind> ::= "compile" | "eval"
280- // <optlevel> ::= "\0" | "\1" | "\2"
281- // <flags> ::= "\u0040" | "\u0100" | "\u0140" | "\u0000"
282- // where 0100 implies annotations, and 0040 implies barry_as_flufl
283- static final String MIME_PREFIX = MIME_TYPE + "-" ;
284- static final int OPT_FLAGS_LEN = 2 ; // 1 char is optlevel, 1 char is flags
285- static final String MIME_KIND_COMPILE = "compile" ;
286- static final String MIME_KIND_EVAL = "eval" ;
287- // Since flags are greater than the highest unicode codepoint, we shift them into more
288- // reasonable values in the mime type. 4 hex digits
289- static final int MIME_FLAG_SHIFTBY = 4 * 4 ;
290- // a dash follows after the opt flag pair
291- static final int MIME_KIND_START = MIME_PREFIX .length () + OPT_FLAGS_LEN + 1 ;
292-
293- private static boolean mimeTypesComplete (ArrayList <String > mimeJavaStrings ) {
294- ArrayList <String > mimeTypes = new ArrayList <>();
295- FutureFeature [] all = FutureFeature .values ();
296- for (int flagset = 0 ; flagset < (1 << all .length ); ++flagset ) {
297- int flags = 0 ;
298- for (int i = 0 ; i < all .length ; ++i ) {
299- if ((flagset & (1 << i )) != 0 ) {
300- flags |= all [i ].flagValue ;
301- }
302- }
303- for (int opt = 0 ; opt <= 2 ; opt ++) {
304- for (String typ : new String []{MIME_KIND_EVAL , MIME_KIND_COMPILE }) {
305- mimeTypes .add (MIME_PREFIX + optFlagsToMime (opt , flags ) + "-" + typ );
306- mimeJavaStrings .add (String .format ("\" %s\\ %d\\ u%04x-%s\" " , MIME_PREFIX , opt , flags >> MIME_FLAG_SHIFTBY , typ ));
307- }
308- }
309- }
310- HashSet <String > currentMimeTypes = new HashSet <>(List .of (PythonLanguage .class .getAnnotation (Registration .class ).characterMimeTypes ()));
311- return currentMimeTypes .containsAll (mimeTypes );
312- }
313-
314- static {
315- ArrayList <String > mimeJavaStrings = new ArrayList <>();
316- assert mimeTypesComplete (mimeJavaStrings ) : "Expected all of {" + String .join (", " , mimeJavaStrings ) + "} in the PythonLanguage characterMimeTypes" ;
317- }
318-
319271 public static final TruffleString [] T_DEFAULT_PYTHON_EXTENSIONS = new TruffleString []{T_PY_EXTENSION , tsLiteral (".pyc" )};
320272
321273 public static final TruffleLogger LOGGER = TruffleLogger .getLogger (ID , PythonLanguage .class );
@@ -515,6 +467,11 @@ protected OptionDescriptors getOptionDescriptors() {
515467 return PythonOptions .DESCRIPTORS ;
516468 }
517469
470+ @ Override
471+ protected OptionDescriptors getSourceOptionDescriptors () {
472+ return PythonSourceOptions .DESCRIPTORS ;
473+ }
474+
518475 @ Override
519476 protected void initializeContext (PythonContext context ) {
520477 if (!isLanguageInitialized ) {
@@ -539,64 +496,56 @@ private synchronized void initializeLanguage() {
539496 }
540497 }
541498
542- private static String optFlagsToMime (int optimize , int flags ) {
543- if (optimize < 0 ) {
544- optimize = 0 ;
545- } else if (optimize > 2 ) {
546- optimize = 2 ;
547- }
548- String optField = new String (new byte []{(byte ) optimize });
549- String flagField = new String (new int []{(flags & FutureFeature .ALL_FLAGS ) >> MIME_FLAG_SHIFTBY }, 0 , 1 );
550- assert flagField .length () == 1 : "flags in mime type ended up a surrogate" ;
551- return optField + flagField ;
552- }
553-
554- public static String getCompileMimeType (int optimize , int flags ) {
555- String optFlags = optFlagsToMime (optimize , flags );
556- return MIME_PREFIX + optFlags + "-compile" ;
557- }
558-
559- public static String getEvalMimeType (int optimize , int flags ) {
560- String optFlags = optFlagsToMime (optimize , flags );
561- return MIME_PREFIX + optFlags + "-eval" ;
499+ public static SourceBuilder setPythonOptions (SourceBuilder sourceBuilder , InputType kind , int optimize , int flags ) {
500+ String sourceKind = switch (kind ) {
501+ case FILE -> "file" ;
502+ case EVAL -> "eval" ;
503+ case SINGLE -> "single" ;
504+ default -> throw CompilerDirectives .shouldNotReachHere ("unsupported source kind: " + kind );
505+ };
506+ return sourceBuilder .mimeType (PythonLanguage .MIME_TYPE ) //
507+ .option ("python.Optimize" , Integer .toString (optimize )) //
508+ .option ("python.Flags" , Integer .toString (flags & FutureFeature .ALL_FLAGS )) //
509+ .option ("python.Kind" , sourceKind );
562510 }
563511
564512 @ Override
565513 protected CallTarget parse (ParsingRequest request ) {
566514 PythonContext context = PythonContext .get (null );
567515 Source source = request .getSource ();
568- if (source .getMimeType () == null || MIME_TYPE .equals (source .getMimeType ())) {
569- if (!request .getArgumentNames ().isEmpty () && source .isInteractive ()) {
570- throw new IllegalStateException ("parse with arguments not allowed for interactive sources" );
571- }
572- InputType inputType = source .isInteractive () ? InputType .SINGLE : InputType .FILE ;
573- return parse (context , source , inputType , true , 0 , source .isInteractive (), request .getArgumentNames (), EnumSet .noneOf (FutureFeature .class ));
516+ if (!request .getArgumentNames ().isEmpty () && source .isInteractive ()) {
517+ throw new IllegalStateException ("parse with arguments not allowed for interactive sources" );
574518 }
575- if (!request .getArgumentNames ().isEmpty ()) {
576- throw new IllegalStateException ("parse with arguments is only allowed for " + MIME_TYPE + " mime type" );
577- }
578-
579- String mime = source .getMimeType ();
580- String prefix = mime .substring (0 , MIME_PREFIX .length ());
581- if (!prefix .equals (MIME_PREFIX )) {
582- throw CompilerDirectives .shouldNotReachHere ("unknown mime type: " + mime );
583- }
584- String kind = mime .substring (MIME_KIND_START );
585- InputType type ;
586- if (kind .equals (MIME_KIND_COMPILE )) {
587- type = InputType .FILE ;
588- } else if (kind .equals (MIME_KIND_EVAL )) {
589- type = InputType .EVAL ;
519+ InputType inputType ;
520+ int optimize ;
521+ EnumSet <FutureFeature > futureFeatures ;
522+ boolean topLevel ;
523+ List <String > argumentNames ;
524+ boolean interactiveTerminal ;
525+ if (source .isInteractive ()) {
526+ inputType = InputType .SINGLE ;
527+ optimize = 0 ;
528+ futureFeatures = EnumSet .noneOf (FutureFeature .class );
529+ topLevel = true ;
530+ argumentNames = request .getArgumentNames ();
531+ interactiveTerminal = true ;
590532 } else {
591- throw CompilerDirectives .shouldNotReachHere ("unknown compilation kind: " + kind + " from mime type: " + mime );
592- }
593- int optimize = mime .codePointAt (MIME_PREFIX .length ());
594- int flags = mime .codePointAt (MIME_PREFIX .length () + 1 ) << MIME_FLAG_SHIFTBY ;
595- if (0 > optimize || optimize > 2 || (flags & ~FutureFeature .ALL_FLAGS ) != 0 ) {
596- throw CompilerDirectives .shouldNotReachHere ("Invalid value for optlevel or flags: " + optimize + "," + flags + " from mime type: " + mime );
533+ var sourceOptions = source .getOptions (this );
534+ String kind = sourceOptions .get (PythonSourceOptions .Kind );
535+ topLevel = kind .isEmpty ();
536+ inputType = switch (kind ) {
537+ case "" , "file" -> InputType .FILE ;
538+ case "eval" -> InputType .EVAL ;
539+ case "single" -> InputType .SINGLE ;
540+ default -> throw CompilerDirectives .shouldNotReachHere ("unknown compilation kind: " + kind );
541+ };
542+ optimize = sourceOptions .get (PythonSourceOptions .Optimize );
543+ int flags = sourceOptions .get (PythonSourceOptions .Flags );
544+ futureFeatures = FutureFeature .fromFlags (flags );
545+ argumentNames = request .getArgumentNames ().isEmpty () ? null : request .getArgumentNames ();
546+ interactiveTerminal = false ;
597547 }
598- assert !source .isInteractive ();
599- return parse (context , source , type , false , optimize , false , null , FutureFeature .fromFlags (flags ));
548+ return parse (context , source , inputType , topLevel , optimize , interactiveTerminal , argumentNames , futureFeatures );
600549 }
601550
602551 public static RootCallTarget callTargetFromBytecode (PythonContext context , Source source , CodeUnit code ) {
@@ -952,7 +901,7 @@ public static TruffleLogger getCompatibilityLogger(Class<?> clazz) {
952901 return TruffleLogger .getLogger (ID , "compatibility." + clazz .getName ());
953902 }
954903
955- public static Source newSource (PythonContext ctxt , TruffleString tsrc , TruffleString name , boolean mayBeFile , String mime ) {
904+ public static Source newSource (PythonContext ctxt , TruffleString tsrc , TruffleString name , boolean mayBeFile , InputType inputType , int optimize , int flags ) {
956905 try {
957906 SourceBuilder sourceBuilder = null ;
958907 String src = tsrc .toJavaStringUncached ();
@@ -977,9 +926,7 @@ public static Source newSource(PythonContext ctxt, TruffleString tsrc, TruffleSt
977926 if (sourceBuilder == null ) {
978927 sourceBuilder = Source .newBuilder (ID , src , name .toJavaStringUncached ());
979928 }
980- if (mime != null ) {
981- sourceBuilder .mimeType (mime );
982- }
929+ sourceBuilder = PythonLanguage .setPythonOptions (sourceBuilder , inputType , optimize , flags );
983930 return newSource (ctxt , sourceBuilder );
984931 } catch (IOException e ) {
985932 throw new IllegalStateException (e );
0 commit comments