Skip to content

Commit b31968a

Browse files
authored
Merge pull request microsoft#23982 from Microsoft/reduceVfsCaughtExceptions
Reduce number of 'caught exceptions' raised by vfs
2 parents 7fb7eec + ea953b5 commit b31968a

File tree

1 file changed

+36
-31
lines changed

1 file changed

+36
-31
lines changed

src/harness/vfs.ts

+36-31
Original file line numberDiff line numberDiff line change
@@ -332,35 +332,20 @@ namespace vfs {
332332
}
333333
}
334334

335-
private _depth: string[] = [];
336-
337335
/**
338336
* Make a directory and all of its parent paths (if they don't exist).
339337
*/
340338
public mkdirpSync(path: string) {
341-
try {
342-
this._depth.push(path);
343-
path = this._resolve(path);
344-
this.mkdirSync(path);
345-
}
346-
catch (e) {
347-
if (e.code === "ENOENT") {
348-
if (this._depth.length > 10) {
349-
console.log(`path: ${path}`);
350-
console.log(`dirname: ${vpath.dirname(path)}`);
351-
console.log(this._depth);
352-
throw e;
353-
}
354-
this.mkdirpSync(vpath.dirname(path));
355-
this.mkdirSync(path);
356-
}
357-
else if (e.code !== "EEXIST") {
358-
throw e;
339+
path = this._resolve(path);
340+
const result = this._walk(path, /*noFollow*/ true, (error, result) => {
341+
if (error.code === "ENOENT") {
342+
this._mkdir(result);
343+
return "retry";
359344
}
360-
}
361-
finally {
362-
this._depth.pop();
363-
}
345+
return "throw";
346+
});
347+
348+
if (!result.node) this._mkdir(result);
364349
}
365350

366351
/**
@@ -464,9 +449,11 @@ namespace vfs {
464449
public mkdirSync(path: string) {
465450
if (this.isReadonly) throw createIOError("EROFS");
466451

467-
const { parent, links, node: existingNode, basename } = this._walk(this._resolve(path), /*noFollow*/ true);
468-
if (existingNode) throw createIOError("EEXIST");
452+
this._mkdir(this._walk(this._resolve(path), /*noFollow*/ true));
453+
}
469454

455+
private _mkdir({ parent, links, node: existingNode, basename }: WalkResult) {
456+
if (existingNode) throw createIOError("EEXIST");
470457
const time = this.time();
471458
const node = this._mknod(parent ? parent.dev : ++devCount, S_IFDIR, /*mode*/ 0o777, time);
472459
this._addLink(parent, links, basename, node, time);
@@ -810,17 +797,18 @@ namespace vfs {
810797
* @param path The path to follow.
811798
* @param noFollow A value indicating whether to *not* dereference a symbolic link at the
812799
* end of a path.
813-
* @param allowPartial A value indicating whether to return a partial result if the node
814-
* at the end of the path cannot be found.
815800
*
816801
* @link http://man7.org/linux/man-pages/man7/path_resolution.7.html
817802
*/
818-
private _walk(path: string, noFollow?: boolean): WalkResult {
803+
private _walk(path: string, noFollow?: boolean, onError?: (error: NodeJS.ErrnoException, fragment: WalkResult) => "retry" | "throw"): WalkResult;
804+
private _walk(path: string, noFollow?: boolean, onError?: (error: NodeJS.ErrnoException, fragment: WalkResult) => "stop" | "retry" | "throw"): WalkResult | undefined;
805+
private _walk(path: string, noFollow?: boolean, onError?: (error: NodeJS.ErrnoException, fragment: WalkResult) => "stop" | "retry" | "throw"): WalkResult | undefined {
819806
let links = this._getRootLinks();
820807
let parent: DirectoryInode | undefined;
821808
let components = vpath.parse(path);
822809
let step = 0;
823810
let depth = 0;
811+
let retry = false;
824812
while (true) {
825813
if (depth >= 40) throw createIOError("ELOOP");
826814
const lastStep = step === components.length - 1;
@@ -830,7 +818,8 @@ namespace vfs {
830818
return { realpath: vpath.format(components), basename, parent, links, node };
831819
}
832820
if (node === undefined) {
833-
throw createIOError("ENOENT");
821+
if (trapError(createIOError("ENOENT"), node)) continue;
822+
return undefined;
834823
}
835824
if (isSymlink(node)) {
836825
const dirname = vpath.format(components.slice(0, step));
@@ -840,15 +829,30 @@ namespace vfs {
840829
components = vpath.parse(symlink).concat(components.slice(step + 1));
841830
step = 0;
842831
depth++;
832+
retry = false;
843833
continue;
844834
}
845835
if (isDirectory(node)) {
846836
links = this._getLinks(node);
847837
parent = node;
848838
step++;
839+
retry = false;
849840
continue;
850841
}
851-
throw createIOError("ENOTDIR");
842+
if (trapError(createIOError("ENOTDIR"), node)) continue;
843+
return undefined;
844+
}
845+
846+
function trapError(error: NodeJS.ErrnoException, node?: Inode) {
847+
const realpath = vpath.format(components.slice(0, step + 1));
848+
const basename = components[step];
849+
const result = !retry && onError ? onError(error, { realpath, basename, parent, links, node }) : "throw";
850+
if (result === "stop") return false;
851+
if (result === "retry") {
852+
retry = true;
853+
return true;
854+
}
855+
throw error;
852856
}
853857
}
854858

@@ -1118,6 +1122,7 @@ namespace vfs {
11181122
export function createIOError(code: keyof typeof IOErrorMessages) {
11191123
const err: NodeJS.ErrnoException = new Error(`${code}: ${IOErrorMessages[code]}`);
11201124
err.code = code;
1125+
if (Error.captureStackTrace) Error.captureStackTrace(err, createIOError);
11211126
return err;
11221127
}
11231128

0 commit comments

Comments
 (0)