Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Implement ops for manipulating file descriptors and HANDLEs #568

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions docs/ops.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@
* [getstderr](#getstderr)
* [getstdin](#getstdin)
* [getstdout](#getstdout)
* [fdopen](#fdopen)
* [open](#open)
* [openasync `jvm`](#openasync-jvm)
* [print](#print)
Expand Down Expand Up @@ -208,6 +209,8 @@
* [lstat_time](#lstat_time)
* [symlink](#symlink)
* [unlink](#unlink)
* [fstat](#fstat)
* [fstat_time](#fstat_time)
- [Type/Conversion Opcodes](#-typeconversion-opcodes)
* [bool](#bool)
* [bootarray `jvm` `moar`](#bootarray-jvm-moar)
Expand Down Expand Up @@ -1632,6 +1635,11 @@ Return the filehandle for standard input.

Return the filehandle for standard output.

## fdopen
* fdopen(int $fd)

Return the filehandle for the given file descriptor.

## open
* `open(str $filename, str $mode)`

Expand Down Expand Up @@ -1872,13 +1880,13 @@ a num, using the OS's stat() function.
## lstat
* `lstat(str $path, int $code --> int)`

Same as stat, but internally uses the OS's lstat() function, which does *not*
Same as stat, but internally uses the OS' lstat() function, which does *not*
follow symlinks.

## lstat_time
* `stat_time(str $path, int $code --> num)`
* `lstat_time(str $path, int $code --> num)`

Same as stat_time, but internally uses the OS's lstat() function, which does
Same as stat_time, but internally uses the OS' lstat() function, which does
*not* follow symlinks.

## symlink
Expand All @@ -1892,6 +1900,18 @@ Create a symbolic link from `$after` to `$before`
Delete the given file $path. Returns 0 on success, -2 if the file
didn't exist. May throw an exception.

## fstat
* fstat(int $fd, int $code --> int)

Same as stat, but internally uses the OS' fstat() function, which takes a file
descriptor instead of a path.

## fstat_time
* fstat_time(int $fd, int $code --> int)

Same as stat_time, but internally uses the OS' fstat() function, which takes a
file descriptor instead of a path.

# <a id="type"></a> Type/Conversion Opcodes

## bool
Expand Down
4 changes: 4 additions & 0 deletions src/vm/js/Operations.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,7 @@ class QAST::OperationsJS {
add_simple_op('getstdout', $T_OBJ, [], :side_effects);
add_simple_op('getstdin', $T_OBJ, [], :side_effects);

add_simple_op('fdopen', $T_OBJ, [$T_INT], :side_effects);
add_simple_op('open', $T_OBJ, [$T_STR, $T_STR], :side_effects);

add_simple_op('opendir', $T_OBJ, [$T_STR], :side_effects);
Expand Down Expand Up @@ -1029,6 +1030,9 @@ class QAST::OperationsJS {
add_simple_op('lstat', $T_INT, [$T_STR, $T_INT], :side_effects);
add_simple_op('lstat_time', $T_NUM, [$T_STR, $T_INT], :side_effects);

add_simple_op('fstat', $T_INT, [$T_INT, $T_INT], :side_effects);
add_simple_op('fstat_time', $T_NUM, [$T_INT, $T_INT], :side_effects);

add_simple_op('fileislink', $T_INT, [$T_STR], :side_effects);

add_simple_op('filewritable', $T_INT, [$T_STR], :side_effects);
Expand Down
33 changes: 33 additions & 0 deletions src/vm/js/nqp-runtime/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,39 @@ op.stat = function(file, code) {
}
};

op.stat_time = function(file, code) {
throw new NQPException('nqp::stat_time is not supported in the browser');
};

op.lstat = function(file, code) {
const EXISTS = 0;
if (code == EXISTS && (file === '/etc/os-release' || file === '/bin/uname' || file === '/usr/bin/uname')) {
return 0;
} else {
throw new NQPException('nqp::lstat is not supported in the browser');
}
};

op.lstat_time = function(file, code) {
throw new NQPException('nqp::lstat_time is not supported in the browser');
};

op.fstat = function(fd, code) {
throw new NQPException('nqp::fstat is not supported in the browser');
};

op.fstat_time = function(fd, code) {
throw new NQPException('nqp::fstat_time is not supported in the browser');
};

op.fdopen = function(fd) {
throw new NQPException('nqp::fdopen is not supported in the browser');
};

op.open = function(file, mode) {
throw new NQPException('nqp::open is not supported in the browser');
};

class Exit {
constructor(code) {
this.code = code;
Expand Down
65 changes: 40 additions & 25 deletions src/vm/js/nqp-runtime/io.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,33 +66,35 @@ op.filereadable = function(path) {
return isFile(path, fs.constants.R_OK);
};

function stat(file, code, lstat) {
const EXISTS = 0;
const FILESIZE = 1;
const ISDIR = 2;
const ISREG = 3;
const ISDEV = 4;
const CREATETIME = 5;
const ACCESSTIME = 6;
const MODIFYTIME = 7;
const CHANGETIME = 8;
const BACKUPTIME = 9;
const UID = 10;
const GID = 11;
const ISLNK = 12;
const PLATFORM_DEV = -1;
const PLATFORM_INODE = -2;
const PLATFORM_MODE = -3;
const PLATFORM_NLINKS = -4;
const PLATFORM_DEVTYPE = -5;
function stat(file, code, lstat, fstat) {
const EXISTS = 0;
const FILESIZE = 1;
const ISDIR = 2;
const ISREG = 3;
const ISDEV = 4;
const CREATETIME = 5;
const ACCESSTIME = 6;
const MODIFYTIME = 7;
const CHANGETIME = 8;
const BACKUPTIME = 9;
const UID = 10;
const GID = 11;
const ISLNK = 12;
const PLATFORM_DEV = -1;
const PLATFORM_INODE = -2;
const PLATFORM_MODE = -3;
const PLATFORM_NLINKS = -4;
const PLATFORM_DEVTYPE = -5;
const PLATFORM_BLOCKSIZE = -6;
const PLATFORM_BLOCKS = -7;
const PLATFORM_BLOCKS = -7;

// we can't use fs.existsSync(file) as it follows symlinks
let stats;
try {
if (lstat || code == ISLNK) {
stats = fs.lstatSync(file);
} else if (fstat) {
stmts = fs.fstatSync(file);
} else {
stats = fs.statSync(file);
}
Expand Down Expand Up @@ -134,19 +136,27 @@ op.fileislink = function(file) {
};

op.stat = function(file, code) {
return stat(file, code, false) | 0;
return stat(file, code, false, false) | 0;
};

op.stat_time = function(file, code) {
return stat(file, code, false);
return stat(file, code, false, false);
};

op.lstat = function(file, code) {
return stat(file, code, true) | 0;
return stat(file, code, true, false) | 0;
};

op.lstat_time = function(file, code) {
return stat(file, code, true);
return stat(file, code, true, false);
};

op.fstat = function(fd, code) {
return stat(fd, code, false, true) | 0;
};

op.fstat_time = function(fd, code) {
return stat(fd, code, false, true);
};

class IOHandle extends NQPObject {
Expand Down Expand Up @@ -273,13 +283,18 @@ function modeToFlags(mode) {
return flags;
}

op.fdopen = function(fd) {
const fh = new FileHandle(fd);
return fh;
};

op.open = function(name, mode) {
try {
const fh = new FileHandle(fs.openSync(name, modeToFlags(mode)));
return fh;
} catch (e) {
if (e.code === 'ENOENT') {
throw new NQPException(`Failed to open file ${name}: No such file or director`);
throw new NQPException(`Failed to open file ${name}: No such file or directory`);
} else {
throw e;
}
Expand Down
Binary file added src/vm/js/nqp-runtime/moar.core
Binary file not shown.
Binary file added src/vm/js/nqp-runtime/node.core
Binary file not shown.
42 changes: 22 additions & 20 deletions src/vm/js/nqp-runtime/shared-io.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
function stat(fs, file, code, lstat) {
const EXISTS = 0;
const FILESIZE = 1;
const ISDIR = 2;
const ISREG = 3;
const ISDEV = 4;
const CREATETIME = 5;
const ACCESSTIME = 6;
const MODIFYTIME = 7;
const CHANGETIME = 8;
const BACKUPTIME = 9;
const UID = 10;
const GID = 11;
const ISLNK = 12;
const PLATFORM_DEV = -1;
const PLATFORM_INODE = -2;
const PLATFORM_MODE = -3;
const PLATFORM_NLINKS = -4;
const PLATFORM_DEVTYPE = -5;
function stat(fs, file, code, lstat, fstat) {
const EXISTS = 0;
const FILESIZE = 1;
const ISDIR = 2;
const ISREG = 3;
const ISDEV = 4;
const CREATETIME = 5;
const ACCESSTIME = 6;
const MODIFYTIME = 7;
const CHANGETIME = 8;
const BACKUPTIME = 9;
const UID = 10;
const GID = 11;
const ISLNK = 12;
const PLATFORM_DEV = -1;
const PLATFORM_INODE = -2;
const PLATFORM_MODE = -3;
const PLATFORM_NLINKS = -4;
const PLATFORM_DEVTYPE = -5;
const PLATFORM_BLOCKSIZE = -6;
const PLATFORM_BLOCKS = -7;
const PLATFORM_BLOCKS = -7;

// we can't use fs.existsSync(file) as it follows symlinks
let stats;
try {
if (lstat || code == ISLNK) {
stats = fs.lstatSync(file);
} else if (fstat) {
stmts = fs.fstatSync(file);
} else {
stats = fs.statSync(file);
}
Expand Down
3 changes: 3 additions & 0 deletions src/vm/jvm/QAST/Compiler.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -2159,8 +2159,11 @@ QAST::OperationsJAST.map_classlib_core_op('print', $TYPE_OPS, 'print', [$RT_STR]
QAST::OperationsJAST.map_classlib_core_op('say', $TYPE_OPS, 'say', [$RT_STR], $RT_STR, :tc);
QAST::OperationsJAST.map_classlib_core_op('stat', $TYPE_OPS, 'stat', [$RT_STR, $RT_INT], $RT_INT);
QAST::OperationsJAST.map_classlib_core_op('lstat', $TYPE_OPS, 'lstat', [$RT_STR, $RT_INT], $RT_INT);
QAST::OperationsJAST.map_classlib_core_op('fstat', $TYPE_OPS, 'fstat', [$RT_INT, $RT_INT], $RT_INT);
QAST::OperationsJAST.map_classlib_core_op('stat_time', $TYPE_OPS, 'stat_time', [$RT_STR, $RT_INT], $RT_NUM);
QAST::OperationsJAST.map_classlib_core_op('lstat_time', $TYPE_OPS, 'lstat_time', [$RT_STR, $RT_INT], $RT_NUM);
QAST::OperationsJAST.map_classlib_core_op('fstat_time', $TYPE_OPS, 'fstat_time', [$RT_INT, $RT_INT], $RT_NUM);
QAST::OperationsJAST.map_classlib_core_op('fdopen', $TYPE_OPS, 'fdopen', [$RT_INT], $RT_OBJ, :tc);
QAST::OperationsJAST.map_classlib_core_op('open', $TYPE_OPS, 'open', [$RT_STR, $RT_STR], $RT_OBJ, :tc);
QAST::OperationsJAST.map_classlib_core_op('readlink', $TYPE_OPS, 'readlink', [$RT_STR], $RT_STR, :tc);
QAST::OperationsJAST.map_classlib_core_op('filereadable', $TYPE_OPS, 'filereadable', [$RT_STR], $RT_INT, :tc);
Expand Down
12 changes: 12 additions & 0 deletions src/vm/jvm/runtime/org/perl6/nqp/runtime/Ops.java
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ public static long lstat(String filename, long status) {
return stat_internal(filename, status, LinkOption.NOFOLLOW_LINKS);
}

public static long fstat(long fd, long status) {
throw new UnsupportedOperationException("nqp::fstat is not supported on the JVM");
}

public static long stat_internal(String filename, long status, LinkOption ... linkOption) {
long rval = -1;

Expand Down Expand Up @@ -381,6 +385,10 @@ public static double lstat_time(String filename, long status) {
return stat_time_internal(filename, status, LinkOption.NOFOLLOW_LINKS);
}

public static double fstat_time(long fd, long status) {
throw new UnsupportedOperationException("nqp::fstat_time is not supported on the JVM");
}

protected static double stat_time_internal(String filename, long status, LinkOption... linkOption) {
String attrName;
switch ((int) status) {
Expand Down Expand Up @@ -408,6 +416,10 @@ protected static double stat_time_internal(String filename, long status, LinkOpt
}
}

public static SixModelObject fdopen(long fd, ThreadContext tc) {
throw new UnsupportedOperationException("nqp::fdopen is not supported on the JVM");
}

public static SixModelObject open(String path, String mode, ThreadContext tc) {
SixModelObject IOType = tc.curFrame.codeRef.staticInfo.compUnit.hllConfig.ioType;
IOHandleInstance h = (IOHandleInstance)IOType.st.REPR.allocate(tc, IOType.st);
Expand Down
3 changes: 3 additions & 0 deletions src/vm/moar/QAST/QASTOperationsMAST.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -2274,7 +2274,10 @@ QAST::MASTOperations.add_core_moarop_mapping('stat', 'stat');
QAST::MASTOperations.add_core_moarop_mapping('stat_time', 'stat_time');
QAST::MASTOperations.add_core_moarop_mapping('lstat', 'lstat');
QAST::MASTOperations.add_core_moarop_mapping('lstat_time', 'lstat_time');
QAST::MASTOperations.add_core_moarop_mapping('fstat', 'fstat');
QAST::MASTOperations.add_core_moarop_mapping('fstat_time', 'fstat_time');
QAST::MASTOperations.add_core_moarop_mapping('open', 'open_fh');
QAST::MASTOperations.add_core_moarop_mapping('fdopen', 'fdopen_fh');
QAST::MASTOperations.add_core_moarop_mapping('filereadable', 'filereadable');
QAST::MASTOperations.add_core_moarop_mapping('filewritable', 'filewritable');
QAST::MASTOperations.add_core_moarop_mapping('fileexecutable', 'fileexecutable');
Expand Down