Skip to content

Commit 8c1414c

Browse files
committed
Merge remote-tracking branch 'upstream/main'
2 parents f178ca7 + 1d1e48b commit 8c1414c

30 files changed

Lines changed: 765 additions & 345 deletions

.github/workflows/swift.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ jobs:
2929
- uses: actions/setup-node@v4
3030
with:
3131
node-version: 25-nightly
32-
32+
- name: Swift Version
33+
run: swift --version
3334
# Is it failing to run tests b/c we're overriding
3435
# what swift binary to use?
3536
- name: Setup Swift for Ubuntu

Sources/Fuzzilli/Base/ProgramBuilder.swift

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,38 @@ import Foundation
1919
/// This provides methods for constructing and appending random
2020
/// instances of the different kinds of operations in a program.
2121
public class ProgramBuilder {
22+
23+
/// Runtime data used by code generators to share data between different stubs inside the same
24+
/// code generator. It is strictly required that all pushed values are also popped again in the
25+
/// same code generator.
26+
public class GeneratorRuntimeData {
27+
private var data = [String : Stack<Variable>]()
28+
29+
public func push(_ key: String, _ value: Variable) {
30+
data[key, default: .init()].push(value)
31+
}
32+
33+
public func pop(_ key: String) -> Variable {
34+
assert(data[key] != nil)
35+
return data[key]!.pop()
36+
}
37+
38+
// Fetch the most recent value for this key and push it back.
39+
public func popAndPush(_ key: String) -> Variable {
40+
assert(data[key] != nil)
41+
return data[key]!.top
42+
}
43+
44+
func reset() {
45+
#if DEBUG
46+
for (key, value) in data {
47+
assert(value.isEmpty, "Stale entries for runtime data '\(key)'")
48+
}
49+
#endif
50+
data.removeAll(keepingCapacity: false)
51+
}
52+
}
53+
2254
/// The fuzzer instance for which this builder is active.
2355
public let fuzzer: Fuzzer
2456

@@ -147,6 +179,9 @@ public class ProgramBuilder {
147179
/// The remaining CodeGenerators to call as part of a building / CodeGen step, these will "clean up" the state and fix the contexts.
148180
public var scheduled: Stack<GeneratorStub> = Stack()
149181

182+
// Runtime data that can be shared between different stubs within a CodeGenerator.
183+
var runtimeData = GeneratorRuntimeData()
184+
150185
/// Stack of active switch blocks.
151186
private var activeSwitchBlocks = Stack<SwitchBlock>()
152187

@@ -214,6 +249,7 @@ public class ProgramBuilder {
214249
activeObjectLiterals.removeAll()
215250
activeClassDefinitions.removeAll()
216251
buildLog?.reset()
252+
runtimeData.reset()
217253
}
218254

219255
/// Finalizes and returns the constructed program, then resets this builder so it can be reused for building another program.
@@ -483,6 +519,36 @@ public class ProgramBuilder {
483519
return probability(0.5) ? randomBuiltinMethodName() : randomCustomMethodName()
484520
}
485521

522+
private static func generateConstrained<T: Equatable>(
523+
_ generator: () -> T,
524+
notIn: any Collection<T>,
525+
fallback: () -> T) -> T {
526+
var result: T
527+
var attempts = 0
528+
repeat {
529+
if attempts >= 10 {
530+
return fallback()
531+
}
532+
result = generator()
533+
attempts += 1
534+
} while notIn.contains(result)
535+
return result
536+
}
537+
538+
// Generate a string not already contained in `notIn` using the provided `generator`. If it fails
539+
// repeatedly, return a random string instead.
540+
public func generateString(_ generator: () -> String, notIn: any Collection<String>) -> String {
541+
Self.generateConstrained(generator, notIn: notIn,
542+
fallback: {String.random(ofLength: Int.random(in: 1...5))})
543+
}
544+
545+
// Find a random variable to use as a string that isn't contained in `notIn`. If it fails
546+
// repeatedly, create a random string literal instead.
547+
public func findOrGenerateStringLikeVariable(notIn: any Collection<Variable>) -> Variable {
548+
return Self.generateConstrained(randomJsVariable, notIn: notIn,
549+
fallback: {loadString(String.random(ofLength: Int.random(in: 1...5)))})
550+
}
551+
486552
// Settings and constants controlling the behavior of randomParameters() below.
487553
// This determines how many variables of a given type need to be visible before
488554
// that type is considered a candidate for a parameter type. For example, if this
@@ -500,15 +566,15 @@ public class ProgramBuilder {
500566
//
501567
// This will attempt to find a parameter types for which at least a few variables of a compatible types are
502568
// currently available to (potentially) later be used as arguments for calling the generated subroutine.
503-
public func randomParameters(n wantedNumberOfParameters: Int? = nil) -> SubroutineDescriptor {
569+
public func randomParameters(n wantedNumberOfParameters: Int? = nil, withRestParameterProbability restProbability: Double = 0.2) -> SubroutineDescriptor {
504570
assert(probabilityOfUsingAnythingAsParameterTypeIfAvoidable >= 0 && probabilityOfUsingAnythingAsParameterTypeIfAvoidable <= 1)
505571

506572
// If the caller didn't specify how many parameters to generated, find an appropriate
507573
// number of parameters based on how many variables are currently visible (and can
508574
// therefore later be used as arguments for calling the new function).
509575
let n: Int
510576
if let requestedN = wantedNumberOfParameters {
511-
assert(requestedN > 0)
577+
assert(requestedN >= 0)
512578
n = requestedN
513579
} else {
514580
switch numberOfVisibleVariables {
@@ -537,15 +603,27 @@ public class ProgramBuilder {
537603
}
538604

539605
var params = ParameterList()
540-
for _ in 0..<n {
606+
607+
let generateRestParameter = n > 0 && probability(restProbability)
608+
let numPlainParams = generateRestParameter ? n - 1 : n
609+
610+
func randomParamType() -> ILType {
541611
if probability(probabilityOfUsingAnythingAsParameterTypeIfAvoidable) {
542-
params.append(.jsAnything)
612+
return .jsAnything
543613
} else {
544-
params.append(.plain(chooseUniform(from: candidates)))
614+
return chooseUniform(from: candidates)
545615
}
546616
}
547617

548-
// TODO: also generate rest parameters and maybe even optional ones sometimes?
618+
for _ in 0..<numPlainParams {
619+
params.append(.plain(randomParamType()))
620+
}
621+
622+
if generateRestParameter {
623+
params.append(.rest(randomParamType()))
624+
}
625+
626+
// TODO: also generate optional parameters sometimes?
549627

550628
return .parameters(params)
551629
}
@@ -2045,7 +2123,7 @@ public class ProgramBuilder {
20452123

20462124
var numberOfGeneratedInstructions = 0
20472125

2048-
// calculate all input requirements of this CodeGenerator.
2126+
// Calculate all input requirements of this CodeGenerator.
20492127
let inputTypes = Set(generator.parts.reduce([]) { res, gen in
20502128
return res + gen.inputs.types
20512129
})

Sources/Fuzzilli/CodeGen/CodeGenerator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,4 +381,4 @@ extension CodeGenerator: CustomStringConvertible {
381381
let names = self.parts.map { $0.name }
382382
return names.joined(separator: ",")
383383
}
384-
}
384+
}

Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,6 @@ public let codeGeneratorWeights = [
192192
"PrototypeOverwriteGenerator": 10,
193193
"CallbackPropertyGenerator": 10,
194194
"MethodCallWithDifferentThisGenerator": 5,
195-
"WeirdClassGenerator": 10,
196195
"ProxyGenerator": 10,
197196
"LengthChangeGenerator": 5,
198197
"ElementKindChangeGenerator": 5,

0 commit comments

Comments
 (0)