35
35
36
36
import java .io .IOException ;
37
37
import java .nio .file .InvalidPathException ;
38
+ import java .util .ArrayList ;
38
39
import java .util .Arrays ;
39
40
import java .util .EnumSet ;
41
+ import java .util .HashSet ;
40
42
import java .util .List ;
41
43
import java .util .concurrent .ConcurrentHashMap ;
42
44
import java .util .concurrent .Semaphore ;
74
76
import com .oracle .graal .python .nodes .exception .TopLevelExceptionHandler ;
75
77
import com .oracle .graal .python .nodes .frame .GetFrameLocalsNode ;
76
78
import com .oracle .graal .python .nodes .frame .MaterializeFrameNode ;
79
+ import com .oracle .graal .python .pegparser .FutureFeature ;
77
80
import com .oracle .graal .python .pegparser .InputType ;
78
81
import com .oracle .graal .python .pegparser .NodeFactory ;
79
82
import com .oracle .graal .python .pegparser .Parser ;
132
135
implementationName = PythonLanguage .IMPLEMENTATION_NAME , //
133
136
version = PythonLanguage .VERSION , //
134
137
characterMimeTypes = {PythonLanguage .MIME_TYPE ,
135
- PythonLanguage .MIME_TYPE_COMPILE0 , PythonLanguage .MIME_TYPE_COMPILE1 , PythonLanguage .MIME_TYPE_COMPILE2 ,
136
- PythonLanguage .MIME_TYPE_EVAL0 , PythonLanguage .MIME_TYPE_EVAL1 , PythonLanguage .MIME_TYPE_EVAL2 }, //
138
+ "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" ,
139
+ "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" ,
140
+ "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" ,
141
+ "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" ,
142
+ "text/x-python-\1 \u0140 -eval" , "text/x-python-\1 \u0140 -compile" , "text/x-python-\2 \u0140 -eval" , "text/x-python-\2 \u0140 -compile" }, //
137
143
byteMimeTypes = {PythonLanguage .MIME_TYPE_BYTECODE }, //
138
144
defaultMimeType = PythonLanguage .MIME_TYPE , //
139
145
dependentLanguages = {"nfi" , "llvm" }, //
@@ -206,14 +212,49 @@ public final class PythonLanguage extends TruffleLanguage<PythonContext> {
206
212
public static final int API_VERSION = 1013 ;
207
213
208
214
public static final String MIME_TYPE = "text/x-python" ;
209
- static final String MIME_TYPE_COMPILE0 = "text/x-python-compile0" ;
210
- static final String MIME_TYPE_COMPILE1 = "text/x-python-compile1" ;
211
- static final String MIME_TYPE_COMPILE2 = "text/x-python-compile2" ;
212
- static final String [] MIME_TYPE_COMPILE = {PythonLanguage .MIME_TYPE_COMPILE0 , PythonLanguage .MIME_TYPE_COMPILE1 , PythonLanguage .MIME_TYPE_COMPILE2 };
213
- static final String [] MIME_TYPE_EVAL = {PythonLanguage .MIME_TYPE_EVAL0 , PythonLanguage .MIME_TYPE_EVAL1 , PythonLanguage .MIME_TYPE_EVAL2 };
214
- static final String MIME_TYPE_EVAL0 = "text/x-python-eval0" ;
215
- static final String MIME_TYPE_EVAL1 = "text/x-python-eval1" ;
216
- static final String MIME_TYPE_EVAL2 = "text/x-python-eval2" ;
215
+
216
+ // the syntax for mime types is as follows
217
+ // <mime> ::= "text/x-python-" <optlevel> <flags> "-" kind
218
+ // <kind> ::= "compile" | "eval"
219
+ // <optlevel> ::= "\0" | "\1" | "\2"
220
+ // <flags> ::= "\u0040" | "\u0100" | "\u0140" | "\u0000"
221
+ // where 0100 implies annotations, and 0040 implies barry_as_flufl
222
+ static final String MIME_PREFIX = MIME_TYPE + "-" ;
223
+ static final int OPT_FLAGS_LEN = 2 ; // 1 char is optlevel, 1 char is flags
224
+ static final String MIME_KIND_COMPILE = "compile" ;
225
+ static final String MIME_KIND_EVAL = "eval" ;
226
+ // Since flags are greater than the highest unicode codepoint, we shift them into more
227
+ // reasonable values in the mime type. 4 hex digits
228
+ static final int MIME_FLAG_SHIFTBY = 4 * 4 ;
229
+ // a dash follows after the opt flag pair
230
+ static final int MIME_KIND_START = MIME_PREFIX .length () + OPT_FLAGS_LEN + 1 ;
231
+
232
+ private static boolean mimeTypesComplete (ArrayList <String > mimeJavaStrings ) {
233
+ ArrayList <String > mimeTypes = new ArrayList <>();
234
+ FutureFeature [] all = FutureFeature .values ();
235
+ for (int flagset = 0 ; flagset < (1 << all .length ); ++flagset ) {
236
+ int flags = 0 ;
237
+ for (int i = 0 ; i < all .length ; ++i ) {
238
+ if ((flagset & (1 << i )) != 0 ) {
239
+ flags |= all [i ].flagValue ;
240
+ }
241
+ }
242
+ for (int opt = 0 ; opt <= 2 ; opt ++) {
243
+ for (String typ : new String []{MIME_KIND_EVAL , MIME_KIND_COMPILE }) {
244
+ mimeTypes .add (MIME_PREFIX + optFlagsToMime (opt , flags ) + "-" + typ );
245
+ mimeJavaStrings .add (String .format ("\" %s\\ %d\\ u%04x-%s\" " , MIME_PREFIX , opt , flags >> MIME_FLAG_SHIFTBY , typ ));
246
+ }
247
+ }
248
+ }
249
+ HashSet <String > currentMimeTypes = new HashSet <>(List .of (PythonLanguage .class .getAnnotation (Registration .class ).characterMimeTypes ()));
250
+ return currentMimeTypes .containsAll (mimeTypes );
251
+ }
252
+
253
+ static {
254
+ ArrayList <String > mimeJavaStrings = new ArrayList <>();
255
+ assert mimeTypesComplete (mimeJavaStrings ) : "Expected all of {" + String .join (", " , mimeJavaStrings ) + "} in the PythonLanguage characterMimeTypes" ;
256
+ }
257
+
217
258
public static final String MIME_TYPE_BYTECODE = "application/x-python-bytecode" ;
218
259
219
260
public static final TruffleString [] T_DEFAULT_PYTHON_EXTENSIONS = new TruffleString []{T_PY_EXTENSION , tsLiteral (".pyc" )};
@@ -388,24 +429,26 @@ protected void initializeContext(PythonContext context) {
388
429
context .initialize ();
389
430
}
390
431
391
- public static String getCompileMimeType (int optimize ) {
392
- if (optimize <= 0 ) {
393
- return MIME_TYPE_COMPILE0 ;
394
- } else if (optimize == 1 ) {
395
- return MIME_TYPE_COMPILE1 ;
396
- } else {
397
- return MIME_TYPE_COMPILE2 ;
432
+ private static String optFlagsToMime (int optimize , int flags ) {
433
+ if (optimize < 0 ) {
434
+ optimize = 0 ;
435
+ } else if (optimize > 2 ) {
436
+ optimize = 2 ;
398
437
}
438
+ String optField = new String (new byte []{(byte ) optimize });
439
+ String flagField = new String (new int []{(flags & FutureFeature .ALL_FLAGS ) >> MIME_FLAG_SHIFTBY }, 0 , 1 );
440
+ assert flagField .length () == 1 : "flags in mime type ended up a surrogate" ;
441
+ return optField + flagField ;
399
442
}
400
443
401
- public static String getEvalMimeType (int optimize ) {
402
- if ( optimize <= 0 ) {
403
- return MIME_TYPE_EVAL0 ;
404
- } else if ( optimize == 1 ) {
405
- return MIME_TYPE_EVAL1 ;
406
- } else {
407
- return MIME_TYPE_EVAL2 ;
408
- }
444
+ public static String getCompileMimeType (int optimize , int flags ) {
445
+ String optFlags = optFlagsToMime ( optimize , flags );
446
+ return MIME_PREFIX + optFlags + "-compile" ;
447
+ }
448
+
449
+ public static String getEvalMimeType ( int optimize , int flags ) {
450
+ String optFlags = optFlagsToMime ( optimize , flags ) ;
451
+ return MIME_PREFIX + optFlags + "-eval" ;
409
452
}
410
453
411
454
@ Override
@@ -417,7 +460,7 @@ protected CallTarget parse(ParsingRequest request) {
417
460
throw new IllegalStateException ("parse with arguments not allowed for interactive sources" );
418
461
}
419
462
InputType inputType = source .isInteractive () ? InputType .SINGLE : InputType .FILE ;
420
- return parse (context , source , inputType , true , 0 , source .isInteractive (), request .getArgumentNames ());
463
+ return parse (context , source , inputType , true , 0 , source .isInteractive (), request .getArgumentNames (), EnumSet . noneOf ( FutureFeature . class ) );
421
464
}
422
465
if (!request .getArgumentNames ().isEmpty ()) {
423
466
throw new IllegalStateException ("parse with arguments is only allowed for " + MIME_TYPE + " mime type" );
@@ -452,19 +495,28 @@ protected CallTarget parse(ParsingRequest request) {
452
495
PBytecodeRootNode rootNode = PBytecodeRootNode .create (this , code , source );
453
496
return PythonUtils .getOrCreateCallTarget (rootNode );
454
497
}
455
- for ( int optimize = 0 ; optimize < MIME_TYPE_EVAL . length ; optimize ++) {
456
- if ( MIME_TYPE_EVAL [ optimize ]. equals ( source .getMimeType ())) {
457
- assert ! source . isInteractive ( );
458
- return parse ( context , source , InputType . EVAL , false , optimize , false , null );
459
- }
498
+
499
+ String mime = source .getMimeType ();
500
+ String prefix = mime . substring ( 0 , MIME_PREFIX . length () );
501
+ if (! prefix . equals ( MIME_PREFIX )) {
502
+ throw CompilerDirectives . shouldNotReachHere ( "unknown mime type: " + mime );
460
503
}
461
- for (int optimize = 0 ; optimize < MIME_TYPE_COMPILE .length ; optimize ++) {
462
- if (MIME_TYPE_COMPILE [optimize ].equals (source .getMimeType ())) {
463
- assert !source .isInteractive ();
464
- return parse (context , source , InputType .FILE , false , optimize , false , null );
465
- }
504
+ String kind = mime .substring (MIME_KIND_START );
505
+ InputType type ;
506
+ if (kind .equals (MIME_KIND_COMPILE )) {
507
+ type = InputType .FILE ;
508
+ } else if (kind .equals (MIME_KIND_EVAL )) {
509
+ type = InputType .EVAL ;
510
+ } else {
511
+ throw CompilerDirectives .shouldNotReachHere ("unknown compilation kind: " + kind + " from mime type: " + mime );
512
+ }
513
+ int optimize = mime .codePointAt (MIME_PREFIX .length ());
514
+ int flags = mime .codePointAt (MIME_PREFIX .length () + 1 ) << MIME_FLAG_SHIFTBY ;
515
+ if (0 > optimize || optimize > 2 || (flags & ~FutureFeature .ALL_FLAGS ) != 0 ) {
516
+ throw CompilerDirectives .shouldNotReachHere ("Invalid value for optlevel or flags: " + optimize + "," + flags + " from mime type: " + mime );
466
517
}
467
- throw CompilerDirectives .shouldNotReachHere ("unknown mime type: " + source .getMimeType ());
518
+ assert !source .isInteractive ();
519
+ return parse (context , source , type , false , optimize , false , null , FutureFeature .fromFlags (flags ));
468
520
}
469
521
470
522
private static Source tryLoadSource (PythonContext context , CodeUnit code , boolean internal , String path ) {
@@ -475,13 +527,14 @@ private static Source tryLoadSource(PythonContext context, CodeUnit code, boolea
475
527
}
476
528
}
477
529
478
- public RootCallTarget parse (PythonContext context , Source source , InputType type , boolean topLevel , int optimize , boolean interactiveTerminal , List <String > argumentNames ) {
530
+ public RootCallTarget parse (PythonContext context , Source source , InputType type , boolean topLevel , int optimize , boolean interactiveTerminal , List <String > argumentNames ,
531
+ EnumSet <FutureFeature > futureFeatures ) {
479
532
RaisePythonExceptionErrorCallback errorCb = new RaisePythonExceptionErrorCallback (source , PythonOptions .isPExceptionWithJavaStacktrace (this ));
480
533
try {
481
534
Parser parser = Compiler .createParser (source .getCharacters ().toString (), errorCb , type , interactiveTerminal );
482
535
ModTy mod = (ModTy ) parser .parse ();
483
536
assert mod != null ;
484
- return compileForBytecodeInterpreter (context , mod , source , topLevel , optimize , argumentNames , errorCb );
537
+ return compileForBytecodeInterpreter (context , mod , source , topLevel , optimize , argumentNames , errorCb , futureFeatures );
485
538
} catch (PException e ) {
486
539
if (topLevel ) {
487
540
PythonUtils .getOrCreateCallTarget (new TopLevelExceptionHandler (this , e )).call ();
@@ -492,7 +545,13 @@ public RootCallTarget parse(PythonContext context, Source source, InputType type
492
545
493
546
@ TruffleBoundary
494
547
public RootCallTarget compileForBytecodeInterpreter (PythonContext context , ModTy mod , Source source , boolean topLevel , int optimize , List <String > argumentNames ,
495
- RaisePythonExceptionErrorCallback errorCallback ) {
548
+ RaisePythonExceptionErrorCallback errorCallback , int flags ) {
549
+ return compileForBytecodeInterpreter (context , mod , source , topLevel , optimize , argumentNames , errorCallback , FutureFeature .fromFlags (flags ));
550
+ }
551
+
552
+ @ TruffleBoundary
553
+ public RootCallTarget compileForBytecodeInterpreter (PythonContext context , ModTy mod , Source source , boolean topLevel , int optimize , List <String > argumentNames ,
554
+ RaisePythonExceptionErrorCallback errorCallback , EnumSet <FutureFeature > futureFeatures ) {
496
555
RaisePythonExceptionErrorCallback errorCb = errorCallback ;
497
556
if (errorCb == null ) {
498
557
errorCb = new RaisePythonExceptionErrorCallback (source , PythonOptions .isPExceptionWithJavaStacktrace (this ));
@@ -503,7 +562,7 @@ public RootCallTarget compileForBytecodeInterpreter(PythonContext context, ModTy
503
562
if (hasArguments ) {
504
563
mod = transformASTForExecutionWithArguments (argumentNames , mod );
505
564
}
506
- CompilationUnit cu = compiler .compile (mod , EnumSet .noneOf (Compiler .Flags .class ), optimize );
565
+ CompilationUnit cu = compiler .compile (mod , EnumSet .noneOf (Compiler .Flags .class ), optimize , futureFeatures );
507
566
CodeUnit co = cu .assemble ();
508
567
RootNode rootNode = PBytecodeRootNode .create (this , co , source , errorCb );
509
568
if (topLevel ) {
@@ -588,7 +647,7 @@ public String getName() {
588
647
@ Override
589
648
public ExecutableNode parse (InlineParsingRequest request ) {
590
649
PythonContext context = PythonContext .get (null );
591
- RootCallTarget callTarget = parse (context , request .getSource (), InputType .EVAL , false , 0 , false , null );
650
+ RootCallTarget callTarget = parse (context , request .getSource (), InputType .EVAL , false , 0 , false , null , EnumSet . noneOf ( FutureFeature . class ) );
592
651
return new ExecutableNode (this ) {
593
652
@ Child private GilNode gilNode = GilNode .create ();
594
653
@ Child private GenericInvokeNode invokeNode = GenericInvokeNode .create ();
0 commit comments