|
| 1 | +// Copyright 2021 The Emscripten Authors. All rights reserved. |
| 2 | +// Emscripten is available under two separate licenses, the MIT license and the |
| 3 | +// University of Illinois/NCSA Open Source License. Both these licenses can be |
| 4 | +// found in the LICENSE file. |
| 5 | +// This file defines the standard streams of the new file system. |
| 6 | +// Current Status: Work in Progress. |
| 7 | +// See https://github.com/emscripten-core/emscripten/issues/15041. |
| 8 | + |
| 9 | +#include <emscripten/html5.h> |
| 10 | +#include <vector> |
| 11 | +#include <wasi/api.h> |
| 12 | + |
| 13 | +#include "special_files.h" |
| 14 | + |
| 15 | +namespace wasmfs::SpecialFiles { |
| 16 | + |
| 17 | +namespace { |
| 18 | + |
| 19 | +class StdinFile : public DataFile { |
| 20 | + void open(oflags_t) override {} |
| 21 | + void close() override {} |
| 22 | + |
| 23 | + __wasi_errno_t write(const uint8_t* buf, size_t len, off_t offset) override { |
| 24 | + return __WASI_ERRNO_INVAL; |
| 25 | + } |
| 26 | + |
| 27 | + __wasi_errno_t read(uint8_t* buf, size_t len, off_t offset) override { |
| 28 | + // TODO: Implement reading from stdin. |
| 29 | + abort(); |
| 30 | + }; |
| 31 | + |
| 32 | + void flush() override {} |
| 33 | + size_t getSize() override { return 0; } |
| 34 | + void setSize(size_t size) override {} |
| 35 | + |
| 36 | +public: |
| 37 | + StdinFile() : DataFile(S_IRUGO, NullBackend, S_IFCHR) { seekable = false; } |
| 38 | +}; |
| 39 | + |
| 40 | +// A standard stream that writes: stdout or stderr. |
| 41 | +class WritingStdFile : public DataFile { |
| 42 | +protected: |
| 43 | + std::vector<char> writeBuffer; |
| 44 | + |
| 45 | + void open(oflags_t) override {} |
| 46 | + void close() override {} |
| 47 | + |
| 48 | + __wasi_errno_t read(uint8_t* buf, size_t len, off_t offset) override { |
| 49 | + return __WASI_ERRNO_INVAL; |
| 50 | + }; |
| 51 | + |
| 52 | + void flush() override { |
| 53 | + // Write a null to flush the output if we have content. |
| 54 | + if (!writeBuffer.empty()) { |
| 55 | + const uint8_t nothing = '\0'; |
| 56 | + write(¬hing, 1, 0); |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + size_t getSize() override { return 0; } |
| 61 | + void setSize(size_t size) override {} |
| 62 | + |
| 63 | + __wasi_errno_t writeToJS(const uint8_t* buf, |
| 64 | + size_t len, |
| 65 | + void (*console_write)(const char*), |
| 66 | + std::vector<char>& fd_write_buffer) { |
| 67 | + for (size_t j = 0; j < len; j++) { |
| 68 | + uint8_t current = buf[j]; |
| 69 | + // Flush on either a null or a newline. |
| 70 | + if (current == '\0' || current == '\n') { |
| 71 | + fd_write_buffer.push_back('\0'); // for null-terminated C strings |
| 72 | + console_write(fd_write_buffer.data()); |
| 73 | + fd_write_buffer.clear(); |
| 74 | + } else { |
| 75 | + fd_write_buffer.push_back(current); |
| 76 | + } |
| 77 | + } |
| 78 | + return __WASI_ERRNO_SUCCESS; |
| 79 | + } |
| 80 | + |
| 81 | +public: |
| 82 | + WritingStdFile() : DataFile(S_IWUGO, NullBackend, S_IFCHR) { |
| 83 | + seekable = false; |
| 84 | + } |
| 85 | +}; |
| 86 | + |
| 87 | +class StdoutFile : public WritingStdFile { |
| 88 | + __wasi_errno_t write(const uint8_t* buf, size_t len, off_t offset) override { |
| 89 | + // Node and worker threads issue in Emscripten: |
| 90 | + // https://github.com/emscripten-core/emscripten/issues/14804. |
| 91 | + // Issue filed in Node: https://github.com/nodejs/node/issues/40961 |
| 92 | + // This is confirmed to occur when running with EXIT_RUNTIME and |
| 93 | + // PROXY_TO_PTHREAD. This results in only a single console.log statement |
| 94 | + // being outputted. The solution for now is to use out() and err() instead. |
| 95 | + return writeToJS(buf, len, &_emscripten_out, writeBuffer); |
| 96 | + } |
| 97 | + |
| 98 | +public: |
| 99 | + StdoutFile() {} |
| 100 | +}; |
| 101 | + |
| 102 | +class StderrFile : public WritingStdFile { |
| 103 | + __wasi_errno_t write(const uint8_t* buf, size_t len, off_t offset) override { |
| 104 | + // Similar issue with Node and worker threads as emscripten_out. |
| 105 | + // TODO: May not want to proxy stderr (fd == 2) to the main thread, as |
| 106 | + // emscripten_err does. |
| 107 | + // This will not show in HTML - a console.warn in a worker is |
| 108 | + // sufficient. This would be a change from the current FS. |
| 109 | + return writeToJS(buf, len, &_emscripten_err, writeBuffer); |
| 110 | + } |
| 111 | + |
| 112 | +public: |
| 113 | + StderrFile() {} |
| 114 | +}; |
| 115 | + |
| 116 | +} // anonymous namespace |
| 117 | + |
| 118 | +std::shared_ptr<DataFile> getStdin() { |
| 119 | + static auto stdin = std::make_shared<StdinFile>(); |
| 120 | + return stdin; |
| 121 | +} |
| 122 | + |
| 123 | +std::shared_ptr<DataFile> getStdout() { |
| 124 | + static auto stdout = std::make_shared<StdoutFile>(); |
| 125 | + return stdout; |
| 126 | +} |
| 127 | + |
| 128 | +std::shared_ptr<DataFile> getStderr() { |
| 129 | + static auto stderr = std::make_shared<StderrFile>(); |
| 130 | + return stderr; |
| 131 | +} |
| 132 | + |
| 133 | +} // namespace wasmfs::SpecialFiles |
0 commit comments