78
78
import com .oracle .graal .python .runtime .PosixSupportLibrary ;
79
79
import com .oracle .graal .python .runtime .PosixSupportLibrary .PosixException ;
80
80
import com .oracle .truffle .api .CompilerDirectives ;
81
+ import com .oracle .truffle .api .dsl .Bind ;
81
82
import com .oracle .truffle .api .dsl .Cached ;
82
83
import com .oracle .truffle .api .dsl .GenerateNodeFactory ;
83
84
import com .oracle .truffle .api .dsl .NodeFactory ;
84
85
import com .oracle .truffle .api .dsl .Specialization ;
86
+ import com .oracle .truffle .api .dsl .Cached .Shared ;
85
87
import com .oracle .truffle .api .frame .VirtualFrame ;
86
88
import com .oracle .truffle .api .library .CachedLibrary ;
87
89
import com .oracle .truffle .api .profiles .ConditionProfile ;
@@ -237,7 +239,7 @@ static Object stat(VirtualFrame frame, PDirEntry self, boolean followSymlinks,
237
239
}
238
240
}
239
241
240
- abstract static class StatHelperNode extends PythonBuiltinBaseNode {
242
+ abstract static class StatHelperSimpleNode extends PythonBuiltinBaseNode {
241
243
242
244
abstract PTuple execute (VirtualFrame frame , PDirEntry self , boolean followSymlinks , boolean catchNoent );
243
245
@@ -253,44 +255,60 @@ static PTuple cachedLStat(PDirEntry self, boolean followSymlinks, boolean catchN
253
255
return self .lstatCache ;
254
256
}
255
257
256
- @ Specialization (guards = "self.getStatCache(followSymlinks) == null" )
257
- PTuple stat (VirtualFrame frame , PDirEntry self , boolean followSymlinks , boolean catchNoent ,
258
+ @ Specialization (guards = {"followSymlinks" , "self.statCache == null" , "isSymlink" }, limit = "1" )
259
+ PTuple uncachedStatWithSymlink (VirtualFrame frame , PDirEntry self , boolean followSymlinks , boolean catchNoent ,
260
+ @ SuppressWarnings ("unused" ) @ Cached IsSymlinkNode isSymlinkNode ,
261
+ @ SuppressWarnings ("unused" ) @ Bind ("isSymlinkNode.executeBoolean(frame, self)" ) boolean isSymlink ,
258
262
@ CachedLibrary ("getPosixSupport()" ) PosixSupportLibrary posixLib ,
259
- @ Cached IsSymlinkNode isSymlinkNode ,
260
- @ Cached StatHelperNode recursiveNode ,
261
- @ Cached CachedPosixPathNode cachedPosixPathNode ,
262
- @ Cached ConditionProfile positiveLongProfile ,
263
- @ Cached ConditionProfile noSymlinkProfile ) {
264
- PTuple res ;
263
+ @ Shared ("cachedPosixPathNode" ) @ Cached CachedPosixPathNode cachedPosixPathNode ,
264
+ @ Shared ("positiveLongProfile" ) @ Cached ConditionProfile positiveLongProfile ) {
265
265
// There are two caches - one for `follow_symlinks=True` and the other for
266
266
// 'follow_symlinks=False`. They are different only when the dir entry is a symlink.
267
- // If it is not, they need to be the same, so we must make sure that fstatat() gets
268
- // called only once.
269
- if (noSymlinkProfile .profile (followSymlinks && !isSymlinkNode .execute (frame , self ))) {
270
- // The entry is not a symlink, so both stat caches need to have the
271
- // same value. Also, the `follow_symlinks=False` cache might already be filled
272
- // in. (In fact, the call to isSymlinkNode in the condition may fill it.)
273
- // So we call ourselves recursively to either use or fill that cache first, and
274
- // the `follow_symlinks=True` cache will be filled below.
275
- res = recursiveNode .execute (frame , self , false , catchNoent );
276
- } else {
277
- int dirFd = self .scandirPath instanceof PosixFd ? ((PosixFd ) self .scandirPath ).fd : AT_FDCWD .value ;
278
- PosixPath posixPath = cachedPosixPathNode .execute (frame , self );
279
- try {
280
- long [] rawStat = posixLib .fstatat (getPosixSupport (), dirFd , posixPath .value , followSymlinks );
281
- res = PosixModuleBuiltins .createStatResult (factory (), positiveLongProfile , rawStat );
282
- } catch (PosixException e ) {
283
- if (catchNoent && e .getErrorCode () == OSErrorEnum .ENOENT .getNumber ()) {
284
- return null ;
285
- }
286
- throw raiseOSErrorFromPosixException (frame , e , posixPath .originalObject );
267
+ return uncachedLStatWithSymlink (frame , self , followSymlinks , catchNoent , posixLib , cachedPosixPathNode , positiveLongProfile );
268
+ }
269
+
270
+ @ Specialization (guards = {"!followSymlinks" , "self.lstatCache == null" })
271
+ PTuple uncachedLStatWithSymlink (VirtualFrame frame , PDirEntry self , boolean followSymlinks , boolean catchNoent ,
272
+ @ CachedLibrary ("getPosixSupport()" ) PosixSupportLibrary posixLib ,
273
+ @ Shared ("cachedPosixPathNode" ) @ Cached CachedPosixPathNode cachedPosixPathNode ,
274
+ @ Shared ("positiveLongProfile" ) @ Cached ConditionProfile positiveLongProfile ) {
275
+ PTuple res ;
276
+ int dirFd = self .scandirPath instanceof PosixFd ? ((PosixFd ) self .scandirPath ).fd : AT_FDCWD .value ;
277
+ PosixPath posixPath = cachedPosixPathNode .execute (frame , self );
278
+ try {
279
+ long [] rawStat = posixLib .fstatat (getPosixSupport (), dirFd , posixPath .value , followSymlinks );
280
+ res = PosixModuleBuiltins .createStatResult (factory (), positiveLongProfile , rawStat );
281
+ } catch (PosixException e ) {
282
+ if (catchNoent && e .getErrorCode () == OSErrorEnum .ENOENT .getNumber ()) {
283
+ return null ;
287
284
}
285
+ throw raiseOSErrorFromPosixException (frame , e , posixPath .originalObject );
288
286
}
289
287
self .setStatCache (followSymlinks , res );
290
288
return res ;
291
289
}
292
290
}
293
291
292
+ abstract static class StatHelperNode extends StatHelperSimpleNode {
293
+ @ Specialization (guards = {"followSymlinks" , "self.statCache == null" , "!isSymlink" })
294
+ static PTuple uncachedStatWithSymlink (VirtualFrame frame , PDirEntry self , boolean followSymlinks , boolean catchNoent ,
295
+ @ SuppressWarnings ("unused" ) @ Cached IsSymlinkNode isSymlinkNode ,
296
+ @ SuppressWarnings ("unused" ) @ Bind ("isSymlinkNode.executeBoolean(frame, self)" ) boolean isSymlink ,
297
+ @ Cached StatHelperSimpleNode recursiveNode ) {
298
+ // There are two caches - one for `follow_symlinks=True` and the other for
299
+ // 'follow_symlinks=False`. They are different only when the dir entry is a symlink.
300
+ // If it is not, they need to be the same, so we must make sure that fstatat() gets
301
+ // called only once. The entry is not a symlink, so both stat caches need to have the
302
+ // same value. Also, the `follow_symlinks=False` cache might already be filled
303
+ // in. (In fact, the call to isSymlinkNode in the condition may fill it.)
304
+ // So we call ourselves recursively to either use or fill that cache first, and
305
+ // the `follow_symlinks=True` cache will be filled below.
306
+ PTuple res = recursiveNode .execute (frame , self , false , catchNoent );
307
+ self .setStatCache (followSymlinks , res );
308
+ return res ;
309
+ }
310
+ }
311
+
294
312
abstract static class TestModeNode extends PythonBuiltinBaseNode {
295
313
296
314
private final long expectedMode ;
@@ -355,7 +373,7 @@ static TestModeNode createDir() {
355
373
@ GenerateNodeFactory
356
374
abstract static class IsSymlinkNode extends PythonUnaryBuiltinNode {
357
375
358
- abstract boolean execute (VirtualFrame frame , PDirEntry self );
376
+ abstract boolean executeBoolean (VirtualFrame frame , PDirEntry self );
359
377
360
378
@ Specialization
361
379
static boolean isSymlink (VirtualFrame frame , PDirEntry self ,
0 commit comments