26
26
package com .oracle .graal .python ;
27
27
28
28
import java .io .IOException ;
29
- import java .lang .ref .WeakReference ;
30
29
import java .util .ArrayList ;
31
30
import java .util .Arrays ;
32
31
import java .util .concurrent .ConcurrentHashMap ;
33
32
import java .util .concurrent .Semaphore ;
34
- import java .util .concurrent .atomic .AtomicReference ;
35
33
import java .util .logging .Level ;
36
34
37
35
import org .graalvm .options .OptionDescriptors ;
52
50
import com .oracle .graal .python .nodes .BuiltinNames ;
53
51
import com .oracle .graal .python .nodes .HiddenAttributes ;
54
52
import com .oracle .graal .python .nodes .NodeFactory ;
53
+ import com .oracle .graal .python .nodes .PRootNode ;
55
54
import com .oracle .graal .python .nodes .call .InvokeNode ;
56
55
import com .oracle .graal .python .nodes .control .TopLevelExceptionHandler ;
57
- import com .oracle .graal .python .nodes .expression .BinaryArithmetic ;
58
56
import com .oracle .graal .python .nodes .expression .ExpressionNode ;
59
- import com .oracle .graal .python .nodes .expression .InplaceArithmetic ;
60
- import com .oracle .graal .python .nodes .expression .TernaryArithmetic ;
61
- import com .oracle .graal .python .nodes .expression .UnaryArithmetic ;
57
+ import com .oracle .graal .python .nodes .util .BadOPCodeNode ;
62
58
import com .oracle .graal .python .parser .PythonParserImpl ;
63
59
import com .oracle .graal .python .runtime .GilNode ;
64
60
import com .oracle .graal .python .runtime .PythonContext ;
109
105
@ TruffleLanguage .Registration (id = PythonLanguage .ID , //
110
106
name = PythonLanguage .NAME , //
111
107
version = PythonLanguage .VERSION , //
112
- characterMimeTypes = PythonLanguage .MIME_TYPE , //
108
+ characterMimeTypes = {PythonLanguage .MIME_TYPE ,
109
+ PythonLanguage .MIME_TYPE_COMPILE0 , PythonLanguage .MIME_TYPE_COMPILE1 , PythonLanguage .MIME_TYPE_COMPILE2 ,
110
+ PythonLanguage .MIME_TYPE_EVAL0 , PythonLanguage .MIME_TYPE_EVAL1 , PythonLanguage .MIME_TYPE_EVAL2 }, //
111
+ byteMimeTypes = {PythonLanguage .MIME_TYPE_BYTECODE }, //
112
+ defaultMimeType = PythonLanguage .MIME_TYPE , //
113
113
dependentLanguages = {"nfi" , "llvm" }, //
114
114
interactive = true , internal = false , //
115
115
contextPolicy = TruffleLanguage .ContextPolicy .SHARED , //
@@ -138,6 +138,15 @@ public final class PythonLanguage extends TruffleLanguage<PythonContext> {
138
138
public static final int API_VERSION = 1013 ;
139
139
140
140
public static final String MIME_TYPE = "text/x-python" ;
141
+ static final String MIME_TYPE_COMPILE0 = "text/x-python-compile0" ;
142
+ static final String MIME_TYPE_COMPILE1 = "text/x-python-compile1" ;
143
+ static final String MIME_TYPE_COMPILE2 = "text/x-python-compile2" ;
144
+ static final String [] MIME_TYPE_COMPILE = {PythonLanguage .MIME_TYPE_COMPILE0 , PythonLanguage .MIME_TYPE_COMPILE1 , PythonLanguage .MIME_TYPE_COMPILE2 };
145
+ static final String [] MIME_TYPE_EVAL = {PythonLanguage .MIME_TYPE_EVAL0 , PythonLanguage .MIME_TYPE_EVAL1 , PythonLanguage .MIME_TYPE_EVAL2 };
146
+ static final String MIME_TYPE_EVAL0 = "text/x-python-eval0" ;
147
+ static final String MIME_TYPE_EVAL1 = "text/x-python-eval1" ;
148
+ static final String MIME_TYPE_EVAL2 = "text/x-python-eval2" ;
149
+ public static final String MIME_TYPE_BYTECODE = "application/x-python-bytecode" ;
141
150
public static final String EXTENSION = ".py" ;
142
151
public static final String [] DEFAULT_PYTHON_EXTENSIONS = new String []{EXTENSION , ".pyc" };
143
152
@@ -152,16 +161,14 @@ public final class PythonLanguage extends TruffleLanguage<PythonContext> {
152
161
public final Assumption singleThreadedAssumption = Truffle .getRuntime ().createAssumption ("Only a single thread is active" );
153
162
154
163
private final NodeFactory nodeFactory ;
155
- private final ConcurrentHashMap < String , RootCallTarget > builtinCallTargetCache = new ConcurrentHashMap <>();
164
+
156
165
/**
157
- * A thread-safe map that maps arithmetic operators (i.e.
158
- * {@link com.oracle.graal.python.nodes.expression.UnaryArithmetic},
159
- * {@link com.oracle.graal.python.nodes.expression.BinaryArithmetic},
160
- * {@link com.oracle.graal.python.nodes.expression.TernaryArithmetic}, and
161
- * {@link com.oracle.graal.python.nodes.expression.InplaceArithmetic}) to call targets. Use this
162
- * map to retrieve a singleton instance (per engine) such that proper AST sharing is possible.
166
+ * A thread-safe map to retrieve (and cache) singleton instances of call targets, e.g., for
167
+ * Arithmetic operations, wrappers, named cext functions, etc. This reduces the number of call
168
+ * targets and allows AST sharing across contexts. The key in this map is either a single value
169
+ * or a list of values.
163
170
*/
164
- private final AtomicReference < ConcurrentHashMap <Object , WeakReference < RootCallTarget >>> arithmeticOpCallTargetCacheRef = new AtomicReference <>();
171
+ private final ConcurrentHashMap <Object , RootCallTarget > cachedCallTargets = new ConcurrentHashMap <>();
165
172
166
173
private final Shape emptyShape = Shape .newBuilder ().allowImplicitCastIntToDouble (false ).allowImplicitCastIntToLong (true ).shapeFlags (0 ).propertyAssumptions (true ).build ();
167
174
@ CompilationFinal (dimensions = 1 ) private final Shape [] builtinTypeInstanceShapes = new Shape [PythonBuiltinClassType .VALUES .length ];
@@ -273,27 +280,72 @@ protected void initializeContext(PythonContext context) {
273
280
context .initialize ();
274
281
}
275
282
283
+ public static String getCompileMimeType (int optimize ) {
284
+ if (optimize <= 0 ) {
285
+ return MIME_TYPE_COMPILE0 ;
286
+ } else if (optimize == 1 ) {
287
+ return MIME_TYPE_COMPILE1 ;
288
+ } else {
289
+ return MIME_TYPE_COMPILE2 ;
290
+ }
291
+ }
292
+
293
+ public static String getEvalMimeType (int optimize ) {
294
+ if (optimize <= 0 ) {
295
+ return MIME_TYPE_EVAL0 ;
296
+ } else if (optimize == 1 ) {
297
+ return MIME_TYPE_EVAL1 ;
298
+ } else {
299
+ return MIME_TYPE_EVAL2 ;
300
+ }
301
+ }
302
+
276
303
@ Override
277
304
protected CallTarget parse (ParsingRequest request ) {
278
305
PythonContext context = getCurrentContext (PythonLanguage .class );
279
306
PythonCore core = context .getCore ();
280
307
Source source = request .getSource ();
281
- CompilerDirectives .transferToInterpreter ();
282
- if (core .isInitialized ()) {
283
- context .initializeMainModule (source .getPath ());
308
+ if (source .getMimeType () == null || MIME_TYPE .equals (source .getMimeType ())) {
309
+ if (!request .getArgumentNames ().isEmpty ()) {
310
+ return PythonUtils .getOrCreateCallTarget (parseWithArguments (request ));
311
+ }
312
+ RootNode root = doParse (context , source , 0 );
313
+ if (root instanceof PRootNode ) {
314
+ ((PRootNode ) root ).triggerDeprecationWarnings ();
315
+ }
316
+ if (core .isInitialized ()) {
317
+ return PythonUtils .getOrCreateCallTarget (new TopLevelExceptionHandler (this , root , source ));
318
+ } else {
319
+ return PythonUtils .getOrCreateCallTarget (root );
320
+ }
284
321
}
285
322
if (!request .getArgumentNames ().isEmpty ()) {
286
- return PythonUtils . getOrCreateCallTarget ( parseWithArguments ( request ) );
323
+ throw new IllegalStateException ( "parse with arguments is only allowed for " + MIME_TYPE + " mime type" );
287
324
}
288
- RootNode root = doParse (context , source );
289
- if (core .isInitialized ()) {
290
- return PythonUtils .getOrCreateCallTarget (new TopLevelExceptionHandler (this , root ));
291
- } else {
292
- return PythonUtils .getOrCreateCallTarget (root );
325
+
326
+ if (MIME_TYPE_BYTECODE .equals (source .getMimeType ())) {
327
+ byte [] bytes = source .getBytes ().toByteArray ();
328
+ if (bytes .length == 0 ) {
329
+ return createCachedCallTarget (l -> new BadOPCodeNode (l ), BadOPCodeNode .class );
330
+ }
331
+ return PythonUtils .getOrCreateCallTarget (core .getSerializer ().deserialize (bytes ));
332
+ }
333
+ for (int optimize = 0 ; optimize < MIME_TYPE_EVAL .length ; optimize ++) {
334
+ if (MIME_TYPE_EVAL [optimize ].equals (source .getMimeType ())) {
335
+ assert !source .isInteractive ();
336
+ return PythonUtils .getOrCreateCallTarget ((RootNode ) core .getParser ().parse (ParserMode .Eval , optimize , core , source , null , null ));
337
+ }
338
+ }
339
+ for (int optimize = 0 ; optimize < MIME_TYPE_COMPILE .length ; optimize ++) {
340
+ if (MIME_TYPE_COMPILE [optimize ].equals (source .getMimeType ())) {
341
+ assert !source .isInteractive ();
342
+ return PythonUtils .getOrCreateCallTarget ((RootNode ) core .getParser ().parse (ParserMode .File , optimize , core , source , null , null ));
343
+ }
293
344
}
345
+ throw CompilerDirectives .shouldNotReachHere ("unknown mime type: " + source .getMimeType ());
294
346
}
295
347
296
- private RootNode doParse (PythonContext context , Source source ) {
348
+ private RootNode doParse (PythonContext context , Source source , int optimize ) {
297
349
ParserMode mode ;
298
350
if (source .isInteractive ()) {
299
351
if (context .getOption (PythonOptions .TerminalIsInteractive )) {
@@ -311,7 +363,7 @@ private RootNode doParse(PythonContext context, Source source) {
311
363
}
312
364
PythonCore pythonCore = context .getCore ();
313
365
try {
314
- return (RootNode ) pythonCore .getParser ().parse (mode , 0 , pythonCore , source , null , null );
366
+ return (RootNode ) pythonCore .getParser ().parse (mode , optimize , pythonCore , source , null , null );
315
367
} catch (PException e ) {
316
368
// handle PException during parsing (PIncompleteSourceException will propagate through)
317
369
PythonUtils .getOrCreateCallTarget (new TopLevelExceptionHandler (this , e )).call ();
@@ -617,7 +669,7 @@ public static TruffleLogger getCompatibilityLogger(Class<?> clazz) {
617
669
return TruffleLogger .getLogger (ID , "compatibility." + clazz .getName ());
618
670
}
619
671
620
- public static Source newSource (PythonContext ctxt , String src , String name , boolean mayBeFile ) {
672
+ public static Source newSource (PythonContext ctxt , String src , String name , boolean mayBeFile , String mime ) {
621
673
try {
622
674
SourceBuilder sourceBuilder = null ;
623
675
if (mayBeFile ) {
@@ -641,6 +693,9 @@ public static Source newSource(PythonContext ctxt, String src, String name, bool
641
693
if (sourceBuilder == null ) {
642
694
sourceBuilder = Source .newBuilder (ID , src , name );
643
695
}
696
+ if (mime != null ) {
697
+ sourceBuilder .mimeType (mime );
698
+ }
644
699
return newSource (ctxt , sourceBuilder );
645
700
} catch (IOException e ) {
646
701
throw new IllegalStateException (e );
@@ -720,10 +775,6 @@ protected void disposeThread(PythonContext context, Thread thread) {
720
775
context .disposeThread (thread );
721
776
}
722
777
723
- public RootCallTarget getOrComputeBuiltinCallTarget (String key , Supplier <RootNode > supplier ) {
724
- return builtinCallTargetCache .computeIfAbsent (key , (k ) -> PythonUtils .getOrCreateCallTarget (supplier .get ()));
725
- }
726
-
727
778
public Shape getEmptyShape () {
728
779
return emptyShape ;
729
780
}
@@ -755,82 +806,6 @@ public Shape getBuiltinTypeInstanceShape(PythonBuiltinClassType type) {
755
806
return shape ;
756
807
}
757
808
758
- /**
759
- * Retrieve a call target for the given {@link UnaryArithmetic} operator. If the no such call
760
- * target exists yet, it will be created lazily. This method is thread-safe and should be used
761
- * for all contexts in this engine to enable AST sharing.
762
- */
763
- @ TruffleBoundary
764
- public RootCallTarget getOrCreateUnaryArithmeticCallTarget (UnaryArithmetic unaryOperator ) {
765
- return getOrCreateArithmeticCallTarget (unaryOperator , unaryOperator ::createCallTarget );
766
- }
767
-
768
- /**
769
- * Retrieve a call target for the given {@link BinaryArithmetic} operator. If the no such call
770
- * target exists yet, it will be created lazily. This method is thread-safe and should be used
771
- * for all contexts in this engine to enable AST sharing.
772
- */
773
- @ TruffleBoundary
774
- public RootCallTarget getOrCreateBinaryArithmeticCallTarget (BinaryArithmetic unaryOperator ) {
775
- return getOrCreateArithmeticCallTarget (unaryOperator , unaryOperator ::createCallTarget );
776
- }
777
-
778
- /**
779
- * Retrieve a call target for the given {@link TernaryArithmetic} operator. If the no such call
780
- * target exists yet, it will be created lazily. This method is thread-safe and should be used
781
- * for all contexts in this engine to enable AST sharing.
782
- */
783
- @ TruffleBoundary
784
- public RootCallTarget getOrCreateTernaryArithmeticCallTarget (TernaryArithmetic unaryOperator ) {
785
- return getOrCreateArithmeticCallTarget (unaryOperator , unaryOperator ::createCallTarget );
786
- }
787
-
788
- /**
789
- * Retrieve a call target for the given {@link InplaceArithmetic} operator. If the no such call
790
- * target exists yet, it will be created lazily. This method is thread-safe and should be used
791
- * for all contexts in this engine to enable AST sharing.
792
- */
793
- @ TruffleBoundary
794
- public RootCallTarget getOrCreateInplaceArithmeticCallTarget (InplaceArithmetic unaryOperator ) {
795
- return getOrCreateArithmeticCallTarget (unaryOperator , unaryOperator ::createCallTarget );
796
- }
797
-
798
- private RootCallTarget getOrCreateArithmeticCallTarget (Object arithmeticOperator , Function <PythonLanguage , RootCallTarget > supplier ) {
799
- CompilerAsserts .neverPartOfCompilation ();
800
- ConcurrentHashMap <Object , WeakReference <RootCallTarget >> arithmeticOpCallTargetCache = arithmeticOpCallTargetCacheRef .get ();
801
- if (arithmeticOpCallTargetCache == null ) {
802
- arithmeticOpCallTargetCache = arithmeticOpCallTargetCacheRef .updateAndGet ((v ) -> {
803
- // IMPORTANT: only create a new instance if we still see 'null'; otherwise we would
804
- // overwrite the update of a different thread
805
- if (v == null ) {
806
- return new ConcurrentHashMap <>();
807
- }
808
- return v ;
809
- });
810
- }
811
-
812
- WeakReference <RootCallTarget > ctRef = arithmeticOpCallTargetCache .compute (arithmeticOperator , (k , v ) -> {
813
- RootCallTarget cachedCallTarget = v != null ? v .get () : null ;
814
- if (cachedCallTarget == null ) {
815
- return new WeakReference <>(supplier .apply (this ));
816
- }
817
- return v ;
818
- });
819
-
820
- RootCallTarget callTarget = ctRef .get ();
821
- if (callTarget == null ) {
822
- // Bad luck: we ensured that there is a mapping in the cache but the weak value got
823
- // collected before we could strongly reference it. Now, we need to be conservative and
824
- // create the call target eagerly, hold a strong reference to it until we've put it into
825
- // the map.
826
- final RootCallTarget callTargetToCache = supplier .apply (this );
827
- callTarget = callTargetToCache ;
828
- arithmeticOpCallTargetCache .computeIfAbsent (arithmeticOperator , (k ) -> new WeakReference <>(callTargetToCache ));
829
- }
830
- assert callTarget != null ;
831
- return callTarget ;
832
- }
833
-
834
809
/**
835
810
* Returns the shape used for the C API symbol cache.
836
811
*/
@@ -852,4 +827,19 @@ public synchronized Shape getHPySymbolCacheShape() {
852
827
}
853
828
return hpySymbolCache ;
854
829
}
830
+
831
+ /**
832
+ * Cache call targets that are created for every new context, based on a single key.
833
+ */
834
+ public RootCallTarget createCachedCallTarget (Function <PythonLanguage , RootNode > rootNodeFunction , Object key ) {
835
+ CompilerAsserts .neverPartOfCompilation ();
836
+ return cachedCallTargets .computeIfAbsent (key , k -> PythonUtils .getOrCreateCallTarget (rootNodeFunction .apply (this )));
837
+ }
838
+
839
+ /**
840
+ * Cache call targets that are created for every new context, based on a list of keys.
841
+ */
842
+ public RootCallTarget createCachedCallTarget (Function <PythonLanguage , RootNode > rootNodeFunction , Object ... cacheKeys ) {
843
+ return createCachedCallTarget (rootNodeFunction , Arrays .asList (cacheKeys ));
844
+ }
855
845
}
0 commit comments