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

Show stdout/stderr from Wasm in terminal #472

Merged
merged 3 commits into from
May 25, 2024

Conversation

omochi
Copy link
Contributor

@omochi omochi commented May 24, 2024

背景

現在 dev.js は Wasmのstdoutとstderrをブラウザのコンソールに転送します。

課題

しかしこれを開くにはちょっと手間がかかります。
また、その出力内容を自動テストするのが難しいです。

提案

Wasmが吐いた stdout, stderr を Dev Server まで届けます。
また、それを Dev Server のコンソールに出力します。

様子

アプリのコード

fputs("hello stdout\n", file: stdout)
fputs("hello stderr\n", file: stderr)

ターミナル

スクリーンショット 2024-05-25 0 32 02

将来の目標

さらに dev server が読み取った内容を外部に伝える機能を追加します。
それを使って Wasmの出力を自動テストできるようになるでしょう。

Copy link
Contributor Author

@omochi omochi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self comment

return
}
/// Respond to WebSocket messages coming from the browser.
nonisolated func webSocketTextHandler(
Copy link
Contributor Author

@omochi omochi May 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここなんですが、関数を返す関数から、普通のメソッドにしました。

まず、いきなり大きなラムダ式を開くコードはややこしいです。
全体に余計なインデントがずっと入るし、
メソッドとラムダの間で他のインタラクトに気をつけて読まないといけません。

通常のメソッドにした方が、 actor のメソッドとしての解析や制御がちゃんと機能します。

selfの弱参照についても、
根本のクロージャで書いた方がどういうポリシーで管理しているかわかりやすいでしょう。

このメソッド自体についてですが、
同期のクロージャから呼びたいので nonisolated にしています。

configuration: ServerWebSocketHandler.Configuration(
onText: self.createWebSocketTextHandler(in: environment, terminal: self.configuration.terminal)
onText: { [weak self] (text) in
self?.webSocketTextHandler(text: text, environment: environment)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

処理は普通にメソッドで書いて、weak self を明示的に書いた方が、
参照がどうなっているかと、メソッドで何をするか、
という独立な要素がコードとして分離できてシンプルです。

self?.webSocketTextHandler(text: text, environment: environment)
},
onBinary: { [weak self] (data) in
self?.webSocketBinaryHandler(data: data)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

新たにバイナリメッセージのハンドリングを追加します。

+ " we can add support for it: https://github.com/swiftwasm/carton\n", inColor: .gray
)
terminal.write(rawStackTrace + "\n")
let terminal = self.configuration.terminal
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self から取ってきます。

return
}

var kind: UInt16 = 0
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2バイトのヘッダをつけるフォーマットにしました。

case .testPassed:
Task { await self.stopTest(hadError: false) }
switch kind {
case 1001:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1001はstdoutのチャンクを転送するコマンドです

case 1001:
// stdout
let chunk = data.subdata(in: 2..<data.count)
if chunk.isEmpty { return }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

空の書き込みが来ることがあるので捨てます。

case let .errorReport(output):
terminal.write("\nAn error occurred:\n", inColor: .red)
terminal.write(output + "\n")
for line in Self.decodeLines(data: chunk) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここまではバイナリで来てますが、
最後のこの場所でコンソールに綺麗に出すために文字列処理します。


Task { await self.stopTest(hadError: true) }
for line in Self.decodeLines(data: chunk) {
terminal.write("stderr: " + line + "\n", inColor: .red)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

エラーは赤くしたんですが色がつきませんでした。
SwiftPMの問題。
swiftlang/swift-package-manager#7589

@@ -44,9 +44,29 @@ const startWasiTask = async () => {

const wasmRunner = WasmRunner(
{
onStdout(chunk) {
const kindBuffer = new ArrayBuffer(2);
new DataView(kindBuffer).setUint16(0, 1001, true);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

頭に2バイトのヘッダをつけてからwebsocketに流します。
リトルエンディアンにするので true を指定しています。

JSのバイナリ処理に慣れてなくて書き方がこれでいいか自信がないです。
なんか冗長です。

@omochi omochi marked this pull request as ready for review May 24, 2024 17:07

case .testPassed:
Task { await self.stopTest(hadError: false) }
switch kind {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please reinterpret with UInt16(littleEndian:) to ensure the bytes are interpreted as little endian

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほど。そういうのがあるんですね。やりました。

@kateinoigakukun kateinoigakukun changed the title [2/2] Dev Serverのコンソールに、Wasmが吐いたstdout, stderrを表示する Show stdout/stderr from Wasm in terminal May 25, 2024
@kateinoigakukun kateinoigakukun merged commit 6cc68d3 into swiftwasm:main May 25, 2024
4 checks passed
@omochi omochi deleted the print-stdout branch May 25, 2024 06:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants