-
-
Notifications
You must be signed in to change notification settings - Fork 46
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -238,9 +238,14 @@ public actor Server { | |
let environment = | ||
head.headers["User-Agent"].compactMap(DestinationEnvironment.init).first | ||
?? .other | ||
let handler = await ServerWebSocketHandler( | ||
let handler = ServerWebSocketHandler( | ||
configuration: ServerWebSocketHandler.Configuration( | ||
onText: self.createWebSocketTextHandler(in: environment, terminal: self.configuration.terminal) | ||
onText: { [weak self] (text) in | ||
self?.webSocketTextHandler(text: text, environment: environment) | ||
}, | ||
onBinary: { [weak self] (data) in | ||
self?.webSocketBinaryHandler(data: data) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 新たにバイナリメッセージのハンドリングを追加します。 |
||
} | ||
) | ||
) | ||
await self.add(connection: Connection(channel: channel)) | ||
|
@@ -329,50 +334,87 @@ public actor Server { | |
} | ||
|
||
extension Server { | ||
/// Returns a handler that responds to WebSocket messages coming from the browser. | ||
func createWebSocketTextHandler( | ||
in environment: DestinationEnvironment, | ||
terminal: InteractiveWriter | ||
) -> @Sendable (String) -> Void { | ||
{ [weak self] text in | ||
guard let self = self else { return } | ||
guard | ||
let data = text.data(using: .utf8), | ||
let event = try? self.decoder.decode(Event.self, from: data) | ||
else { | ||
return | ||
} | ||
/// Respond to WebSocket messages coming from the browser. | ||
nonisolated func webSocketTextHandler( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ここなんですが、関数を返す関数から、普通のメソッドにしました。 まず、いきなり大きなラムダ式を開くコードはややこしいです。 通常のメソッドにした方が、 actor のメソッドとしての解析や制御がちゃんと機能します。 selfの弱参照についても、 このメソッド自体についてですが、 |
||
text: String, | ||
environment: DestinationEnvironment | ||
) { | ||
guard | ||
let data = text.data(using: .utf8), | ||
let event = try? self.decoder.decode(Event.self, from: data) | ||
else { | ||
return | ||
} | ||
|
||
switch event { | ||
case let .stackTrace(rawStackTrace): | ||
if let stackTrace = rawStackTrace.parsedStackTrace(in: environment) { | ||
terminal.write("\nAn error occurred, here's a stack trace for it:\n", inColor: .red) | ||
stackTrace.forEach { item in | ||
terminal.write(" \(item.symbol)", inColor: .cyan) | ||
terminal.write(" at \(item.location ?? "<unknown>")\n", inColor: .gray) | ||
} | ||
} else { | ||
terminal.write("\nAn error occurred, here's the raw stack trace for it:\n", inColor: .red) | ||
terminal.write( | ||
" Please create an issue or PR to the Carton repository\n" | ||
+ " with your browser name and this raw stack trace so\n" | ||
+ " we can add support for it: https://github.com/swiftwasm/carton\n", inColor: .gray | ||
) | ||
terminal.write(rawStackTrace + "\n") | ||
let terminal = self.configuration.terminal | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
switch event { | ||
case let .stackTrace(rawStackTrace): | ||
if let stackTrace = rawStackTrace.parsedStackTrace(in: environment) { | ||
terminal.write("\nAn error occurred, here's a stack trace for it:\n", inColor: .red) | ||
stackTrace.forEach { item in | ||
terminal.write(" \(item.symbol)", inColor: .cyan) | ||
terminal.write(" at \(item.location ?? "<unknown>")\n", inColor: .gray) | ||
} | ||
} else { | ||
terminal.write("\nAn error occurred, here's the raw stack trace for it:\n", inColor: .red) | ||
terminal.write( | ||
" Please create an issue or PR to the Carton repository\n" | ||
+ " with your browser name and this raw stack trace so\n" | ||
+ " we can add support for it: https://github.com/swiftwasm/carton\n", inColor: .gray | ||
) | ||
terminal.write(rawStackTrace + "\n") | ||
} | ||
|
||
case let .testRunOutput(output): | ||
TestsParser().parse(output, terminal) | ||
|
||
case .testPassed: | ||
Task { await self.stopTest(hadError: false) } | ||
|
||
case let .errorReport(output): | ||
terminal.write("\nAn error occurred:\n", inColor: .red) | ||
terminal.write(output + "\n") | ||
|
||
case let .testRunOutput(output): | ||
TestsParser().parse(output, terminal) | ||
Task { await self.stopTest(hadError: true) } | ||
} | ||
} | ||
|
||
private static func decodeLines(data: Data) -> [String] { | ||
let text = String(decoding: data, as: UTF8.self) | ||
return text.components(separatedBy: .newlines) | ||
} | ||
|
||
nonisolated func webSocketBinaryHandler(data: Data) { | ||
let terminal = self.configuration.terminal | ||
|
||
if data.count < 2 { | ||
return | ||
} | ||
|
||
var kind: UInt16 = 0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 2バイトのヘッダをつけるフォーマットにしました。 |
||
_ = withUnsafeMutableBytes(of: &kind) { (buffer) in | ||
data.copyBytes(to: buffer, from: 0..<2) | ||
} | ||
|
||
case .testPassed: | ||
Task { await self.stopTest(hadError: false) } | ||
switch kind { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please reinterpret with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. なるほど。そういうのがあるんですね。やりました。 |
||
case 1001: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1001はstdoutのチャンクを転送するコマンドです |
||
// stdout | ||
let chunk = data.subdata(in: 2..<data.count) | ||
if chunk.isEmpty { return } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ここまではバイナリで来てますが、 |
||
terminal.write("stdout: " + line + "\n") | ||
} | ||
case 1002: | ||
// stderr | ||
let chunk = data.subdata(in: 2..<data.count) | ||
if chunk.isEmpty { return } | ||
|
||
Task { await self.stopTest(hadError: true) } | ||
for line in Self.decodeLines(data: chunk) { | ||
terminal.write("stderr: " + line + "\n", inColor: .red) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. エラーは赤くしたんですが色がつきませんでした。 |
||
} | ||
default: break | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,16 @@ | ||
print("hello dev server") | ||
#if os(WASI) | ||
import WASILibc | ||
typealias FILEPointer = OpaquePointer | ||
#else | ||
import Darwin | ||
typealias FILEPointer = UnsafeMutablePointer<FILE> | ||
#endif | ||
|
||
func fputs(_ string: String, file: FILEPointer) { | ||
_ = string.withCString { (cstr) in | ||
fputs(cstr, file) | ||
} | ||
} | ||
|
||
fputs("hello stdout\n", file: stdout) | ||
fputs("hello stderr\n", file: stderr) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,9 +44,29 @@ const startWasiTask = async () => { | |
|
||
const wasmRunner = WasmRunner( | ||
{ | ||
onStdout(chunk) { | ||
const kindBuffer = new ArrayBuffer(2); | ||
new DataView(kindBuffer).setUint16(0, 1001, true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 頭に2バイトのヘッダをつけてからwebsocketに流します。 JSのバイナリ処理に慣れてなくて書き方がこれでいいか自信がないです。 |
||
|
||
const buffer = new Uint8Array(2 + chunk.length); | ||
buffer.set(new Uint8Array(kindBuffer), 0); | ||
buffer.set(chunk, 2); | ||
|
||
socket.send(buffer); | ||
}, | ||
onStdoutLine(line) { | ||
console.log(line); | ||
}, | ||
onStderr(chunk) { | ||
const kindBuffer = new ArrayBuffer(2); | ||
new DataView(kindBuffer).setUint16(0, 1002, true); | ||
|
||
const buffer = new Uint8Array(2 + chunk.length); | ||
buffer.set(new Uint8Array(kindBuffer), 0); | ||
buffer.set(chunk, 2); | ||
|
||
socket.send(buffer); | ||
}, | ||
onStderrLine(line) { | ||
console.error(line); | ||
} | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
処理は普通にメソッドで書いて、weak self を明示的に書いた方が、
参照がどうなっているかと、メソッドで何をするか、
という独立な要素がコードとして分離できてシンプルです。