Skip to content

Commit 7fcba01

Browse files
LiedtkeV8-internal LUCI CQ
authored andcommitted
JavaScriptExecutor: Support large outputs
This is needed for a tool that uses the JavaScriptExecutor and produces a large amount of output (the list of all builtins available in the global scope). Bug: 487347678 Change-Id: Ib83ee2ae33a609e5b8ce1598b14892a8cedfd0a4 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9047637 Reviewed-by: Danylo Mocherniuk <mdanylo@google.com> Commit-Queue: Matthias Liedtke <mliedtke@google.com>
1 parent 621f98c commit 7fcba01

2 files changed

Lines changed: 48 additions & 13 deletions

File tree

Sources/Fuzzilli/Util/JavaScriptExecutor.swift

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,30 @@ import WinSDK
1919
#endif /* os(Windows) */
2020

2121
public class JavaScriptExecutor {
22+
// helper to support program output larger than the Pipe()'s buffer.
23+
private final class OutputBuffer: @unchecked Sendable {
24+
private var data = Data()
25+
private let lock = NSLock()
26+
27+
func append(_ newData: Data) {
28+
lock.lock()
29+
defer { lock.unlock() }
30+
data.append(newData)
31+
}
32+
33+
var count: Int {
34+
lock.lock()
35+
defer { lock.unlock() }
36+
return data.count
37+
}
38+
39+
var currentData: Data {
40+
lock.lock()
41+
defer { lock.unlock() }
42+
return data
43+
}
44+
}
45+
2246
/// Path to the js shell binary.
2347
let executablePath: String
2448

@@ -84,6 +108,11 @@ public class JavaScriptExecutor {
84108
let outputPipe = Pipe()
85109
let errorPipe = Pipe()
86110

111+
let outputData = OutputBuffer()
112+
outputPipe.fileHandleForReading.readabilityHandler = { handle in
113+
outputData.append(handle.availableData)
114+
}
115+
87116
// Write input into file.
88117
let url = FileManager.default.temporaryDirectory
89118
.appendingPathComponent(UUID().uuidString)
@@ -138,19 +167,11 @@ public class JavaScriptExecutor {
138167
try FileManager.default.removeItem(at: url)
139168

140169
// Fetch and return the output.
141-
var output = ""
142-
if let data = String(data: outputPipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) {
143-
output = data
144-
} else {
145-
output = "Process output is not valid UTF-8"
146-
}
147-
var error = ""
148-
if let data = String(data: errorPipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) {
149-
error = data
150-
} else {
151-
error = "Process stderr is not valid UTF-8"
152-
}
153-
var outcome: Result.Outcome
170+
var output = String(data: outputData.currentData, encoding: .utf8)
171+
?? "Process output is not valid UTF-8"
172+
let error = String(data: errorPipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)
173+
?? "Process stderr is not valid UTF-8"
174+
let outcome: Result.Outcome
154175
if timedOut {
155176
outcome = .timedOut
156177
output += "\nError: Timed out"

Tests/FuzzilliTests/TestUtils.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,17 @@ func buildAndLiftProgram(buildFunc: (ProgramBuilder) -> ()) -> String {
8585
return buildAndLiftProgram(withLiftingOptions: [], buildFunc: buildFunc)
8686
}
8787

88+
class TestUtilsTests: XCTestCase {
89+
90+
// Test that running a program via the JavaScriptExecutor that produces a large output succeeds.
91+
// (This test case exists because when using a raw Pipe() as stdout without any further
92+
// handling, the child process will be interrupted once the buffer size (64KB on Linux
93+
// apparently) is filled.)
94+
func testJavaScriptExecutorLargeOutput() throws {
95+
let jsProg = "console.log(JSON.stringify(Array(50000).fill(1)))"
96+
let runner = try GetJavaScriptExecutorOrSkipTest()
97+
let result = try runner.executeScript(jsProg, withTimeout: 10)
98+
XCTAssert(result.isSuccess, "\(result.output)\n\(result.error)")
99+
XCTAssertGreaterThan(result.output.count, 100_000)
100+
}
101+
}

0 commit comments

Comments
 (0)