Skip to content
Merged
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
3 changes: 2 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
## Bug fixes
* Compiler: fix purity of comparison functions (again) (#2092)
* Compiler: fix inlining (#2107)
* Runtime/wasm: fix Unix.times (#2096)
* Ppx: disable spurious warning for unused "self" in object literal (#2128)
* Ppx: fix labelled arguments for methods (#2126)
* Runtime/wasm: fix Unix.times (#2096)
* Runtime: runtime with target-env=browser should not rely on "require(..)" (#2129)
* Runtime: fix fake filesystem with path containing special regexp chars. (#2132)

# 6.2.0 (2025-07-30) - Lille

Expand Down
33 changes: 33 additions & 0 deletions compiler/tests-compiler/sys_fs.ml
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,36 @@ f (); Sys.chdir "/static"; f ()
EXPECTED ERROR
EXPECTED ERROR
|}]

let%expect_test "check fake filesystem with path containing special regex chars" =
compile_and_run
{|
let f () =
let touch path =
let oc = open_out path in
close_out oc
in
Sys.mkdir "test.dir" 0o777;
Sys.mkdir "test_dir" 0o777;
touch "test.dir/dot";
touch "test_dir/underscore";
print_endline "test.dir:";
Array.iter print_endline (Sys.readdir "test.dir");
print_endline "test_dir:";
Array.iter print_endline (Sys.readdir "test_dir");
print_endline ""
in
f (); Sys.chdir "/static"; f ()
|};
[%expect
{|
test.dir:
dot
test_dir:
underscore

test.dir:
dot
test_dir:
underscore
|}]
13 changes: 4 additions & 9 deletions runtime/js/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ function MlFile() {}

//Provides: path_is_absolute
//Requires: fs_node_supported
//Requires: jsoo_is_win32
function make_path_is_absolute() {
function posix(path) {
if (path.charAt(0) === "/") return ["", path.slice(1)];
Expand All @@ -72,13 +73,7 @@ function make_path_is_absolute() {
}
return;
}
if (
fs_node_supported() &&
globalThis.process &&
globalThis.process.platform
) {
return globalThis.process.platform === "win32" ? win32 : posix;
} else return posix;
return jsoo_is_win32 ? win32 : posix;
}
var path_is_absolute = make_path_is_absolute();

Expand Down Expand Up @@ -278,7 +273,7 @@ function caml_sys_rename(o, n) {
var n_root = resolve_fs_device(n);
if (o_root.device !== n_root.device)
caml_failwith("caml_sys_rename: cannot move file between two filesystem");
if (!o_root.device.rename) caml_failwith("caml_sys_rename: no implemented");
if (!o_root.device.rename) caml_failwith("caml_sys_rename: not implemented");
o_root.device.rename(o_root.rest, n_root.rest);
}

Expand Down Expand Up @@ -363,7 +358,7 @@ function caml_read_file_content(name) {
var file = root.device.open(root.rest, { rdonly: 1 });
var len = file.length();
var buf = new Uint8Array(len);
file.read(buf, 0, len);
file.read(buf, 0, len, false);
return caml_string_of_uint8_array(buf);
}
caml_raise_no_such_file(caml_jsstring_of_string(name));
Expand Down
34 changes: 23 additions & 11 deletions runtime/js/fs_fake.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ class MlFakeDevice {

rmdir(name, raise_unix) {
var name_slash = name === "" ? "" : this.slash(name);
var r = new RegExp("^" + name_slash + "([^/]+)");
if (!this.exists(name))
caml_raise_system_error(
raise_unix,
Expand All @@ -167,7 +166,7 @@ class MlFakeDevice {
this.nm(name),
);
for (var n in this.content) {
if (n.match(r))
if (n.startsWith(name_slash) && n !== name_slash)
caml_raise_system_error(
raise_unix,
"ENOTEMPTY",
Expand All @@ -187,14 +186,17 @@ class MlFakeDevice {
if (!this.is_dir(name)) {
caml_raise_sys_error(name + ": Not a directory");
}
var r = new RegExp("^" + name_slash + "([^/]+)");
var seen = {};
var a = [];
for (var n in this.content) {
var m = n.match(r);
if (m && !seen[m[1]]) {
seen[m[1]] = true;
a.push(m[1]);
if (n.startsWith(name_slash) && n !== name_slash) {
var last = n.indexOf("/", name_slash.length);
if (last < 0) last = undefined;
var m = n.slice(name_slash.length, last);
if (m && !seen[m]) {
seen[m] = true;
a.push(m);
}
}
}
return a;
Expand Down Expand Up @@ -291,7 +293,7 @@ class MlFakeDevice {
this.nm(name),
);
file = this.content[name];
if (f.truncate) file.truncate();
if (f.truncate) file.truncate(0);
} else if (f.create) {
this.create_dir_if_needed(name);
this.content[name] = new MlFakeFile(caml_create_bytes(0));
Expand Down Expand Up @@ -360,8 +362,9 @@ class MlFakeFile extends MlFile {

truncate(len) {
var old = this.data;
var old_len = caml_ml_bytes_length(old);
this.data = caml_create_bytes(len | 0);
caml_blit_bytes(old, 0, this.data, 0, len);
caml_blit_bytes(old, 0, this.data, 0, Math.min(len, old_len));
}

length() {
Expand Down Expand Up @@ -391,12 +394,13 @@ class MlFakeFile extends MlFile {
if (offset + len >= clen) {
len = clen - offset;
}
if (len) {
if (len > 0) {
var data = caml_create_bytes(len | 0);
caml_blit_bytes(this.data, offset, data, 0, len);
buf.set(caml_uint8_array_of_bytes(data), pos);
return len;
}
return len;
return 0;
}
}

Expand Down Expand Up @@ -461,6 +465,9 @@ class MlFakeFd_out extends MlFakeFile {
seek(_len, _whence, raise_unix) {
caml_raise_system_error(raise_unix, "ESPIPE", "lseek", "illegal seek");
}
pos() {
return -1;
}

close() {
this.log = undefined;
Expand Down Expand Up @@ -544,6 +551,11 @@ class MlFakeFd {
);
this.offset = offset;
this.seeked = true;
return offset;
}

pos() {
return this.offset;
}

close() {
Expand Down
43 changes: 28 additions & 15 deletions runtime/js/fs_node.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

//Provides: jsoo_is_win32
var jsoo_is_win32 =
globalThis.Deno?.build?.os === "windows" ||
globalThis.process?.platform === "win32";

//Provides: fs_node_supported
function fs_node_supported() {
return globalThis.process?.versions?.node !== undefined;
Expand All @@ -29,7 +34,8 @@ function fs_node_supported() {

//Provides: MlNodeDevice
//Requires: MlNodeFd, caml_raise_sys_error, caml_string_of_jsstring
//Requires: caml_raise_nodejs_error, fs_node_stats_from_js
//Requires: caml_raise_nodejs_error, ocaml_stats_from_node_stats
//Requires: jsoo_is_win32
class MlNodeDevice {
constructor(root) {
this.fs = require("node:fs");
Expand Down Expand Up @@ -122,7 +128,7 @@ class MlNodeDevice {
}

access(name, f, raise_unix) {
var consts = require("node:fs").constants;
var consts = this.fs.constants;
var res = 0;
for (var key in f) {
switch (key) {
Expand All @@ -133,10 +139,7 @@ class MlNodeDevice {
res |= consts.W_OK;
break;
case "x":
res |=
globalThis.process?.platform === "win32"
? consts.R_OK
: consts.X_OK;
res |= jsoo_is_win32 ? consts.R_OK : consts.X_OK;
break;
case "f":
res |= consts.F_OK;
Expand All @@ -152,7 +155,7 @@ class MlNodeDevice {
}

open(name, f, perms, raise_unix) {
var consts = require("node:fs").constants;
var consts = this.fs.constants;
var res = 0;
for (var key in f) {
switch (key) {
Expand Down Expand Up @@ -210,7 +213,7 @@ class MlNodeDevice {
}

rename(o, n, raise_unix) {
if (globalThis.process?.platform === "win32") {
if (jsoo_is_win32) {
try {
var target = this.nm(n);
var source = this.nm(o);
Expand Down Expand Up @@ -257,7 +260,7 @@ class MlNodeDevice {
stat(name, large, raise_unix) {
try {
var js_stats = this.fs.statSync(this.nm(name));
return fs_node_stats_from_js(js_stats, large);
return ocaml_stats_from_node_stats(js_stats, large);
} catch (err) {
caml_raise_nodejs_error(err, raise_unix);
}
Expand All @@ -266,7 +269,7 @@ class MlNodeDevice {
lstat(name, large, raise_unix) {
try {
var js_stats = this.fs.lstatSync(this.nm(name));
return fs_node_stats_from_js(js_stats, large);
return ocaml_stats_from_node_stats(js_stats, large);
} catch (err) {
caml_raise_nodejs_error(err, raise_unix);
}
Expand Down Expand Up @@ -321,9 +324,9 @@ class MlNodeDevice {
}
}

//Provides: fs_node_stats_from_js
//Provides: ocaml_stats_from_node_stats
//Requires: caml_int64_of_float
function fs_node_stats_from_js(js_stats, large) {
function ocaml_stats_from_node_stats(js_stats, large) {
/* ===Unix.file_kind===
* type file_kind =
* S_REG (** Regular file *)
Expand Down Expand Up @@ -389,7 +392,7 @@ class MlNodeDevice {}

//Provides: MlNodeFd
//Requires: MlFile, caml_uint8_array_of_string, caml_uint8_array_of_bytes, caml_bytes_set, caml_raise_sys_error
//Requires: caml_raise_nodejs_error, caml_raise_system_error, fs_node_stats_from_js
//Requires: caml_raise_nodejs_error, caml_raise_system_error, ocaml_stats_from_node_stats
class MlNodeFd extends MlFile {
constructor(fd, flags) {
super();
Expand All @@ -400,12 +403,13 @@ class MlNodeFd extends MlFile {
var stats = this.fs.fstatSync(fd);
flags.noSeek =
stats.isCharacterDevice() || stats.isFIFO() || stats.isSocket();
this.offset = this.flags.append ? stats.size : 0;
} catch (err) {
// The fstat will fail on standard streams under Windows with node
// 18 (and lower). See https://github.com/libuv/libuv/pull/3811.
flags.noSeek = true;
this.offset = 0;
}
this.offset = this.flags.append ? stats.size : 0;
this.seeked = false;
}

Expand All @@ -418,6 +422,11 @@ class MlNodeFd extends MlFile {
}
}

isatty() {
var tty = require("node:tty");
return tty.isatty(this.fd) ? 1 : 0;
}

length() {
try {
return this.fs.fstatSync(this.fd).size;
Expand Down Expand Up @@ -487,10 +496,14 @@ class MlNodeFd extends MlFile {
return this.offset;
}

pos() {
return this.offset;
}

stat(large) {
try {
var js_stats = this.fs.fstatSync(this.fd);
return fs_node_stats_from_js(js_stats, large);
return ocaml_stats_from_node_stats(js_stats, large);
} catch (err) {
caml_raise_nodejs_error(err, /* raise Unix_error */ 1);
}
Expand Down
9 changes: 5 additions & 4 deletions runtime/js/io.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ var caml_sys_fds = new Array(3);
function caml_sys_close(fd) {
var x = caml_sys_fds[fd];
if (x) {
x.file.close();
x.file.close(false);
delete caml_sys_fds[fd];
}
return 0;
Expand Down Expand Up @@ -204,7 +204,7 @@ function caml_ml_open_descriptor_out(fd) {
var buffered = file.flags.buffered !== undefined ? file.flags.buffered : 1;
var channel = {
file: file,
offset: file.offset,
offset: file.pos(),
fd: fd,
opened: true,
out: true,
Expand All @@ -230,7 +230,7 @@ function caml_ml_open_descriptor_in(fd) {
var refill = null;
var channel = {
file: file,
offset: file.offset,
offset: file.pos(),
fd: fd,
opened: true,
out: false,
Expand Down Expand Up @@ -360,6 +360,7 @@ function caml_refill(chan) {
chan.buffer,
chan.buffer_max,
chan.buffer.length - chan.buffer_max,
false,
);
chan.offset += nread;
chan.buffer_max += nread;
Expand Down Expand Up @@ -568,7 +569,7 @@ function caml_ml_flush(chanid) {
);
} else {
for (var pos = 0; pos < chan.buffer_curr; ) {
pos += chan.file.write(chan.buffer, pos, chan.buffer_curr - pos);
pos += chan.file.write(chan.buffer, pos, chan.buffer_curr - pos, false);
}
}
chan.offset += chan.buffer_curr;
Expand Down
3 changes: 2 additions & 1 deletion runtime/js/sys.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,8 @@ function caml_sys_const_backend_type() {
}

//Provides: os_type
var os_type = globalThis.process?.platform === "win32" ? "Win32" : "Unix";
//Requires: jsoo_is_win32
var os_type = jsoo_is_win32 ? "Win32" : "Unix";

//Provides: caml_sys_get_config const
//Requires: caml_string_of_jsbytes, os_type
Expand Down
Loading
Loading