emscripten-core / emscripten-core/emscripten
std::random_device fails on windows with NODERAWFS
- Dominant language
- C++
- Stars
- 27.6k
- Forks
- 3.6k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 105
Description
Given the following emscripten-bug.cpp
```cpp
#include
#include
#ifdef __cplusplus
extern "C" {
#endif
void emscripten_test() {
std::random_device rd;
// Fails on windows if NODERAWFS is enabled
std::cerr << "Random " << rd() << "\n";
std::cerr << "Random " << rd() << "\n";
std::cerr << "Random " << rd() << "\n";
}
#ifdef __cplusplus
}
#endif
```
Compiled via
```
em++ -Wall -O3 -pipe -DNDEBUG -fomit-frame-pointer -std=c++11 -c \
-o build/emscripten-bug.o emscripten-bug.cpp
emcc build/emscripten-bug.o -o build/emscripten-bug.js \
--js-opts 0 -O3 \
-s WASM=0 \
-s DETERMINISTIC=1 \
-s EXPORTED_FUNCTIONS="['_emscripten_test']" \
-s USE_CLOSURE_COMPILER=0 \
-s ALLOW_MEMORY_GROWTH=1 \
-s ENVIRONMENT=node \
-s NODERAWFS=1 \
--memory-init-file 0
```
And invoked by
```js
const sass = require('./build/emscripten-bug');
sass._emscripten_test();
```
This fails to work on windows. Either removing the `random_device` or using `NODERAWFS=0` fixes the problem, but I really want to have both :/
The reason why it fails is because `NODERAWFS` overwrites the virtual FS with the following code:
```js
if (ENVIRONMENT_IS_NODE) {
var _wrapNodeError = function(func) {
return function() {
try {
return func.apply(this, arguments)
} catch (e) {
if (!e.code) throw e;
throw new FS.ErrnoError(ERRNO_CODES[e.code]);
}
}
};
var VFS = Object.assign({}, FS);
for (var _key in NODERAWFS) {
FS[_key] = _wrapNodeError(NODERAWFS[_key]);
}
} else {
throw new Error("NODERAWFS is currently only supported on Node.js environment.");
}
```
The error happens when it tries to read `/dev/urandom`. Logically this file will not be available on windows and it seems that's why the original virtual FS defines those directly via:
```js
FS.createDevice('/dev', 'random', random_device);
FS.createDevice('/dev', 'urandom', random_device);
```
So instead of using the "shimmed" device it tries to use the real ones.
The fix seems somewhat simple, but I'm not sure what the best way is.
I think it's reasonable to use the VFS for `dev` and `proc` even if `NODERAWFS` is enabled, as those are probably never really portable. Maybe another flag is needed (or use `NODERAWFS=2`) so users can decide whether they want to use "emulated" `dev` and `proc` instead of real ones.
Following is a quick hack/hotfix to get it working:
```js
if (ENVIRONMENT_IS_NODE) {
var VFS = Object.assign({}, FS);
var _wrapNodeError = function(key, func) {
return function() {
if (arguments.length && arguments['0'].match && arguments['0'].match(/^\/+(?:dev|proc)\//)) {
return VFS[key].apply(this, arguments);
}
try {
return func.apply(this, arguments)
} catch (e) {
if (!e.code) throw e;
throw new FS.ErrnoError(ERRNO_CODES[e.code]);
}
}
};
for (var _key in NODERAWFS) {
FS[_key] = _wrapNodeError(_key, NODERAWFS[_key]);
}
} else {
throw new Error("NODERAWFS is currently only supported on Node.js environment.");
}
```
I might even be able to come up with a PR once it's decided how you want to move forward with this. Thank you and have a nice day!
Contributor guide
Assessment
This issue has not been assessed yet.