From 23d84170eaa86ca34d298f42ee59715cd2abd68f Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Mon, 15 Sep 2025 20:40:16 -0700 Subject: [PATCH 01/35] [temporal] Fill in timeZoneLike overloads Most of these APIs take in a timezone string or a zoned datetime. I plan to make timezone strings something that can be generated in a future CL. I think with this the Temporal API surface is fully covered. Bug: 439921647 Change-Id: I6a6a6964d8ecaa0c4f2f828a254387488b8f49ab Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8593480 Auto-Submit: Manish Goregaokar Reviewed-by: Matthias Liedtke Commit-Queue: Matthias Liedtke --- .../Environment/JavaScriptEnvironment.swift | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index 34a2b477e..48d9ba44d 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -421,6 +421,7 @@ public class JavaScriptEnvironment: ComponentBase { registerOptionsBag(.jsTemporalDurationTotalOfSettings) registerOptionsBag(.toBase64Settings) registerOptionsBag(.fromBase64Settings) + registerOptionsBag(.jsTemporalPlainDateToZDTSettings) registerTemporalFieldsObject(.jsTemporalPlainTimeLikeObject, forWith: false, dateFields: false, timeFields: true, zonedFields: false) registerTemporalFieldsObject(.jsTemporalPlainDateLikeObject, forWith: false, dateFields: true, timeFields: false, zonedFields: false) @@ -837,7 +838,12 @@ public struct OptionsBag { self.properties = properties let properties = properties.mapValues { // This list can be expanded over time as long as createOptionsBag() supports this - assert($0.isEnumeration || $0.Is(.number | .integer | .boolean) || $0.Is(OptionsBag.jsTemporalRelativeTo), "Found unsupported option type \($0) in options bag \(name)") + assert($0.isEnumeration || $0.Is(.number | .integer | .boolean) || + // Has a producing generator registered + $0.Is(.jsTemporalPlainTime) || + // Has explicit support in createOptionsBag + $0.Is(OptionsBag.jsTemporalRelativeTo), + "Found unsupported option type \($0) in options bag \(name)") return $0 | .undefined; } self.group = ObjectGroup(name: name, instanceType: nil, properties: properties, overloads: [:]) @@ -2199,6 +2205,13 @@ public extension ObjectGroup { return possibleParams.map { [.plain($0)] => .boolean } } + private static func temporalTimeZoneLikeSignature(signature: (ILType) -> Signature) -> [Signature] { + return [ + signature(jsTemporalTimeZoneLike), + signature(.jsTemporalZonedDateTime), + ] + } + /// Object group modelling the JavaScript Temporal builtin static let jsTemporalObject = ObjectGroup( name: "Temporal", @@ -2221,14 +2234,13 @@ public extension ObjectGroup { name: "Temporal.Now", instanceType: .jsTemporalNow, properties: [:], - methods: [ - "timeZoneId": [] => .string, - "instant": [] => .jsTemporalInstant, - // TODO(manishearth, 439921647) Potentially hint to the generator that these are timezone-like - "plainDateTimeISO": [.opt(.string)] => .jsTemporalPlainDateTime, - "zonedDateTimeISO": [.opt(.string)] => .jsTemporalZonedDateTime, - "plainDateISO": [.opt(.string)] => .jsTemporalPlainDate, - "plainTimeISO": [.opt(.string)] => .jsTemporalPlainTime, + overloads: [ + "timeZoneId": [[] => .string], + "instant": [[] => .jsTemporalInstant], + "plainDateTimeISO": temporalTimeZoneLikeSignature { [.opt($0)] => .jsTemporalPlainDateTime }, + "zonedDateTimeISO": temporalTimeZoneLikeSignature { [.opt($0)] => .jsTemporalZonedDateTime }, + "plainDateISO": temporalTimeZoneLikeSignature { [.opt($0)] => .jsTemporalPlainDate }, + "plainTimeISO": temporalTimeZoneLikeSignature { [.opt($0)] => .jsTemporalPlainTime }, ] ) @@ -2250,7 +2262,7 @@ public extension ObjectGroup { "toString": [[.opt(jsTemporalToStringSettings)] => .string], "toJSON": [[] => .string], "toLocaleString": [[.opt(.string), .opt(jsTemporalToLocaleStringSettings)] => .string], - "toZonedDateTimeISO": [[jsTemporalTimeZoneLike] => .jsTemporalZonedDateTime], + "toZonedDateTimeISO": temporalTimeZoneLikeSignature { [.plain($0)] => .jsTemporalZonedDateTime }, ] ) @@ -2462,7 +2474,9 @@ public extension ObjectGroup { "since": temporalUntilSinceSignature(possibleParams: jsTemporalPlainDateLikeParameters), "equals": temporalEqualsSignature(possibleParams: jsTemporalPlainDateLikeParameters), "toPlainDateTime": jsTemporalPlainTimeLikeParameters.map {[.opt($0)] => .jsTemporalPlainDateTime}, - "toZonedDateTime": [[.jsAnything] => .jsAnything], + "toZonedDateTime": temporalTimeZoneLikeSignature { [.plain($0)] => .jsTemporalZonedDateTime } + // Can also accept an object with timezone/time fields + + [[.plain(OptionsBag.jsTemporalPlainDateToZDTSettings.group.instanceType)] => .jsTemporalZonedDateTime], "toString": [[.opt(jsTemporalToStringSettings)] => .string], "toJSON": [[] => .string], "toLocaleString": [[.opt(.string), .opt(jsTemporalToLocaleStringSettings)] => .string], @@ -2502,7 +2516,7 @@ public extension ObjectGroup { "toString": [[.opt(jsTemporalToStringSettings)] => .string], "toJSON": [[] => .string], "toLocaleString": [[.opt(.string), .opt(jsTemporalToLocaleStringSettings)] => .string], - "toZonedDateTime": [[.string, .opt(OptionsBag.jsTemporalZonedInterpretationSettings.group.instanceType)] => .jsTemporalZonedDateTime], + "toZonedDateTime": temporalTimeZoneLikeSignature { [.plain($0), .opt(OptionsBag.jsTemporalZonedInterpretationSettings.group.instanceType)] => .jsTemporalZonedDateTime }, "toPlainDate": [[] => .jsTemporalPlainDate], "toPlainTime": [[] => .jsTemporalPlainTime], ] @@ -2687,7 +2701,7 @@ public extension ObjectGroup { // Stringy objects // TODO(manishearth, 439921647) support stringy objects - fileprivate static let jsTemporalTimeZoneLike = Parameter.string + fileprivate static let jsTemporalTimeZoneLike = ILType.string // Options objects fileprivate static let jsTemporalDifferenceSettings = OptionsBag.jsTemporalDifferenceSettingOrRoundTo.group.instanceType @@ -2781,6 +2795,14 @@ extension OptionsBag { "relativeTo": jsTemporalRelativeTo, ] ) + + static let jsTemporalPlainDateToZDTSettings = OptionsBag( + name: "TemporalPlainDateToZDTSettings", + properties: [ + "timeZone": ObjectGroup.jsTemporalTimeZoneLike, + "plainTime": .jsTemporalPlainTime, + ] + ) } // Base64 From 52de2a3d6b839a0ce81c61d94af94c5bf2045d18 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Wed, 17 Sep 2025 14:30:58 +0200 Subject: [PATCH 02/35] Remove outdated comment about purity of Wasm tags The Operation.Attributes.isPure option was removed as part of commit a2a7e95472a422b8e146f5609559511c6964170a. Change-Id: I7e6e293d48f99804b03c579f8cc0eb0765d0b0c4 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8598616 Auto-Submit: Matthias Liedtke Commit-Queue: Dominik Klemba Reviewed-by: Dominik Klemba --- Sources/Fuzzilli/FuzzIL/JsOperations.swift | 2 -- Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 2 -- 2 files changed, 4 deletions(-) diff --git a/Sources/Fuzzilli/FuzzIL/JsOperations.swift b/Sources/Fuzzilli/FuzzIL/JsOperations.swift index 9f3007b16..be65db79f 100644 --- a/Sources/Fuzzilli/FuzzIL/JsOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/JsOperations.swift @@ -2536,8 +2536,6 @@ class CreateWasmTag: JsOperation { init(parameterTypes: [ILType]) { self.parameterTypes = parameterTypes - // Note that tags in wasm are nominal (differently to types) meaning that two tags with the same input are not - // the same, therefore this operation is not considered to be .pure. super.init(numOutputs: 1, attributes: [], requiredContext: [.javascript]) } } diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index 398c13268..f4ded593f 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -880,8 +880,6 @@ final class WasmDefineTag: WasmOperation { init(parameterTypes: [ILType]) { self.parameterTypes = parameterTypes - // Note that tags in wasm are nominal (differently to types) meaning that two tags with the same input are not - // the same, therefore this operation is not considered to be .pure. super.init(numOutputs: 1, attributes: [], requiredContext: [.wasm]) } } From 00e72ab549fc48ac50d7e851200be99a8334154a Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Mon, 15 Sep 2025 21:48:08 -0700 Subject: [PATCH 03/35] Add support for named strings, use for timezones This adds a new concept: "named strings", which are string-typed objects that are expected to have greater structure. Like options bags, producing generators can be registered for these. Unfortunately I don't have a good way of testing this since I can't see any way to trawl through the generated code other than parsing the output of dumpCurrentProgram(). Seems to work, though. Change-Id: I6a6a6964fef394af4595f0302d785a9c6da27de3 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8593716 Reviewed-by: Carl Smith Commit-Queue: Manish Goregaokar --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 16 ++++++++---- .../Environment/JavaScriptEnvironment.swift | 3 ++- Sources/Fuzzilli/FuzzIL/Instruction.swift | 10 +++++-- Sources/Fuzzilli/FuzzIL/JSTyper.swift | 8 ++++-- Sources/Fuzzilli/FuzzIL/JsOperations.swift | 4 ++- Sources/Fuzzilli/FuzzIL/TypeSystem.swift | 11 +++++++- Sources/Fuzzilli/Lifting/FuzzILLifter.swift | 6 ++++- .../Fuzzilli/Mutators/OperationMutator.swift | 2 ++ Sources/Fuzzilli/Protobuf/operations.pb.swift | 22 +++++++++++++++- Sources/Fuzzilli/Protobuf/operations.proto | 1 + Tests/FuzzilliTests/JSTyperTests.swift | 26 +++++++++++++++++-- Tests/FuzzilliTests/TypeSystemTest.swift | 10 +++++++ 12 files changed, 103 insertions(+), 16 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 7899a9ce0..51c444cc5 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -648,6 +648,12 @@ public class ProgramBuilder { if type.isEnumeration { return self.loadString(type.enumValues.randomElement()!) } + let producingGenerator = self.fuzzer.environment.getProducingGenerator(ofType: type); + if let producingGenerator { + if probability(producingGenerator.probability) { + return producingGenerator.generator(self) + } + } return self.loadString(self.randomString()) }), (.boolean, { return self.loadBool(probability(0.5)) }), (.bigint, { return self.loadBigInt(self.randomInt()) }), @@ -2047,8 +2053,8 @@ public class ProgramBuilder { } @discardableResult - public func loadString(_ value: String) -> Variable { - return emit(LoadString(value: value)).output + public func loadString(_ value: String, customName: String? = nil) -> Variable { + return emit(LoadString(value: value, customName: customName)).output } @discardableResult @@ -4444,15 +4450,15 @@ public class ProgramBuilder { func randomTimeZone() -> Variable { // Bias towards knownTimeZoneIdentifiers since it's a larger array if probability(0.7) { - return loadString(chooseUniform(from: TimeZone.knownTimeZoneIdentifiers)) + return loadString(chooseUniform(from: TimeZone.knownTimeZoneIdentifiers), customName: "TemporalTimeZoneString") } else { - return loadString(chooseUniform(from: TimeZone.abbreviationDictionary.keys)) + return loadString(chooseUniform(from: TimeZone.abbreviationDictionary.keys), customName: "TemporalTimeZoneString") } } @discardableResult func randomUTCOffset() -> Variable { - return loadString(randomUTCOffsetString(mayHaveSeconds: true)) + return loadString(randomUTCOffsetString(mayHaveSeconds: true), customName: "TemporalTimeZoneString") } // Generate an object with fields from diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index 48d9ba44d..28fac5358 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -434,6 +434,7 @@ public class JavaScriptEnvironment: ComponentBase { // This isn't a normal "temporal fields object" but is similar, and needs a similar producing generator registerObjectGroup(.jsTemporalDurationLikeObject) addProducingGenerator(forType: ObjectGroup.jsTemporalDurationLikeObject.instanceType, with: { b in b.createTemporalDurationFieldsObject() }) + addProducingGenerator(forType: ObjectGroup.jsTemporalTimeZoneLike, with: { b in chooseUniform(from: [b.randomTimeZone, b.randomUTCOffset])() }) // Temporal types are produced by a large number of methods; which means findOrGenerateType(), when asked to produce // a Temporal type, will tend towards trying to call a method on another Temporal type, which needs more Temporal types, @@ -2701,7 +2702,7 @@ public extension ObjectGroup { // Stringy objects // TODO(manishearth, 439921647) support stringy objects - fileprivate static let jsTemporalTimeZoneLike = ILType.string + fileprivate static let jsTemporalTimeZoneLike = ILType.namedString(ofName: "TemporalTimeZoneString") // Options objects fileprivate static let jsTemporalDifferenceSettings = OptionsBag.jsTemporalDifferenceSettingOrRoundTo.group.instanceType diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index bb0a72e95..097019100 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -549,7 +549,12 @@ extension Instruction: ProtobufConvertible { case .loadFloat(let op): $0.loadFloat = Fuzzilli_Protobuf_LoadFloat.with { $0.value = op.value } case .loadString(let op): - $0.loadString = Fuzzilli_Protobuf_LoadString.with { $0.value = op.value } + $0.loadString = Fuzzilli_Protobuf_LoadString.with { + $0.value = op.value; + if let customName = op.customName { + $0.customName = customName + } + } case .loadBoolean(let op): $0.loadBoolean = Fuzzilli_Protobuf_LoadBoolean.with { $0.value = op.value } case .loadUndefined: @@ -1815,7 +1820,8 @@ extension Instruction: ProtobufConvertible { case .loadFloat(let p): op = LoadFloat(value: p.value) case .loadString(let p): - op = LoadString(value: p.value) + let customName = p.customName.isEmpty ? nil: p.customName; + op = LoadString(value: p.value, customName: customName) case .loadBoolean(let p): op = LoadBoolean(value: p.value) case .loadUndefined: diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index e1a8a2ab8..88315791a 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -1368,8 +1368,12 @@ public struct JSTyper: Analyzer { case .loadFloat: set(instr.output, .float) - case .loadString: - set(instr.output, .jsString) + case .loadString(let op): + if let customName = op.customName { + set(instr.output, .namedString(ofName: customName)) + } else { + set(instr.output, .jsString) + } case .loadBoolean: set(instr.output, .boolean) diff --git a/Sources/Fuzzilli/FuzzIL/JsOperations.swift b/Sources/Fuzzilli/FuzzIL/JsOperations.swift index be65db79f..a8e11f73c 100644 --- a/Sources/Fuzzilli/FuzzIL/JsOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/JsOperations.swift @@ -183,9 +183,11 @@ final class LoadString: JsOperation { override var opcode: Opcode { .loadString(self) } let value: String + let customName: String? - init(value: String) { + init(value: String, customName: String? = nil) { self.value = value + self.customName = customName super.init(numOutputs: 1, attributes: [.isMutable]) } } diff --git a/Sources/Fuzzilli/FuzzIL/TypeSystem.swift b/Sources/Fuzzilli/FuzzIL/TypeSystem.swift index 850d5a60a..a5fbdb944 100644 --- a/Sources/Fuzzilli/FuzzIL/TypeSystem.swift +++ b/Sources/Fuzzilli/FuzzIL/TypeSystem.swift @@ -161,6 +161,15 @@ public struct ILType: Hashable { return ILType(definiteType: .string, ext: ext) } + /// Constructs an named string: this is a string that typically has some complex format. + /// + /// Most code will treat these as strings, but the JavaScriptEnvironment can register + /// producingGenerators for them so they can be generated more intelligently. + public static func namedString(ofName name: String) -> ILType { + let ext = TypeExtension(group: name, properties: Set(), methods: Set(), signature: nil, wasmExt: nil) + return ILType(definiteType: .string, ext: ext) + } + /// An object for which it is not known what properties or methods it has, if any. public static let unknownObject: ILType = .object() @@ -484,7 +493,7 @@ public struct ILType: Hashable { } public var isEnumeration : Bool { - return Is(.string) && ext != nil + return Is(.string) && ext != nil && !ext!.properties.isEmpty } public var group: String? { diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift index 8776f036b..a6f88d118 100644 --- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift +++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift @@ -47,7 +47,11 @@ public class FuzzILLifter: Lifter { w.emit("\(output()) <- LoadFloat '\(op.value)'") case .loadString(let op): - w.emit("\(output()) <- LoadString '\(op.value)'") + if let customName = op.customName { + w.emit("\(output()) <- LoadString '\(op.value)' \(customName)") + } else { + w.emit("\(output()) <- LoadString '\(op.value)'") + } case .loadRegExp(let op): w.emit("\(output()) <- LoadRegExp '\(op.pattern)' '\(op.flags.asString())'") diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift index bfea45d4a..f426b94be 100644 --- a/Sources/Fuzzilli/Mutators/OperationMutator.swift +++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift @@ -84,6 +84,8 @@ public class OperationMutator: BaseInstructionMutator { return result } ) + // Note: This explicitly discards customName since we may have created a string that no longer + // matches the original schema. newOp = LoadString(value: newString) case .loadRegExp(let op): newOp = withEqualProbability({ diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index 01584f4f0..5e4768728 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -1654,9 +1654,20 @@ public struct Fuzzilli_Protobuf_LoadString: Sendable { public var value: String = String() + public var customName: String { + get {return _customName ?? String()} + set {_customName = newValue} + } + /// Returns true if `customName` has been explicitly set. + public var hasCustomName: Bool {return self._customName != nil} + /// Clears the value of `customName`. Subsequent reads from it will return its default value. + public mutating func clearCustomName() {self._customName = nil} + public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} + + fileprivate var _customName: String? = nil } public struct Fuzzilli_Protobuf_LoadBoolean: Sendable { @@ -6208,7 +6219,7 @@ extension Fuzzilli_Protobuf_LoadFloat: SwiftProtobuf.Message, SwiftProtobuf._Mes extension Fuzzilli_Protobuf_LoadString: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".LoadString" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}value\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}value\0\u{1}customName\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -6217,20 +6228,29 @@ extension Fuzzilli_Protobuf_LoadString: SwiftProtobuf.Message, SwiftProtobuf._Me // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch fieldNumber { case 1: try { try decoder.decodeSingularStringField(value: &self.value) }() + case 2: try { try decoder.decodeSingularStringField(value: &self._customName) }() default: break } } } public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 if !self.value.isEmpty { try visitor.visitSingularStringField(value: self.value, fieldNumber: 1) } + try { if let v = self._customName { + try visitor.visitSingularStringField(value: v, fieldNumber: 2) + } }() try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Fuzzilli_Protobuf_LoadString, rhs: Fuzzilli_Protobuf_LoadString) -> Bool { if lhs.value != rhs.value {return false} + if lhs._customName != rhs._customName {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/Sources/Fuzzilli/Protobuf/operations.proto b/Sources/Fuzzilli/Protobuf/operations.proto index c8f81a97a..f7aae1bf5 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -35,6 +35,7 @@ message LoadFloat { message LoadString { string value = 1; + optional string customName = 2; } message LoadBoolean { diff --git a/Tests/FuzzilliTests/JSTyperTests.swift b/Tests/FuzzilliTests/JSTyperTests.swift index f95d45f08..739585b29 100644 --- a/Tests/FuzzilliTests/JSTyperTests.swift +++ b/Tests/FuzzilliTests/JSTyperTests.swift @@ -1838,16 +1838,26 @@ class JSTyperTests: XCTestCase { var callCount = 0 var returnedVar: Variable? = nil // A simple generator - func generate(builder: ProgramBuilder) -> Variable { + func generateObject(builder: ProgramBuilder) -> Variable { callCount += 1 let val = builder.loadString("mockValue") let variable = builder.createObject(with: ["mockField": val]) returnedVar = variable return variable } + + let mockNamedString = ILType.namedString(ofName: "NamedString"); + func generateString(builder: ProgramBuilder) -> Variable { + callCount += 1 + let val = builder.loadString("mockStringValue", customName: "NamedString") + returnedVar = val + return val + } + let fuzzer = makeMockFuzzer() fuzzer.environment.registerObjectGroup(mockObject) - fuzzer.environment.addProducingGenerator(forType: mockObject.instanceType, with: generate) + fuzzer.environment.addProducingGenerator(forType: mockObject.instanceType, with: generateObject) + fuzzer.environment.addProducingGenerator(forType: mockNamedString, with: generateString) let b = fuzzer.makeBuilder() b.buildPrefix() @@ -1857,6 +1867,18 @@ class JSTyperTests: XCTestCase { XCTAssertEqual(callCount, 1) // Test that the returned variable matches the generated one XCTAssertEqual(variable, returnedVar) + + + // Try to get it to invoke the string generator + let variable2 = b.findOrGenerateType(mockNamedString) + // Test that the generator was invoked + XCTAssertEqual(callCount, 2) + // Test that the returned variable matches the generated one + XCTAssertEqual(variable2, returnedVar) + + // Test that the returned variable gets typed correctly + XCTAssert(b.type(of: variable2).Is(mockNamedString)) + XCTAssertEqual(b.type(of: variable2).group, "NamedString") } func testFindConstructor() { diff --git a/Tests/FuzzilliTests/TypeSystemTest.swift b/Tests/FuzzilliTests/TypeSystemTest.swift index f626372c9..f0486c4f3 100644 --- a/Tests/FuzzilliTests/TypeSystemTest.swift +++ b/Tests/FuzzilliTests/TypeSystemTest.swift @@ -1023,6 +1023,16 @@ class TypeSystemTests: XCTestCase { } + func testNamedStrings() { + let namedA = ILType.namedString(ofName: "A") + XCTAssert(namedA.Is(.string)) + let namedB = ILType.namedString(ofName: "B") + XCTAssertEqual(namedA | namedB, .string) + XCTAssertEqual(namedA & namedB, .nothing) + let objectA = ILType.object(ofGroup: "A", withProperties: ["a"]) + XCTAssertEqual(namedA & objectA, .nothing) + } + func testTypeDescriptions() { // Test primitive types XCTAssertEqual(ILType.undefined.description, ".undefined") From c211b5b4708a78219082f731d67495aaccf71ac3 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Tue, 16 Sep 2025 16:24:14 +0200 Subject: [PATCH 04/35] [wasm] Add signature type definition to wasm type groups This is the very first step in allowing "full" wasm-gc signatures with indexed reference types as parameters and output types. This only adds an option to define such signatures inside a Wasm type group and use them in a few selected statements like `ref.null` but doesn't adapt any of the complex statements like wasm function definitions, blocks, loops, ... It also doesn't add code generators for signature types, so as a first step they are "ProgramBuilder vaporware" for writing test cases. Bug: 445356784 Change-Id: I5b9d2ff4791fcb09c4c41d4685949d2b6b460685 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8595216 Commit-Queue: Matthias Liedtke Reviewed-by: Manos Koukoutos --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 5 ++ Sources/Fuzzilli/FuzzIL/Instruction.swift | 7 +++ Sources/Fuzzilli/FuzzIL/JSTyper.swift | 29 +++++++++++ Sources/Fuzzilli/FuzzIL/JsOperations.swift | 12 +++++ Sources/Fuzzilli/FuzzIL/Opcodes.swift | 3 +- Sources/Fuzzilli/FuzzIL/TypeSystem.swift | 19 +++++++ Sources/Fuzzilli/Lifting/FuzzILLifter.swift | 4 ++ .../Fuzzilli/Lifting/JavaScriptLifter.swift | 1 + Sources/Fuzzilli/Lifting/WasmLifter.swift | 12 ++++- .../Fuzzilli/Mutators/OperationMutator.swift | 1 + Sources/Fuzzilli/Protobuf/operations.pb.swift | 49 +++++++++++++++++++ Sources/Fuzzilli/Protobuf/operations.proto | 5 ++ Sources/Fuzzilli/Protobuf/program.pb.swift | 28 ++++++++++- Sources/Fuzzilli/Protobuf/program.proto | 1 + Tests/FuzzilliTests/TypeSystemTest.swift | 6 +++ Tests/FuzzilliTests/WasmTests.swift | 31 +++++++++++- 16 files changed, 209 insertions(+), 4 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 51c444cc5..481372101 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -4176,6 +4176,11 @@ public class ProgramBuilder { return Array(emit(WasmEndTypeGroup(typesCount: types.count), withInputs: types).outputs) } + @discardableResult + func wasmDefineSignatureType(signature: WasmSignature, indexTypes: [Variable]) -> Variable { + return emit(WasmDefineSignatureType(signature: signature), withInputs: indexTypes).output + } + @discardableResult func wasmDefineArrayType(elementType: ILType, mutability: Bool, indexType: Variable? = nil) -> Variable { let inputs = indexType != nil ? [indexType!] : [] diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index 097019100..d2db55c62 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -1539,6 +1539,11 @@ extension Instruction: ProtobufConvertible { $0.wasmBeginTypeGroup = Fuzzilli_Protobuf_WasmBeginTypeGroup() case .wasmEndTypeGroup(_): $0.wasmEndTypeGroup = Fuzzilli_Protobuf_WasmEndTypeGroup() + case .wasmDefineSignatureType(let op): + $0.wasmDefineSignatureType = Fuzzilli_Protobuf_WasmDefineSignatureType.with { + $0.parameterTypes = op.signature.parameterTypes.map(ILTypeToWasmTypeEnum) + $0.outputTypes = op.signature.outputTypes.map(ILTypeToWasmTypeEnum) + } case .wasmDefineArrayType(let op): $0.wasmDefineArrayType = Fuzzilli_Protobuf_WasmDefineArrayType.with { $0.elementType = ILTypeToWasmTypeEnum(op.elementType) @@ -2527,6 +2532,8 @@ extension Instruction: ProtobufConvertible { op = WasmEndTypeGroup(typesCount: inouts.count / 2) case .wasmDefineArrayType(let p): op = WasmDefineArrayType(elementType: WasmTypeEnumToILType(p.elementType), mutability: p.mutability) + case .wasmDefineSignatureType(let p): + op = WasmDefineSignatureType(signature: p.parameterTypes.map(WasmTypeEnumToILType) => p.outputTypes.map(WasmTypeEnumToILType)) case .wasmDefineStructType(let p): op = WasmDefineStructType(fields: p.fields.map { field in return WasmDefineStructType.Field(type: WasmTypeEnumToILType(field.type), mutability: field.mutability) diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index 88315791a..d09add6be 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -432,6 +432,32 @@ public struct JSTyper: Analyzer { } } + mutating func addSignatureType(def: Variable, signature: WasmSignature, inputs: ArraySlice) { + var inputs = inputs.makeIterator() + let resolveType = { (paramType: ILType) in + if paramType.requiredInputCount() == 0 { + return paramType + } + assert(paramType.Is(.wasmRef(.Index(), nullability: true))) + let typeDef = inputs.next()! + let elementDesc = type(of: typeDef).wasmTypeDefinition!.description + if elementDesc == .selfReference { + // TODO(mliedtke): Implement this before we add code genertors for signature types. + fatalError("self and forward references not implemented for signatures, yet.") + } + return type(of: typeDef).wasmTypeDefinition! + .getReferenceTypeTo(nullability: paramType.wasmReferenceType!.nullability) + } + + let tgIndex = isWithinTypeGroup ? typeGroups.count - 1 : -1 + let resolvedParameterTypes = signature.parameterTypes.map(resolveType) + let resolvedOutputTypes = signature.outputTypes.map(resolveType) + set(def, .wasmTypeDef(description: WasmSignatureTypeDescription(signature: resolvedParameterTypes => resolvedOutputTypes, typeGroupIndex: tgIndex))) + if isWithinTypeGroup { + typeGroups[typeGroups.count - 1].append(def) + } + } + mutating func addArrayType(def: Variable, elementType: ILType, mutability: Bool, elementRef: Variable? = nil) { let tgIndex = isWithinTypeGroup ? typeGroups.count - 1 : -1 let resolvedElementType: ILType @@ -1808,6 +1834,9 @@ public struct JSTyper: Analyzer { } finishTypeGroup() + case .wasmDefineSignatureType(let op): + addSignatureType(def: instr.output, signature: op.signature, inputs: instr.inputs) + case .wasmDefineArrayType(let op): let elementRef = op.elementType.requiredInputCount() == 1 ? instr.input(0) : nil addArrayType(def: instr.output, elementType: op.elementType, mutability: op.mutability, elementRef: elementRef) diff --git a/Sources/Fuzzilli/FuzzIL/JsOperations.swift b/Sources/Fuzzilli/FuzzIL/JsOperations.swift index a8e11f73c..4bcfbaf4b 100644 --- a/Sources/Fuzzilli/FuzzIL/JsOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/JsOperations.swift @@ -2591,6 +2591,18 @@ class WasmDefineStructType: WasmTypeOperation { } } +class WasmDefineSignatureType: WasmTypeOperation { + override var opcode: Opcode { .wasmDefineSignatureType(self) } + let signature: WasmSignature + + init(signature: WasmSignature) { + self.signature = signature + let numInputs = (signature.outputTypes + signature.parameterTypes).map { + $0.requiredInputCount() }.reduce(0) { $0 + $1 } + super.init(numInputs: numInputs, numOutputs: 1, requiredContext: [.wasmTypeGroup]) + } +} + class WasmDefineForwardOrSelfReference: WasmTypeOperation { override var opcode: Opcode { .wasmDefineForwardOrSelfReference(self) } diff --git a/Sources/Fuzzilli/FuzzIL/Opcodes.swift b/Sources/Fuzzilli/FuzzIL/Opcodes.swift index cdfce3a66..c067e9678 100644 --- a/Sources/Fuzzilli/FuzzIL/Opcodes.swift +++ b/Sources/Fuzzilli/FuzzIL/Opcodes.swift @@ -353,7 +353,8 @@ enum Opcode { case wasmExternConvertAny(WasmExternConvertAny) case wasmMemoryCopy(WasmMemoryCopy) case wasmDefineElementSegment(WasmDefineElementSegment) - case wasmDropElementSegment(WasmDropElementSegment) case wasmTableInit(WasmTableInit) + case wasmDropElementSegment(WasmDropElementSegment) case wasmTableCopy(WasmTableCopy) + case wasmDefineSignatureType(WasmDefineSignatureType) } diff --git a/Sources/Fuzzilli/FuzzIL/TypeSystem.swift b/Sources/Fuzzilli/FuzzIL/TypeSystem.swift index a5fbdb944..5863d7d56 100644 --- a/Sources/Fuzzilli/FuzzIL/TypeSystem.swift +++ b/Sources/Fuzzilli/FuzzIL/TypeSystem.swift @@ -2089,6 +2089,25 @@ class WasmTypeDescription: Hashable, CustomStringConvertible { } } +class WasmSignatureTypeDescription: WasmTypeDescription { + let signature: WasmSignature + + init(signature: WasmSignature, typeGroupIndex: Int) { + self.signature = signature + super.init(typeGroupIndex: typeGroupIndex, superType: .WasmFunc) + } + + override func format(abbreviate: Bool) -> String { + let abbreviated = "\(super.format(abbreviate: abbreviate)) Func" + if abbreviate { + return abbreviated + } + let paramTypes = signature.parameterTypes.map {$0.abbreviated}.joined(separator: ", ") + let outputTypes = signature.outputTypes.map {$0.abbreviated}.joined(separator: ", ") + return "\(abbreviated)[[\(paramTypes)] => [\(outputTypes)]]" + } +} + class WasmArrayTypeDescription: WasmTypeDescription { var elementType: ILType let mutability: Bool diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift index a6f88d118..8a1ef5709 100644 --- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift +++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift @@ -1326,6 +1326,10 @@ public class FuzzILLifter: Lifter { let outputs = instr.outputs.map(lift).joined(separator: ", ") w.emit("\(outputs) <- WasmEndTypeGroup [\(inputs)]") + case .wasmDefineSignatureType(let op): + let inputs = instr.inputs.map(lift).joined(separator: ", ") + w.emit("\(output()) <- WasmDefineSignatureType(\(op.signature)) [\(inputs)]") + case .wasmDefineArrayType(let op): let typeInput = op.elementType.requiredInputCount() == 1 ? " \(input(0))" : "" w.emit("\(output()) <- WasmDefineArrayType \(op.elementType) mutability=\(op.mutability)\(typeInput)") diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift index cfae09dfb..76f5fba08 100644 --- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift +++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift @@ -1695,6 +1695,7 @@ public class JavaScriptLifter: Lifter { .wasmSimdLoad(_), .wasmBeginTypeGroup(_), .wasmEndTypeGroup(_), + .wasmDefineSignatureType(_), .wasmDefineArrayType(_), .wasmDefineStructType(_), .wasmDefineForwardOrSelfReference(_), diff --git a/Sources/Fuzzilli/Lifting/WasmLifter.swift b/Sources/Fuzzilli/Lifting/WasmLifter.swift index 09fe73331..287d1cc5e 100644 --- a/Sources/Fuzzilli/Lifting/WasmLifter.swift +++ b/Sources/Fuzzilli/Lifting/WasmLifter.swift @@ -605,6 +605,16 @@ public class WasmLifter { data += try encodeType(field.type) data += [field.mutability ? 1 : 0] } + } else if let signatureDesc = desc as? WasmSignatureTypeDescription { + data += [0x60] + data += Leb128.unsignedEncode(signatureDesc.signature.parameterTypes.count) + for parameterType in signatureDesc.signature.parameterTypes { + data += try encodeType(parameterType) + } + data += Leb128.unsignedEncode(signatureDesc.signature.outputTypes.count) + for outputType in signatureDesc.signature.outputTypes { + data += try encodeType(outputType) + } } else { fatalError("Unsupported WasmTypeDescription!") } @@ -881,7 +891,7 @@ public class WasmLifter { } } - // Active element segments + // Active element segments for case let .table(instruction) in self.exports { let table = instruction!.op as! WasmDefineTable let definedEntries = table.definedEntries diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift index f426b94be..fac287942 100644 --- a/Sources/Fuzzilli/Mutators/OperationMutator.swift +++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift @@ -693,6 +693,7 @@ public class OperationMutator: BaseInstructionMutator { .wasmEndTypeGroup(_), .wasmDefineArrayType(_), .wasmDefineStructType(_), + .wasmDefineSignatureType(_), .wasmDefineForwardOrSelfReference(_), .wasmResolveForwardReference(_), .wasmArrayNewFixed(_), diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index 5e4768728..8c49f9596 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -5609,6 +5609,20 @@ public struct Fuzzilli_Protobuf_WasmEndTypeGroup: Sendable { public init() {} } +public struct Fuzzilli_Protobuf_WasmDefineSignatureType: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var parameterTypes: [Fuzzilli_Protobuf_WasmILType] = [] + + public var outputTypes: [Fuzzilli_Protobuf_WasmILType] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + public struct Fuzzilli_Protobuf_WasmDefineArrayType: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -14693,6 +14707,41 @@ extension Fuzzilli_Protobuf_WasmEndTypeGroup: SwiftProtobuf.Message, SwiftProtob } } +extension Fuzzilli_Protobuf_WasmDefineSignatureType: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".WasmDefineSignatureType" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameterTypes\0\u{1}outputTypes\0") + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeRepeatedMessageField(value: &self.parameterTypes) }() + case 2: try { try decoder.decodeRepeatedMessageField(value: &self.outputTypes) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.parameterTypes.isEmpty { + try visitor.visitRepeatedMessageField(value: self.parameterTypes, fieldNumber: 1) + } + if !self.outputTypes.isEmpty { + try visitor.visitRepeatedMessageField(value: self.outputTypes, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Fuzzilli_Protobuf_WasmDefineSignatureType, rhs: Fuzzilli_Protobuf_WasmDefineSignatureType) -> Bool { + if lhs.parameterTypes != rhs.parameterTypes {return false} + if lhs.outputTypes != rhs.outputTypes {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension Fuzzilli_Protobuf_WasmDefineArrayType: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmDefineArrayType" public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}elementType\0\u{1}mutability\0") diff --git a/Sources/Fuzzilli/Protobuf/operations.proto b/Sources/Fuzzilli/Protobuf/operations.proto index f7aae1bf5..7ea8d0d94 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -1476,6 +1476,11 @@ message WasmBeginTypeGroup { message WasmEndTypeGroup { } +message WasmDefineSignatureType { + repeated WasmILType parameterTypes = 1; + repeated WasmILType outputTypes = 2; +} + message WasmDefineArrayType { WasmILType elementType = 1; bool mutability = 2; diff --git a/Sources/Fuzzilli/Protobuf/program.pb.swift b/Sources/Fuzzilli/Protobuf/program.pb.swift index e0cf07d1d..336123aef 100644 --- a/Sources/Fuzzilli/Protobuf/program.pb.swift +++ b/Sources/Fuzzilli/Protobuf/program.pb.swift @@ -2665,6 +2665,14 @@ public struct Fuzzilli_Protobuf_Instruction: Sendable { set {operation = .wasmTableCopy(newValue)} } + public var wasmDefineSignatureType: Fuzzilli_Protobuf_WasmDefineSignatureType { + get { + if case .wasmDefineSignatureType(let v)? = operation {return v} + return Fuzzilli_Protobuf_WasmDefineSignatureType() + } + set {operation = .wasmDefineSignatureType(newValue)} + } + public var unknownFields = SwiftProtobuf.UnknownStorage() public enum OneOf_Operation: Equatable, Sendable { @@ -2995,6 +3003,7 @@ public struct Fuzzilli_Protobuf_Instruction: Sendable { case wasmTableInit(Fuzzilli_Protobuf_WasmTableInit) case wasmDropElementSegment(Fuzzilli_Protobuf_WasmDropElementSegment) case wasmTableCopy(Fuzzilli_Protobuf_WasmTableCopy) + case wasmDefineSignatureType(Fuzzilli_Protobuf_WasmDefineSignatureType) } @@ -3043,7 +3052,7 @@ fileprivate let _protobuf_package = "fuzzilli.protobuf" extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Instruction" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}inouts\0\u{1}opIdx\0\u{1}nop\0\u{1}loadInteger\0\u{1}loadBigInt\0\u{1}loadFloat\0\u{1}loadString\0\u{1}loadBoolean\0\u{1}loadUndefined\0\u{1}loadNull\0\u{1}loadThis\0\u{1}loadArguments\0\u{1}createNamedVariable\0\u{1}loadDisposableVariable\0\u{1}loadAsyncDisposableVariable\0\u{1}loadRegExp\0\u{1}beginObjectLiteral\0\u{1}objectLiteralAddProperty\0\u{1}objectLiteralAddElement\0\u{1}objectLiteralAddComputedProperty\0\u{1}objectLiteralCopyProperties\0\u{1}objectLiteralSetPrototype\0\u{1}beginObjectLiteralMethod\0\u{1}endObjectLiteralMethod\0\u{1}beginObjectLiteralComputedMethod\0\u{1}endObjectLiteralComputedMethod\0\u{1}beginObjectLiteralGetter\0\u{1}endObjectLiteralGetter\0\u{1}beginObjectLiteralSetter\0\u{1}endObjectLiteralSetter\0\u{1}endObjectLiteral\0\u{1}beginClassDefinition\0\u{1}beginClassConstructor\0\u{1}endClassConstructor\0\u{1}classAddInstanceProperty\0\u{1}classAddInstanceElement\0\u{1}classAddInstanceComputedProperty\0\u{1}beginClassInstanceMethod\0\u{1}endClassInstanceMethod\0\u{1}beginClassInstanceGetter\0\u{1}endClassInstanceGetter\0\u{1}beginClassInstanceSetter\0\u{1}endClassInstanceSetter\0\u{1}classAddStaticProperty\0\u{1}classAddStaticElement\0\u{1}classAddStaticComputedProperty\0\u{1}beginClassStaticInitializer\0\u{1}endClassStaticInitializer\0\u{1}beginClassStaticMethod\0\u{1}endClassStaticMethod\0\u{1}beginClassStaticGetter\0\u{1}endClassStaticGetter\0\u{1}beginClassStaticSetter\0\u{1}endClassStaticSetter\0\u{1}classAddPrivateInstanceProperty\0\u{1}beginClassPrivateInstanceMethod\0\u{1}endClassPrivateInstanceMethod\0\u{1}classAddPrivateStaticProperty\0\u{1}beginClassPrivateStaticMethod\0\u{1}endClassPrivateStaticMethod\0\u{1}endClassDefinition\0\u{1}createArray\0\u{1}createIntArray\0\u{1}createFloatArray\0\u{1}createArrayWithSpread\0\u{1}createTemplateString\0\u{1}getProperty\0\u{1}setProperty\0\u{1}updateProperty\0\u{1}deleteProperty\0\u{1}configureProperty\0\u{1}getElement\0\u{1}setElement\0\u{1}updateElement\0\u{1}deleteElement\0\u{1}configureElement\0\u{1}getComputedProperty\0\u{1}setComputedProperty\0\u{1}updateComputedProperty\0\u{1}deleteComputedProperty\0\u{1}configureComputedProperty\0\u{1}typeOf\0\u{1}void\0\u{1}testInstanceOf\0\u{1}testIn\0\u{1}beginPlainFunction\0\u{1}endPlainFunction\0\u{1}beginArrowFunction\0\u{1}endArrowFunction\0\u{1}beginGeneratorFunction\0\u{1}endGeneratorFunction\0\u{1}beginAsyncFunction\0\u{1}endAsyncFunction\0\u{1}beginAsyncArrowFunction\0\u{1}endAsyncArrowFunction\0\u{1}beginAsyncGeneratorFunction\0\u{1}endAsyncGeneratorFunction\0\u{1}beginConstructor\0\u{1}endConstructor\0\u{1}directive\0\u{1}return\0\u{1}yield\0\u{1}yieldEach\0\u{1}await\0\u{1}callFunction\0\u{1}callFunctionWithSpread\0\u{1}construct\0\u{1}constructWithSpread\0\u{1}callMethod\0\u{1}callMethodWithSpread\0\u{1}callComputedMethod\0\u{1}callComputedMethodWithSpread\0\u{1}unaryOperation\0\u{1}binaryOperation\0\u{1}ternaryOperation\0\u{1}update\0\u{1}dup\0\u{1}reassign\0\u{1}destructArray\0\u{1}destructArrayAndReassign\0\u{1}destructObject\0\u{1}destructObjectAndReassign\0\u{1}compare\0\u{1}eval\0\u{1}beginWith\0\u{1}endWith\0\u{1}callSuperConstructor\0\u{1}callSuperMethod\0\u{1}getPrivateProperty\0\u{1}setPrivateProperty\0\u{1}updatePrivateProperty\0\u{1}callPrivateMethod\0\u{1}getSuperProperty\0\u{1}setSuperProperty\0\u{1}getComputedSuperProperty\0\u{1}setComputedSuperProperty\0\u{1}updateSuperProperty\0\u{1}beginIf\0\u{1}beginElse\0\u{1}endIf\0\u{1}beginWhileLoopHeader\0\u{1}beginWhileLoopBody\0\u{1}endWhileLoop\0\u{1}beginDoWhileLoopBody\0\u{1}beginDoWhileLoopHeader\0\u{1}endDoWhileLoop\0\u{1}beginForLoopInitializer\0\u{1}beginForLoopCondition\0\u{1}beginForLoopAfterthought\0\u{1}beginForLoopBody\0\u{1}endForLoop\0\u{1}beginForInLoop\0\u{1}endForInLoop\0\u{1}beginForOfLoop\0\u{1}beginForOfLoopWithDestruct\0\u{1}endForOfLoop\0\u{1}beginRepeatLoop\0\u{1}endRepeatLoop\0\u{1}loopBreak\0\u{1}loopContinue\0\u{1}beginTry\0\u{1}beginCatch\0\u{1}beginFinally\0\u{1}endTryCatchFinally\0\u{1}throwException\0\u{1}beginCodeString\0\u{1}endCodeString\0\u{1}beginBlockStatement\0\u{1}endBlockStatement\0\u{1}beginSwitch\0\u{1}beginSwitchCase\0\u{1}beginSwitchDefaultCase\0\u{1}endSwitchCase\0\u{1}endSwitch\0\u{1}switchBreak\0\u{1}loadNewTarget\0\u{1}print\0\u{1}explore\0\u{1}probe\0\u{1}fixup\0\u{1}beginWasmModule\0\u{1}endWasmModule\0\u{1}createWasmGlobal\0\u{1}createWasmMemory\0\u{1}createWasmTable\0\u{1}createWasmJSTag\0\u{1}createWasmTag\0\u{1}wrapPromising\0\u{1}wrapSuspending\0\u{1}bindMethod\0\u{1}bindFunction\0\u{1}consti64\0\u{1}consti32\0\u{1}constf32\0\u{1}constf64\0\u{1}wasmReturn\0\u{1}wasmJsCall\0\u{1}wasmi32CompareOp\0\u{1}wasmi64CompareOp\0\u{1}wasmf32CompareOp\0\u{1}wasmf64CompareOp\0\u{1}wasmi32EqualZero\0\u{1}wasmi64EqualZero\0\u{1}wasmi32BinOp\0\u{1}wasmi64BinOp\0\u{1}wasmi32UnOp\0\u{1}wasmi64UnOp\0\u{1}wasmf32BinOp\0\u{1}wasmf64BinOp\0\u{1}wasmf32UnOp\0\u{1}wasmf64UnOp\0\u{1}wasmWrapi64Toi32\0\u{1}wasmTruncatef32Toi32\0\u{1}wasmTruncatef64Toi32\0\u{1}wasmExtendi32Toi64\0\u{1}wasmTruncatef32Toi64\0\u{1}wasmTruncatef64Toi64\0\u{1}wasmConverti32Tof32\0\u{1}wasmConverti64Tof32\0\u{1}wasmDemotef64Tof32\0\u{1}wasmConverti32Tof64\0\u{1}wasmConverti64Tof64\0\u{1}wasmPromotef32Tof64\0\u{1}wasmReinterpretf32Asi32\0\u{1}wasmReinterpretf64Asi64\0\u{1}wasmReinterpreti32Asf32\0\u{1}wasmReinterpreti64Asf64\0\u{1}wasmSignExtend8Intoi32\0\u{1}wasmSignExtend16Intoi32\0\u{1}wasmSignExtend8Intoi64\0\u{1}wasmSignExtend16Intoi64\0\u{1}wasmSignExtend32Intoi64\0\u{1}wasmTruncateSatf32Toi32\0\u{1}wasmTruncateSatf64Toi32\0\u{1}wasmTruncateSatf32Toi64\0\u{1}wasmTruncateSatf64Toi64\0\u{1}wasmReassign\0\u{1}wasmDefineGlobal\0\u{1}wasmDefineTable\0\u{1}wasmDefineMemory\0\u{1}wasmDefineDataSegment\0\u{1}wasmLoadGlobal\0\u{1}wasmStoreGlobal\0\u{1}wasmTableGet\0\u{1}wasmTableSet\0\u{1}wasmTableSize\0\u{1}wasmTableGrow\0\u{1}wasmCallIndirect\0\u{1}wasmCallDirect\0\u{1}wasmReturnCallDirect\0\u{1}wasmReturnCallIndirect\0\u{1}wasmMemoryLoad\0\u{1}wasmMemoryStore\0\u{1}wasmAtomicLoad\0\u{1}wasmAtomicStore\0\u{1}wasmAtomicRMW\0\u{1}wasmAtomicCmpxchg\0\u{1}wasmMemorySize\0\u{1}wasmMemoryGrow\0\u{1}wasmMemoryFill\0\u{1}wasmMemoryInit\0\u{1}wasmDropDataSegment\0\u{1}beginWasmFunction\0\u{1}endWasmFunction\0\u{1}wasmBeginBlock\0\u{1}wasmEndBlock\0\u{1}wasmBeginLoop\0\u{1}wasmEndLoop\0\u{1}wasmBranch\0\u{1}wasmBranchIf\0\u{1}wasmBranchTable\0\u{1}wasmNop\0\u{1}wasmBeginIf\0\u{1}wasmBeginElse\0\u{1}wasmEndIf\0\u{1}wasmBeginTryTable\0\u{1}wasmEndTryTable\0\u{1}wasmBeginTry\0\u{1}wasmBeginCatchAll\0\u{1}wasmBeginCatch\0\u{1}wasmEndTry\0\u{1}wasmBeginTryDelegate\0\u{1}wasmEndTryDelegate\0\u{1}wasmThrow\0\u{1}wasmRethrow\0\u{1}wasmThrowRef\0\u{1}wasmDefineTag\0\u{1}constSimd128\0\u{1}wasmSimd128Compare\0\u{1}wasmSimd128IntegerUnOp\0\u{1}wasmSimd128IntegerBinOp\0\u{1}wasmSimd128IntegerTernaryOp\0\u{1}wasmSimd128FloatUnOp\0\u{1}wasmSimd128FloatBinOp\0\u{1}wasmSimd128FloatTernaryOp\0\u{1}wasmSimdSplat\0\u{1}wasmSimdExtractLane\0\u{1}wasmSimdReplaceLane\0\u{1}wasmSimdStoreLane\0\u{1}wasmSimdLoadLane\0\u{1}wasmSimdLoad\0\u{1}wasmUnreachable\0\u{1}wasmSelect\0\u{1}wasmBeginTypeGroup\0\u{1}wasmEndTypeGroup\0\u{1}wasmDefineArrayType\0\u{1}wasmDefineStructType\0\u{1}wasmDefineForwardOrSelfReference\0\u{1}wasmResolveForwardReference\0\u{1}wasmArrayNewFixed\0\u{1}wasmArrayNewDefault\0\u{1}wasmArrayLen\0\u{1}wasmArrayGet\0\u{1}wasmArraySet\0\u{1}wasmStructNewDefault\0\u{1}wasmStructGet\0\u{1}wasmStructSet\0\u{1}wasmRefNull\0\u{1}wasmRefIsNull\0\u{1}wasmRefI31\0\u{1}wasmI31Get\0\u{1}wasmAnyConvertExtern\0\u{1}wasmExternConvertAny\0\u{1}wasmMemoryCopy\0\u{1}wasmDefineElementSegment\0\u{1}wasmTableInit\0\u{1}wasmDropElementSegment\0\u{1}wasmTableCopy\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}inouts\0\u{1}opIdx\0\u{1}nop\0\u{1}loadInteger\0\u{1}loadBigInt\0\u{1}loadFloat\0\u{1}loadString\0\u{1}loadBoolean\0\u{1}loadUndefined\0\u{1}loadNull\0\u{1}loadThis\0\u{1}loadArguments\0\u{1}createNamedVariable\0\u{1}loadDisposableVariable\0\u{1}loadAsyncDisposableVariable\0\u{1}loadRegExp\0\u{1}beginObjectLiteral\0\u{1}objectLiteralAddProperty\0\u{1}objectLiteralAddElement\0\u{1}objectLiteralAddComputedProperty\0\u{1}objectLiteralCopyProperties\0\u{1}objectLiteralSetPrototype\0\u{1}beginObjectLiteralMethod\0\u{1}endObjectLiteralMethod\0\u{1}beginObjectLiteralComputedMethod\0\u{1}endObjectLiteralComputedMethod\0\u{1}beginObjectLiteralGetter\0\u{1}endObjectLiteralGetter\0\u{1}beginObjectLiteralSetter\0\u{1}endObjectLiteralSetter\0\u{1}endObjectLiteral\0\u{1}beginClassDefinition\0\u{1}beginClassConstructor\0\u{1}endClassConstructor\0\u{1}classAddInstanceProperty\0\u{1}classAddInstanceElement\0\u{1}classAddInstanceComputedProperty\0\u{1}beginClassInstanceMethod\0\u{1}endClassInstanceMethod\0\u{1}beginClassInstanceGetter\0\u{1}endClassInstanceGetter\0\u{1}beginClassInstanceSetter\0\u{1}endClassInstanceSetter\0\u{1}classAddStaticProperty\0\u{1}classAddStaticElement\0\u{1}classAddStaticComputedProperty\0\u{1}beginClassStaticInitializer\0\u{1}endClassStaticInitializer\0\u{1}beginClassStaticMethod\0\u{1}endClassStaticMethod\0\u{1}beginClassStaticGetter\0\u{1}endClassStaticGetter\0\u{1}beginClassStaticSetter\0\u{1}endClassStaticSetter\0\u{1}classAddPrivateInstanceProperty\0\u{1}beginClassPrivateInstanceMethod\0\u{1}endClassPrivateInstanceMethod\0\u{1}classAddPrivateStaticProperty\0\u{1}beginClassPrivateStaticMethod\0\u{1}endClassPrivateStaticMethod\0\u{1}endClassDefinition\0\u{1}createArray\0\u{1}createIntArray\0\u{1}createFloatArray\0\u{1}createArrayWithSpread\0\u{1}createTemplateString\0\u{1}getProperty\0\u{1}setProperty\0\u{1}updateProperty\0\u{1}deleteProperty\0\u{1}configureProperty\0\u{1}getElement\0\u{1}setElement\0\u{1}updateElement\0\u{1}deleteElement\0\u{1}configureElement\0\u{1}getComputedProperty\0\u{1}setComputedProperty\0\u{1}updateComputedProperty\0\u{1}deleteComputedProperty\0\u{1}configureComputedProperty\0\u{1}typeOf\0\u{1}void\0\u{1}testInstanceOf\0\u{1}testIn\0\u{1}beginPlainFunction\0\u{1}endPlainFunction\0\u{1}beginArrowFunction\0\u{1}endArrowFunction\0\u{1}beginGeneratorFunction\0\u{1}endGeneratorFunction\0\u{1}beginAsyncFunction\0\u{1}endAsyncFunction\0\u{1}beginAsyncArrowFunction\0\u{1}endAsyncArrowFunction\0\u{1}beginAsyncGeneratorFunction\0\u{1}endAsyncGeneratorFunction\0\u{1}beginConstructor\0\u{1}endConstructor\0\u{1}directive\0\u{1}return\0\u{1}yield\0\u{1}yieldEach\0\u{1}await\0\u{1}callFunction\0\u{1}callFunctionWithSpread\0\u{1}construct\0\u{1}constructWithSpread\0\u{1}callMethod\0\u{1}callMethodWithSpread\0\u{1}callComputedMethod\0\u{1}callComputedMethodWithSpread\0\u{1}unaryOperation\0\u{1}binaryOperation\0\u{1}ternaryOperation\0\u{1}update\0\u{1}dup\0\u{1}reassign\0\u{1}destructArray\0\u{1}destructArrayAndReassign\0\u{1}destructObject\0\u{1}destructObjectAndReassign\0\u{1}compare\0\u{1}eval\0\u{1}beginWith\0\u{1}endWith\0\u{1}callSuperConstructor\0\u{1}callSuperMethod\0\u{1}getPrivateProperty\0\u{1}setPrivateProperty\0\u{1}updatePrivateProperty\0\u{1}callPrivateMethod\0\u{1}getSuperProperty\0\u{1}setSuperProperty\0\u{1}getComputedSuperProperty\0\u{1}setComputedSuperProperty\0\u{1}updateSuperProperty\0\u{1}beginIf\0\u{1}beginElse\0\u{1}endIf\0\u{1}beginWhileLoopHeader\0\u{1}beginWhileLoopBody\0\u{1}endWhileLoop\0\u{1}beginDoWhileLoopBody\0\u{1}beginDoWhileLoopHeader\0\u{1}endDoWhileLoop\0\u{1}beginForLoopInitializer\0\u{1}beginForLoopCondition\0\u{1}beginForLoopAfterthought\0\u{1}beginForLoopBody\0\u{1}endForLoop\0\u{1}beginForInLoop\0\u{1}endForInLoop\0\u{1}beginForOfLoop\0\u{1}beginForOfLoopWithDestruct\0\u{1}endForOfLoop\0\u{1}beginRepeatLoop\0\u{1}endRepeatLoop\0\u{1}loopBreak\0\u{1}loopContinue\0\u{1}beginTry\0\u{1}beginCatch\0\u{1}beginFinally\0\u{1}endTryCatchFinally\0\u{1}throwException\0\u{1}beginCodeString\0\u{1}endCodeString\0\u{1}beginBlockStatement\0\u{1}endBlockStatement\0\u{1}beginSwitch\0\u{1}beginSwitchCase\0\u{1}beginSwitchDefaultCase\0\u{1}endSwitchCase\0\u{1}endSwitch\0\u{1}switchBreak\0\u{1}loadNewTarget\0\u{1}print\0\u{1}explore\0\u{1}probe\0\u{1}fixup\0\u{1}beginWasmModule\0\u{1}endWasmModule\0\u{1}createWasmGlobal\0\u{1}createWasmMemory\0\u{1}createWasmTable\0\u{1}createWasmJSTag\0\u{1}createWasmTag\0\u{1}wrapPromising\0\u{1}wrapSuspending\0\u{1}bindMethod\0\u{1}bindFunction\0\u{1}consti64\0\u{1}consti32\0\u{1}constf32\0\u{1}constf64\0\u{1}wasmReturn\0\u{1}wasmJsCall\0\u{1}wasmi32CompareOp\0\u{1}wasmi64CompareOp\0\u{1}wasmf32CompareOp\0\u{1}wasmf64CompareOp\0\u{1}wasmi32EqualZero\0\u{1}wasmi64EqualZero\0\u{1}wasmi32BinOp\0\u{1}wasmi64BinOp\0\u{1}wasmi32UnOp\0\u{1}wasmi64UnOp\0\u{1}wasmf32BinOp\0\u{1}wasmf64BinOp\0\u{1}wasmf32UnOp\0\u{1}wasmf64UnOp\0\u{1}wasmWrapi64Toi32\0\u{1}wasmTruncatef32Toi32\0\u{1}wasmTruncatef64Toi32\0\u{1}wasmExtendi32Toi64\0\u{1}wasmTruncatef32Toi64\0\u{1}wasmTruncatef64Toi64\0\u{1}wasmConverti32Tof32\0\u{1}wasmConverti64Tof32\0\u{1}wasmDemotef64Tof32\0\u{1}wasmConverti32Tof64\0\u{1}wasmConverti64Tof64\0\u{1}wasmPromotef32Tof64\0\u{1}wasmReinterpretf32Asi32\0\u{1}wasmReinterpretf64Asi64\0\u{1}wasmReinterpreti32Asf32\0\u{1}wasmReinterpreti64Asf64\0\u{1}wasmSignExtend8Intoi32\0\u{1}wasmSignExtend16Intoi32\0\u{1}wasmSignExtend8Intoi64\0\u{1}wasmSignExtend16Intoi64\0\u{1}wasmSignExtend32Intoi64\0\u{1}wasmTruncateSatf32Toi32\0\u{1}wasmTruncateSatf64Toi32\0\u{1}wasmTruncateSatf32Toi64\0\u{1}wasmTruncateSatf64Toi64\0\u{1}wasmReassign\0\u{1}wasmDefineGlobal\0\u{1}wasmDefineTable\0\u{1}wasmDefineMemory\0\u{1}wasmDefineDataSegment\0\u{1}wasmLoadGlobal\0\u{1}wasmStoreGlobal\0\u{1}wasmTableGet\0\u{1}wasmTableSet\0\u{1}wasmTableSize\0\u{1}wasmTableGrow\0\u{1}wasmCallIndirect\0\u{1}wasmCallDirect\0\u{1}wasmReturnCallDirect\0\u{1}wasmReturnCallIndirect\0\u{1}wasmMemoryLoad\0\u{1}wasmMemoryStore\0\u{1}wasmAtomicLoad\0\u{1}wasmAtomicStore\0\u{1}wasmAtomicRMW\0\u{1}wasmAtomicCmpxchg\0\u{1}wasmMemorySize\0\u{1}wasmMemoryGrow\0\u{1}wasmMemoryFill\0\u{1}wasmMemoryInit\0\u{1}wasmDropDataSegment\0\u{1}beginWasmFunction\0\u{1}endWasmFunction\0\u{1}wasmBeginBlock\0\u{1}wasmEndBlock\0\u{1}wasmBeginLoop\0\u{1}wasmEndLoop\0\u{1}wasmBranch\0\u{1}wasmBranchIf\0\u{1}wasmBranchTable\0\u{1}wasmNop\0\u{1}wasmBeginIf\0\u{1}wasmBeginElse\0\u{1}wasmEndIf\0\u{1}wasmBeginTryTable\0\u{1}wasmEndTryTable\0\u{1}wasmBeginTry\0\u{1}wasmBeginCatchAll\0\u{1}wasmBeginCatch\0\u{1}wasmEndTry\0\u{1}wasmBeginTryDelegate\0\u{1}wasmEndTryDelegate\0\u{1}wasmThrow\0\u{1}wasmRethrow\0\u{1}wasmThrowRef\0\u{1}wasmDefineTag\0\u{1}constSimd128\0\u{1}wasmSimd128Compare\0\u{1}wasmSimd128IntegerUnOp\0\u{1}wasmSimd128IntegerBinOp\0\u{1}wasmSimd128IntegerTernaryOp\0\u{1}wasmSimd128FloatUnOp\0\u{1}wasmSimd128FloatBinOp\0\u{1}wasmSimd128FloatTernaryOp\0\u{1}wasmSimdSplat\0\u{1}wasmSimdExtractLane\0\u{1}wasmSimdReplaceLane\0\u{1}wasmSimdStoreLane\0\u{1}wasmSimdLoadLane\0\u{1}wasmSimdLoad\0\u{1}wasmUnreachable\0\u{1}wasmSelect\0\u{1}wasmBeginTypeGroup\0\u{1}wasmEndTypeGroup\0\u{1}wasmDefineArrayType\0\u{1}wasmDefineStructType\0\u{1}wasmDefineForwardOrSelfReference\0\u{1}wasmResolveForwardReference\0\u{1}wasmArrayNewFixed\0\u{1}wasmArrayNewDefault\0\u{1}wasmArrayLen\0\u{1}wasmArrayGet\0\u{1}wasmArraySet\0\u{1}wasmStructNewDefault\0\u{1}wasmStructGet\0\u{1}wasmStructSet\0\u{1}wasmRefNull\0\u{1}wasmRefIsNull\0\u{1}wasmRefI31\0\u{1}wasmI31Get\0\u{1}wasmAnyConvertExtern\0\u{1}wasmExternConvertAny\0\u{1}wasmMemoryCopy\0\u{1}wasmDefineElementSegment\0\u{1}wasmTableInit\0\u{1}wasmDropElementSegment\0\u{1}wasmTableCopy\0\u{1}wasmDefineSignatureType\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -7298,6 +7307,19 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M self.operation = .wasmTableCopy(v) } }() + case 329: try { + var v: Fuzzilli_Protobuf_WasmDefineSignatureType? + var hadOneofValue = false + if let current = self.operation { + hadOneofValue = true + if case .wasmDefineSignatureType(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.operation = .wasmDefineSignatureType(v) + } + }() default: break } } @@ -8620,6 +8642,10 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M guard case .wasmTableCopy(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 328) }() + case .wasmDefineSignatureType?: try { + guard case .wasmDefineSignatureType(let v)? = self.operation else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 329) + }() case nil: break } try unknownFields.traverse(visitor: &visitor) diff --git a/Sources/Fuzzilli/Protobuf/program.proto b/Sources/Fuzzilli/Protobuf/program.proto index 152bf07c3..529cb5611 100644 --- a/Sources/Fuzzilli/Protobuf/program.proto +++ b/Sources/Fuzzilli/Protobuf/program.proto @@ -352,6 +352,7 @@ message Instruction { WasmTableInit wasmTableInit = 326; WasmDropElementSegment wasmDropElementSegment = 327; WasmTableCopy wasmTableCopy = 328; + WasmDefineSignatureType wasmDefineSignatureType = 329; } } diff --git a/Tests/FuzzilliTests/TypeSystemTest.swift b/Tests/FuzzilliTests/TypeSystemTest.swift index f0486c4f3..dedccad09 100644 --- a/Tests/FuzzilliTests/TypeSystemTest.swift +++ b/Tests/FuzzilliTests/TypeSystemTest.swift @@ -1147,6 +1147,12 @@ class TypeSystemTests: XCTestCase { XCTAssertEqual(structDef.description, ".wasmTypeDef(1 Struct[mutable .wasmf32, " + "immutable .wasmRef(null Index 1 Struct), mutable .wasmRef(null Index 0 Array)])") + let signatureDesc = WasmSignatureTypeDescription( + signature: [.wasmi32, arrayRef] => [structRef, .wasmNullRef], typeGroupIndex: 0) + let signatureDef = ILType.wasmTypeDef(description: signatureDesc) + XCTAssertEqual(signatureDef.description, + ".wasmTypeDef(0 Func[[.wasmi32, .wasmRef(null Index 0 Array)] => " + + "[.wasmRef(Index 1 Struct), .wasmRef(.Abstract(null WasmNone))]])") // A generic index type without a type description. // These are e.g. used by the element types for arrays and structs inside the operation as diff --git a/Tests/FuzzilliTests/WasmTests.swift b/Tests/FuzzilliTests/WasmTests.swift index a193db418..407a20103 100644 --- a/Tests/FuzzilliTests/WasmTests.swift +++ b/Tests/FuzzilliTests/WasmTests.swift @@ -4131,7 +4131,7 @@ class WasmFoundationTests: XCTestCase { b.buildWasmModule { wasmModule in let function = wasmModule.addWasmFunction(with: [] => []) { _, _, _ in return []} let segment = wasmModule.addElementSegment(elementsType: .wasmFunctionDef(), elements: [function]) - wasmModule.addWasmFunction(with: [] => []) { f, _, _ in + wasmModule.addWasmFunction(with: [] => []) { f, _, _ in f.wasmDropElementSegment(elementSegment: segment) return [] } @@ -4365,6 +4365,35 @@ class WasmGCTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "-100,156,42,42,-10000,55536\n") } + func testSignature() throws { + let runner = try GetJavaScriptExecutorOrSkipTest() + let jsProg = buildAndLiftProgram { b in + let typeGroup = b.wasmDefineTypeGroup { + let arrayi32 = b.wasmDefineArrayType(elementType: .wasmi32, mutability: true) + let signature = b.wasmDefineSignatureType( + signature: [.wasmRef(.Index(), nullability: true), .wasmi32] => [.wasmi32], + indexTypes: [arrayi32]) + return [arrayi32, signature] + } + + let module = b.buildWasmModule { wasmModule in + wasmModule.addWasmFunction(with: [] => [.wasmFuncRef]) { function, label, args in + // TODO(mliedtke): Do something more useful with the signature type than + // defining a null value for it and testing that it's implicitly convertible to + // .wasmFuncRef. + [function.wasmRefNull(typeDef: typeGroup[1])] + } + } + + let exports = module.loadExports() + let outputFunc = b.createNamedVariable(forBuiltin: "output") + let wasmOut = b.callMethod(module.getExportedMethod(at: 0), on: exports, withArgs: []) + b.callFunction(outputFunc, withArgs: [wasmOut]) + } + + testForOutput(program: jsProg, runner: runner, outputString: "null\n") + } + func testSelfReferenceType() throws { let runner = try GetJavaScriptExecutorOrSkipTest() let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) From 9a11fad20e43f0427c83e7ef135d7f65b7628ed0 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 18 Sep 2025 15:43:06 -0700 Subject: [PATCH 05/35] Extend named string support to enums Enums are now tagged with a name. If they are registered on the environment, their type info can even be recovered. This does not make the mutator attempt to stick within the enum. Worth experimenting with separately. Change-Id: I6a6a696493e097d348b58fada0a850881269d064 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8603428 Reviewed-by: Matthias Liedtke Commit-Queue: Manish Goregaokar --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 10 ++++-- .../Environment/JavaScriptEnvironment.swift | 32 +++++++++++++++++++ Sources/Fuzzilli/FuzzIL/JSTyper.swift | 7 +++- Tests/FuzzilliTests/JSTyperTests.swift | 16 ++++++++-- 4 files changed, 60 insertions(+), 5 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 481372101..8c9d14e50 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -646,7 +646,7 @@ public class ProgramBuilder { (.integer, { return self.loadInt(self.randomInt()) }), (.string, { if type.isEnumeration { - return self.loadString(type.enumValues.randomElement()!) + return self.loadEnum(type) } let producingGenerator = self.fuzzer.environment.getProducingGenerator(ofType: type); if let producingGenerator { @@ -2057,6 +2057,12 @@ public class ProgramBuilder { return emit(LoadString(value: value, customName: customName)).output } + @discardableResult + public func loadEnum(_ type: ILType) -> Variable { + assert(type.isEnumeration) + return loadString(chooseUniform(from: type.enumValues), customName: type.group) + } + @discardableResult public func loadBool(_ value: Bool) -> Variable { return emit(LoadBoolean(value: value)).output @@ -4425,7 +4431,7 @@ public class ProgramBuilder { // and let the mutator prune things let dict: [String : Variable] = bag.properties.filter {_ in probability(0.8)}.mapValues { if $0.isEnumeration { - return loadString(chooseUniform(from: $0.enumValues)) + return loadEnum($0) // relativeTo doesn't have an ObjectGroup so we cannot just register a producingGenerator for it } else if $0.Is(OptionsBag.jsTemporalRelativeTo) { return findOrGenerateType(chooseUniform(from: [.jsTemporalZonedDateTime, .jsTemporalPlainDateTime, diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index 28fac5358..6209d2527 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -295,6 +295,7 @@ public class JavaScriptEnvironment: ComponentBase { private var builtinTypes: [String: ILType] = [:] private var groups: [String: ObjectGroup] = [:] + private var enums: [String: ILType] = [:] // Producing generators, keyed on `type.group` private var producingGenerators: [String: (generator: EnvironmentValueGenerator, probability: Double)] = [:] @@ -413,6 +414,19 @@ public class JavaScriptEnvironment: ComponentBase { registerObjectGroup(group) } + registerEnumeration(.jsTemporalCalendarEnum) + registerEnumeration(ObjectGroup.jsTemporalDirectionParam) + registerEnumeration(OptionsBag.jsTemporalUnitEnum) + registerEnumeration(OptionsBag.jsTemporalRoundingModeEnum) + registerEnumeration(OptionsBag.jsTemporalShowCalendarEnum) + registerEnumeration(OptionsBag.jsTemporalShowOffsetEnum) + registerEnumeration(OptionsBag.jsTemporalShowTimeZoneEnum) + registerEnumeration(OptionsBag.jsTemporalOverflowEnum) + registerEnumeration(OptionsBag.jsTemporalDisambiguationEnum) + registerEnumeration(OptionsBag.jsTemporalOffsetEnum) + registerEnumeration(OptionsBag.base64Alphabet) + registerEnumeration(OptionsBag.base64LastChunkHandling) + registerOptionsBag(.jsTemporalDifferenceSettingOrRoundTo) registerOptionsBag(.jsTemporalToStringSettings) registerOptionsBag(.jsTemporalOverflowSettings) @@ -601,6 +615,13 @@ public class JavaScriptEnvironment: ComponentBase { return actualType } + public func registerEnumeration(_ type: ILType) { + assert(type.isEnumeration) + let group = type.group! + assert(enums[group] == nil) + enums[group] = type + } + public func registerObjectGroup(_ group: ObjectGroup) { assert(groups[group.name] == nil) groups[group.name] = group @@ -685,6 +706,13 @@ public class JavaScriptEnvironment: ComponentBase { public func registerOptionsBag(_ bag: OptionsBag) { registerObjectGroup(bag.group) + + for property in bag.properties.values { + if property.isEnumeration { + assert(enums[property.group!] != nil, "Enum \(property.group!) used in options bag but not registered on the JavaScriptEnvironment") + } + } + addProducingGenerator(forType: bag.group.instanceType, with: { b in b.createOptionsBag(bag) }) } @@ -739,6 +767,10 @@ public class JavaScriptEnvironment: ComponentBase { return [.forUnknownFunction] } + public func getEnum(ofName name: String) -> ILType? { + return enums[name] + } + public func getProducingMethods(ofType type: ILType) -> [(group: String, method: String)] { guard let array = producingMethods[type] else { return [] diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index d09add6be..f9bf01995 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -1396,7 +1396,12 @@ public struct JSTyper: Analyzer { case .loadString(let op): if let customName = op.customName { - set(instr.output, .namedString(ofName: customName)) + if let enumTy = environment.getEnum(ofName: customName) { + set(instr.output, enumTy) + } else { + set(instr.output, .namedString(ofName: customName)) + } + } else { set(instr.output, .jsString) } diff --git a/Tests/FuzzilliTests/JSTyperTests.swift b/Tests/FuzzilliTests/JSTyperTests.swift index 739585b29..30d999e15 100644 --- a/Tests/FuzzilliTests/JSTyperTests.swift +++ b/Tests/FuzzilliTests/JSTyperTests.swift @@ -1824,7 +1824,7 @@ class JSTyperTests: XCTestCase { func testProducingGenerators() { // Make a simple object - let mockEnum = ILType.enumeration(ofName: "mockField", withValues: ["mockValue"]); + let mockEnum = ILType.enumeration(ofName: "MockEnum", withValues: ["mockValue"]); let mockObject = ObjectGroup( name: "MockObject", instanceType: nil, @@ -1837,10 +1837,12 @@ class JSTyperTests: XCTestCase { // Some things to keep track of how the generator was called var callCount = 0 var returnedVar: Variable? = nil + var generatedEnum: Variable? = nil // A simple generator func generateObject(builder: ProgramBuilder) -> Variable { callCount += 1 - let val = builder.loadString("mockValue") + let val = builder.loadEnum(mockEnum) + generatedEnum = val let variable = builder.createObject(with: ["mockField": val]) returnedVar = variable return variable @@ -1856,6 +1858,7 @@ class JSTyperTests: XCTestCase { let fuzzer = makeMockFuzzer() fuzzer.environment.registerObjectGroup(mockObject) + fuzzer.environment.registerEnumeration(mockEnum) fuzzer.environment.addProducingGenerator(forType: mockObject.instanceType, with: generateObject) fuzzer.environment.addProducingGenerator(forType: mockNamedString, with: generateString) let b = fuzzer.makeBuilder() @@ -1879,6 +1882,15 @@ class JSTyperTests: XCTestCase { // Test that the returned variable gets typed correctly XCTAssert(b.type(of: variable2).Is(mockNamedString)) XCTAssertEqual(b.type(of: variable2).group, "NamedString") + + // We already generated a mockEnum, look for it. + let foundEnum = b.randomVariable(ofType: mockEnum)! + // Test that it picked up the existing generated variable. + XCTAssertEqual(generatedEnum, foundEnum) + + // Test that the returned variable gets typed correctly. + XCTAssert(b.type(of: foundEnum).Is(mockEnum)) + XCTAssertEqual(b.type(of: foundEnum).group, "MockEnum") } func testFindConstructor() { From fbd7eef1e65ea17b03804d3d34be08e4f20f8b62 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Thu, 18 Sep 2025 12:45:28 +0200 Subject: [PATCH 06/35] [wasm] Implement self and forward references inside signatures This adds the necessary logic to resolve self and forward references inside Wasm signatures. Due to the missing usages of these signature types, there aren't great ways to test these features, yet. Bug: 445356784 Change-Id: Icb5b99eff1872d301e6775a64546004d07b046fb Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8602557 Reviewed-by: Manos Koukoutos Commit-Queue: Matthias Liedtke --- Sources/Fuzzilli/FuzzIL/JSTyper.swift | 37 +++++++++++++++++++----- Sources/Fuzzilli/FuzzIL/TypeSystem.swift | 2 +- Tests/FuzzilliTests/WasmTests.swift | 9 ++++-- 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index f9bf01995..e2301d0fd 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -434,24 +434,47 @@ public struct JSTyper: Analyzer { mutating func addSignatureType(def: Variable, signature: WasmSignature, inputs: ArraySlice) { var inputs = inputs.makeIterator() - let resolveType = { (paramType: ILType) in + let tgIndex = isWithinTypeGroup ? typeGroups.count - 1 : -1 + + // Temporary variable to use by the resolveType capture. It would be nicer to use + // higher-order functions for this but resolveType has to be a mutating func which doesn't + // seem to work well with escaping functions. + var isParameter = true + let resolveType = { (i: Int, paramType: ILType) in if paramType.requiredInputCount() == 0 { return paramType } assert(paramType.Is(.wasmRef(.Index(), nullability: true))) let typeDef = inputs.next()! - let elementDesc = type(of: typeDef).wasmTypeDefinition!.description + let elementDesc = type(of: typeDef).wasmTypeDefinition!.description! if elementDesc == .selfReference { - // TODO(mliedtke): Implement this before we add code genertors for signature types. - fatalError("self and forward references not implemented for signatures, yet.") + // Register a resolver callback. See `addArrayType` for details. + if isParameter { + selfReferences[typeDef, default: []].append({typer, replacement in + let desc = typer.type(of: def).wasmTypeDefinition!.description as! WasmSignatureTypeDescription + var params = desc.signature.parameterTypes + params[i] = typer.type(of: replacement ?? def) + desc.signature = params => desc.signature.outputTypes + }) + } else { + selfReferences[typeDef, default: []].append({typer, replacement in + let desc = typer.type(of: def).wasmTypeDefinition!.description as! WasmSignatureTypeDescription + var outputTypes = desc.signature.outputTypes + let nullability = outputTypes[i].wasmReferenceType!.nullability + outputTypes[i] = typer.type(of: replacement ?? def).wasmTypeDefinition!.getReferenceTypeTo(nullability: nullability) + desc.signature = desc.signature.parameterTypes => outputTypes + }) + } + } + registerTypeGroupDependency(from: tgIndex, to: elementDesc.typeGroupIndex) return type(of: typeDef).wasmTypeDefinition! .getReferenceTypeTo(nullability: paramType.wasmReferenceType!.nullability) } - let tgIndex = isWithinTypeGroup ? typeGroups.count - 1 : -1 - let resolvedParameterTypes = signature.parameterTypes.map(resolveType) - let resolvedOutputTypes = signature.outputTypes.map(resolveType) + let resolvedParameterTypes = signature.parameterTypes.enumerated().map(resolveType) + isParameter = false // TODO(mliedtke): Is there a nicer way to capture this? + let resolvedOutputTypes = signature.outputTypes.enumerated().map(resolveType) set(def, .wasmTypeDef(description: WasmSignatureTypeDescription(signature: resolvedParameterTypes => resolvedOutputTypes, typeGroupIndex: tgIndex))) if isWithinTypeGroup { typeGroups[typeGroups.count - 1].append(def) diff --git a/Sources/Fuzzilli/FuzzIL/TypeSystem.swift b/Sources/Fuzzilli/FuzzIL/TypeSystem.swift index 5863d7d56..c0e81beaf 100644 --- a/Sources/Fuzzilli/FuzzIL/TypeSystem.swift +++ b/Sources/Fuzzilli/FuzzIL/TypeSystem.swift @@ -2090,7 +2090,7 @@ class WasmTypeDescription: Hashable, CustomStringConvertible { } class WasmSignatureTypeDescription: WasmTypeDescription { - let signature: WasmSignature + var signature: WasmSignature init(signature: WasmSignature, typeGroupIndex: Int) { self.signature = signature diff --git a/Tests/FuzzilliTests/WasmTests.swift b/Tests/FuzzilliTests/WasmTests.swift index 407a20103..0932b2421 100644 --- a/Tests/FuzzilliTests/WasmTests.swift +++ b/Tests/FuzzilliTests/WasmTests.swift @@ -4370,9 +4370,11 @@ class WasmGCTests: XCTestCase { let jsProg = buildAndLiftProgram { b in let typeGroup = b.wasmDefineTypeGroup { let arrayi32 = b.wasmDefineArrayType(elementType: .wasmi32, mutability: true) + let selfRef = b.wasmDefineForwardOrSelfReference() let signature = b.wasmDefineSignatureType( - signature: [.wasmRef(.Index(), nullability: true), .wasmi32] => [.wasmi32], - indexTypes: [arrayi32]) + signature: [.wasmRef(.Index(), nullability: true), .wasmi32] => + [.wasmi32, .wasmRef(.Index(), nullability: true)], + indexTypes: [arrayi32, selfRef]) return [arrayi32, signature] } @@ -4381,6 +4383,9 @@ class WasmGCTests: XCTestCase { // TODO(mliedtke): Do something more useful with the signature type than // defining a null value for it and testing that it's implicitly convertible to // .wasmFuncRef. + // TODO(mliedtke): Also properly test for self and forward references in both + // parameter and return types as well as type group dependencies once signatures + // are usable with more interesting operations. [function.wasmRefNull(typeDef: typeGroup[1])] } } From bcbb448a7229222f2956fe9b70d612e5db8d44dc Mon Sep 17 00:00:00 2001 From: Carl Smith Date: Mon, 22 Sep 2025 15:09:15 +0200 Subject: [PATCH 07/35] Implement a new CodeGeneration approach. CodeGenerators are now annotated with the types they produce and the Contexts they provide. This allows us to pick an (almost) arbitrary CodeGenerator and nest it in others such that we can definitely execute it. Add a ContextGraph that makes this assembly possible. Because we now have explicit yield points in CodeGenerators we can remove the buildRecursive method. Bug: 408966832 Change-Id: I0320bdd5fa64bd687cea87075b6607ceb61d779d Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8386801 Reviewed-by: Matthias Liedtke Commit-Queue: Carl Smith --- Package.swift | 5 + Sources/Fuzzilli/Base/ContextGraph.swift | 222 ++ Sources/Fuzzilli/Base/ProgramBuilder.swift | 714 +++-- Sources/Fuzzilli/CodeGen/CodeGenerator.swift | 228 +- .../CodeGen/CodeGeneratorWeights.swift | 13 +- Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 2690 ++++++++++++----- .../Fuzzilli/CodeGen/WasmCodeGenerators.swift | 1524 +++++++--- Sources/Fuzzilli/FuzzIL/Context.swift | 7 +- Sources/Fuzzilli/Fuzzer.swift | 31 +- Sources/Fuzzilli/Modules/Statistics.swift | 18 +- Sources/Fuzzilli/Util/MockFuzzer.swift | 2 +- .../FuzzilliCli/Profiles/DuktapeProfile.swift | 2 +- .../Profiles/JerryscriptProfile.swift | 2 +- Sources/FuzzilliCli/Profiles/NjsProfile.swift | 2 +- .../Profiles/V8CommonProfile.swift | 56 +- .../Profiles/V8HoleFuzzingProfile.swift | 2 +- Sources/FuzzilliCli/Profiles/V8Profile.swift | 1 - Sources/FuzzilliCli/Profiles/XSProfile.swift | 16 +- Sources/FuzzilliCli/main.swift | 6 +- Tests/FuzzilliTests/ContextGraphTest.swift | 66 + Tests/FuzzilliTests/ProgramBuilderTest.swift | 183 +- 21 files changed, 4142 insertions(+), 1648 deletions(-) create mode 100644 Sources/Fuzzilli/Base/ContextGraph.swift create mode 100644 Tests/FuzzilliTests/ContextGraphTest.swift diff --git a/Package.swift b/Package.swift index b48be6aef..e7198b332 100644 --- a/Package.swift +++ b/Package.swift @@ -26,6 +26,10 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-protobuf.git", from: "1.31.0"), + .package( + url: "https://github.com/apple/swift-collections.git", + .upToNextMinor(from: "1.2.0") + ), ], targets: [ .target(name: "libsocket", @@ -42,6 +46,7 @@ let package = Package( .target(name: "Fuzzilli", dependencies: [ .product(name: "SwiftProtobuf", package: "swift-protobuf"), + .product(name: "Collections", package: "swift-collections"), "libsocket", "libreprl", "libcoverage"], diff --git a/Sources/Fuzzilli/Base/ContextGraph.swift b/Sources/Fuzzilli/Base/ContextGraph.swift new file mode 100644 index 000000000..38d6992dd --- /dev/null +++ b/Sources/Fuzzilli/Base/ContextGraph.swift @@ -0,0 +1,222 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Collections + +public class ContextGraph { + // This is an edge, it holds all Generators that provide the `to` context at some point. + // Another invariant is that each Generator will keep the original context, i.e. it will return to the `from` conetxt. + struct EdgeKey: Hashable { + let from: Context + let to: Context + + public init(from: Context, to: Context) { + self.from = from + self.to = to + } + } + + // This struct describes the value of an edge in this ContextGraph. + // It holds all `CodeGenerator`s that go from one context to another in a direct transition. + public struct GeneratorEdge { + var generators: [CodeGenerator] = [] + + // Adds a generator to this Edge. + public mutating func addGenerator(_ generator: CodeGenerator) { + generators.append(generator) + } + } + + // This is a Path that goes from one Context to another via (usually more than one) `GeneratorEdge`. It is a full path in the Graph. + // Every Edge in a path may be provided by various CodeGenerators. + public struct Path { + let edges: [GeneratorEdge] + + // For each edge, pick a random Generator that provides that edge. + public func randomConcretePath() -> [CodeGenerator] { + edges.map { edge in + chooseUniform(from: edge.generators) + } + } + } + + // This is the Graph, each pair of from and to, maps to a `GeneratorEdge`. + var edges: [EdgeKey: GeneratorEdge] = [:] + + public init(for generators: WeightedList, withLogger logger: Logger) { + // Technically we don't need any generator to emit the .javascript context, as this is provided by the toplevel. + var providedContexts = Set([.javascript]) + var requiredContexts = Set() + + for generator in generators { + generator.providedContexts.forEach { ctx in + providedContexts.insert(ctx) + } + + requiredContexts.insert(generator.requiredContext) + } + + // Check that every part that provides something is used by the next part of the Generator. This is a simple consistency check. + for generator in generators where generator.parts.count > 1 { + var currentContext = Context(generator.parts[0].providedContext) + + for i in 1.. [[EdgeKey]] { + // Do simple BFS to find all possible paths. + var queue: Deque<[Context]> = [[src]] + var paths: [[Context]] = [] + var seenNodes = Set([src]) + + while !queue.isEmpty { + // use popFirst here from the deque. + let currentPath = queue.popFirst()! + + let currentNode = currentPath.last! + + if currentNode == dst { + paths.append(currentPath) + } + + // Get all possible edges from here on and push all of those to the queue. + for edge in self.edges where edge.key.from == currentNode && !seenNodes.contains(edge.key.to) { + // Prevent cycles, we don't care about complicated paths, but rather simple direct paths. + seenNodes.insert(edge.key.to) + queue.append(currentPath + [edge.key.to]) + } + } + + if paths.isEmpty { + return [] + } + + // Reduce this to Edges structs that we can easily look up. + var edgePaths: [[EdgeKey]] = [] + for path in paths { + var edgePath: [EdgeKey] = [] + for i in 0..<(path.count - 1) { + let edge = EdgeKey(from: path[i], to: path[i+1]) + edgePath.append(edge) + } + edgePaths.append(edgePath) + } + + return edgePaths + } + + func getGenerators(from src: Context, to dst: Context) -> GeneratorEdge? { + self.edges[EdgeKey(from: src, to: dst)] + } + + // TODO(cffsmith) implement this to filter for generators that are actually reachable, we can use this to avoid picking a .javascript generator when we're in .wasmFunction context for example. + // This is needed since we cannot close Contexts or go back in time yet. This essentially calculates all reachable destinations from `src`. + // The caller can then use `fuzzer.codeGenerators.filter { $0.requiredContext.contains() }` and pick a random one from that subset. + func getReachableContexts(from src: Context) -> [Context] { + // Do simple BFS to find all possible paths. + var queue: Deque<[Context]> = [[src]] + var paths: [[Context]] = [] + var seenNodes = Set([src]) + + while !queue.isEmpty { + let currentPath = queue.popFirst()! + + let currentNode = currentPath.last! + + var stillExploring = false + + // Get all possible edges from here on and push all of those to the queue. + for edge in self.edges where edge.key.from == currentNode && !seenNodes.contains(edge.key.to) { + // Prevent cycles, we don't care about complicated paths, but rather simple direct paths. + stillExploring = true + seenNodes.insert(edge.key.to) + queue.append(currentPath + [edge.key.to]) + } + + // If we haven't added another node, it means we have found an "end". + if !stillExploring { + paths.append(currentPath) + } + } + + if paths.isEmpty { + return [] + } + + // Map to all reachable contexts. so all that are on the path. + let contextSet = paths.reduce(Set()) { res, path in + res.union(path) + } + return Array(contextSet) + } + + // The return value is a list of possible Paths. + // A Path is a possible way from one context to another. + public func getCodeGeneratorPaths(from src: Context, to dst: Context) -> [Path]? { + + let paths = getAllPaths(from: src, to: dst) + + if paths.isEmpty { + return nil + } + + return paths.map { edges in + Path(edges: edges.map { edge in + self.edges[edge]! + }) + } + } +} diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 8c9d14e50..67a0a9ff0 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -76,7 +76,7 @@ public class ProgramBuilder { /// Visible variables management. /// The `scopes` stack contains one entry per currently open scope containing all variables created in that scope. - private var scopes = Stack<[Variable]>([[]]) + private(set) var scopes = Stack<[Variable]>([[]]) /// The `variablesInScope` array simply contains all variables that are currently in scope. It is effectively the `scopes` stack flattened. private var variablesInScope = [Variable]() @@ -102,6 +102,17 @@ public class ProgramBuilder { /// literals is created inside a method/getter/setter of another object literals. private var activeObjectLiterals = Stack() + /// If we open a new function, we save its Variable here. + /// This allows CodeGenerators to refer to their Variable after they emit the + /// `End*Function` operation. This allows them to call the function after closing it. + /// Since they cannot refer to the Variable as it usually is created in the head part of the Generator. + private var lastFunctionVariables = Stack() + + /// Just a getter to get the top most, i.e. last function Variable. + public var lastFunctionVariable: Variable { + return lastFunctionVariables.top + } + /// When building object literals, the state for the current literal is exposed through this member and /// can be used to add fields to the literal or to determine if some field already exists. public var currentObjectLiteral: ObjectLiteral { @@ -133,6 +144,9 @@ public class ProgramBuilder { return activeClassDefinitions.top } + /// The remaining CodeGenerators to call as part of a building / CodeGen step, these will "clean up" the state and fix the contexts. + public var scheduled: Stack = Stack() + /// Stack of active switch blocks. private var activeSwitchBlocks = Stack() @@ -195,10 +209,12 @@ public class ProgramBuilder { jsTyper.reset() activeObjectLiterals.removeAll() activeClassDefinitions.removeAll() + buildLog?.reset() } /// Finalizes and returns the constructed program, then resets this builder so it can be reused for building another program. public func finalize() -> Program { + assert(scheduled.isEmpty) let program = Program(code: code, parent: parent, comments: comments, contributors: contributors) reset() return program @@ -591,7 +607,6 @@ public class ProgramBuilder { public func findOrGenerateType(_ type: ILType, maxNumberOfVariablesToGenerate: Int = 100) -> Variable { assert(context.contains(.javascript)) -// assert(argumentGenerationVariableBudget == nil) argumentGenerationVariableBudget.push(numVariables + maxNumberOfVariablesToGenerate) @@ -676,6 +691,16 @@ public class ProgramBuilder { // Note that builtin constructors are handled above in the maybeGenerateConstructorAsPath call. return self.randomVariable(forUseAs: .constructor()) }), + (.wasmTypeDef(), { + // Call into the WasmTypeGroup generator (or other that provide a .wasmTypeDef) + let generators = self.fuzzer.codeGenerators.filter { gen in + gen.produces.contains { type in + type.Is(.wasmTypeDef()) + } + } + let _ = self.complete(generator: generators.randomElement(), withBudget: 5) + return self.randomVariable(ofType: .wasmTypeDef())! + }), (.object(), { func useMethodToProduce(_ method: (group: String, method: String)) -> Variable { let group = self.fuzzer.environment.type(ofGroup: method.group) @@ -757,17 +782,22 @@ public class ProgramBuilder { } else if let property = maybeProperty { return usePropertyToProduce(property) } - let codeGenerators = - self.fuzzer.codeGenerators.filter({$0.requiredContext.isSubset(of: self.context) && - !$0.isRecursive && $0.produces != nil && $0.produces!.Is(type)}) - if codeGenerators.count > 0 { - let generator = codeGenerators.randomElement() - self.run(generator) - // The generator we ran above is supposed to generate the - // requested type. If no variable of that type exists - // now, then either the generator or its annotation is - // wrong. - return self.randomVariable(ofTypeOrSubtype: type)! + let generators = self.fuzzer.codeGenerators.filter({ + // Right now only use generators that require a single context. + $0.parts.last!.requiredContext.isSingle && + $0.parts.last!.requiredContext.satisfied(by: self.context) && + $0.parts.last!.produces.contains(where: { producedType in + producedType.Is(type) + }) + }) + if generators.count > 0 { + let generator = generators.randomElement() + let _ = self.complete(generator: generator, withBudget: 10) + // The generator we ran above is supposed to generate the + // requested type. If no variable of that type exists + // now, then either the generator or its annotation is + // wrong. + return self.randomVariable(ofTypeOrSubtype: type)! } // Otherwise this is one of the following: // 1. an object with more type information, i.e. it has a group, but no associated builtin, e.g. we cannot construct it with new. @@ -1654,12 +1684,6 @@ public class ProgramBuilder { // parent budget and the (absolute) threshold for recursive code generation. // - /// The first "knob": this mainly determines the shape of generated code as it determines how large block bodies are relative to their surrounding code. - /// This also influences the nesting depth of the generated code, as recursive code generators are only invoked if enough "budget" is still available. - /// These are writable so they can be reconfigured in tests. - var minRecursiveBudgetRelativeToParentBudget = 0.05 - var maxRecursiveBudgetRelativeToParentBudget = 0.50 - /// The second "knob": the minimum budget required to be able to invoke recursive code generators. public static let minBudgetForRecursiveCodeGeneration = 5 @@ -1673,24 +1697,101 @@ public class ProgramBuilder { case generatingAndSplicing } - // Keeps track of the state of one buildInternal() invocation. These are tracked in a stack, one entry for each recursive call. - // This is a class so that updating the currently active state is possible without push/pop. - private class BuildingState { - let initialBudget: Int - let mode: BuildingMode - var recursiveBuildingAllowed = true - var nextRecursiveBlockOfCurrentGenerator = 1 - var totalRecursiveBlocksOfCurrentGenerator: Int? = nil - // An optional budget for recursive building. - var recursiveBudget: Int? = nil + struct BuildLog { + enum ActionOutcome: CustomStringConvertible { + case success + case failed(String?) + case started + + var description: String { + switch self { + case .success: + return "✅" + case .failed(let reason): + if let reason { + return "❌: \(reason)" + } else { + return "❌" + } + case .started: + return "started" + } + } + } + + struct BuildAction { + var name: String + var outcome = ActionOutcome.started + var produces: [ILType] + } + + var pendingActions: Stack = Stack() + var actions = [(BuildAction, Int)]() + var indent = 0 - init(initialBudget: Int, mode: BuildingMode) { - assert(initialBudget > 0) - self.initialBudget = initialBudget - self.mode = mode + mutating func startAction(_ actionName: String, produces: [ILType]) { + let action = BuildAction(name: actionName, produces: produces) + // Mark this action as `.started`. + actions.append((action, indent)) + // Push the action onto the pending stack, we will need to complete or fail it later. + pendingActions.push(action) + indent += 1 + } + + mutating func succeedAction(_ newlyCreatedVariableTypes: [ILType]) { + indent -= 1 + var finishedAction = pendingActions.pop() + finishedAction.outcome = .success + actions.append((finishedAction, indent)) + #if DEBUG + // Now Check that we've seen these new types. + for t in finishedAction.produces { + if !newlyCreatedVariableTypes.contains(where: { + $0.Is(t) + }) { + var fatalErrorString = "" + fatalErrorString += "Action: \(finishedAction.name)\n" + fatalErrorString += "Action guaranteed it would produce: \(finishedAction.produces)\n" + fatalErrorString += "\(getLogString())\n" + fatalErrorString += "newlyCreatedVariableTypes: \(newlyCreatedVariableTypes) does not contain expected type \(t)" + fatalError(fatalErrorString) + } + } + #endif + } + + mutating func reportFailure(reason: String? = nil) { + indent -= 1 + var failedAction = pendingActions.pop() + failedAction.outcome = .failed(reason) + actions.append((failedAction, indent)) + } + + func getLogString() -> String { + var logString = "Build log:\n" + for (action, indent) in actions { + let tab = String(repeating: " ", count: indent) + logString.append("\(tab)\(action.name): \(action.outcome)\n") + } + return logString + } + + mutating func reset() { + assert(pendingActions.isEmpty, "We should have completed all pending build actions, either failed or succeeded.") + // This is basically equivalent with the statement above. + assert(indent == 0) + actions.removeAll() } } - private var buildStack = Stack() + + + // The BuildLog records all `run` and `complete` calls on this ProgramBuilder, be it through mutation or generation. + #if DEBUG + // We definitely want to have the BuildLog in DEBUG builds. + var buildLog: BuildLog? = BuildLog() + #else + var buildLog: BuildLog? = fuzzer.config.logLevel.isAtLeast(.verbose) ? BuildLog() : nil + #endif /// Build random code at the current position in the program. /// @@ -1701,118 +1802,42 @@ public class ProgramBuilder { /// Building code requires that there are visible variables available as inputs for CodeGenerators or as replacement variables for splicing. /// When building new programs, `buildPrefix()` can be used to generate some initial variables. `build()` purposely does not call /// `buildPrefix()` itself so that the budget isn't accidentally spent just on prefix code (which is probably less interesting). - public func build(n: Int = 1, by mode: BuildingMode = .generatingAndSplicing) { - assert(buildStack.isEmpty) - buildInternal(initialBuildingBudget: n, mode: mode) - assert(buildStack.isEmpty) - } - - /// Recursive code building. Used by CodeGenerators for example to fill the bodies of generated blocks. - public func buildRecursive(block: Int = 1, of numBlocks: Int = 1, n optionalBudget: Int? = nil) { - assert(!buildStack.isEmpty) - let parentState = buildStack.top - - assert(parentState.mode != .splicing) - assert(parentState.recursiveBuildingAllowed) // If this fails, a recursive CodeGenerator is probably not marked as recursive. - assert(numBlocks >= 1) - assert(block >= 1 && block <= numBlocks) - assert(parentState.nextRecursiveBlockOfCurrentGenerator == block, "next = \(parentState.nextRecursiveBlockOfCurrentGenerator), block = \(block)") - assert((parentState.totalRecursiveBlocksOfCurrentGenerator ?? numBlocks) == numBlocks) - - parentState.nextRecursiveBlockOfCurrentGenerator = block + 1 - parentState.totalRecursiveBlocksOfCurrentGenerator = numBlocks - - // Determine the budget for this recursive call as a fraction of the parent's initial budget. - var recursiveBudget: Double - if let specifiedBudget = parentState.recursiveBudget { - assert(specifiedBudget > 0) - recursiveBudget = Double(specifiedBudget) - } else { - let factor = Double.random(in: minRecursiveBudgetRelativeToParentBudget...maxRecursiveBudgetRelativeToParentBudget) - assert(factor > 0.0 && factor < 1.0) - let parentBudget = parentState.initialBudget - recursiveBudget = Double(parentBudget) * factor - } + public func build(n budget: Int, by buildingMode: BuildingMode = .generatingAndSplicing) { - // Now split the budget between all sibling blocks. - recursiveBudget /= Double(numBlocks) - recursiveBudget.round(.up) - assert(recursiveBudget >= 1.0) + /// The number of CodeGenerators we want to call per level. + let splitFactor = 2 - // Finally, if a custom budget was requested, choose the smaller of the two values. - if let requestedBudget = optionalBudget { - assert(requestedBudget > 0) - recursiveBudget = min(recursiveBudget, Double(requestedBudget)) + // If the corpus is empty, we have to pick generating here, this is only relevant for the first sample. + let mode: BuildingMode = if fuzzer.corpus.isEmpty { + .generating + } else { + if buildingMode == .generatingAndSplicing { + chooseUniform(from: [.generating, .splicing]) + } else { + buildingMode + } } - buildInternal(initialBuildingBudget: Int(recursiveBudget), mode: parentState.mode) - } - - private func buildInternal(initialBuildingBudget: Int, mode: BuildingMode) { - assert(initialBuildingBudget > 0) + // Now depending on the budget we will do one of these things: + // 1. Large budget is still here. Pick a scheduled CodeGenerator, or a random CodeGenerator. + // a. See if we can execute it immediately and call into build if it yields. (and split budgets). + // b. if not, schedule it, pick a generator that get's us closer to the target context. + // c. see if we need to solve input constraints of scheduled generators. + // 2. budget is low + // a. Call scheduled GeneratorStubs or return. // Both splicing and code generation can sometimes fail, for example if no other program with the necessary features exists. // To avoid infinite loops, we bail out after a certain number of consecutive failures. var consecutiveFailures = 0 - let state = BuildingState(initialBudget: initialBuildingBudget, mode: mode) - buildStack.push(state) - defer { buildStack.pop() } - var remainingBudget = initialBuildingBudget + var remainingBudget = budget // Unless we are only splicing, find all generators that have the required context. We must always have at least one suitable code generator. let origContext = context - var availableGenerators = WeightedList() - if state.mode != .splicing { - availableGenerators = fuzzer.codeGenerators.filter({ $0.requiredContext.isSubset(of: origContext) }) - assert(!availableGenerators.isEmpty) - } - - struct BuildLog { - enum ActionOutcome { - case success - case failed - } - - struct BuildAction { - var action: String - var outcome: ActionOutcome? - } - - var actions = [BuildAction]() - - mutating func startAction(_ action: String) { - // Make sure that we have either completed our last build step or we haven't started any build steps yet. - assert(actions.isEmpty || actions[actions.count - 1].outcome != nil) - actions.append(BuildAction(action: action)) - } - - mutating func endAction(withOutcome outcome: ActionOutcome) { - assert(!actions.isEmpty && actions[actions.count - 1].outcome == nil) - actions[actions.count - 1].outcome = outcome - } - - } - - var buildLog = fuzzer.config.logLevel.isAtLeast(.verbose) ? BuildLog() : nil while remainingBudget > 0 { assert(context == origContext, "Code generation or splicing must not change the current context") - if state.recursiveBuildingAllowed && - remainingBudget < ProgramBuilder.minBudgetForRecursiveCodeGeneration && - availableGenerators.contains(where: { !$0.isRecursive }) { - // No more recursion at this point since the remaining budget is too small. - state.recursiveBuildingAllowed = false - availableGenerators = availableGenerators.filter({ !$0.isRecursive }) - assert(state.mode == .splicing || !availableGenerators.isEmpty) - } - - var mode = state.mode - if mode == .generatingAndSplicing { - mode = fuzzer.corpus.isEmpty ? .generating : chooseUniform(from: [.generating, .splicing]) - } - let codeSizeBefore = code.count switch mode { case .generating: @@ -1820,18 +1845,51 @@ public class ProgramBuilder { // visible Variables. Therefore we should always have some Variables visible if we want to use them. assert(hasVisibleVariables, "CodeGenerators assume that there are visible variables to use. Use buildPrefix() to generate some initial variables in a new program") - // Reset the code generator specific part of the state. - state.nextRecursiveBlockOfCurrentGenerator = 1 - state.totalRecursiveBlocksOfCurrentGenerator = nil + var generator: CodeGenerator? = nil + + // If the budget is low, we will pick a CodeGenerator that is directly usable from the current context. + // If we still have budget left, we will instead pick any CodeGenerator that is reachable from the current context, which means that we might go from .javascript to .wasmFunction. + if remainingBudget < ProgramBuilder.minBudgetForRecursiveCodeGeneration { + generator = fuzzer.codeGenerators.filter({ + $0.requiredContext.isSubset(of: context) + }).randomElement() + + guard generator != nil else { + fatalError("need a callable generator from every context!") + } + } else { + var counter = 0 + // We now try to assemble a Generator that we want to use. + while generator == nil { + // If we haven't managed to find a suitable CodeGenerator, we will try again but only consider CodeGenerators that are reachable from the current context. This should always work. + if counter == 10 { + generator = fuzzer.codeGenerators.filter({ + $0.requiredContext.isSubset(of: context) + }).randomElement()! + break + } + // Select a random CodeGenerator that is reachable from the current context and run it. + let reachableContexts = fuzzer.contextGraph.getReachableContexts(from: context) + let possibleGenerators = fuzzer.codeGenerators.filter({ generator in + reachableContexts.reduce(false) { res, reachable in + return res || generator.requiredContext.isSubset(of: reachable) + } + }) + + assert(!possibleGenerators.isEmpty) + let randomGenerator = possibleGenerators.randomElement() + // After having picked a generator, we might need to nest it in other generators that provide the necessary contexts. + generator = assembleSyntheticGenerator(for: randomGenerator) + counter += 1 + } + } - // Select a random generator and run it. - let generator = availableGenerators.randomElement() - buildLog?.startAction(generator.name) - run(generator) + // TODO: think about this and if we want to split this so that we get more CodeGenerators on the same level? + let _ = complete(generator: generator!, withBudget: remainingBudget / splitFactor) case .splicing: let program = fuzzer.corpus.randomElementForSplicing() - buildLog?.startAction("splicing") + buildLog?.startAction("splicing", produces: []) splice(from: program) default: @@ -1842,25 +1900,30 @@ public class ProgramBuilder { let emittedInstructions = codeSizeAfter - codeSizeBefore remainingBudget -= emittedInstructions if emittedInstructions > 0 { - buildLog?.endAction(withOutcome: .success) - consecutiveFailures = 0 + if mode == .splicing { + buildLog?.succeedAction([]) + } } else { - buildLog?.endAction(withOutcome: .failed) + if mode == .splicing { + buildLog?.reportFailure() + } consecutiveFailures += 1 guard consecutiveFailures < 10 else { // When splicing, this is somewhat expected as we may not find code to splice if we're in a restricted // context (e.g. we're inside a switch, but can't find another program with switch-cases). // However, when generating code this should happen very rarely since we should always be able to // generate code, not matter what context we are currently in. - if state.mode != .splicing { - logger.warning("Too many consecutive failures during code building with mode .\(state.mode). Bailing out.") - if let actions = buildLog?.actions { - logger.verbose("Build log:") - for action in actions { - logger.verbose(" \(action.action): \(action.outcome!)") - } + if mode != .splicing { + if let log = buildLog { + logger.verbose(log.getLogString()) } } + // If we have requested generatingAndSplicing initially and + // then decided to splice and fail here, we generate as a + // fallback. + if buildingMode == .generatingAndSplicing && mode == .splicing { + build(n: budget, by: .generating) + } return } } @@ -1871,9 +1934,6 @@ public class ProgramBuilder { /// Returns both the number of generated instructions and of newly created variables. @discardableResult public func buildValues(_ n: Int) -> (generatedInstructions: Int, generatedVariables: Int) { - // Either we are in .javascript and see no variables, or we are in a wasm function and also don't see any variables. - assert(context.isValueBuildableContext) - var valueGenerators = fuzzer.codeGenerators.filter({ $0.isValueGenerator }) // Filter for the current context valueGenerators = valueGenerators.filter { context.contains($0.requiredContext) } @@ -1887,18 +1947,17 @@ public class ProgramBuilder { // budget and allows us to run code generators when building recursively. We probably don't want to run // splicing here since splicing isn't as careful as code generation and may lead to invalid code more quickly. // The `initialBudget` isn't really used (since we specify a `recursiveBudget`), so can be an arbitrary value. - let state = BuildingState(initialBudget: 2 * n, mode: .generating) - state.recursiveBudget = n - buildStack.push(state) - defer { buildStack.pop() } + + let currentBudget = 2 * n while numberOfVisibleVariables - previousNumberOfVisibleVariables < n { + let generator = valueGenerators.randomElement() - assert(generator.requiredContext.isValueBuildableContext && generator.inputs.count == 0) - state.nextRecursiveBlockOfCurrentGenerator = 1 - state.totalRecursiveBlocksOfCurrentGenerator = nil - let numberOfGeneratedInstructions = run(generator) + // Just fully run the generator without yielding back. + // Think about changing this and calling into the higher level build logic? + // TODO arbitrary budget here right now, change this to some split factor? + let numberOfGeneratedInstructions = self.complete(generator: generator, withBudget: currentBudget / 5) assert(numberOfGeneratedInstructions > 0, "ValueGenerators must always succeed") totalNumberOfGeneratedInstructions += numberOfGeneratedInstructions @@ -1917,7 +1976,7 @@ public class ProgramBuilder { public func buildPrefix() { // Each value generators should generate at least 3 variables, and we probably want to run at least a // few of them (maybe roughly >= 3), so the number of variables to build shouldn't be set too low. - assert(CodeGenerator.numberOfValuesToGenerateByValueGenerators == 3) + assert(GeneratorStub.numberOfValuesToGenerateByValueGenerators == 3) let numValuesToBuild = Int.random(in: 10...15) trace("Start of prefix code") @@ -1936,7 +1995,7 @@ public class ProgramBuilder { // We need to update the inputs later, so take note of the visible variables here. let oldVisibleVariables = visibleVariables - build(n: defaultCodeGenerationAmount, by: mode) + build(n: defaultCodeGenerationAmount) let newVisibleVariables = visibleVariables.filter { v in let t = type(of: v) @@ -1954,12 +2013,248 @@ public class ProgramBuilder { append(Instruction(newOp, inouts: Array(newInputs) + newOutputs, flags: instr.flags)) } + // This function knows its own budget, and splits it to its yield points. + public func complete(generator: CodeGenerator, withBudget budget: Int) -> Int { + trace("Executing Generator \(generator.expandedName)") + let actionName = "Generator: " + generator.expandedName + buildLog?.startAction(actionName, produces: generator.produces) + let visibleVariablesBefore = visibleVariables + + let depth = scheduled.count + + // Split budget evenly at yield points. + let budgetPerYieldPoint = budget / generator.parts.count + + var numberOfGeneratedInstructions = 0 + + // calculate all input requirements of this CodeGenerator. + let inputTypes = Set(generator.parts.reduce([]) { res, gen in + return res + gen.inputs.types + }) + + var availableTypes = inputTypes.filter { + randomVariable(ofType: $0) != nil + } + + // Add the current context to the seen Contexts as well. + var seenContexts: [Context] = [context] + + let contextsAndTypes = generator.parts.map { ($0.providedContext, $0.inputs.types) } + + // Check if the can be produced along this generator, otherwise we need to bail. + for (contexts, types) in contextsAndTypes { + // We've seen the current context. + for context in contexts { + seenContexts.append(context) + } + + for type in types { + // If we don't have the type available, check if we can produce it in the current context or a seen context. + if !availableTypes.contains(where: { + type.Is($0) + }) { + // Check if we have generators that can produce the type reachable from this context. + let reachableContexts: Context = seenContexts.reduce(Context.empty) { res, ctx in [res, fuzzer.contextGraph.getReachableContexts(from: ctx).reduce(Context.empty) { res, ctx in [res, ctx]}] + } + + // Right now this checks if the generator is a subset of the full reachable context (a single bitfield with all reachable contexts). + // TODO: We need to also do some graph thingies here and add our requested types to the queue to see if we can fulfill the requested types. if we see that a generator produces a type, we need to put its input requirements onto the queue and start over? + // Maybe overkill, but also cool. + let callableGenerators = fuzzer.codeGenerators.filter { + $0.requiredContext.isSubset(of: reachableContexts) + } + + // Filter to see if they produce this type. Crucially to avoid dependency cycles, these also need to be valuegenerators. + let canProduceThisType = callableGenerators.contains(where: { generator in + generator.produces.contains(where: { $0.Is(type) }) + }) + + // We cannot run if this is false. + if !canProduceThisType { + // TODO(cffsmith): track some statistics on how often this happens. + buildLog?.reportFailure(reason: "Cannot produce type \(type) starting in original context \(context).") + return 0 + } else { + // Mark the type as available. + availableTypes.insert(type) + } + } + } + } + + // Try to create the types that we need for this generator. + // At this point we've guaranteed that we can produce the types somewhere along the yield points of this generator. + createRequiredInputVariables(forTypes: inputTypes) + + // Push the remaining stubs, we need to call them to close all Contexts properly. + for part in generator.tail.reversed() { + scheduled.push(part) + } + + // This runs the first part of the generator. + numberOfGeneratedInstructions += self.run(generator.head) + + // If this generator says it provides a context, it must do so, it cannot fail because we would not be able to continue with the rest of the generator. + // TODO(cffsmith): implement some forking / merging mode for the Code Object? that way we could "roll back" some changes. + let subsetContext = generator.head.providedContext.reduce(Context.empty) { res, context in + return [res, context] + } + + assert(subsetContext.isSubset(of: context), "Generators that claim to provide contexts cannot fail to provide those contexts \(generator.head.name).") + + // While our local stack is not removed, we need to call into build and call the scheduled stubs. + while scheduled.count > depth { + let codeSizePre = code.count + // Check if we need to or can create types here. + createRequiredInputVariables(forTypes: inputTypes) + // Build into the block. + build(n: budgetPerYieldPoint) + // Call the next scheduled stub. + let _ = callNext() + numberOfGeneratedInstructions += code.count - codeSizePre + } + + if numberOfGeneratedInstructions > 0 { + buildLog?.succeedAction( + Set(visibleVariables) + .subtracting(Set(visibleVariablesBefore)) + .map(self.type) + ) + } else { + buildLog?.reportFailure() + } + + // I guess this is kind of implied by the logic above, yet if someone calls an extra closer in build somehow this would catch it. + assert(depth == scheduled.count, "Build stack is not balanced") + return numberOfGeneratedInstructions + } + + // Todo, the context graph could also find ideal paths that allow type creation. + private func createRequiredInputVariables(forTypes types: Set) { + for type in types { + if type.Is(.jsAnything) && context.contains(.javascript) { + let _ = findOrGenerateType(type) + } else { + if type.Is(.wasmAnything) && context.contains(.wasmFunction) { + // Check if we can produce it with findOrGenerateWasmVar + let _ = currentWasmFunction.generateRandomWasmVar(ofType: type) + } + if randomVariable(ofType: type) == nil { + // Check for other CodeGenerators that can produce the given type in this context. + let usableGenerators = fuzzer.codeGenerators.filter { + $0.requiredContext.isSubset(of: context) && + $0.produces.contains { + $0.Is(type) + } + } + + // Cannot build type here. + if usableGenerators.isEmpty { + // Continue here though, as we might be able to create Variables for other types. + continue + } + + let generator = usableGenerators.randomElement() + + let _ = complete(generator: generator, withBudget: 5) + } + } + } + } + + // The `mainGenerator` is the actual generator that we want to run, we now might need to schedule other generators first to reach a necessary context. + public func assembleSyntheticGenerator(for mainGenerator: CodeGenerator) -> CodeGenerator? { + // We can directly run this CodeGenerator here. + if context.contains(mainGenerator.requiredContext) { + return mainGenerator + } + + // Get all the generators for each edge and pick one of them. + + // We might be in a context that is a union, e.g. .javascript | .subroutine. We then need to get all Paths from both possible single contexts. + let paths: [ContextGraph.Path] = Context.allCases.reduce([]) { pathArray, possibleContext in + + if context.contains(possibleContext) { + // Walk through generated Graph and find a path. + let paths = fuzzer.contextGraph.getCodeGeneratorPaths(from: possibleContext, to: mainGenerator.requiredContext) ?? [] + return pathArray + paths + } + + return pathArray + } + + if paths.isEmpty { + logger.warning("Found no paths in context \(context) for requested generator \(mainGenerator.name)") + return nil + } + + // Pick a random path. + let path = chooseUniform(from: paths) + + // For each edge in the path, pick a random generator. + // This is now a list of CodeGenerators, i.e. pairs of logical units. + let generators: [CodeGenerator] = path.randomConcretePath() + + // So we can now assemble a synthetic generator that invokes our picked generator. + // We start by taking our first CodeGenerator, that will open the next context that is necessary. + // This is an incomplete CodeGenerator right now, as it only contains one part that opens a new context. + var syntheticGenerator = generators[0].parts + + // For all other Stubs we will now successively insert the next CodeGenerator stubs into the right "spots". + // + // We now try to insert the next part of the first CodeGenerator into our synthetic generator. + // [some context] + // We now go to the next Edge of our path and try to insert that head in the correct spot. + // [some context] [another context] + // Since every part of the CodeGenerator requires its previous context, we can insert them in the right spot. + // At the very end, we will insert the actual CodeGenerator that we want to call. + // This essentially amounts to an insertion sort. + // (Because we might have CodeGenerators with multiple parts and different contexts, we cannot do this without sorting) + for subGenerator in generators[1...] + [mainGenerator] { + for (idx, part) in syntheticGenerator.enumerated() { + if part.providedContext.contains(where: { ctx in + subGenerator.head.requiredContext.satisfied(by: ctx) + }) { + // Insert this codegenerator here after this stub. + syntheticGenerator.insert(contentsOf: subGenerator.parts, at: idx + 1) + break + } + } + } + + // This is our synthetic CodeGenerator. + return CodeGenerator("Synthetic", syntheticGenerator) + } + + // Calls the next scheduled generator. + private func callNext() -> Int { + // Check if we should pass variables to this closer somehow? + if !scheduled.isEmpty { + let generator = scheduled.pop() + let generatedInstructions = self.run(generator) + + let subsetContext = generator.providedContext.reduce(Context.empty) { res, context in + return [res, context] + } + + assert(subsetContext.isSubset(of: context), "Generators that claim to provide contexts cannot fail to provide those contexts \(generator.name).") + + + return generatedInstructions + } else { + return 0 + } + } + /// Runs a code generator in the current context and returns the number of generated instructions. @discardableResult - private func run(_ generator: CodeGenerator) -> Int { - assert(generator.requiredContext.isSubset(of: context)) + private func run(_ generator: GeneratorStub) -> Int { + // Any of the required Context constraints need to be satisfied. + assert(generator.requiredContext.satisfied(by: context)) + let visibleVariablesBefore = visibleVariables trace("Executing code generator \(generator.name)") + buildLog?.startAction(generator.name, produces: generator.produces) var inputs = [Variable]() switch generator.inputs.mode { case .loose: @@ -1971,6 +2266,11 @@ public class ProgramBuilder { for inputType in generator.inputs.types { guard let input = randomVariable(ofType: inputType) else { // Cannot run this generator + if generator.providedContext != [] { + fatalError("This generator is supposed to provide a context but cannot as we've failed to find the necessary inputs.") + } + // This early return also needs to report a failure. + buildLog?.reportFailure(reason: "Cannot find variable that satifies input constraints \(inputType).") return 0 } inputs.append(input) @@ -1981,6 +2281,13 @@ public class ProgramBuilder { if numGeneratedInstructions > 0 { contributors.insert(generator) + buildLog?.succeedAction( + Set(visibleVariables) + .subtracting(Set(visibleVariablesBefore)) + .map(self.type) + ) + } else { + buildLog?.reportFailure(reason: "Generator itself failed to produce any instructions.") } return numGeneratedInstructions @@ -2010,7 +2317,7 @@ public class ProgramBuilder { } @discardableResult - private func emit(_ op: Operation, withInputs inputs: [Variable] = [], types: [ILType]? = nil) -> Instruction { + public func emit(_ op: Operation, withInputs inputs: [Variable] = [], types: [ILType]? = nil) -> Instruction { var inouts = inputs for _ in 0.. WasmGlobal { - // TODO: Add simd128 and nullrefs. - withEqualProbability( - {.wasmf32(Float32(self.randomFloat()))}, - {.wasmf64(self.randomFloat())}, - {.wasmi32(Int32(truncatingIfNeeded: self.randomInt()))}, - {.wasmi64(self.randomInt())}, - {.externref}, - {.exnref}, - {.i31ref}) + /// Produces a WasmGlobal that is valid to create in the given Context. + public func randomWasmGlobal(forContext context: Context) -> WasmGlobal { + switch context { + case .javascript: + // These are valid in JS according to: https://webassembly.github.io/spec/js-api/#globals. + // TODO: add simd128 and anyfunc. + return withEqualProbability( + {.wasmf32(Float32(self.randomFloat()))}, + {.wasmf64(self.randomFloat())}, + {.wasmi32(Int32(truncatingIfNeeded: self.randomInt()))}, + {.wasmi64(self.randomInt())}, + {.externref}) + case .wasm: + // TODO: Add simd128 and nullrefs. + return withEqualProbability( + {.wasmf32(Float32(self.randomFloat()))}, + {.wasmf64(self.randomFloat())}, + {.wasmi32(Int32(truncatingIfNeeded: self.randomInt()))}, + {.wasmi64(self.randomInt())}, + {.externref}, + {.exnref}, + {.i31ref}) + default: + fatalError("Unsupported context \(context) for a WasmGlobal.") + } } public func randomTagParameters() -> [ILType] { @@ -4252,7 +4575,7 @@ public class ProgramBuilder { /// Set the parameter types for the next function, method, or constructor, which must be the the start of a function or method definition. /// Parameter types (and signatures in general) are only valid for the duration of the program generation, as they cannot be preserved across mutations. /// As such, the parameter types are linked to their instruction through the index of the instruction in the program. - private func setParameterTypesForNextSubroutine(_ parameterTypes: ParameterList) { + public func setParameterTypesForNextSubroutine(_ parameterTypes: ParameterList) { jsTyper.setParameters(forSubroutineStartingAt: code.count, to: parameterTypes) } @@ -4410,6 +4733,23 @@ public class ProgramBuilder { .wasmEndTryTable(_), .wasmEndBlock(_): activeWasmModule!.blockSignatures.pop() + case .beginPlainFunction(_), + .beginArrowFunction(_), + .beginAsyncArrowFunction(_), + .beginAsyncFunction(_), + .beginAsyncGeneratorFunction(_), + .beginCodeString(_), + .beginGeneratorFunction(_): + assert(instr.numOutputs == 1) + lastFunctionVariables.push(instr.output) + case .endPlainFunction(_), + .endArrowFunction(_), + .endAsyncArrowFunction(_), + .endAsyncFunction(_), + .endAsyncGeneratorFunction(_), + .endCodeString(_), + .endGeneratorFunction(_): + lastFunctionVariables.pop() default: assert(!instr.op.requiredContext.contains(.objectLiteral)) diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerator.swift b/Sources/Fuzzilli/CodeGen/CodeGenerator.swift index edb6251c5..08b3ff5b8 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerator.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerator.swift @@ -23,7 +23,7 @@ fileprivate struct ValueGeneratorAdapter: GeneratorAdapter { let f: ValueGeneratorFunc func run(in b: ProgramBuilder, with inputs: [Variable]) { assert(inputs.isEmpty) - f(b, CodeGenerator.numberOfValuesToGenerateByValueGenerators) + f(b, GeneratorStub.numberOfValuesToGenerateByValueGenerators) } } @@ -77,20 +77,17 @@ fileprivate struct GeneratorAdapter4Args: GeneratorAdapter { } } -public class CodeGenerator: Contributor { +public class GeneratorStub: Contributor { /// Whether this code generator is a value generator. A value generator will create at least one new variable containing /// a newly created value (e.g. a primitive value or some kind of object). Further, value generators must be able to /// run even if there are no existing variables. This way, they can be used to "bootstrap" code generation. - public let isValueGenerator: Bool + public var isValueStub: Bool { + self.inputs.isEmpty && !self.produces.isEmpty + } /// How many different values of the same type ValueGenerators should aim to generate. public static let numberOfValuesToGenerateByValueGenerators = 3 - /// Whether this code generator is recursive, i.e. will generate further code for example to generate the body of a block. - /// This is used to determie whether to run a certain code generator. For example, if only a few more instructions should - /// be generated during program building, calling a recursive code generator will likely result in too many instructions. - public let isRecursive: Bool - /// Describes the inputs expected by a CodeGenerator. public struct Inputs { /// How the inputs should be treated. @@ -111,6 +108,10 @@ public class CodeGenerator: Contributor { return types.count } + public var isEmpty: Bool { + types.count == 0 + } + // No inputs. public static var none: Inputs { return Inputs(types: [], mode: .loose) @@ -164,27 +165,92 @@ public class CodeGenerator: Contributor { /// The inputs expected by this generator. public let inputs: Inputs - public let produces: ILType? + /// The types this CodeGenerator produces + /// ProgramBuilding will assert that these types are (newly) available after running this CodeGenerator. + public let produces: [ILType] + + public enum ContextRequirement { + // If this GeneratorStub has a single Context requirement, which may still be comprised of multiple Context values. + // E.g. .single([.javascript | .method]) or .single([.javascript, .method]) (they're equivalent). + case single(Context) + // If this GeneratorStub has any one of the Context requirements. + // E.g. .either([.javascript, .classDefinition]) which is basically either one of .javascript or .classDefinition + case either([Context]) + + var isSingle : Bool { + self.getSingle() != nil + } + + var isJavascript: Bool { + self.getSingle() == .javascript + } + + func getSingle() -> Context? { + switch self { + case .single(let ctx): + return ctx + default: + return nil + } + } + + // Whether the ContextRequirement is satisified for the given (current) context. + func satisfied(by context: Context) -> Bool { + switch self { + case .single(let ctx): + return ctx.isSubset(of: context) + case .either(let ctxs): + // We do this check here since we cannot check on construction whether we have used .either correctly. + assert(ctxs.count > 1, "Something seems wrong, one should have more than a single context here") + // Any of the Contexts needs to be satisfied. + return ctxs.contains(where: { ctx in + ctx.isSubset(of: context) + }) + } + } + + // Whether the provided Context is exactly the required context. + // Usually this means that if it is `.either` all the those Contexts need to be there. + // This is used in consistency checking in `ContextGraph` initialization where a previous generator might open multiple contexts. + func matches(_ context: Context) -> Bool { + switch self { + case .single(let ctx): + return ctx == context + case .either(let ctxs): + // We do this check here since we cannot check on construction whether we have used .either correctly. + assert(ctxs.count > 1, "Something seems wrong, one should have more than a single context here") + // All contexts need to be in the current context. + return ctxs.allSatisfy { ctx in + ctx.isSubset(of: context) + } + } + } + } /// The contexts in which this code generator can run. - /// This code generator will only be executed if requiredContext.isSubset(of: currentContext) - public let requiredContext: Context + /// This is an Array of subsets of Contexts, one of them has to be runnable. + /// An invariant is now, that a Generator *cannot* `provide` any Context if it might open either one of multiple Contexts. + /// We are checking the other direction of this in the initialization of `ContextGraph`. + /// This code generator will only be executed if any requiredContext.isSubset(of: currentContext) + public let requiredContext: ContextRequirement + + /// The context that is provided by running this Generator. + /// This is always a list of the single contexts that are provided, e.g. [.javascript] + public let providedContext: [Context] /// Warpper around the actual generator function called. private let adapter: GeneratorAdapter - fileprivate init(name: String, isValueGenerator: Bool, isRecursive: Bool, inputs: Inputs, produces: ILType? = nil, context: Context, adapter: GeneratorAdapter) { - assert(!isValueGenerator || context.isValueBuildableContext) - assert(!isValueGenerator || inputs.count == 0) - assert(inputs.count == adapter.expectedNumberOfInputs) + fileprivate init(name: String, inputs: Inputs, produces: [ILType] = [], context: ContextRequirement, providedContext: [Context] = [], adapter: GeneratorAdapter) { - self.isValueGenerator = isValueGenerator - self.isRecursive = isRecursive self.inputs = inputs self.produces = produces self.requiredContext = context + self.providedContext = providedContext self.adapter = adapter super.init(name: name) + + assert(inputs.count == adapter.expectedNumberOfInputs) } /// Execute this code generator, generating new code at the current position in the ProgramBuilder. @@ -199,56 +265,120 @@ public class CodeGenerator: Contributor { return addedInstructions } - public convenience init(_ name: String, inContext context: Context = .javascript, produces: ILType? = nil, _ f: @escaping GeneratorFuncNoArgs) { - self.init(name: name, isValueGenerator: false, isRecursive: false, inputs: .none, produces: produces, context: context, adapter: GeneratorAdapterNoArgs(f: f)) + public convenience init(_ name: String, inContext context: ContextRequirement = .single(.javascript), produces: [ILType] = [], provides: [Context] = [], _ f: @escaping GeneratorFuncNoArgs) { + self.init(name: name, inputs: .none, produces: produces, context: context, providedContext: provides, adapter: GeneratorAdapterNoArgs(f: f)) } - public convenience init(_ name: String, inContext context: Context = .javascript, inputs: Inputs, produces: ILType? = nil, _ f: @escaping GeneratorFunc1Arg) { + public convenience init(_ name: String, inContext context: ContextRequirement = .single(.javascript), inputs: Inputs, produces: [ILType] = [], provides: [Context] = [], _ f: @escaping GeneratorFunc1Arg) { assert(inputs.count == 1) - self.init(name: name, isValueGenerator: false, isRecursive: false, inputs: inputs, produces: produces, context: context, adapter: GeneratorAdapter1Arg(f: f)) + self.init(name: name, inputs: inputs, produces: produces, context: context, providedContext: provides, adapter: GeneratorAdapter1Arg(f: f)) } - public convenience init(_ name: String, inContext context: Context = .javascript, inputs: Inputs, produces: ILType? = nil, _ f: @escaping GeneratorFunc2Args) { + public convenience init(_ name: String, inContext context: ContextRequirement = .single(.javascript), inputs: Inputs, produces: [ILType] = [], provides: [Context] = [], _ f: @escaping GeneratorFunc2Args) { assert(inputs.count == 2) - self.init(name: name, isValueGenerator: false, isRecursive: false, inputs: inputs, produces: produces, context: context, adapter: GeneratorAdapter2Args(f: f)) + self.init(name: name, inputs: inputs, produces: produces, context: context, providedContext: provides, adapter: GeneratorAdapter2Args(f: f)) } - public convenience init(_ name: String, inContext context: Context = .javascript, inputs: Inputs, produces: ILType? = nil, _ f: @escaping GeneratorFunc3Args) { + public convenience init(_ name: String, inContext context: ContextRequirement = .single(.javascript), inputs: Inputs, produces: [ILType] = [], provides: [Context] = [], _ f: @escaping GeneratorFunc3Args) { assert(inputs.count == 3) - self.init(name: name, isValueGenerator: false, isRecursive: false, inputs: inputs, produces: produces, context: context, adapter: GeneratorAdapter3Args(f: f)) + self.init(name: name, inputs: inputs, produces: produces, context: context, providedContext: provides, adapter: GeneratorAdapter3Args(f: f)) } - public convenience init(_ name: String, inContext context: Context = .javascript, inputs: Inputs, produces: ILType? = nil, _ f: @escaping GeneratorFunc4Args) { + public convenience init(_ name: String, inContext context: ContextRequirement = .single(.javascript), inputs: Inputs, produces: [ILType] = [], provides: [Context] = [], _ f: @escaping GeneratorFunc4Args) { assert(inputs.count == 4) - self.init(name: name, isValueGenerator: false, isRecursive: false, inputs: inputs, produces: produces, context: context, adapter: GeneratorAdapter4Args(f: f)) + self.init(name: name, inputs: inputs, produces: produces, context: context, providedContext: provides, adapter: GeneratorAdapter4Args(f: f)) } } -// Constructors for recursive CodeGenerators. -public func RecursiveCodeGenerator(_ name: String, inContext context: Context = .javascript, _ f: @escaping GeneratorFuncNoArgs) -> CodeGenerator { - return CodeGenerator(name: name, isValueGenerator: false, isRecursive: true, inputs: .none, context: context, adapter: GeneratorAdapterNoArgs(f: f)) -} +public class CodeGenerator { + let parts: [GeneratorStub] + public let name: String + // This is a pre-calculated array of contexts that is provided by this CodeGenerator + // Here, I think we might have different Contexts that each yield point could provide, e.g. [.javascript | .subroutine, .wasmFunction]. Unsure if there is a situation where this matters? instead of having .javascript | .subroutine | .wasmFunction? + public let providedContexts: [Context] -public func RecursiveCodeGenerator(_ name: String, inContext context: Context = .javascript, inputs: CodeGenerator.Inputs, _ f: @escaping GeneratorFunc1Arg) -> CodeGenerator { - assert(inputs.count == 1) - return CodeGenerator(name: name, isValueGenerator: false, isRecursive: true, inputs: inputs, context: context, adapter: GeneratorAdapter1Arg(f: f)) -} -public func RecursiveCodeGenerator(_ name: String, inContext context: Context = .javascript, inputs: CodeGenerator.Inputs, _ f: @escaping GeneratorFunc2Args) -> CodeGenerator { - assert(inputs.count == 2) - return CodeGenerator(name: name, isValueGenerator: false, isRecursive: true, inputs: inputs, context: context, adapter: GeneratorAdapter2Args(f: f)) -} + public init(_ name: String, _ generators: [GeneratorStub]) { + self.parts = generators + self.name = name -// Constructors for ValueGenerators. A ValueGenerator is a CodeGenerator that produces one or more variables containing newly created values/objects. -// Further, a ValueGenerator must be able to run when there are no existing variables so that it can be used to bootstrap code generation. -public func ValueGenerator(_ name: String, _ f: @escaping ValueGeneratorFunc) -> CodeGenerator { - return CodeGenerator(name: name, isValueGenerator: true, isRecursive: false, inputs: .none, context: .javascript, adapter: ValueGeneratorAdapter(f: f)) -} + // Calculate all contexts provided at any time by this CodeGenerator. + var ctxSet = Set() + generators.forEach { gen in + gen.providedContext.forEach { ctx in + ctxSet.insert(ctx) + } + } -public func ValueGenerator(_ name: String, inContext context: Context, _ f: @escaping ValueGeneratorFunc) -> CodeGenerator { - return CodeGenerator(name: name, isValueGenerator: true, isRecursive: false, inputs: .none, context: context, adapter: ValueGeneratorAdapter(f: f)) -} + self.providedContexts = Array(ctxSet) + } -public func RecursiveValueGenerator(_ name: String, _ f: @escaping ValueGeneratorFunc) -> CodeGenerator { - return CodeGenerator(name: name, isValueGenerator: true, isRecursive: true, inputs: .none, context: .javascript, adapter: ValueGeneratorAdapter(f: f)) + // This essentially means that all stubs have no requirements. + // Usually there is only a single element in the CodeGenerator if it is a ValueGenerator. + public var isValueGenerator: Bool { + return self.parts.allSatisfy {$0.isValueStub} + } + + // This is the context required by the first part of the CodeGenerator. + public var requiredContext: Context { + // This has to be a single one for the first Generator. + // Current limitation of the Generation Logic. + assert(self.parts.first!.requiredContext.isSingle) + return self.parts.first!.requiredContext.getSingle()! + } + + // TODO(cffsmith): Maybe return an array of ILType Arrays, essentially describing at which yield point which type is available? + // This would allow us to maybe use "innerOutputs" to find suitable points to insert other Generators (that require such types). + // Slight complication is that some variables will only be in scope inside the CodeGenerator, e.g. if there is an EndWasmModule somewhere all Wasm variables will go out of scope. + public var produces: [ILType] { + return self.parts.last!.produces + } + + public var head: GeneratorStub { + return self.parts.first! + } + + // The tail of this CodeGenerator. + public var tail: Array.SubSequence { + return self.parts[1...] + } + + public var expandedName : String { + if self.name == "Synthetic" { + return "Synthetic(\(self.parts.map{$0.name}.joined(separator: ",")))" + } else { + return self.name + } + } + + public convenience init(_ name: String, inContext context: GeneratorStub.ContextRequirement = .single(.javascript), produces: [ILType] = [], provides: [Context] = [], _ f: @escaping GeneratorFuncNoArgs) { + self.init(name, [GeneratorStub(name: name, inputs: .none, produces: produces, context: context, providedContext: provides, adapter: GeneratorAdapterNoArgs(f: f))]) + } + + public convenience init(_ name: String, inContext context: GeneratorStub.ContextRequirement = .single(.javascript), inputs: GeneratorStub.Inputs, produces: [ILType] = [], provides: [Context] = [], _ f: @escaping GeneratorFunc1Arg) { + assert(inputs.count == 1) + self.init(name, [GeneratorStub(name: name, inputs: inputs, produces: produces, context: context, providedContext: provides, adapter: GeneratorAdapter1Arg(f: f))]) + } + + public convenience init(_ name: String, inContext context: GeneratorStub.ContextRequirement = .single(.javascript), inputs: GeneratorStub.Inputs, produces: [ILType] = [], provides: [Context] = [], _ f: @escaping GeneratorFunc2Args) { + assert(inputs.count == 2) + self.init(name, [GeneratorStub(name: name, inputs: inputs, produces: produces, context: context, providedContext: provides, adapter: GeneratorAdapter2Args(f: f))]) + } + + public convenience init(_ name: String, inContext context: GeneratorStub.ContextRequirement = .single(.javascript), inputs: GeneratorStub.Inputs, produces: [ILType] = [], provides: [Context] = [], _ f: @escaping GeneratorFunc3Args) { + assert(inputs.count == 3) + self.init(name, [GeneratorStub(name: name, inputs: inputs, produces: produces, context: context, providedContext: provides, adapter: GeneratorAdapter3Args(f: f))]) + } + + public convenience init(_ name: String, inContext context: GeneratorStub.ContextRequirement = .single(.javascript), inputs: GeneratorStub.Inputs, produces: [ILType] = [], provides: [Context] = [], _ f: @escaping GeneratorFunc4Args) { + assert(inputs.count == 4) + self.init(name, [GeneratorStub(name: name, inputs: inputs, produces: produces, context: context, providedContext: provides, adapter: GeneratorAdapter4Args(f: f))]) + } } + +extension CodeGenerator: CustomStringConvertible { + public var description: String { + let names = self.parts.map { $0.name } + return names.joined(separator: ",") + } +} \ No newline at end of file diff --git a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift index 3db5d39a9..37698cba2 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift @@ -63,10 +63,10 @@ public let codeGeneratorWeights = [ "ObjectLiteralComputedMethodGenerator": 3, "ObjectLiteralGetterGenerator": 3, "ObjectLiteralSetterGenerator": 3, - - // The following generators determine how frequently different - // types of fields are generated in class definitions. - "ClassConstructorGenerator": 10, // Will only run if no constructor exists yet +// +// // The following generators determine how frequently different +// // types of fields are generated in class definitions. + "ClassConstructorGenerator": 10, "ClassInstancePropertyGenerator": 5, "ClassInstanceElementGenerator": 5, "ClassInstanceComputedPropertyGenerator": 5, @@ -176,7 +176,9 @@ public let codeGeneratorWeights = [ "SwitchCaseBreakGenerator": 5, "LoopBreakGenerator": 5, "ContinueGenerator": 5, + "TryCatchFinallyGenerator": 5, "TryCatchGenerator": 5, + "TryFinallyGenerator": 5, "ThrowGenerator": 1, "BlockStatementGenerator": 1, @@ -219,7 +221,6 @@ public let codeGeneratorWeights = [ // This weight is important as we need to have a module for the other generators to work. // As they all require .wasm context. "WasmModuleGenerator": 35, - "WasmTypeAndModuleGenerator": 35, "WasmDefineMemoryGenerator": 8, "WasmDefineDataSegmentGenerator": 8, "WasmDropDataSegmentGenerator": 5, @@ -338,7 +339,7 @@ public let codeGeneratorWeights = [ // Wasm-gc type generators // These run in the javascript context and define types to be used within wasm modules. - "WasmRecursiveTypeGroupGenerator": 5, + "WasmTypeGroupGenerator": 5, "WasmArrayTypeGenerator": 5, "WasmStructTypeGenerator": 5, "WasmSelfReferenceGenerator": 5, diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 803029901..93b016d55 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -19,7 +19,7 @@ // public let CodeGenerators: [CodeGenerator] = [ // - // Value Generators: Code Generators that generate one or more new values. + // Value Generators: Code Generators that generate one or more new values, i.e. they have a `produces` annotation. // // These behave like any other CodeGenerator in that they will be randomly chosen to generate code // and have a weight assigned to them to determine how frequently they are selected, but in addition @@ -33,73 +33,65 @@ public let CodeGenerators: [CodeGenerator] = [ // - Should generate |n| different values of the same type, but may generate fewer. // - May be recursive, for example to fill bodies of newly created blocks. // - ValueGenerator("IntegerGenerator") { b, n in - for _ in 0..= maxProperties) - let properties = Array(b.fuzzer.environment.customProperties.shuffled().prefix(Int.random(in: 1...maxProperties))) + let properties = Array( + b.fuzzer.environment.customProperties.shuffled().prefix( + Int.random(in: 1...maxProperties))) // Define a constructor function... let c = b.buildConstructor(with: b.randomParameters()) { args in @@ -298,21 +297,25 @@ public let CodeGenerators: [CodeGenerator] = [ // Add a few random properties to the |this| object. for property in properties { - let value = b.hasVisibleVariables ? b.randomJsVariable() : b.loadInt(b.randomInt()) + let value = + b.hasVisibleVariables + ? b.randomJsVariable() : b.loadInt(b.randomInt()) b.setProperty(property, of: this, to: value) } } assert(b.type(of: c).signature != nil) - assert(b.type(of: c).signature!.outputType.Is(.object(withProperties: properties))) + assert( + b.type(of: c).signature!.outputType.Is( + .object(withProperties: properties))) // and create a few instances with it. - for _ in 0..= 10 { + propertyName = String.random(ofLength: Int.random(in: 1...5)) + break + } propertyName = b.randomCustomPropertyName() attempts += 1 } while b.currentObjectLiteral.properties.contains(propertyName) - b.currentObjectLiteral.addProperty(propertyName, as: b.randomJsVariable()) + b.currentObjectLiteral.addProperty( + propertyName, as: b.randomJsVariable()) }, - CodeGenerator("ObjectLiteralElementGenerator", inContext: .objectLiteral, inputs: .one) { b, value in - assert(b.context.contains(.objectLiteral) && !b.context.contains(.javascript)) + CodeGenerator( + "ObjectLiteralElementGenerator", inContext: .single(.objectLiteral), inputs: .one + ) { b, value in + assert( + b.context.contains(.objectLiteral) + && !b.context.contains(.javascript)) // Select an element that hasn't already been added to this literal. var index = b.randomIndex() @@ -464,14 +504,23 @@ public let CodeGenerators: [CodeGenerator] = [ b.currentObjectLiteral.addElement(index, as: value) }, - CodeGenerator("ObjectLiteralComputedPropertyGenerator", inContext: .objectLiteral, inputs: .one) { b, value in - assert(b.context.contains(.objectLiteral) && !b.context.contains(.javascript)) + CodeGenerator( + "ObjectLiteralComputedPropertyGenerator", inContext: .single(.objectLiteral), + inputs: .one + ) { b, value in + assert( + b.context.contains(.objectLiteral) + && !b.context.contains(.javascript)) // Try to find a computed property that hasn't already been added to this literal. var propertyName: Variable var attempts = 0 repeat { - guard attempts < 10 else { return } + if attempts >= 10 { + // Could not find anything. + // Since this CodeGenerator does not produce anything it is fine to bail. + return + } propertyName = b.randomJsVariable() attempts += 1 } while b.currentObjectLiteral.computedProperties.contains(propertyName) @@ -479,13 +528,21 @@ public let CodeGenerators: [CodeGenerator] = [ b.currentObjectLiteral.addComputedProperty(propertyName, as: value) }, - CodeGenerator("ObjectLiteralCopyPropertiesGenerator", inContext: .objectLiteral, inputs: .preferred(.object())) { b, object in - assert(b.context.contains(.objectLiteral) && !b.context.contains(.javascript)) + CodeGenerator( + "ObjectLiteralCopyPropertiesGenerator", inContext: .single(.objectLiteral), + inputs: .preferred(.object()) + ) { b, object in + assert( + b.context.contains(.objectLiteral) + && !b.context.contains(.javascript)) b.currentObjectLiteral.copyProperties(from: object) }, - CodeGenerator("ObjectLiteralPrototypeGenerator", inContext: .objectLiteral) { b in - assert(b.context.contains(.objectLiteral) && !b.context.contains(.javascript)) + CodeGenerator("ObjectLiteralPrototypeGenerator", inContext: .single(.objectLiteral)) + { b in + assert( + b.context.contains(.objectLiteral) + && !b.context.contains(.javascript)) // There should only be one __proto__ field in an object literal. guard !b.currentObjectLiteral.hasPrototype else { return } @@ -494,101 +551,221 @@ public let CodeGenerators: [CodeGenerator] = [ b.currentObjectLiteral.setPrototype(to: proto) }, - RecursiveCodeGenerator("ObjectLiteralMethodGenerator", inContext: .objectLiteral) { b in - assert(b.context.contains(.objectLiteral) && !b.context.contains(.javascript)) - - // Try to find a method that hasn't already been added to this literal. - var methodName: String - var attempts = 0 - repeat { - guard attempts < 10 else { return } - methodName = b.randomCustomMethodName() - attempts += 1 - } while b.currentObjectLiteral.methods.contains(methodName) - - b.currentObjectLiteral.addMethod(methodName, with: b.randomParameters()) { args in - b.buildRecursive() - b.doReturn(b.randomJsVariable()) - } - }, - - RecursiveCodeGenerator("ObjectLiteralComputedMethodGenerator", inContext: .objectLiteral) { b in - assert(b.context.contains(.objectLiteral) && !b.context.contains(.javascript)) - - // Try to find a computed method name that hasn't already been added to this literal. - var methodName: Variable - var attempts = 0 - repeat { - guard attempts < 10 else { return } - methodName = b.randomJsVariable() - attempts += 1 - } while b.currentObjectLiteral.computedMethods.contains(methodName) - - b.currentObjectLiteral.addComputedMethod(methodName, with: b.randomParameters()) { args in - b.buildRecursive() - b.doReturn(b.randomJsVariable()) - } - }, - - RecursiveCodeGenerator("ObjectLiteralGetterGenerator", inContext: .objectLiteral) { b in - assert(b.context.contains(.objectLiteral) && !b.context.contains(.javascript)) - - // Try to find a property that hasn't already been added and for which a getter has not yet been installed. - var propertyName: String - var attempts = 0 - repeat { - guard attempts < 10 else { return } - propertyName = b.randomCustomPropertyName() - attempts += 1 - } while b.currentObjectLiteral.properties.contains(propertyName) || b.currentObjectLiteral.getters.contains(propertyName) - - b.currentObjectLiteral.addGetter(for: propertyName) { this in - b.buildRecursive() - b.doReturn(b.randomJsVariable()) - } - }, - - RecursiveCodeGenerator("ObjectLiteralSetterGenerator", inContext: .objectLiteral) { b in - assert(b.context.contains(.objectLiteral) && !b.context.contains(.javascript)) - - // Try to find a property that hasn't already been added and for which a setter has not yet been installed. - var propertyName: String - var attempts = 0 - repeat { - guard attempts < 10 else { return } - propertyName = b.randomCustomPropertyName() - attempts += 1 - } while b.currentObjectLiteral.properties.contains(propertyName) || b.currentObjectLiteral.setters.contains(propertyName) - - b.currentObjectLiteral.addSetter(for: propertyName) { this, v in - b.buildRecursive() - } - }, - - RecursiveCodeGenerator("ClassConstructorGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) - - guard !b.currentClassDefinition.hasConstructor else { - // There must only be one constructor - return - } + CodeGenerator( + "ObjectLiteralMethodGenerator", + [ + GeneratorStub( + "ObjectLiteralMethodBeginGenerator", inContext: .single(.objectLiteral), + provides: [.javascript, .subroutine, .method] + ) { b in + assert( + b.context.contains(.objectLiteral) + && !b.context.contains(.javascript)) + + // Try to find a method that hasn't already been added to this literal. + var methodName: String + var attempts = 0 + repeat { + if attempts >= 10 { + methodName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + methodName = b.randomCustomMethodName() + attempts += 1 + } while b.currentObjectLiteral.methods.contains(methodName) + + let randomParameters = b.randomParameters() + b.setParameterTypesForNextSubroutine( + randomParameters.parameterTypes) + b.emit( + BeginObjectLiteralMethod( + methodName: methodName, + parameters: randomParameters.parameters)) + }, + GeneratorStub("ObjectLiteralMethodEndGenerator", inContext: .single([.javascript, .subroutine, .method])) { b in + b.emit(EndObjectLiteralMethod()) + }, + ]), + + CodeGenerator( + "ObjectLiteralComputedMethodGenerator", + [ + GeneratorStub( + "ObjectLiteralComputedMethodBeginGenerator", + inContext: .single(.objectLiteral), + provides: [.javascript, .subroutine, .method] + ) { b in + assert( + b.context.contains(.objectLiteral) + && !b.context.contains(.javascript)) + + // Try to find a computed method name that hasn't already been added to this literal. + var methodName: Variable + var attempts = 0 + repeat { + if attempts >= 10 { + methodName = b.loadString( + String.random(ofLength: Int.random(in: 1...5))) + break + } + methodName = b.randomJsVariable() + attempts += 1 + } while b.currentObjectLiteral.computedMethods.contains( + methodName) + let parameters = b.randomParameters() + b.setParameterTypesForNextSubroutine(parameters.parameterTypes) + b.emit( + BeginObjectLiteralComputedMethod( + parameters: parameters.parameters), + withInputs: [methodName]) + }, + GeneratorStub( + "ObjectLiteralComputedMethodEndGenerator", + inContext: .single([.javascript, .subroutine, .method]), + inputs: .one + ) { b, inp in + b.doReturn(inp) + b.emit(EndObjectLiteralComputedMethod()) + + }, + ]), + + CodeGenerator( + "ObjectLiteralGetterGenerator", + [ + GeneratorStub( + "ObjectLiteralGetterBeginGenerator", + inContext: .single(.objectLiteral), + provides: [.javascript, .subroutine, .method] + ) { b in + assert( + b.context.contains(.objectLiteral) + && !b.context.contains(.javascript)) + + // Try to find a property that hasn't already been added and for which a getter has not yet been installed. + var propertyName: String + var attempts = 0 + repeat { + if attempts >= 10 { + // We should not fail here but also don't produce a syntax error. + propertyName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + propertyName = b.randomCustomPropertyName() + attempts += 1 + } while b.currentObjectLiteral.properties.contains(propertyName) + || b.currentObjectLiteral.getters.contains(propertyName) + + b.emit(BeginObjectLiteralGetter(propertyName: propertyName)) + + }, + GeneratorStub( + "ObjectLiteralGetterEndGenerator", + inContext: .single([.javascript, .subroutine, .method]), + inputs: .one + ) { b, inp in + b.doReturn(inp) + b.emit(EndObjectLiteralGetter()) + }, + ]), + + CodeGenerator( + "ObjectLiteralSetterGenerator", + [ + GeneratorStub( + "ObjectLiteralSetterBeginGenerator", + inContext: .single(.objectLiteral), + provides: [.javascript, .subroutine, .method] + ) { b in + assert( + b.context.contains(.objectLiteral) + && !b.context.contains(.javascript)) + + // Try to find a property that hasn't already been added and for which a setter has not yet been installed. + var propertyName: String + var attempts = 0 + repeat { + if attempts >= 10 { + // We should not fail here but also don't produce a syntax error. + propertyName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + propertyName = b.randomCustomPropertyName() + attempts += 1 + } while b.currentObjectLiteral.properties.contains(propertyName) + || b.currentObjectLiteral.setters.contains(propertyName) + + b.emit(BeginObjectLiteralSetter(propertyName: propertyName)) + }, + GeneratorStub( + "ObjectLiteralSetterEndGenerator", + inContext: .single([.javascript, .subroutine, .method]) + ) { b in + b.emit(EndObjectLiteralSetter()) + }, + ]), + + CodeGenerator( + "ClassConstructorGenerator", + [ + GeneratorStub( + "ClassConstructorBeginGenerator", + inContext: .single(.classDefinition), + provides: [] + ) { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) + + guard !b.currentClassDefinition.hasConstructor else { + // There must only be one constructor + // If we bail here, we are *not* in .javaScript context so we cannot provide it here for other chains. + return + } - b.currentClassDefinition.addConstructor(with: b.randomParameters()) { args in - let this = args[0] - // Derived classes must call `super()` before accessing this, but non-derived classes must not call `super()`. - if b.currentClassDefinition.isDerivedClass { - b.hide(this) // We need to hide |this| so it isn't used as argument for `super()` - let signature = b.currentSuperConstructorType().signature ?? Signature.forUnknownFunction - let args = b.randomArguments(forCallingFunctionWithSignature: signature) - b.callSuperConstructor(withArgs: args) - b.unhide(this) - } - b.buildRecursive() - } - }, + let randomParameters = b.randomParameters() + + b.setParameterTypesForNextSubroutine( + randomParameters.parameterTypes) + + let args = b.emit( + BeginClassConstructor( + parameters: randomParameters.parameters) + ).innerOutputs + + let this = args[0] + // Derived classes must call `super()` before accessing this, but non-derived classes must not call `super()`. + if b.currentClassDefinition.isDerivedClass { + b.hide(this) // We need to hide |this| so it isn't used as argument for `super()` + let signature = + b.currentSuperConstructorType().signature + ?? Signature.forUnknownFunction + let args = b.randomArguments( + forCallingFunctionWithSignature: signature) + b.callSuperConstructor(withArgs: args) + b.unhide(this) + } + }, + GeneratorStub( + "ClassConstructorEndGenerator", + // This can run in either context and will do different things + // depending on if the previous generator succeeded. + inContext: .either([.javascript, .classDefinition]) + ) { b in + if b.context.contains(.javascript) { + b.emit(EndClassConstructor()) + } + }, + ]), - CodeGenerator("ClassInstancePropertyGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) + CodeGenerator("ClassInstancePropertyGenerator", inContext: .single(.classDefinition)) + { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added to this literal. var propertyName: String @@ -597,14 +774,18 @@ public let CodeGenerators: [CodeGenerator] = [ guard attempts < 10 else { return } propertyName = b.randomCustomPropertyName() attempts += 1 - } while b.currentClassDefinition.instanceProperties.contains(propertyName) + } while b.currentClassDefinition.instanceProperties.contains( + propertyName) var value: Variable? = probability(0.5) ? b.randomJsVariable() : nil b.currentClassDefinition.addInstanceProperty(propertyName, value: value) }, - CodeGenerator("ClassInstanceElementGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) + CodeGenerator("ClassInstanceElementGenerator", inContext: .single(.classDefinition)) + { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) // Select an element that hasn't already been added to this literal. var index = b.randomIndex() @@ -617,8 +798,12 @@ public let CodeGenerators: [CodeGenerator] = [ b.currentClassDefinition.addInstanceElement(index, value: value) }, - CodeGenerator("ClassInstanceComputedPropertyGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) + CodeGenerator( + "ClassInstanceComputedPropertyGenerator", inContext: .single(.classDefinition) + ) { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) // Try to find a computed property that hasn't already been added to this literal. var propertyName: Variable @@ -627,73 +812,149 @@ public let CodeGenerators: [CodeGenerator] = [ guard attempts < 10 else { return } propertyName = b.randomJsVariable() attempts += 1 - } while b.currentClassDefinition.instanceComputedProperties.contains(propertyName) + } while b.currentClassDefinition.instanceComputedProperties.contains( + propertyName) let value = probability(0.5) ? b.randomJsVariable() : nil - b.currentClassDefinition.addInstanceComputedProperty(propertyName, value: value) - }, - - RecursiveCodeGenerator("ClassInstanceMethodGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) - - // Try to find a method that hasn't already been added to this class. - var methodName: String - var attempts = 0 - repeat { - guard attempts < 10 else { return } - methodName = b.randomCustomMethodName() - attempts += 1 - } while b.currentClassDefinition.instanceMethods.contains(methodName) - - b.currentClassDefinition.addInstanceMethod(methodName, with: b.randomParameters()) { args in - b.buildRecursive() - b.doReturn(b.randomJsVariable()) - } - }, - - RecursiveCodeGenerator("ClassInstanceGetterGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) - - // Try to find a property that hasn't already been added and for which a getter has not yet been installed. - var propertyName: String - var attempts = 0 - repeat { - guard attempts < 10 else { return } - propertyName = b.randomCustomPropertyName() - attempts += 1 - } while b.currentClassDefinition.instanceProperties.contains(propertyName) || b.currentClassDefinition.instanceGetters.contains(propertyName) - - b.currentClassDefinition.addInstanceGetter(for: propertyName) { this in - b.buildRecursive() - b.doReturn(b.randomJsVariable()) - } - }, - - RecursiveCodeGenerator("ClassInstanceSetterGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) - - // Try to find a property that hasn't already been added and for which a setter has not yet been installed. - var propertyName: String - var attempts = 0 - repeat { - guard attempts < 10 else { return } - propertyName = b.randomCustomPropertyName() - attempts += 1 - } while b.currentClassDefinition.instanceProperties.contains(propertyName) || b.currentClassDefinition.instanceSetters.contains(propertyName) - - b.currentClassDefinition.addInstanceSetter(for: propertyName) { this, v in - b.buildRecursive() - } - }, - - CodeGenerator("ClassStaticPropertyGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) + b.currentClassDefinition.addInstanceComputedProperty( + propertyName, value: value) + }, + + CodeGenerator( + "ClassInstanceMethodGenerator", + [ + GeneratorStub( + "ClassInstanceMethodBeginGenerator", + inContext: .single(.classDefinition), + provides: [.javascript, .subroutine, .method, .classMethod] + ) { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) + + // Try to find a method that hasn't already been added to this class. + var methodName: String + var attempts = 0 + repeat { + if attempts >= 10 { + methodName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + methodName = b.randomCustomMethodName() + attempts += 1 + } while b.currentClassDefinition.instanceMethods.contains( + methodName) + + let parameters = b.randomParameters() + b.setParameterTypesForNextSubroutine(parameters.parameterTypes) + b.emit( + BeginClassInstanceMethod( + methodName: methodName, + parameters: parameters.parameters)) + }, + GeneratorStub( + "ClassInstanceMethodEndGenerator", + inContext: .single([.javascript, .subroutine, .method, .classMethod]) + ) { b in + b.doReturn(b.randomJsVariable()) + b.emit(EndClassInstanceMethod()) + }, + + ]), + + CodeGenerator( + "ClassInstanceGetterGenerator", + [ + GeneratorStub( + "ClassInstanceGetterBeginGenerator", + inContext: .single(.classDefinition), + provides: [.javascript, .subroutine, .method, .classMethod] + ) { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) + + // Try to find a property that hasn't already been added and for which a getter has not yet been installed. + var propertyName: String + var attempts = 0 + repeat { + if attempts >= 10 { + propertyName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + propertyName = b.randomCustomPropertyName() + attempts += 1 + } while b.currentClassDefinition.instanceProperties.contains( + propertyName) + || b.currentClassDefinition.instanceGetters.contains( + propertyName) + + b.emit(BeginClassInstanceGetter(propertyName: propertyName)) + }, + GeneratorStub( + "ClassInstanceGetterEndGenerator", inContext: .single([.javascript, .subroutine, .method, .classMethod]) + ) { b in + b.doReturn(b.randomJsVariable()) + b.emit(EndClassInstanceGetter()) + }, + ]), + + CodeGenerator( + "ClassInstanceSetterGenerator", + [ + GeneratorStub( + "ClassInstanceSetterBeginGenerator", + inContext: .single(.classDefinition), + provides: [.javascript, .subroutine, .method, .classMethod] + ) { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) + + // Try to find a property that hasn't already been added and for which a setter has not yet been installed. + var propertyName: String + var attempts = 0 + repeat { + if attempts >= 10 { + propertyName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + propertyName = b.randomCustomPropertyName() + attempts += 1 + } while b.currentClassDefinition.instanceProperties.contains( + propertyName) + || b.currentClassDefinition.instanceSetters.contains( + propertyName) + + b.emit(BeginClassInstanceSetter(propertyName: propertyName)) + }, + GeneratorStub( + "ClassInstanceSetterEndGenerator", + inContext: .single([.javascript, .method, .subroutine, .classMethod]) + ) { b in + b.emit(EndClassInstanceSetter()) + }, + + ]), + + CodeGenerator("ClassStaticPropertyGenerator", inContext: .single(.classDefinition)) { + b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added to this literal. var propertyName: String var attempts = 0 repeat { - guard attempts < 10 else { return } + if attempts >= 10 { + propertyName = String.random( + ofLength: Int.random(in: 1...5)) + break + } propertyName = b.randomCustomPropertyName() attempts += 1 } while b.currentClassDefinition.staticProperties.contains(propertyName) @@ -702,8 +963,11 @@ public let CodeGenerators: [CodeGenerator] = [ b.currentClassDefinition.addStaticProperty(propertyName, value: value) }, - CodeGenerator("ClassStaticElementGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) + CodeGenerator("ClassStaticElementGenerator", inContext: .single(.classDefinition)) { + b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) // Select an element that hasn't already been added to this literal. var index = b.randomIndex() @@ -716,85 +980,180 @@ public let CodeGenerators: [CodeGenerator] = [ b.currentClassDefinition.addStaticElement(index, value: value) }, - CodeGenerator("ClassStaticComputedPropertyGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) + CodeGenerator( + "ClassStaticComputedPropertyGenerator", inContext: .single(.classDefinition) + ) { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) // Try to find a computed property that hasn't already been added to this literal. var propertyName: Variable var attempts = 0 repeat { - guard attempts < 10 else { return } + guard attempts < 10 else { + // We are in .classDefinition context here and cannot create new JavaScript variables, so just bail here. + return + } propertyName = b.randomJsVariable() attempts += 1 - } while b.currentClassDefinition.staticComputedProperties.contains(propertyName) + } while b.currentClassDefinition.staticComputedProperties.contains( + propertyName) let value = probability(0.5) ? b.randomJsVariable() : nil - b.currentClassDefinition.addStaticComputedProperty(propertyName, value: value) - }, - - RecursiveCodeGenerator("ClassStaticInitializerGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) - - b.currentClassDefinition.addStaticInitializer { this in - b.buildRecursive() - } - }, - - RecursiveCodeGenerator("ClassStaticMethodGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) - - // Try to find a method that hasn't already been added to this class. - var methodName: String - var attempts = 0 - repeat { - guard attempts < 10 else { return } - methodName = b.randomCustomMethodName() - attempts += 1 - } while b.currentClassDefinition.staticMethods.contains(methodName) - - b.currentClassDefinition.addStaticMethod(methodName, with: b.randomParameters()) { args in - b.buildRecursive() - b.doReturn(b.randomJsVariable()) - } - }, - - RecursiveCodeGenerator("ClassStaticGetterGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) - - // Try to find a property that hasn't already been added and for which a getter has not yet been installed. - var propertyName: String - var attempts = 0 - repeat { - guard attempts < 10 else { return } - propertyName = b.randomCustomPropertyName() - attempts += 1 - } while b.currentClassDefinition.staticProperties.contains(propertyName) || b.currentClassDefinition.staticGetters.contains(propertyName) - - b.currentClassDefinition.addStaticGetter(for: propertyName) { this in - b.buildRecursive() - b.doReturn(b.randomJsVariable()) - } - }, - - RecursiveCodeGenerator("ClassStaticSetterGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) - - // Try to find a property that hasn't already been added and for which a setter has not yet been installed. - var propertyName: String - var attempts = 0 - repeat { - guard attempts < 10 else { return } - propertyName = b.randomCustomPropertyName() - attempts += 1 - } while b.currentClassDefinition.staticProperties.contains(propertyName) || b.currentClassDefinition.staticSetters.contains(propertyName) - - b.currentClassDefinition.addStaticSetter(for: propertyName) { this, v in - b.buildRecursive() - } - }, - - CodeGenerator("ClassPrivateInstancePropertyGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) + b.currentClassDefinition.addStaticComputedProperty( + propertyName, value: value) + }, + + CodeGenerator( + "ClassStaticInitializerGenerator", + [ + GeneratorStub( + "ClassStaticInitializerBeginGenerator", + inContext: .single(.classDefinition), + provides: [.javascript, .method, .classMethod] + ) { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) + + b.emit(BeginClassStaticInitializer()) + }, + GeneratorStub( + "ClassStaticInitializerEndGenerator", + inContext: .single([.javascript, .method, .classMethod]) + ) { b in + b.emit(EndClassStaticInitializer()) + }, + ]), + + CodeGenerator( + "ClassStaticMethodGenerator", + [ + GeneratorStub( + "ClassStaticMethodBeginGenerator", + inContext: .single(.classDefinition), + provides: [.javascript, .method, .subroutine, .classMethod] + ) { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) + + // Try to find a method that hasn't already been added to this class. + var methodName: String + var attempts = 0 + repeat { + if attempts >= 10 { + methodName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + methodName = b.randomCustomMethodName() + attempts += 1 + } while b.currentClassDefinition.staticMethods.contains( + methodName) + + let parameters = b.randomParameters() + + b.setParameterTypesForNextSubroutine(parameters.parameterTypes) + b.emit( + BeginClassStaticMethod( + methodName: methodName, + parameters: parameters.parameters)) + + }, + GeneratorStub( + "ClassStaticMethodEndGenerator", + inContext: .single([.javascript, .classMethod, .subroutine, .method]) + ) { b in + b.doReturn(b.randomJsVariable()) + b.emit(EndClassStaticMethod()) + }, + ]), + + CodeGenerator( + "ClassStaticGetterGenerator", + [ + GeneratorStub( + "ClassStaticGetterBeginGenerator", + inContext: .single(.classDefinition), + provides: [.javascript, .subroutine, .method, .classMethod] + ) { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) + + // Try to find a property that hasn't already been added and for which a getter has not yet been installed. + var propertyName: String + var attempts = 0 + repeat { + if attempts >= 10 { + propertyName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + propertyName = b.randomCustomPropertyName() + attempts += 1 + } while b.currentClassDefinition.staticProperties.contains( + propertyName) + || b.currentClassDefinition.staticGetters.contains( + propertyName) + + b.emit(BeginClassStaticGetter(propertyName: propertyName)) + }, + GeneratorStub( + "ClassStaticGetterEndGenerator", + inContext: .single([.javascript, .subroutine, .method, .classMethod]) + ) { b in + b.doReturn(b.randomJsVariable()) + b.emit(EndClassStaticGetter()) + }, + ]), + + CodeGenerator( + "ClassStaticSetterGenerator", + [ + GeneratorStub( + "ClassStaticSetterBeginGenerator", + inContext: .single(.classDefinition), + provides: [.javascript, .subroutine, .method, .classMethod] + ) { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) + + // Try to find a property that hasn't already been added and for which a setter has not yet been installed. + var propertyName: String + var attempts = 0 + repeat { + if attempts >= 10 { + propertyName = String.random(ofLength: Int.random(in: 1...5)) + break + } + propertyName = b.randomCustomPropertyName() + attempts += 1 + } while b.currentClassDefinition.staticProperties.contains( + propertyName) + || b.currentClassDefinition.staticSetters.contains( + propertyName) + + b.emit(BeginClassStaticSetter(propertyName: propertyName)) + + }, + GeneratorStub( + "ClassStaticSetterEndGenerator", + inContext: .single([.javascript, .subroutine, .method, .classMethod]) + ) { b in + b.emit(EndClassStaticSetter()) + }, + ]), + + CodeGenerator( + "ClassPrivateInstancePropertyGenerator", inContext: .single(.classDefinition) + ) { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) // Try to find a private field that hasn't already been added to this literal. var propertyName: String @@ -806,29 +1165,56 @@ public let CodeGenerators: [CodeGenerator] = [ } while b.currentClassDefinition.privateFields.contains(propertyName) var value = probability(0.5) ? b.randomJsVariable() : nil - b.currentClassDefinition.addPrivateInstanceProperty(propertyName, value: value) - }, - - RecursiveCodeGenerator("ClassPrivateInstanceMethodGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) - - // Try to find a private field that hasn't already been added to this class. - var methodName: String - var attempts = 0 - repeat { - guard attempts < 10 else { return } - methodName = b.randomCustomMethodName() - attempts += 1 - } while b.currentClassDefinition.privateFields.contains(methodName) - - b.currentClassDefinition.addPrivateInstanceMethod(methodName, with: b.randomParameters()) { args in - b.buildRecursive() - b.doReturn(b.randomJsVariable()) - } - }, + b.currentClassDefinition.addPrivateInstanceProperty( + propertyName, value: value) + }, + + CodeGenerator( + "ClassPrivateInstanceMethodGenerator", + [ + GeneratorStub( + "ClassPrivateInstanceMethodBeginGenerator", + inContext: .single(.classDefinition), + provides: [.javascript, .subroutine, .method, .classMethod] + ) { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) + + // Try to find a private field that hasn't already been added to this class. + var methodName: String + var attempts = 0 + repeat { + if attempts >= 10 { + methodName = String.random(ofLength: Int.random(in: 1...5)) + break + } + methodName = b.randomCustomMethodName() + attempts += 1 + } while b.currentClassDefinition.privateFields.contains( + methodName) + + let parameters = b.randomParameters() + b.emit( + BeginClassPrivateInstanceMethod( + methodName: methodName, + parameters: parameters.parameters)) + }, + GeneratorStub( + "ClassPrivateInstanceMethodEndGenerator", + inContext: .single([.javascript, .subroutine, .method, .classMethod]) + ) { b in + b.doReturn(b.randomJsVariable()) + b.emit(EndClassPrivateInstanceMethod()) + }, + ]), - CodeGenerator("ClassPrivateStaticPropertyGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) + CodeGenerator( + "ClassPrivateStaticPropertyGenerator", inContext: .single(.classDefinition) + ) { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) // Try to find a private field that hasn't already been added to this literal. var propertyName: String @@ -840,28 +1226,58 @@ public let CodeGenerators: [CodeGenerator] = [ } while b.currentClassDefinition.privateFields.contains(propertyName) var value = probability(0.5) ? b.randomJsVariable() : nil - b.currentClassDefinition.addPrivateStaticProperty(propertyName, value: value) - }, - - RecursiveCodeGenerator("ClassPrivateStaticMethodGenerator", inContext: .classDefinition) { b in - assert(b.context.contains(.classDefinition) && !b.context.contains(.javascript)) - - // Try to find a private field that hasn't already been added to this class. - var methodName: String - var attempts = 0 - repeat { - guard attempts < 10 else { return } - methodName = b.randomCustomMethodName() - attempts += 1 - } while b.currentClassDefinition.privateFields.contains(methodName) + b.currentClassDefinition.addPrivateStaticProperty( + propertyName, value: value) + }, + + CodeGenerator( + "ClassPrivateStaticMethodGenerator", + [ + GeneratorStub( + "ClassPrivateStaticMethodBeginGenerator", + inContext: .single(.classDefinition), + provides: [.javascript, .subroutine, .method, .classMethod] + ) { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) + + // Try to find a private field that hasn't already been added to this class. + var methodName: String + var attempts = 0 + repeat { + if attempts >= 10 { + methodName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + methodName = b.randomCustomMethodName() + attempts += 1 + } while b.currentClassDefinition.privateFields.contains( + methodName) + + let parameters = b.randomParameters() + b.emit( + BeginClassPrivateStaticMethod( + methodName: methodName, + parameters: parameters.parameters)) + }, + GeneratorStub( + "ClassPrivateStaticMethodEndGenerator", + inContext: .single([.javascript, .subroutine, .method, .classMethod]) + ) { b in + b.doReturn(b.randomJsVariable()) + b.emit(EndClassPrivateStaticMethod()) + }, - b.currentClassDefinition.addPrivateStaticMethod(methodName, with: b.randomParameters()) { args in - b.buildRecursive() - b.doReturn(b.randomJsVariable()) - } - }, + ]), - CodeGenerator("ArrayWithSpreadGenerator") { b in + // Setting the input to one, makes this *not* a value generator, as we use b.randomJsVariable inside. + CodeGenerator( + "ArrayWithSpreadGenerator", + inputs: .one, + produces: [.jsArray] + ) { b, _ in var initialValues = [Variable]() for _ in 0..() for _ in 0..() for _ in 0..= 4) propertyNames.shuffle() @@ -1995,18 +3003,20 @@ public let CodeGenerators: [CodeGenerator] = [ } }, - CodeGenerator("IteratorGenerator") { b in + CodeGenerator("IteratorGenerator", produces: [.iterable]) { b in let Symbol = b.createNamedVariable(forBuiltin: "Symbol") b.hide(Symbol) let iteratorSymbol = b.getProperty("iterator", of: Symbol) b.hide(iteratorSymbol) let iterableObject = b.buildObjectLiteral { obj in - obj.addComputedMethod(iteratorSymbol, with: .parameters(n: 0)) { _ in + obj.addComputedMethod(iteratorSymbol, with: .parameters(n: 0)) { + _ in let counter = b.loadInt(10) let iterator = b.buildObjectLiteral { obj in obj.addMethod("next", with: .parameters(n: 0)) { _ in b.unary(.PostDec, counter) - let done = b.compare(counter, with: b.loadInt(0), using: .equal) + let done = b.compare( + counter, with: b.loadInt(0), using: .equal) let result = b.buildObjectLiteral { obj in obj.addProperty("done", as: done) obj.addProperty("value", as: counter) @@ -2022,45 +3032,53 @@ public let CodeGenerators: [CodeGenerator] = [ b.setType(ofVariable: iterableObject, to: .iterable + .object()) }, - CodeGenerator("LoadNewTargetGenerator", inContext: .subroutine) { b in + CodeGenerator("LoadNewTargetGenerator", inContext: .single(.subroutine)) { b in assert(b.context.contains(.subroutine)) b.loadNewTarget() }, // TODO: think about merging this with the regular ConstructorCallGenerator. - CodeGenerator("ApiConstructorCallGenerator", inputs: .required(.constructor())) { b, c in + CodeGenerator( + "ApiConstructorCallGenerator", inputs: .required(.constructor()) + ) { b, c in let signature = b.type(of: c).signature ?? Signature.forUnknownFunction - b.buildTryCatchFinally(tryBody: { - let args = b.findOrGenerateArguments(forSignature: signature) - b.construct(c, withArgs: args) - }, catchBody: { _ in }) + b.buildTryCatchFinally( + tryBody: { + let args = b.findOrGenerateArguments(forSignature: signature) + b.construct(c, withArgs: args) + }, catchBody: { _ in }) }, // TODO: think about merging this with the regular MethodCallGenerator. - CodeGenerator("ApiMethodCallGenerator", inputs: .required(.object())) { b, o in + CodeGenerator("ApiMethodCallGenerator", inputs: .required(.object())) { + b, o in let methodName = b.type(of: o).randomMethod() ?? b.randomMethodName() - let signature = chooseUniform(from: b.methodSignatures(of: methodName, on: o)) + let signature = chooseUniform( + from: b.methodSignatures(of: methodName, on: o)) - b.buildTryCatchFinally(tryBody: { - let args = b.findOrGenerateArguments(forSignature: signature) - b.callMethod(methodName, on: o, withArgs: args) - }, catchBody: { _ in }) + b.buildTryCatchFinally( + tryBody: { + let args = b.findOrGenerateArguments(forSignature: signature) + b.callMethod(methodName, on: o, withArgs: args) + }, catchBody: { _ in }) }, - CodeGenerator("ApiFunctionCallGenerator", inputs: .required(.function())) { b, f in + CodeGenerator("ApiFunctionCallGenerator", inputs: .required(.function())) { + b, f in let signature = b.type(of: f).signature ?? Signature.forUnknownFunction - b.buildTryCatchFinally(tryBody: { - let args = b.findOrGenerateArguments(forSignature: signature) - b.callFunction(f, withArgs: args) - }, catchBody: { _ in }) + b.buildTryCatchFinally( + tryBody: { + let args = b.findOrGenerateArguments(forSignature: signature) + b.callFunction(f, withArgs: args) + }, catchBody: { _ in }) }, ] -extension Array where Element == CodeGenerator { - public func get(_ name: String) -> CodeGenerator { +extension Array where Element == GeneratorStub { + public func get(_ name: String) -> GeneratorStub { for generator in self { if generator.name == name { return generator diff --git a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift index 14b2a1a2b..69f20de4d 100644 --- a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift @@ -22,11 +22,19 @@ public let WasmCodeGenerators: [CodeGenerator] = [ /// Wasm related generators in JavaScript - CodeGenerator("WasmGlobalGenerator", inContext: .javascript) { b in - b.createWasmGlobal(value: b.randomWasmGlobal(), isMutable: probability(0.5)) - }, - - CodeGenerator("WasmMemoryGenerator", inContext: .javascript) { b in + CodeGenerator( + "WasmGlobalGenerator", + inContext: .single(.javascript), + produces: [.object(ofGroup: "WasmGlobal")] + ) { b in + b.createWasmGlobal(value: b.randomWasmGlobal(forContext: .javascript), isMutable: probability(0.5)) + }, + + CodeGenerator( + "WasmMemoryGenerator", + inContext: .single(.javascript), + produces: [.object(ofGroup: "WasmMemory")] + ) { b in let minPages = Int.random(in: 0..<10) let isShared = probability(0.5) var maxPages: Int? = nil @@ -36,121 +44,128 @@ public let WasmCodeGenerators: [CodeGenerator] = [ b.createWasmMemory(minPages: minPages, maxPages: maxPages, isShared: isShared) }, - CodeGenerator("WasmTagGenerator", inContext: .javascript) { b in + CodeGenerator( + "WasmTagGenerator", + inContext: .single(.javascript), + produces: [.object(ofGroup: "WasmTag")] + ) { b in if probability(0.5) { b.createWasmJSTag() } else { b.createWasmTag(parameterTypes: b.randomTagParameters()) } }, - + // // Wasm Module Generator, this is fairly important as it creates the context necessary to run the Wasm CodeGenerators. - RecursiveCodeGenerator("WasmModuleGenerator", inContext: .javascript) { b in - let m = b.buildWasmModule { m in - b.buildRecursive() - } - - let exports = m.loadExports() - }, - - RecursiveCodeGenerator("WasmTypeAndModuleGenerator", inContext: .javascript) { b in - // TODO(cffsmith): We might want to lower this once we can CodeGen into a TypeGroup. - let numRandomTypes = Int.random(in: 0...5) - - for i in 1..<(numRandomTypes + 1) { - b.wasmDefineTypeGroup { - b.buildRecursive(block: i, of: numRandomTypes + 1) - } - } - - let m = b.buildWasmModule { m in - b.buildRecursive(block: numRandomTypes + 1, of: numRandomTypes + 1) - } - - let exports = m.loadExports() - }, - - RecursiveCodeGenerator("WasmLegacyTryCatchComplexGenerator", inContext: .javascript) { b in - let emitCatchAll = Int.random(in: 0...1) - let catchCount = Int.random(in: 0...3) - var blockIndex = 1 - let blockCount = 2 + catchCount + emitCatchAll - // Create a few tags in JS. - b.createWasmJSTag() - b.createWasmTag(parameterTypes: b.randomTagParameters()) - let m = b.buildWasmModule { m in - // Create a few tags in Wasm. - m.addTag(parameterTypes: b.randomTagParameters()) - m.addTag(parameterTypes: b.randomTagParameters()) - // Build some other wasm module stuff (tables, memories, gobals, ...) - b.buildRecursive(block: blockIndex, of: blockCount, n: 4) - blockIndex += 1 - let functionSignature = b.randomWasmSignature() - m.addWasmFunction(with: functionSignature) { function, functionLabel, args in - b.buildPrefix() - function.wasmBuildLegacyTry(with: [] => [], args: [], body: {label, _ in - b.buildRecursive(block: blockIndex, of: blockCount, n: 4) - blockIndex += 1 - for _ in 0.. [], args: []) { label, args in - b.buildRecursive() - } - }, - - RecursiveCodeGenerator("WasmBlockWithSignatureGenerator", inContext: .wasmFunction) { b in - let function = b.currentWasmModule.currentWasmFunction - // Choose a few random wasm values as arguments if available. - let args = b.randomWasmBlockArguments(upTo: 5) - let parameters = args.map {b.type(of: $0)} - let outputTypes = b.randomWasmBlockOutputTypes(upTo: 5) - function.wasmBuildBlockWithResults(with: parameters => outputTypes, args: args) { label, args in - b.buildRecursive() - return outputTypes.map(function.findOrGenerateWasmVar) - } - }, - - RecursiveCodeGenerator("WasmLoopGenerator", inContext: .wasmFunction) { b in + CodeGenerator( + "WasmBlockGenerator", + [ + GeneratorStub( + "WasmBeginBlockGenerator", + inContext: .single(.wasmFunction), + provides: [.wasmBlock] + ) { b in + b.emit(WasmBeginBlock(with: [] => [])) + }, + GeneratorStub( + "WasmEndBlockGenerator", + inContext: .single(.wasmBlock) + ) { b in + b.emit(WasmEndBlock(outputTypes: [])) + }, + ]), + + CodeGenerator( + "WasmBlockWithSignatureGenerator", + [ + GeneratorStub( + "WasmBeginBlockGenerator", + inContext: .single(.wasmFunction), + provides: [.wasmBlock] + ) { b in + let args = b.randomWasmBlockArguments(upTo: 5) + let parameters = args.map(b.type) + + let outputTypes = b.randomWasmBlockOutputTypes(upTo: 5) + let signature = parameters => outputTypes + b.emit(WasmBeginBlock(with: signature), withInputs: args) + }, + GeneratorStub( + "WasmEndBlockGenerator", + inContext: .single(.wasmBlock) + ) { b in + let signature = b.currentWasmSignature + let function = b.currentWasmFunction + let outputs = signature.outputTypes.map( + function.findOrGenerateWasmVar) + b.emit( + WasmEndBlock(outputTypes: signature.outputTypes), + withInputs: outputs) + }, + ]), + + // TODO: think about how we can turn this into a mulit-part Generator + CodeGenerator("WasmLoopGenerator", inContext: .single(.wasmFunction)) { b in let function = b.currentWasmModule.currentWasmFunction let loopCtr = function.consti32(10) function.wasmBuildLoop(with: [] => []) { label, args in - let result = function.wasmi32BinOp(loopCtr, function.consti32(1), binOpKind: .Sub) + let result = function.wasmi32BinOp( + loopCtr, function.consti32(1), binOpKind: .Sub) function.wasmReassign(variable: loopCtr, to: result) - b.buildRecursive() + b.build(n: defaultCodeGenerationAmount) // Backedge of loop, we continue if it is not equal to zero. - let isNotZero = function.wasmi32CompareOp(loopCtr, function.consti32(0), using: .Ne) - function.wasmBranchIf(isNotZero, to: label, hint: b.randomWasmBranchHint()) + let isNotZero = function.wasmi32CompareOp( + loopCtr, function.consti32(0), using: .Ne) + function.wasmBranchIf( + isNotZero, to: label, hint: b.randomWasmBranchHint()) } }, - RecursiveCodeGenerator("WasmLoopWithSignatureGenerator", inContext: .wasmFunction) { b in + CodeGenerator("WasmLoopWithSignatureGenerator", inContext: .single(.wasmFunction)) { + b in let function = b.currentWasmModule.currentWasmFunction // Count upwards here to make it slightly more different from the other loop generator. // Also, instead of using reassign, this generator uses the signature to pass and update the loop counter. let randomArgs = b.randomWasmBlockArguments(upTo: 5) - let randomArgTypes = randomArgs.map{b.type(of: $0)} + let randomArgTypes = randomArgs.map { b.type(of: $0) } let args = [function.consti32(0)] + randomArgs let parameters = args.map(b.type) let outputTypes = b.randomWasmBlockOutputTypes(upTo: 5) // Note that due to the do-while style implementation, the actual iteration count is at least 1. let iterationCount = Int32.random(in: 0...16) - function.wasmBuildLoop(with: parameters => outputTypes, args: args) { label, loopArgs in - b.buildRecursive() - let loopCtr = function.wasmi32BinOp(args[0], function.consti32(1), binOpKind: .Add) - let condition = function.wasmi32CompareOp(loopCtr, function.consti32(iterationCount), using: .Lt_s) - let backedgeArgs = [loopCtr] + randomArgTypes.map{b.randomVariable(ofType: $0)!} - function.wasmBranchIf(condition, to: label, args: backedgeArgs, hint: b.randomWasmBranchHint()) + function.wasmBuildLoop(with: parameters => outputTypes, args: args) { + label, loopArgs in + b.build(n: defaultCodeGenerationAmount) + let loopCtr = function.wasmi32BinOp( + args[0], function.consti32(1), binOpKind: .Add) + let condition = function.wasmi32CompareOp( + loopCtr, function.consti32(iterationCount), using: .Lt_s) + let backedgeArgs = + [loopCtr] + randomArgTypes.map { b.randomVariable(ofType: $0)! } + function.wasmBranchIf( + condition, to: label, args: backedgeArgs, + hint: b.randomWasmBranchHint()) return outputTypes.map(function.findOrGenerateWasmVar) } }, - RecursiveCodeGenerator("WasmLegacyTryCatchGenerator", inContext: .wasmFunction) { b in + // TODO Turn this into a multi-part Generator + CodeGenerator("WasmLegacyTryCatchGenerator", inContext: .single(.wasmFunction)) { + b in let function = b.currentWasmModule.currentWasmFunction // Choose a few random wasm values as arguments if available. let args = b.randomWasmBlockArguments(upTo: 5) let parameters = args.map(b.type) - let tags = (0.. [], args: args) { label, args in - b.buildRecursive(block: 1, of: recursiveCallCount, n: 4) + function.wasmBuildLegacyTry(with: parameters => [], args: args) { + label, args in + b.build(n: 4) for (i, tag) in tags.enumerated() { function.WasmBuildLegacyCatch(tag: tag) { _, _, _ in - b.buildRecursive(block: 2 + i, of: recursiveCallCount, n: 4) + b.build(n: 4) } } } catchAllBody: { label in - b.buildRecursive(block: 2 + tags.count, of: recursiveCallCount, n: 4) + b.build(n: 4) } }, - RecursiveCodeGenerator("WasmLegacyTryCatchWithResultGenerator", inContext: .wasmFunction) { b in + CodeGenerator( + "WasmLegacyTryCatchWithResultGenerator", inContext: .single(.wasmFunction) + ) { b in let function = b.currentWasmModule.currentWasmFunction // Choose a few random wasm values as arguments if available. let args = b.randomWasmBlockArguments(upTo: 5) let parameters = args.map(b.type) - let tags = (0.. outputTypes let recursiveCallCount = 2 + tags.count - function.wasmBuildLegacyTryWithResult(with: signature, args: args, body: { label, args in - b.buildRecursive(block: 1, of: recursiveCallCount, n: 4) - return outputTypes.map(function.findOrGenerateWasmVar) - }, catchClauses: tags.enumerated().map {i, tag in (tag, {_, _, _ in - b.buildRecursive(block: 2 + i, of: recursiveCallCount, n: 4) - return outputTypes.map(function.findOrGenerateWasmVar) - })}, catchAllBody: { label in - b.buildRecursive(block: 2 + tags.count, of: recursiveCallCount, n: 4) - return outputTypes.map(function.findOrGenerateWasmVar) - }) + function.wasmBuildLegacyTryWithResult( + with: signature, args: args, + body: { label, args in + b.build(n: 4) + return outputTypes.map(function.findOrGenerateWasmVar) + }, + catchClauses: tags.enumerated().map { i, tag in + ( + tag, + { _, _, _ in + b.build(n: 4) + return outputTypes.map(function.findOrGenerateWasmVar) + } + ) + }, + catchAllBody: { label in + b.build(n: 4) + return outputTypes.map(function.findOrGenerateWasmVar) + }) }, - RecursiveCodeGenerator("WasmLegacyTryDelegateGenerator", inContext: .wasmFunction, inputs: .required(.anyLabel)) { b, label in + // TODO split this into a multi-part Generator. + CodeGenerator( + "WasmLegacyTryCatchWithResultGenerator", inContext: .single(.wasmFunction) + ) { b in let function = b.currentWasmModule.currentWasmFunction // Choose a few random wasm values as arguments if available. let args = b.randomWasmBlockArguments(upTo: 5) - let outputTypes = b.randomWasmBlockOutputTypes(upTo: 3) let parameters = args.map(b.type) - function.wasmBuildLegacyTryDelegateWithResult(with: parameters => outputTypes, args: args, body: { _, _ in - b.buildRecursive() - return outputTypes.map(function.findOrGenerateWasmVar) - }, delegate: label) - }, - - // The variable we reassign to has to be a numerical primitive, e.g. something that looks like a number (can be a global) - // We cannot reassign to a .wasmFuncRef or .wasmExternRef though, as they need to be in a local slot. - RecursiveCodeGenerator("WasmIfElseGenerator", inContext: .wasmFunction, inputs: .required(.wasmi32, .wasmNumericalPrimitive)) { b, conditionVar, outputVar in - let function = b.currentWasmModule.currentWasmFunction - - let assignProb = probability(0.2) - - function.wasmBuildIfElse(conditionVar, hint: b.randomWasmBranchHint()) { - b.buildRecursive(block: 1, of: 2, n: 4) - if let variable = b.randomVariable(ofType: b.type(of: outputVar)) { - function.wasmReassign(variable: variable, to: outputVar) - } - } elseBody: { - b.buildRecursive(block: 2, of: 2, n: 4) - if let variable = b.randomVariable(ofType: b.type(of: outputVar)) { - function.wasmReassign(variable: variable, to: outputVar) - } - } + let tags = (0.. outputTypes + let recursiveCallCount = 2 + tags.count + function.wasmBuildLegacyTryWithResult( + with: signature, args: args, + body: { label, args in + b.build(n: 4) + return outputTypes.map(function.findOrGenerateWasmVar) + }, + catchClauses: tags.enumerated().map { i, tag in + ( + tag, + { _, _, _ in + b.build(n: 4) + return outputTypes.map(function.findOrGenerateWasmVar) + } + ) + }, + catchAllBody: { label in + b.build(n: 4) + return outputTypes.map(function.findOrGenerateWasmVar) + }) }, - RecursiveCodeGenerator("WasmIfElseWithSignatureGenerator", inContext: .wasmFunction, inputs: .required(.wasmi32)) { b, conditionVar in + CodeGenerator( + "WasmLegacyTryDelegateGenerator", inContext: .single(.wasmFunction), + inputs: .required(.anyLabel) + ) { b, label in let function = b.currentWasmModule.currentWasmFunction // Choose a few random wasm values as arguments if available. let args = b.randomWasmBlockArguments(upTo: 5) + let outputTypes = b.randomWasmBlockOutputTypes(upTo: 3) let parameters = args.map(b.type) - let outputTypes = b.randomWasmBlockOutputTypes(upTo: 5) - function.wasmBuildIfElseWithResult(conditionVar, hint: b.randomWasmBranchHint(), signature: parameters => outputTypes, args: args) { label, args in - b.buildRecursive(block: 1, of: 2, n: 4) - return outputTypes.map(function.findOrGenerateWasmVar) - } elseBody: { label, args in - b.buildRecursive(block: 2, of: 2, n: 4) - return outputTypes.map(function.findOrGenerateWasmVar) - } - }, - - CodeGenerator("WasmSelectGenerator", inContext: .wasmFunction, inputs: .required(.wasmi32)) { b, condition in + function.wasmBuildLegacyTryDelegateWithResult( + with: parameters => outputTypes, args: args, + body: { _, _ in + b.build(n: 4) + return outputTypes.map(function.findOrGenerateWasmVar) + }, delegate: label) + }, + + CodeGenerator( + "WasmIfElseGenerator", + [ + GeneratorStub( + "WasmBeginIfGenerator", + inContext: .single(.wasmFunction), + inputs: .required(.wasmi32), + provides: [.wasmBlock, .wasmFunction] + ) { b, condition in + b.emit( + WasmBeginIf(hint: b.randomWasmBranchHint()), + withInputs: [condition]) + }, + GeneratorStub( + "WasmBeginElseGenerator", + inContext: .single([.wasmFunction, .wasmBlock]), + provides: [.wasmBlock, .wasmFunction] + ) { b in + b.emit(WasmBeginElse()) + }, + GeneratorStub( + "WasmEndIfElseGenerator", + inContext: .single([.wasmFunction, .wasmBlock]) + ) { b in + b.emit(WasmEndIf()) + }, + ]), + + CodeGenerator( + "WasmIfElseWithSignatureGenerator", + [ + GeneratorStub( + "WasmBeginIfGenerator", + inContext: .single(.wasmFunction), + inputs: .required(.wasmi32), + provides: [.wasmBlock, .wasmFunction] + ) { b, condition in + let args = b.randomWasmBlockArguments(upTo: 5) + let parameters = args.map(b.type) + let outputTypes = b.randomWasmBlockOutputTypes(upTo: 5) + b.emit( + WasmBeginIf( + with: parameters => outputTypes, + hint: b.randomWasmBranchHint()), + withInputs: args + [condition]) + }, + GeneratorStub( + "WasmBeginElseGenerator", + inContext: .single([.wasmBlock, .wasmFunction]), + provides: [.wasmFunction, .wasmBlock] + ) { b in + let function = b.currentWasmFunction + let signature = b.currentWasmSignature + let trueResults = signature.outputTypes.map( + function.findOrGenerateWasmVar) + b.emit(WasmBeginElse(with: signature), withInputs: trueResults) + }, + GeneratorStub( + "WasmEndIfGenerator", + inContext: .single([.wasmFunction, .wasmBlock]) + ) { b in + let function = b.currentWasmFunction + let signature = b.currentWasmSignature + let falseResults = signature.outputTypes.map( + function.findOrGenerateWasmVar) + b.emit( + WasmEndIf(outputTypes: signature.outputTypes), + withInputs: falseResults) + }, + ]), + + CodeGenerator( + "WasmSelectGenerator", + inContext: .single(.wasmFunction), + inputs: .required(.wasmi32) + ) { b, condition in let function = b.currentWasmModule.currentWasmFunction // The condition is an i32, so we should always find at least that one as a possible input. let trueValue = b.randomVariable(ofType: .wasmPrimitive)! let falseValue = b.randomVariable(ofType: b.type(of: trueValue))! - function.wasmSelect(on: condition, trueValue: trueValue, falseValue: falseValue) + function.wasmSelect( + on: condition, trueValue: trueValue, falseValue: falseValue) }, - CodeGenerator("WasmThrowGenerator", inContext: .wasmFunction, inputs: .required(.object(ofGroup: "WasmTag"))) { b, tag in + CodeGenerator( + "WasmThrowGenerator", + inContext: .single(.wasmFunction), + inputs: .required(.object(ofGroup: "WasmTag")) + ) { b, tag in let function = b.currentWasmModule.currentWasmFunction let wasmTagType = b.type(of: tag).wasmTagType! if wasmTagType.isJSTag { @@ -1069,125 +1537,191 @@ public let WasmCodeGenerators: [CodeGenerator] = [ function.WasmBuildThrow(tag: tag, inputs: args) }, - CodeGenerator("WasmLegacyRethrowGenerator", inContext: .wasmFunction, inputs: .required(.exceptionLabel)) { b, exception in + CodeGenerator( + "WasmLegacyRethrowGenerator", + inContext: .single(.wasmFunction), + inputs: .required(.exceptionLabel) + ) { b, exception in let function = b.currentWasmModule.currentWasmFunction function.wasmBuildLegacyRethrow(exception) }, - CodeGenerator("WasmThrowRefGenerator", inContext: .wasmFunction, inputs: .required(.wasmExnRef)) { b, exception in + CodeGenerator( + "WasmThrowRefGenerator", inContext: .single(.wasmFunction), + inputs: .required(.wasmExnRef) + ) { b, exception in let function = b.currentWasmModule.currentWasmFunction function.wasmBuildThrowRef(exception: exception) }, - CodeGenerator("WasmDefineTagGenerator", inContext: .wasm) {b in + CodeGenerator( + "WasmDefineTagGenerator", inContext: .single(.wasm), + produces: [.object(ofGroup: "WasmTag")] + ) { b in b.currentWasmModule.addTag(parameterTypes: b.randomTagParameters()) }, - CodeGenerator("WasmBranchGenerator", inContext: .wasmFunction, inputs: .required(.anyLabel)) { b, label in + CodeGenerator( + "WasmBranchGenerator", + inContext: .single(.wasmFunction), + inputs: .required(.anyLabel) + ) { b, label in let function = b.currentWasmModule.currentWasmFunction - let args = b.type(of: label).wasmLabelType!.parameters.map(function.findOrGenerateWasmVar) + let args = b.type(of: label).wasmLabelType!.parameters.map( + function.findOrGenerateWasmVar) function.wasmBranch(to: label, args: args) }, - CodeGenerator("WasmBranchIfGenerator", inContext: .wasmFunction, inputs: .required(.anyLabel, .wasmi32)) { b, label, conditionVar in + CodeGenerator( + "WasmBranchIfGenerator", inContext: .single(.wasmFunction), + inputs: .required(.anyLabel, .wasmi32) + ) { b, label, conditionVar in let function = b.currentWasmModule.currentWasmFunction - let args = b.type(of: label).wasmLabelType!.parameters.map(function.findOrGenerateWasmVar) - function.wasmBranchIf(conditionVar, to: label, args: args, hint: b.randomWasmBranchHint()) + let args = b.type(of: label).wasmLabelType!.parameters.map( + function.findOrGenerateWasmVar) + function.wasmBranchIf( + conditionVar, to: label, args: args, hint: b.randomWasmBranchHint()) }, - RecursiveCodeGenerator("WasmBranchTableGenerator", inContext: .wasmFunction, inputs: .required(.wasmi32)) { b, value in + // TODO split this into a multi-part Generator. + CodeGenerator( + "WasmBranchTableGenerator", inContext: .single(.wasmFunction), + inputs: .required(.wasmi32) + ) { b, value in let function = b.currentWasmModule.currentWasmFunction // Choose parameter types for the br_table. If we can find an existing label, just use that // label types as it allows use to reuse existing (and therefore more interesting) blocks. - let parameterTypes = if let label = b.randomVariable(ofType: .anyLabel) { - b.type(of: label).wasmLabelType!.parameters - } else { - b.randomWasmBlockOutputTypes(upTo: 3) - } + let parameterTypes = + if let label = b.randomVariable(ofType: .anyLabel) { + b.type(of: label).wasmLabelType!.parameters + } else { + b.randomWasmBlockOutputTypes(upTo: 3) + } let extraBlockCount = Int.random(in: 1...5) let valueCount = Int.random(in: 0...20) let signature = [] => parameterTypes (0.. outputTypes, args: []) + return outputTypes + } + // Look up the labels. In most cases these will be exactly the ones produced by the blocks + // above but also any other matching existing block could be used. (Similar, tags with the + // same parameter types could also be mapped to the same block.) + let labels = outputTypesList.map { outputTypes in + b.randomVariable(ofType: .label(outputTypes))! + } + let catches = zip(tags, withExnRef).map { + tag, withExnRef -> WasmBeginTryTable.CatchKind in + tag == nil + ? (withExnRef ? .AllRef : .AllNoRef) + : (withExnRef ? .Ref : .NoRef) + } - let outputTypesList = zip(tags, withExnRef).map { tag, withExnRef in - var outputTypes: [ILType] = if let tag = tag { - b.type(of: tag).wasmTagType!.parameters - } else { - [] - } - if withExnRef { - outputTypes.append(.wasmExnRef) + var tryArgs = b.randomWasmBlockArguments(upTo: 5) + let tryParameters = tryArgs.map { b.type(of: $0) } + let tryOutputTypes = b.randomWasmBlockOutputTypes(upTo: 5) + tryArgs += zip(tags, labels).map { tag, label in + tag == nil ? [label] : [tag!, label] + }.joined() + function.wasmBuildTryTable( + with: tryParameters => tryOutputTypes, args: tryArgs, + catches: catches + ) { _, _ in + b.build(n: defaultCodeGenerationAmount) + return tryOutputTypes.map(function.findOrGenerateWasmVar) + } + outputTypesList.reversed().enumerated().forEach { + n, outputTypes in + let results = outputTypes.map( + function.findOrGenerateWasmVar) + function.wasmEndBlock( + outputTypes: outputTypes, args: results) + b.build(n: defaultCodeGenerationAmount) + } } - function.wasmBeginBlock(with: [] => outputTypes, args: []) - return outputTypes - } - // Look up the labels. In most cases these will be exactly the ones produced by the blocks - // above but also any other matching existing block could be used. (Similar, tags with the - // same parameter types could also be mapped to the same block.) - let labels = outputTypesList.map {outputTypes in b.randomVariable(ofType: .label(outputTypes))!} - let catches = zip(tags, withExnRef).map { tag, withExnRef -> WasmBeginTryTable.CatchKind in - tag == nil ? (withExnRef ? .AllRef : .AllNoRef) : (withExnRef ? .Ref : .NoRef) - } - - var tryArgs = b.randomWasmBlockArguments(upTo: 5) - let tryParameters = tryArgs.map {b.type(of: $0)} - let tryOutputTypes = b.randomWasmBlockOutputTypes(upTo: 5) - tryArgs += zip(tags, labels).map {tag, label in tag == nil ? [label] : [tag!, label]}.joined() - function.wasmBuildTryTable(with: tryParameters => tryOutputTypes, args: tryArgs, catches: catches) { _, _ in - b.buildRecursive(block: 1, of: tags.count + 1, n: 4) - return tryOutputTypes.map(function.findOrGenerateWasmVar) - } - outputTypesList.reversed().enumerated().forEach { n, outputTypes in - let results = outputTypes.map(function.findOrGenerateWasmVar) - function.wasmEndBlock(outputTypes: outputTypes, args: results) - b.buildRecursive(block: n + 2, of: tags.count + 1, n: 4) - } - }, + ]), + + CodeGenerator( + "ConstSimd128Generator", inContext: .single(.wasmFunction), + produces: [.wasmSimd128] + ) { b in + let function = b.currentWasmModule.currentWasmFunction + function.constSimd128( + value: (0..<16).map { _ in UInt8.random(in: UInt8.min...UInt8.max) } + ) + }, + + CodeGenerator( + "WasmSimd128IntegerUnOpGenerator", inContext: .single(.wasmFunction), + inputs: .required(.wasmSimd128), produces: [] + ) { b, input in + let shape = chooseUniform( + from: WasmSimd128Shape.allCases.filter { return !$0.isFloat() }) + let unOpKind = chooseUniform( + from: WasmSimd128IntegerUnOpKind.allCases.filter { + return $0.isValidForShape(shape: shape) + }) - CodeGenerator("ConstSimd128Generator", inContext: .wasmFunction) { b in let function = b.currentWasmModule.currentWasmFunction - function.constSimd128(value: (0 ..< 16).map { _ in UInt8.random(in: UInt8.min ... UInt8.max) }) - }, - - CodeGenerator("WasmSimd128IntegerUnOpGenerator", inContext: .wasmFunction, inputs: .required(.wasmSimd128)) { b, input in - let shape = chooseUniform(from: WasmSimd128Shape.allCases.filter{ !$0.isFloat() }) - let unOpKind = chooseUniform(from: WasmSimd128IntegerUnOpKind.allCases.filter{ - $0.isValidForShape(shape: shape) - }) - - let function = b.currentWasmModule.currentWasmFunction; function.wasmSimd128IntegerUnOp(input, shape, unOpKind) }, - CodeGenerator("WasmSimd128IntegerBinOpGenerator", inContext: .wasmFunction, inputs: .required(.wasmSimd128)) { b, lhs in - let shape = chooseUniform(from: WasmSimd128Shape.allCases.filter{ !$0.isFloat() }) - let binOpKind = chooseUniform(from: WasmSimd128IntegerBinOpKind.allCases.filter{ - $0.isValidForShape(shape: shape) - }) - let function = b.currentWasmModule.currentWasmFunction; + CodeGenerator( + "WasmSimd128IntegerBinOpGenerator", inContext: .single(.wasmFunction), + inputs: .required(.wasmSimd128), produces: [.wasmSimd128] + ) { b, lhs in + let shape = chooseUniform( + from: WasmSimd128Shape.allCases.filter { return !$0.isFloat() }) + let binOpKind = chooseUniform( + from: WasmSimd128IntegerBinOpKind.allCases.filter { + return $0.isValidForShape(shape: shape) + }) + let function = b.currentWasmModule.currentWasmFunction // Shifts take an i32 as an rhs input, the others take a regular .wasmSimd128 input. let rhsType = binOpKind.isShift() ? ILType.wasmi32 : .wasmSimd128 @@ -1195,7 +1729,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ function.wasmSimd128IntegerBinOp(lhs, rhs, shape, binOpKind) }, - CodeGenerator("WasmSimd128IntegerTernaryOpGenerator", inContext: .wasmFunction, inputs: .required(.wasmSimd128, .wasmSimd128, .wasmSimd128)) { b, left, mid, right in + CodeGenerator("WasmSimd128IntegerTernaryOpGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmSimd128, .wasmSimd128, .wasmSimd128)) { b, left, mid, right in let shape = chooseUniform(from: WasmSimd128Shape.allCases.filter{ !$0.isFloat() } ) let ternaryOpKind = chooseUniform(from: WasmSimd128IntegerTernaryOpKind.allCases.filter{ $0.isValidForShape(shape: shape) @@ -1205,27 +1739,37 @@ public let WasmCodeGenerators: [CodeGenerator] = [ function.wasmSimd128IntegerTernaryOp(left, mid, right, shape, ternaryOpKind) }, - CodeGenerator("WasmSimd128FloatUnOpGenerator", inContext: .wasmFunction, inputs: .required(.wasmSimd128)) { b, input in - let shape = chooseUniform(from: WasmSimd128Shape.allCases.filter{ $0.isFloat() }) - let unOpKind = chooseUniform(from: WasmSimd128FloatUnOpKind.allCases.filter{ - $0.isValidForShape(shape: shape) - }) + CodeGenerator( + "WasmSimd128FloatUnOpGenerator", inContext: .single(.wasmFunction), + inputs: .required(.wasmSimd128), produces: [.wasmSimd128] + ) { b, input in + let shape = chooseUniform( + from: WasmSimd128Shape.allCases.filter { return $0.isFloat() }) + let unOpKind = chooseUniform( + from: WasmSimd128FloatUnOpKind.allCases.filter { + return $0.isValidForShape(shape: shape) + }) - let function = b.currentWasmModule.currentWasmFunction; + let function = b.currentWasmModule.currentWasmFunction function.wasmSimd128FloatUnOp(input, shape, unOpKind) }, - CodeGenerator("WasmSimd128FloatBinOpGenerator", inContext: .wasmFunction, inputs: .required(.wasmSimd128, .wasmSimd128)) { b, lhs, rhs in - let shape = chooseUniform(from: WasmSimd128Shape.allCases.filter{ $0.isFloat() }) - let binOpKind = chooseUniform(from: WasmSimd128FloatBinOpKind.allCases.filter{ - $0.isValidForShape(shape: shape) - }) + CodeGenerator( + "WasmSimd128FloatBinOpGenerator", inContext: .single(.wasmFunction), + inputs: .required(.wasmSimd128, .wasmSimd128), produces: [.wasmSimd128] + ) { b, lhs, rhs in + let shape = chooseUniform( + from: WasmSimd128Shape.allCases.filter { return $0.isFloat() }) + let binOpKind = chooseUniform( + from: WasmSimd128FloatBinOpKind.allCases.filter { + return $0.isValidForShape(shape: shape) + }) - let function = b.currentWasmModule.currentWasmFunction; + let function = b.currentWasmModule.currentWasmFunction function.wasmSimd128FloatBinOp(lhs, rhs, shape, binOpKind) }, - CodeGenerator("WasmSimd128FloatTernaryOpGenerator", inContext: .wasmFunction, inputs: .required(.wasmSimd128, .wasmSimd128, .wasmSimd128)) { b, left, mid, right in + CodeGenerator("WasmSimd128FloatTernaryOpGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmSimd128, .wasmSimd128, .wasmSimd128)) { b, left, mid, right in let shape = chooseUniform(from: WasmSimd128Shape.allCases.filter{ $0.isFloat() } ) let ternaryOpKind = chooseUniform(from: WasmSimd128FloatTernaryOpKind.allCases.filter{ $0.isValidForShape(shape: shape) @@ -1235,7 +1779,10 @@ public let WasmCodeGenerators: [CodeGenerator] = [ function.wasmSimd128FloatTernaryOp(left, mid, right, shape, ternaryOpKind) }, - CodeGenerator("WasmSimd128CompareGenerator", inContext: .wasmFunction, inputs: .required(.wasmSimd128, .wasmSimd128)) { b, lhs, rhs in + CodeGenerator( + "WasmSimd128CompareGenerator", inContext: .single(.wasmFunction), + inputs: .required(.wasmSimd128, .wasmSimd128), produces: [.wasmSimd128] + ) { b, lhs, rhs in let shape = chooseUniform(from: WasmSimd128Shape.allCases) let compareOpKind = b.randomSimd128CompareOpKind(shape) @@ -1243,52 +1790,81 @@ public let WasmCodeGenerators: [CodeGenerator] = [ function.wasmSimd128Compare(lhs, rhs, shape, compareOpKind) }, - CodeGenerator("WasmSimdSplatGenerator", inContext: .wasmFunction) {b in - let function = b.currentWasmModule.currentWasmFunction; + CodeGenerator("WasmSimdSplatGenerator", inContext: .single(.wasmFunction)) { b in + let function = b.currentWasmModule.currentWasmFunction let kind = chooseUniform(from: WasmSimdSplat.Kind.allCases) - function.wasmSimdSplat(kind: kind, function.findOrGenerateWasmVar(ofType: kind.laneType())) + function.wasmSimdSplat( + kind: kind, function.findOrGenerateWasmVar(ofType: kind.laneType())) }, - CodeGenerator("WasmSimdExtractLaneGenerator", inContext: .wasmFunction, inputs: .required(.wasmSimd128)) { b, input in + CodeGenerator( + "WasmSimdExtractLaneGenerator", inContext: .single(.wasmFunction), + inputs: .required(.wasmSimd128) + ) { b, input in let function = b.currentWasmModule.currentWasmFunction let kind = chooseUniform(from: WasmSimdExtractLane.Kind.allCases) - function.wasmSimdExtractLane(kind: kind, input, Int.random(in: 0.. + // This needs to stay in sync with the provided codeGenerators. + public private(set) var contextGraph: ContextGraph + /// The active program templates. These are only used if the HybridEngine is enabled. public let programTemplates: WeightedList @@ -169,6 +172,7 @@ public class Fuzzer { self.engine = engine self.mutators = mutators self.codeGenerators = codeGenerators + self.programTemplates = programTemplates self.evaluator = evaluator self.environment = environment @@ -177,6 +181,7 @@ public class Fuzzer { self.runner = scriptRunner self.minimizer = minimizer self.logger = Logger(withLabel: "Fuzzer") + self.contextGraph = ContextGraph(for: codeGenerators, withLogger: self.logger) // Pass-through any postprocessor to the generative engine. if let postProcessor = engine.postProcessor { @@ -216,6 +221,8 @@ public class Fuzzer { guard generators.contains(where: { $0.isValueGenerator }) else { fatalError("Code generators must contain at least one value generator") } + // This builds a graph that we need later for scheduling generators. + self.contextGraph = ContextGraph(for: generators, withLogger: self.logger) self.codeGenerators = generators } @@ -272,17 +279,19 @@ public class Fuzzer { let nameMaxLength = self.codeGenerators.map({ $0.name.count }).max()! for generator in self.codeGenerators { - if generator.invocationCount > 100 && generator.invocationSuccessRate! < 0.2 { - let percentage = Statistics.percentageOrNa(generator.invocationSuccessRate, 7) - let name = generator.name.rightPadded(toLength: nameMaxLength) - let invocations = String(format: "%12d", generator.invocationCount) - self.logger.warning("Code generator \(name) might have too restrictive dynamic requirements. Its successful invocation rate is only \(percentage)% after \(invocations) invocations") - } - if generator.totalSamples >= 100 && generator.correctnessRate! < 0.05 { - let name = generator.name.rightPadded(toLength: nameMaxLength) - let percentage = Statistics.percentageOrNa(generator.correctnessRate, 7) - let totalSamples = String(format: "%10d", generator.totalSamples) - self.logger.warning("Code generator \(name) might be broken. Correctness rate is only \(percentage)% after \(totalSamples) generated samples") + for stub in generator.parts { + if stub.invocationCount > 100 && stub.invocationSuccessRate! < 0.2 { + let percentage = Statistics.percentageOrNa(stub.invocationSuccessRate, 7) + let name = stub.name.rightPadded(toLength: nameMaxLength) + let invocations = String(format: "%12d", stub.invocationCount) + self.logger.warning("Code generator \(name) might have too restrictive dynamic requirements. Its successful invocation rate is only \(percentage)% after \(invocations) invocations") + } + if stub.totalSamples >= 100 && stub.correctnessRate! < 0.05 { + let name = stub.name.rightPadded(toLength: nameMaxLength) + let percentage = Statistics.percentageOrNa(stub.correctnessRate, 7) + let totalSamples = String(format: "%10d", stub.totalSamples) + self.logger.warning("Code generator \(name) might be broken. Correctness rate is only \(percentage)% after \(totalSamples) generated samples") + } } } for template in self.programTemplates { diff --git a/Sources/Fuzzilli/Modules/Statistics.swift b/Sources/Fuzzilli/Modules/Statistics.swift index 47c2e0c58..e974e7648 100644 --- a/Sources/Fuzzilli/Modules/Statistics.swift +++ b/Sources/Fuzzilli/Modules/Statistics.swift @@ -235,14 +235,16 @@ public class Statistics: Module { let nameMaxLength = fuzzer.codeGenerators.map({ $0.name.count }).max()! for generator in fuzzer.codeGenerators { - let name = generator.name.rightPadded(toLength: nameMaxLength) - let correctnessRate = Self.percentageOrNa(generator.correctnessRate, 7) - let interestingSamplesRate = Self.percentageOrNa(generator.interestingSamplesRate, 7) - let timeoutRate = Self.percentageOrNa(generator.timeoutRate, 6) - let avgInstructionsAdded = String(format: "%.2f", generator.avgNumberOfInstructionsGenerated).leftPadded(toLength: 5) - let invocationSuccessRate = Self.percentageOrNa(generator.invocationSuccessRate, 6) - let samplesGenerated = generator.totalSamples - self.logger.verbose(" \(name) : Invocation Success: \(invocationSuccessRate), Correctness rate: \(correctnessRate), Interesting sample rate: \(interestingSamplesRate), Timeout rate: \(timeoutRate), Avg. # of instructions added: \(avgInstructionsAdded), Total # of generated samples: \(samplesGenerated)") + for stub in generator.parts { + let name = stub.name.rightPadded(toLength: nameMaxLength) + let correctnessRate = Self.percentageOrNa(stub.correctnessRate, 7) + let interestingSamplesRate = Self.percentageOrNa(stub.interestingSamplesRate, 7) + let timeoutRate = Self.percentageOrNa(stub.timeoutRate, 6) + let avgInstructionsAdded = String(format: "%.2f", stub.avgNumberOfInstructionsGenerated).leftPadded(toLength: 5) + let invocationSuccessRate = Self.percentageOrNa(stub.invocationSuccessRate, 6) + let samplesGenerated = stub.totalSamples + self.logger.verbose(" \(name) : Invocation Success: \(invocationSuccessRate), Correctness rate: \(correctnessRate), Interesting sample rate: \(interestingSamplesRate), Timeout rate: \(timeoutRate), Avg. # of instructions added: \(avgInstructionsAdded), Total # of generated samples: \(samplesGenerated)") + } } } } diff --git a/Sources/Fuzzilli/Util/MockFuzzer.swift b/Sources/Fuzzilli/Util/MockFuzzer.swift index 85827b427..a7bab9463 100644 --- a/Sources/Fuzzilli/Util/MockFuzzer.swift +++ b/Sources/Fuzzilli/Util/MockFuzzer.swift @@ -117,7 +117,7 @@ public func makeMockFuzzer(config maybeConfiguration: Configuration? = nil, engi let codeGenerators = WeightedList( (CodeGenerators + WasmCodeGenerators).map { guard let weight = codeGeneratorWeights[$0.name] else { - fatalError("Missing weight for code generator \($0.name) in CodeGeneratorWeights.swift") + fatalError("Missing weight for CodeGenerator \($0.name) in CodeGeneratorWeights.swift") } return ($0, weight) } + additionalCodeGenerators) diff --git a/Sources/FuzzilliCli/Profiles/DuktapeProfile.swift b/Sources/FuzzilliCli/Profiles/DuktapeProfile.swift index c277af56e..cb144cf5e 100644 --- a/Sources/FuzzilliCli/Profiles/DuktapeProfile.swift +++ b/Sources/FuzzilliCli/Profiles/DuktapeProfile.swift @@ -47,7 +47,7 @@ let duktapeProfile = Profile( additionalProgramTemplates: WeightedList([]), disabledCodeGenerators: [], - + disabledMutators: [], additionalBuiltins: [ diff --git a/Sources/FuzzilliCli/Profiles/JerryscriptProfile.swift b/Sources/FuzzilliCli/Profiles/JerryscriptProfile.swift index b000043f9..96a549071 100644 --- a/Sources/FuzzilliCli/Profiles/JerryscriptProfile.swift +++ b/Sources/FuzzilliCli/Profiles/JerryscriptProfile.swift @@ -47,7 +47,7 @@ let jerryscriptProfile = Profile( additionalProgramTemplates: WeightedList([]), disabledCodeGenerators: [], - + disabledMutators: [], additionalBuiltins: [ diff --git a/Sources/FuzzilliCli/Profiles/NjsProfile.swift b/Sources/FuzzilliCli/Profiles/NjsProfile.swift index 6d6efe243..ce1ddce53 100644 --- a/Sources/FuzzilliCli/Profiles/NjsProfile.swift +++ b/Sources/FuzzilliCli/Profiles/NjsProfile.swift @@ -56,4 +56,4 @@ let njsProfile = Profile( additionalObjectGroups: [], optionalPostProcessor: nil -) \ No newline at end of file +) diff --git a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift index 56e925526..aee92edd2 100644 --- a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift @@ -96,7 +96,7 @@ public let TurbofanVerifyTypeGenerator = CodeGenerator("TurbofanVerifyTypeGenera b.eval("%VerifyType(%@)", with: [v]) } -public let WorkerGenerator = RecursiveCodeGenerator("WorkerGenerator") { b in +public let WorkerGenerator = CodeGenerator("WorkerGenerator") { b in let workerSignature = Signature(withParameterCount: Int.random(in: 0...3)) // TODO(cffsmith): currently Fuzzilli does not know that this code is sent @@ -110,11 +110,11 @@ public let WorkerGenerator = RecursiveCodeGenerator("WorkerGenerator") { b in // Generate a random onmessage handler for incoming messages. let onmessageFunction = b.buildPlainFunction(with: .parameters(n: 1)) { args in - b.buildRecursive(block: 1, of: 2) + b.build(n: Int.random(in: 2...5)) } b.setProperty("onmessage", of: this, to: onmessageFunction) - b.buildRecursive(block: 2, of: 2) + b.build(n: Int.random(in: 3...10)) } let workerConstructor = b.createNamedVariable(forBuiltin: "Worker") @@ -169,37 +169,33 @@ public let MapTransitionFuzzer = ProgramTemplate("MapTransitionFuzzer") { b in } // Temporarily overwrite the active code generators with the following generators... - let primitiveValueGenerator = ValueGenerator("PrimitiveValue") { b, n in - for _ in 0..([ - (primitiveValueGenerator, 2), + (primitiveCodeGenerator, 2), (createObjectGenerator, 1), (objectMakerGenerator, 1), (objectConstructorGenerator, 1), @@ -294,7 +290,7 @@ public let MapTransitionFuzzer = ProgramTemplate("MapTransitionFuzzer") { b in (functionJitCallGenerator, 2) ])) - // ... run some of the ValueGenerators to create some initial objects ... + // ... run some of the CodeGenerators to create some initial objects ... b.buildPrefix() // ... and generate a bunch of code. b.build(n: 100, by: .generating) diff --git a/Sources/FuzzilliCli/Profiles/V8HoleFuzzingProfile.swift b/Sources/FuzzilliCli/Profiles/V8HoleFuzzingProfile.swift index 3162811c1..8d6b75f6a 100644 --- a/Sources/FuzzilliCli/Profiles/V8HoleFuzzingProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8HoleFuzzingProfile.swift @@ -17,7 +17,7 @@ import Fuzzilli // This value generator inserts Hole leaks into the program. Use this if you // want to fuzz for Memory Corruption using holes, this should be used in // conjunction with the --hole-fuzzing runtime flag. -fileprivate let HoleLeakGenerator = ValueGenerator("HoleLeakGenerator") { b, args in +fileprivate let HoleLeakGenerator = CodeGenerator("HoleLeakGenerator", produces: [.jsAnything]) { b in b.eval("%LeakHole()", hasOutput: true) } diff --git a/Sources/FuzzilliCli/Profiles/V8Profile.swift b/Sources/FuzzilliCli/Profiles/V8Profile.swift index 8b1cf70d1..8b5ce4da2 100644 --- a/Sources/FuzzilliCli/Profiles/V8Profile.swift +++ b/Sources/FuzzilliCli/Profiles/V8Profile.swift @@ -14,7 +14,6 @@ import Fuzzilli - let v8Profile = Profile( processArgs: { randomize in var args = [ diff --git a/Sources/FuzzilliCli/Profiles/XSProfile.swift b/Sources/FuzzilliCli/Profiles/XSProfile.swift index 30dfa58da..3277d0e52 100644 --- a/Sources/FuzzilliCli/Profiles/XSProfile.swift +++ b/Sources/FuzzilliCli/Profiles/XSProfile.swift @@ -58,17 +58,17 @@ fileprivate let HardenGenerator = CodeGenerator("HardenGenerator", inputs: .requ b.callFunction(harden, withArgs: [obj]) } -fileprivate let ModuleSourceGenerator = RecursiveCodeGenerator("ModuleSourceGenerator") { b in +fileprivate let ModuleSourceGenerator = CodeGenerator("ModuleSourceGenerator") { b in let moduleSourceConstructor = b.createNamedVariable(forBuiltin: "ModuleSource") let code = b.buildCodeString() { - b.buildRecursive() + b.build(n: 5) } b.construct(moduleSourceConstructor, withArgs: [code]) } -fileprivate let CompartmentGenerator = RecursiveCodeGenerator("CompartmentGenerator") { b in +fileprivate let CompartmentGenerator = CodeGenerator("CompartmentGenerator") { b in let compartmentConstructor = b.createNamedVariable(forBuiltin: "Compartment") var endowments = [String: Variable]() // may be used as endowments argument or globalLexicals @@ -84,16 +84,16 @@ fileprivate let CompartmentGenerator = RecursiveCodeGenerator("CompartmentGenera // to do: populate moduleMap let moduleMapObject = b.createObject(with: moduleMap) let resolveHook = b.buildPlainFunction(with: .parameters(n: 2)) { _ in - b.buildRecursive(block: 1, of: 4) + b.build(n: 5) b.doReturn(b.randomJsVariable()) } let moduleMapHook = b.buildPlainFunction(with: .parameters(n: 1)) { _ in - b.buildRecursive(block: 2, of: 4) + b.build(n: 5) b.doReturn(b.randomJsVariable()) } let loadNowHook = b.dup(moduleMapHook) let loadHook = b.buildAsyncFunction(with: .parameters(n: 1)) { _ in - b.buildRecursive(block: 3, of: 4) + b.build(n: 5) b.doReturn(b.randomJsVariable()) } options["resolveHook"] = resolveHook @@ -111,7 +111,7 @@ fileprivate let CompartmentGenerator = RecursiveCodeGenerator("CompartmentGenera if probability(0.5) { let code = b.buildCodeString() { - b.buildRecursive(block: 4, of: 4) + b.build(n: 5) } b.callMethod("evaluate", on: compartment, withArgs: [code]) } @@ -131,7 +131,7 @@ fileprivate let UnicodeStringGenerator = CodeGenerator("UnicodeStringGenerator") fileprivate let CompartmentEvaluateGenerator = CodeGenerator("CompartmentEvaluateGenerator", inputs: .required(.object(ofGroup: "Compartment"))) { b, target in let code = b.buildCodeString() { - b.buildRecursive() + b.build(n: 5) } b.callMethod("evaluate", on: target, withArgs: [code]) } diff --git a/Sources/FuzzilliCli/main.swift b/Sources/FuzzilliCli/main.swift index c4746bd09..11466c6e6 100644 --- a/Sources/FuzzilliCli/main.swift +++ b/Sources/FuzzilliCli/main.swift @@ -312,7 +312,7 @@ if swarmTesting { logger.info("Weight | CodeGenerator") } -let disabledGenerators = Set(profile.disabledCodeGenerators) +let disableCodeGenerators = Set(profile.disabledCodeGenerators) let additionalCodeGenerators = profile.additionalCodeGenerators let codeGeneratorsToUse = if enableWasm { @@ -324,14 +324,14 @@ let codeGeneratorsToUse = if enableWasm { let standardCodeGenerators: [(CodeGenerator, Int)] = codeGeneratorsToUse.map { guard let weight = codeGeneratorWeights[$0.name] else { - logger.fatal("Missing weight for code generator \($0.name) in CodeGeneratorWeights.swift") + logger.fatal("Missing weight for CodeGenerator \($0.name) in CodeGeneratorWeights.swift") } return ($0, weight) } var codeGenerators: WeightedList = WeightedList([]) for (generator, var weight) in (additionalCodeGenerators + standardCodeGenerators) { - if disabledGenerators.contains(generator.name) { + if disableCodeGenerators.contains(generator.name) { continue } diff --git a/Tests/FuzzilliTests/ContextGraphTest.swift b/Tests/FuzzilliTests/ContextGraphTest.swift new file mode 100644 index 000000000..7a1265a5f --- /dev/null +++ b/Tests/FuzzilliTests/ContextGraphTest.swift @@ -0,0 +1,66 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import XCTest +@testable import Fuzzilli + +class ContextGraphTests: XCTestCase { + func testReachabilityCalculation() { + let fuzzer = makeMockFuzzer() + let contextGraph = ContextGraph(for: fuzzer.codeGenerators, withLogger: Logger(withLabel: "Test")) + + let reachableContexts = Set(contextGraph.getReachableContexts(from: .javascript)) + + let reachableContexts2 = Set(contextGraph.getReachableContexts(from: .javascript)) + + XCTAssertEqual(reachableContexts, reachableContexts2) + + XCTAssertEqual(reachableContexts, + Set([.javascript, + .method, + .classMethod, + .switchCase, + .classDefinition, + .switchBlock, + .asyncFunction, + .wasmFunction, + .wasm, + .with, + .loop, + .wasmBlock, + .generatorFunction, + .objectLiteral, + .subroutine, + .wasmTypeGroup])) + } + + func testSubsetReachabilityCalculation() { + let fuzzer = makeMockFuzzer() + let contextGraph = ContextGraph(for: fuzzer.codeGenerators, withLogger: Logger(withLabel: "Test")) + let reachableContextsWasm = Set(contextGraph.getReachableContexts(from: .wasm)) + let reachableContextsWasm2 = Set(contextGraph.getReachableContexts(from: .wasm)) + + XCTAssertEqual(reachableContextsWasm, reachableContextsWasm2) + + let reachableContextsWasmFunction = Set(contextGraph.getReachableContexts(from: .wasmFunction)) + let reachableContextsJavaScript = Set(contextGraph.getReachableContexts(from: .javascript)) + + XCTAssertTrue(reachableContextsWasmFunction.isSubset(of: reachableContextsWasm)) + XCTAssertEqual(reachableContextsWasm, + Set([.wasmFunction, + .wasm, + .wasmBlock])) + XCTAssertTrue(reachableContextsWasm.isSubset(of: reachableContextsJavaScript)) + } +} diff --git a/Tests/FuzzilliTests/ProgramBuilderTest.swift b/Tests/FuzzilliTests/ProgramBuilderTest.swift index 64c36ac67..4467636a8 100644 --- a/Tests/FuzzilliTests/ProgramBuilderTest.swift +++ b/Tests/FuzzilliTests/ProgramBuilderTest.swift @@ -42,6 +42,30 @@ class ProgramBuilderTests: XCTestCase { XCTAssertLessThanOrEqual(averageSize, 2*N) } + func testTemplateBuilding() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + let numPrograms = 100 + let maxExpectedProgramSize = 1000 + var sumOfProgramSizes = 0 + + for _ in 0..([ @@ -105,27 +136,25 @@ class ProgramBuilderTests: XCTestCase { for _ in 0..<10 { b.buildPrefix() let prefixSize = b.currentNumberOfInstructions - b.build(n: 100, by: .generating) + b.build(n: N, by: .generating) let program = b.finalize() // In this case, the size of the generated code must be exactly the requested size. - XCTAssertEqual(program.size - prefixSize, 100) + XCTAssertEqual(program.size - prefixSize, N) } } func testShapeOfGeneratedCode2() { let fuzzer = makeMockFuzzer() let b = fuzzer.makeBuilder() + let N = 100 - b.minRecursiveBudgetRelativeToParentBudget = 0.25 - b.maxRecursiveBudgetRelativeToParentBudget = 0.25 - - let simpleGenerator = ValueGenerator("SimpleGenerator") { b, _ in + let simpleGenerator = CodeGenerator("SimpleGenerator", produces: [.integer]) { b in b.loadInt(Int64.random(in: 0..<100)) } - let recursiveGenerator = RecursiveCodeGenerator("RecursiveGenerator") { b in + let recursiveGenerator = CodeGenerator("RecursiveGenerator") { b in b.buildRepeatLoop(n: 5) { _ in - b.buildRecursive() + b.build(n: 5) } } fuzzer.setCodeGenerators(WeightedList([ @@ -135,15 +164,19 @@ class ProgramBuilderTests: XCTestCase { for _ in 0..<10 { b.buildPrefix() - let prefixSize = b.currentNumberOfInstructions - b.build(n: 100, by: .generating) - let program = b.finalize() + let _ = b.currentNumberOfInstructions + // let prefixSize = b.currentNumberOfInstructions + b.build(n: N, by: .generating) + let _ = b.finalize() + // let program = b.finalize() // Uncomment to see the "shape" of generated programs on the console. //print(FuzzILLifter().lift(program)) - // The size may be larger, but only roughly by 100 * 0.25 + 100 * 0.25**2 + 100 * 0.25**3 ... (each block may overshoot its budget by roughly the maximum recursive block size). - XCTAssertLessThan(program.size - prefixSize, 150) + // TODO: We need some robust testing that tests whether we emit code + // in the correct distributions, instead of just checking here + // against a hard number, which might fail sparsely. + // XCTAssertLessThan(program.size - prefixSize, N * 4) } } @@ -2549,12 +2582,10 @@ class ProgramBuilderTests: XCTestCase { // Check that the intermediate variables were generated as part of the recursion. let d8 = b.randomVariable(ofType: jsD8) - XCTAssertNotNil(d8) - XCTAssert(b.type(of: d8!).Is(jsD8)) + XCTAssert(d8 != nil && b.type(of: d8!).Is(jsD8)) let d8Test = b.randomVariable(ofType: jsD8Test) - XCTAssertNotNil(d8Test) - XCTAssert(b.type(of: d8Test!).Is(jsD8Test)) + XCTAssert(d8Test != nil && b.type(of: d8Test!).Is(jsD8Test)) } func testFindOrGenerateTypeWithGlobalConstructor() { @@ -2641,10 +2672,10 @@ class ProgramBuilderTests: XCTestCase { XCTAssert(b.type(of: var3).Is(type3)) // Get a random variable and then change the type let var1 = b.randomVariable(ofTypeOrSubtype: type1) - XCTAssertNotNil(var1) - XCTAssertEqual(var1, var3) + XCTAssert(var1 != nil) + XCTAssert(var1 == var3) let var4 = b.randomVariable(ofTypeOrSubtype: type4) - XCTAssertNil(var4) + XCTAssert(var4 == nil) } func testFindOrGenerateTypeWithSubtype() { @@ -2737,7 +2768,7 @@ class ProgramBuilderTests: XCTestCase { let type1 = ILType.object(ofGroup: "group1", withProperties: [], withMethods: []) let group1 = ObjectGroup(name: "group1", instanceType: type1, properties: [:], methods: [:]) - let testGenerator = CodeGenerator("testGenerator", produces: type1) { b in + let testGenerator = CodeGenerator("testGenerator", produces: [type1]) { b in let builtin = b.createNamedVariable(forBuiltin: "foo") b.callFunction(builtin) } @@ -2749,9 +2780,10 @@ class ProgramBuilderTests: XCTestCase { let fuzzer = makeMockFuzzer(config: config, environment: env, codeGenerators: [(testGenerator, 1)]) let b = fuzzer.makeBuilder() - b.buildPrefix() + // Manually create a Variable, we don't want to use buildPrefix as we then might accidentally already create variable of type `type1`. + b.loadInt(42) - XCTAssertNil(b.randomVariable(ofTypeOrSubtype: type1)) + XCTAssertTrue(b.randomVariable(ofTypeOrSubtype: type1) == nil) let obj = b.findOrGenerateType(type1) XCTAssert(b.type(of: obj).Is(type1)) } @@ -2905,4 +2937,107 @@ class ProgramBuilderTests: XCTestCase { let expected = b.finalize() XCTAssertEqual(actual, expected) } + + func testWasmBranchGeneratorSchedulingTest() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + b.buildPrefix() + + // Pick the Branch Generator. + let generator = fuzzer.codeGenerators.filter { + $0.name == "WasmBranchGenerator" + }[0] + + // Now build this. + let syntheticGenerator = b.assembleSyntheticGenerator(for: generator) + XCTAssertNotNil(syntheticGenerator) + + // TODO: Hm I guess we're missing the block generator that produces a label in the CodeGenerator. + // See WasmBlockGenerator. + // There should be some logic that allows some nesting of a WasmBlockGenerator. + let _ = b.complete(generator: syntheticGenerator!, withBudget: 30) + // XCTAssertGreaterThan(numGeneratedInstructions, 30) + } + + func testWasmCallDirectGeneratorSchedulingTest() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + b.buildPrefix() + + // Pick the Branch Generator. + let generator = fuzzer.codeGenerators.filter { + $0.name == "WasmStructNewDefaultGenerator" + }[0] + + // Now build this. + let syntheticGenerator = b.assembleSyntheticGenerator(for: generator) + XCTAssertNotNil(syntheticGenerator) + + let numGeneratedInstructions = b.complete(generator: syntheticGenerator!, withBudget: 30) + XCTAssertGreaterThan(numGeneratedInstructions, 0) + } + + func testWasmMemorySizeSchedulingTest() { + let fuzzer = makeMockFuzzer() + let numPrograms = 100 + + for _ in 0...numPrograms { + let b = fuzzer.makeBuilder() + b.buildPrefix() + + let generator = fuzzer.codeGenerators.filter { + $0.name == "WasmMemorySizeGenerator" + }[0] + + // Now build this. + let syntheticGenerator = b.assembleSyntheticGenerator(for: generator) + XCTAssertNotNil(syntheticGenerator) + + let N = 30 + // We might generate a lot more than 30 instructions to fulfill the constraints. + let numGeneratedInstructions = b.complete(generator: syntheticGenerator!, withBudget: N) + + let program = b.finalize() + + XCTAssertTrue(program.code.contains(where: { instr in + if case .wasmMemorySize = instr.op.opcode { + return true + } else { + return false + } + })) + XCTAssertGreaterThan(numGeneratedInstructions, 0) + } + } + + func testThatGeneratorsExistAndAreBuildableFromJs() { + let fuzzer = makeMockFuzzer() + let tries: Int = 10 + + var failures: [String: Int] = [:] + + for generator in fuzzer.codeGenerators { + let b = fuzzer.makeBuilder() + b.buildPrefix() + + if let syntheticGenerator = b.assembleSyntheticGenerator(for: generator) { + let generatedInstructions = b.complete(generator: syntheticGenerator, withBudget: 40) + + if generatedInstructions == 0 { + failures[generator.name, default: 0] += 1 + } + XCTAssertLessThan(syntheticGenerator.parts.count, 10) + } else { + XCTFail("Unable to generate synthetic CodeGenerator for \(generator.name) from JS.") + } + } + + for (name, failureCount) in failures { + if failureCount == tries { + // This might fail very sparsely, if so, we might want to check the offending Generator to see if we can improve handling for it. + // OTOH this is a fuzzer and we sometimes have weird situations... :) + XCTFail("\(name) always failed to complete.") + } + } + } } From 486960a04b5e714b0a0549f650a13cd719630a6c Mon Sep 17 00:00:00 2001 From: Carl Smith Date: Mon, 22 Sep 2025 15:56:26 +0200 Subject: [PATCH 08/35] Remove .with and .wasmBlock context. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These contexts were never required by anything, as such we can just remove them. Change-Id: Ied21ed55ef8c4a0cf860b635fdb2b9b1ea448d86 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8570516 Reviewed-by: Samuel Groß Reviewed-by: Matthias Liedtke Commit-Queue: Carl Smith --- Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 6 ++--- .../Fuzzilli/CodeGen/WasmCodeGenerators.swift | 24 ++++++++--------- Sources/Fuzzilli/FuzzIL/Context.swift | 27 +++++-------------- Sources/Fuzzilli/FuzzIL/JsOperations.swift | 2 +- Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 16 +++++------ Tests/FuzzilliTests/AnalyzerTest.swift | 6 ++--- Tests/FuzzilliTests/ContextGraphTest.swift | 5 +--- 7 files changed, 34 insertions(+), 52 deletions(-) diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 93b016d55..0802e1037 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -2752,8 +2752,7 @@ public let CodeGenerators: [CodeGenerator] = [ [ GeneratorStub( "WithStatementBeginGenerator", - inputs: .preferred(.object()), - provides: [.with] + inputs: .preferred(.object()) ) { b, obj in b.emit(BeginWith(), withInputs: [obj]) for i in 1...3 { @@ -2764,8 +2763,7 @@ public let CodeGenerators: [CodeGenerator] = [ } }, GeneratorStub( - "WithStatementEndGenerator", - inContext: .single(.with) + "WithStatementEndGenerator" ) { b in b.emit(EndWith()) }, diff --git a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift index 69f20de4d..f1816e9a8 100644 --- a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift @@ -1238,13 +1238,13 @@ public let WasmCodeGenerators: [CodeGenerator] = [ GeneratorStub( "WasmBeginBlockGenerator", inContext: .single(.wasmFunction), - provides: [.wasmBlock] + provides: [.wasmFunction] ) { b in b.emit(WasmBeginBlock(with: [] => [])) }, GeneratorStub( "WasmEndBlockGenerator", - inContext: .single(.wasmBlock) + inContext: .single(.wasmFunction) ) { b in b.emit(WasmEndBlock(outputTypes: [])) }, @@ -1256,7 +1256,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ GeneratorStub( "WasmBeginBlockGenerator", inContext: .single(.wasmFunction), - provides: [.wasmBlock] + provides: [.wasmFunction] ) { b in let args = b.randomWasmBlockArguments(upTo: 5) let parameters = args.map(b.type) @@ -1267,7 +1267,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ }, GeneratorStub( "WasmEndBlockGenerator", - inContext: .single(.wasmBlock) + inContext: .single(.wasmFunction) ) { b in let signature = b.currentWasmSignature let function = b.currentWasmFunction @@ -1445,7 +1445,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ "WasmBeginIfGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmi32), - provides: [.wasmBlock, .wasmFunction] + provides: [.wasmFunction] ) { b, condition in b.emit( WasmBeginIf(hint: b.randomWasmBranchHint()), @@ -1453,14 +1453,14 @@ public let WasmCodeGenerators: [CodeGenerator] = [ }, GeneratorStub( "WasmBeginElseGenerator", - inContext: .single([.wasmFunction, .wasmBlock]), - provides: [.wasmBlock, .wasmFunction] + inContext: .single(.wasmFunction), + provides: [.wasmFunction] ) { b in b.emit(WasmBeginElse()) }, GeneratorStub( "WasmEndIfElseGenerator", - inContext: .single([.wasmFunction, .wasmBlock]) + inContext: .single(.wasmFunction) ) { b in b.emit(WasmEndIf()) }, @@ -1473,7 +1473,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ "WasmBeginIfGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmi32), - provides: [.wasmBlock, .wasmFunction] + provides: [.wasmFunction] ) { b, condition in let args = b.randomWasmBlockArguments(upTo: 5) let parameters = args.map(b.type) @@ -1486,8 +1486,8 @@ public let WasmCodeGenerators: [CodeGenerator] = [ }, GeneratorStub( "WasmBeginElseGenerator", - inContext: .single([.wasmBlock, .wasmFunction]), - provides: [.wasmFunction, .wasmBlock] + inContext: .single(.wasmFunction), + provides: [.wasmFunction] ) { b in let function = b.currentWasmFunction let signature = b.currentWasmSignature @@ -1497,7 +1497,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ }, GeneratorStub( "WasmEndIfGenerator", - inContext: .single([.wasmFunction, .wasmBlock]) + inContext: .single(.wasmFunction) ) { b in let function = b.currentWasmFunction let signature = b.currentWasmSignature diff --git a/Sources/Fuzzilli/FuzzIL/Context.swift b/Sources/Fuzzilli/FuzzIL/Context.swift index ea0853760..550610934 100644 --- a/Sources/Fuzzilli/FuzzIL/Context.swift +++ b/Sources/Fuzzilli/FuzzIL/Context.swift @@ -22,14 +22,12 @@ public struct Context: OptionSet, Hashable, CaseIterable { .method, .classMethod, .loop, - .with, .objectLiteral, .classDefinition, .switchBlock, .switchCase, .wasm, .wasmFunction, - .wasmBlock, .wasmTypeGroup, .empty, ] @@ -59,29 +57,24 @@ public struct Context: OptionSet, Hashable, CaseIterable { public static let classMethod = Context(rawValue: 1 << 5) // Inside a loop. public static let loop = Context(rawValue: 1 << 6) - // Inside a with statement. - public static let with = Context(rawValue: 1 << 7) // Inside an object literal. - public static let objectLiteral = Context(rawValue: 1 << 8) + public static let objectLiteral = Context(rawValue: 1 << 7) // Inside a class definition. - public static let classDefinition = Context(rawValue: 1 << 9) + public static let classDefinition = Context(rawValue: 1 << 8) // Inside a switch block. - public static let switchBlock = Context(rawValue: 1 << 10) + public static let switchBlock = Context(rawValue: 1 << 9) // Inside a switch case. - public static let switchCase = Context(rawValue: 1 << 11) + public static let switchCase = Context(rawValue: 1 << 10) // Inside a wasm module - public static let wasm = Context(rawValue: 1 << 12) + public static let wasm = Context(rawValue: 1 << 11) // Inside a function in a wasm module - public static let wasmFunction = Context(rawValue: 1 << 13) - // Inside a block of a wasm function, allows branches - public static let wasmBlock = Context(rawValue: 1 << 14) + public static let wasmFunction = Context(rawValue: 1 << 12) // Inside a wasm recursive type group definition. - public static let wasmTypeGroup = Context(rawValue: 1 << 15) + public static let wasmTypeGroup = Context(rawValue: 1 << 13) public static let empty = Context([]) public var inWasm: Bool { - // .wasmBlock is propagating surrounding context self.contains(.wasm) || self.contains(.wasmFunction) } } @@ -110,9 +103,6 @@ extension Context: CustomStringConvertible { if self.contains(.loop) { strings.append(".loop") } - if self.contains(.with) { - strings.append(".with") - } if self.contains(.objectLiteral) { strings.append(".objectLiteral") } @@ -131,9 +121,6 @@ extension Context: CustomStringConvertible { if self.contains(.wasmFunction) { strings.append(".wasmFunction") } - if self.contains(.wasmBlock) { - strings.append(".wasmBlock") - } if self.contains(.wasmTypeGroup) { strings.append(".wasmTypeGroup") } diff --git a/Sources/Fuzzilli/FuzzIL/JsOperations.swift b/Sources/Fuzzilli/FuzzIL/JsOperations.swift index 4bcfbaf4b..1bf2a4e0c 100644 --- a/Sources/Fuzzilli/FuzzIL/JsOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/JsOperations.swift @@ -1801,7 +1801,7 @@ final class BeginWith: JsOperation { override var opcode: Opcode { .beginWith(self) } init() { - super.init(numInputs: 1, attributes: [.isBlockStart, .propagatesSurroundingContext], contextOpened: [.javascript, .with]) + super.init(numInputs: 1, attributes: [.isBlockStart, .propagatesSurroundingContext], contextOpened: [.javascript]) } } diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index f4ded593f..c05115eba 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -1251,7 +1251,7 @@ final class WasmBeginBlock: WasmOperation { init(with signature: WasmSignature) { self.signature = signature - super.init(numInputs: signature.parameterTypes.count, numInnerOutputs: signature.parameterTypes.count + 1, attributes: [.isBlockStart, .propagatesSurroundingContext], requiredContext: [.wasmFunction], contextOpened: [.wasmBlock]) + super.init(numInputs: signature.parameterTypes.count, numInnerOutputs: signature.parameterTypes.count + 1, attributes: [.isBlockStart, .propagatesSurroundingContext], requiredContext: [.wasmFunction]) } } @@ -1262,7 +1262,7 @@ final class WasmEndBlock: WasmOperation { init(outputTypes: [ILType]) { self.outputTypes = outputTypes - super.init(numInputs: outputTypes.count, numOutputs: outputTypes.count, attributes: [.isBlockEnd, .resumesSurroundingContext], requiredContext: [.wasmFunction, .wasmBlock]) + super.init(numInputs: outputTypes.count, numOutputs: outputTypes.count, attributes: [.isBlockEnd, .resumesSurroundingContext], requiredContext: [.wasmFunction]) } } @@ -1286,7 +1286,7 @@ final class WasmBeginIf: WasmOperation { // value stack and that the condition is the first value to be removed from the stack, so // it needs to be the last one pushed to it. // Inner outputs: 1 label (used for branch instructions) plus all the parameters. - super.init(numInputs: signature.parameterTypes.count + 1, numInnerOutputs: 1 + signature.parameterTypes.count, attributes: [.isBlockStart, .propagatesSurroundingContext, .isMutable], requiredContext: [.wasmFunction], contextOpened: [.wasmBlock]) + super.init(numInputs: signature.parameterTypes.count + 1, numInnerOutputs: 1 + signature.parameterTypes.count, attributes: [.isBlockStart, .propagatesSurroundingContext, .isMutable], requiredContext: [.wasmFunction]) } } @@ -1299,7 +1299,7 @@ final class WasmBeginElse: WasmOperation { // The WasmBeginElse acts both as a block end for the true case and as a block start for the // false case. As such, its input types are the results from the true block and its inner // output types are the same as for the corresponding WasmBeginIf. - super.init(numInputs: signature.outputTypes.count, numInnerOutputs: 1 + signature.parameterTypes.count, attributes: [.isBlockStart, .isBlockEnd, .propagatesSurroundingContext], requiredContext: [.wasmFunction], contextOpened: [.wasmBlock]) + super.init(numInputs: signature.outputTypes.count, numInnerOutputs: 1 + signature.parameterTypes.count, attributes: [.isBlockStart, .isBlockEnd, .propagatesSurroundingContext], requiredContext: [.wasmFunction]) } } @@ -1309,7 +1309,7 @@ final class WasmEndIf: WasmOperation { init(outputTypes: [ILType] = []) { self.outputTypes = outputTypes - super.init(numInputs: outputTypes.count, numOutputs: outputTypes.count, attributes: [.isBlockEnd], requiredContext: [.wasmBlock, .wasmFunction]) + super.init(numInputs: outputTypes.count, numOutputs: outputTypes.count, attributes: [.isBlockEnd], requiredContext: [.wasmFunction]) } } @@ -1352,7 +1352,7 @@ final class WasmBeginTryTable: WasmOperation { self.catches = catches let inputTagCount = catches.count {$0 == .Ref || $0 == .NoRef} let inputLabelCount = catches.count - super.init(numInputs: signature.parameterTypes.count + inputLabelCount + inputTagCount , numInnerOutputs: signature.parameterTypes.count + 1, attributes: [.isBlockStart, .propagatesSurroundingContext], requiredContext: [.wasmFunction], contextOpened: [.wasmBlock]) + super.init(numInputs: signature.parameterTypes.count + inputLabelCount + inputTagCount , numInnerOutputs: signature.parameterTypes.count + 1, attributes: [.isBlockStart, .propagatesSurroundingContext], requiredContext: [.wasmFunction]) } } @@ -1362,7 +1362,7 @@ final class WasmEndTryTable: WasmOperation { init(outputTypes: [ILType]) { self.outputTypes = outputTypes - super.init(numInputs: outputTypes.count, numOutputs: outputTypes.count, attributes: [.isBlockEnd, .resumesSurroundingContext], requiredContext: [.wasmFunction, .wasmBlock]) + super.init(numInputs: outputTypes.count, numOutputs: outputTypes.count, attributes: [.isBlockEnd, .resumesSurroundingContext], requiredContext: [.wasmFunction]) } } @@ -1372,7 +1372,7 @@ final class WasmBeginTry: WasmOperation { init(with signature: WasmSignature) { self.signature = signature - super.init(numInputs: signature.parameterTypes.count, numInnerOutputs: signature.parameterTypes.count + 1, attributes: [.isBlockStart, .propagatesSurroundingContext], requiredContext: [.wasmFunction], contextOpened: [.wasmBlock]) + super.init(numInputs: signature.parameterTypes.count, numInnerOutputs: signature.parameterTypes.count + 1, attributes: [.isBlockStart, .propagatesSurroundingContext], requiredContext: [.wasmFunction]) } } diff --git a/Tests/FuzzilliTests/AnalyzerTest.swift b/Tests/FuzzilliTests/AnalyzerTest.swift index 4bffb5cde..546e99cd7 100644 --- a/Tests/FuzzilliTests/AnalyzerTest.swift +++ b/Tests/FuzzilliTests/AnalyzerTest.swift @@ -120,14 +120,14 @@ class AnalyzerTests: XCTestCase { XCTAssertEqual(b.context, .javascript) let obj = b.loadString("HelloWorld") b.buildWith(obj) { - XCTAssertEqual(b.context, [.javascript, .with]) + XCTAssertEqual(b.context, [.javascript]) b.buildPlainFunction(with: .parameters(n: 3)) { _ in XCTAssertEqual(b.context, [.javascript, .subroutine]) b.buildWith(obj) { - XCTAssertEqual(b.context, [.javascript, .subroutine, .with]) + XCTAssertEqual(b.context, [.javascript, .subroutine]) } } - XCTAssertEqual(b.context, [.javascript, .with]) + XCTAssertEqual(b.context, [.javascript]) b.createNamedVariable(b.randomPropertyName(), declarationMode: .none) } diff --git a/Tests/FuzzilliTests/ContextGraphTest.swift b/Tests/FuzzilliTests/ContextGraphTest.swift index 7a1265a5f..1ffdbc914 100644 --- a/Tests/FuzzilliTests/ContextGraphTest.swift +++ b/Tests/FuzzilliTests/ContextGraphTest.swift @@ -36,9 +36,7 @@ class ContextGraphTests: XCTestCase { .asyncFunction, .wasmFunction, .wasm, - .with, .loop, - .wasmBlock, .generatorFunction, .objectLiteral, .subroutine, @@ -59,8 +57,7 @@ class ContextGraphTests: XCTestCase { XCTAssertTrue(reachableContextsWasmFunction.isSubset(of: reachableContextsWasm)) XCTAssertEqual(reachableContextsWasm, Set([.wasmFunction, - .wasm, - .wasmBlock])) + .wasm])) XCTAssertTrue(reachableContextsWasm.isSubset(of: reachableContextsJavaScript)) } } From 67b1b439ccb5efb226f655c8ac7b18bd210e8d8c Mon Sep 17 00:00:00 2001 From: Carl Smith Date: Mon, 22 Sep 2025 16:26:45 +0200 Subject: [PATCH 09/35] Register Enumerations defined in Profiles. Custom enumerations in Profiles are now registered on the Environment at startup. Change-Id: Id95da20c7ef81a7d1f7377f87c408df27ab086c0 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8609376 Commit-Queue: Carl Smith Reviewed-by: Matthias Liedtke --- .../Environment/JavaScriptEnvironment.swift | 7 ++++++- .../FuzzilliCli/Profiles/DuktapeProfile.swift | 2 ++ Sources/FuzzilliCli/Profiles/JSCProfile.swift | 2 ++ .../Profiles/JerryscriptProfile.swift | 2 ++ Sources/FuzzilliCli/Profiles/NjsProfile.swift | 2 ++ Sources/FuzzilliCli/Profiles/Profile.swift | 1 + Sources/FuzzilliCli/Profiles/QjsProfile.swift | 2 ++ Sources/FuzzilliCli/Profiles/QtjsProfile.swift | 2 ++ Sources/FuzzilliCli/Profiles/Serenity.swift | 2 ++ .../Profiles/SpidermonkeyProfile.swift | 2 ++ .../Profiles/V8HoleFuzzingProfile.swift | 18 ++++++++++++++++-- Sources/FuzzilliCli/Profiles/V8Profile.swift | 2 ++ .../Profiles/V8SandboxProfile.swift | 6 ++++-- Sources/FuzzilliCli/Profiles/XSProfile.swift | 2 ++ Sources/FuzzilliCli/main.swift | 5 ++++- 15 files changed, 51 insertions(+), 6 deletions(-) diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index 6209d2527..779a8db41 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -303,7 +303,7 @@ public class JavaScriptEnvironment: ComponentBase { private var producingProperties: [ILType: [(group: String, property: String)]] = [:] private var subtypes: [ILType: [ILType]] = [:] - public init(additionalBuiltins: [String: ILType] = [:], additionalObjectGroups: [ObjectGroup] = []) { + public init(additionalBuiltins: [String: ILType] = [:], additionalObjectGroups: [ObjectGroup] = [], additionalEnumerations: [ILType] = []) { super.init(name: "JavaScriptEnvironment") // Build model of the JavaScript environment @@ -427,6 +427,11 @@ public class JavaScriptEnvironment: ComponentBase { registerEnumeration(OptionsBag.base64Alphabet) registerEnumeration(OptionsBag.base64LastChunkHandling) + for enumeration in additionalEnumerations { + assert(enumeration.isEnumeration) + registerEnumeration(enumeration) + } + registerOptionsBag(.jsTemporalDifferenceSettingOrRoundTo) registerOptionsBag(.jsTemporalToStringSettings) registerOptionsBag(.jsTemporalOverflowSettings) diff --git a/Sources/FuzzilliCli/Profiles/DuktapeProfile.swift b/Sources/FuzzilliCli/Profiles/DuktapeProfile.swift index cb144cf5e..853aeae20 100644 --- a/Sources/FuzzilliCli/Profiles/DuktapeProfile.swift +++ b/Sources/FuzzilliCli/Profiles/DuktapeProfile.swift @@ -63,5 +63,7 @@ let duktapeProfile = Profile( additionalObjectGroups: [], + additionalEnumerations: [], + optionalPostProcessor: nil ) diff --git a/Sources/FuzzilliCli/Profiles/JSCProfile.swift b/Sources/FuzzilliCli/Profiles/JSCProfile.swift index 3607b2ff8..e415ed64c 100644 --- a/Sources/FuzzilliCli/Profiles/JSCProfile.swift +++ b/Sources/FuzzilliCli/Profiles/JSCProfile.swift @@ -127,5 +127,7 @@ let jscProfile = Profile( additionalObjectGroups: [], + additionalEnumerations: [], + optionalPostProcessor: nil ) diff --git a/Sources/FuzzilliCli/Profiles/JerryscriptProfile.swift b/Sources/FuzzilliCli/Profiles/JerryscriptProfile.swift index 96a549071..b59833a4c 100644 --- a/Sources/FuzzilliCli/Profiles/JerryscriptProfile.swift +++ b/Sources/FuzzilliCli/Profiles/JerryscriptProfile.swift @@ -59,5 +59,7 @@ let jerryscriptProfile = Profile( additionalObjectGroups: [], + additionalEnumerations: [], + optionalPostProcessor: nil ) diff --git a/Sources/FuzzilliCli/Profiles/NjsProfile.swift b/Sources/FuzzilliCli/Profiles/NjsProfile.swift index ce1ddce53..5b629c028 100644 --- a/Sources/FuzzilliCli/Profiles/NjsProfile.swift +++ b/Sources/FuzzilliCli/Profiles/NjsProfile.swift @@ -55,5 +55,7 @@ let njsProfile = Profile( additionalObjectGroups: [], + additionalEnumerations: [], + optionalPostProcessor: nil ) diff --git a/Sources/FuzzilliCli/Profiles/Profile.swift b/Sources/FuzzilliCli/Profiles/Profile.swift index d3a0038c4..726499d82 100644 --- a/Sources/FuzzilliCli/Profiles/Profile.swift +++ b/Sources/FuzzilliCli/Profiles/Profile.swift @@ -35,6 +35,7 @@ struct Profile { let additionalBuiltins: [String: ILType] let additionalObjectGroups: [ObjectGroup] + let additionalEnumerations: [ILType] // An optional post-processor that is executed for every sample generated for fuzzing and can modify it. let optionalPostProcessor: FuzzingPostProcessor? diff --git a/Sources/FuzzilliCli/Profiles/QjsProfile.swift b/Sources/FuzzilliCli/Profiles/QjsProfile.swift index 6bd8e60ec..83dd77d2c 100644 --- a/Sources/FuzzilliCli/Profiles/QjsProfile.swift +++ b/Sources/FuzzilliCli/Profiles/QjsProfile.swift @@ -57,5 +57,7 @@ let qjsProfile = Profile( additionalObjectGroups: [], + additionalEnumerations: [], + optionalPostProcessor: nil ) diff --git a/Sources/FuzzilliCli/Profiles/QtjsProfile.swift b/Sources/FuzzilliCli/Profiles/QtjsProfile.swift index d77968663..0ba7ff22c 100644 --- a/Sources/FuzzilliCli/Profiles/QtjsProfile.swift +++ b/Sources/FuzzilliCli/Profiles/QtjsProfile.swift @@ -65,5 +65,7 @@ let qtjsProfile = Profile( additionalObjectGroups: [], + additionalEnumerations: [], + optionalPostProcessor: nil ) diff --git a/Sources/FuzzilliCli/Profiles/Serenity.swift b/Sources/FuzzilliCli/Profiles/Serenity.swift index ac6f25e3a..286b5a520 100644 --- a/Sources/FuzzilliCli/Profiles/Serenity.swift +++ b/Sources/FuzzilliCli/Profiles/Serenity.swift @@ -52,5 +52,7 @@ let serenityProfile = Profile( additionalObjectGroups: [], + additionalEnumerations: [], + optionalPostProcessor: nil ) diff --git a/Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift b/Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift index fac719046..a7ce9123d 100644 --- a/Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift +++ b/Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift @@ -118,5 +118,7 @@ let spidermonkeyProfile = Profile( additionalObjectGroups: [], + additionalEnumerations: [], + optionalPostProcessor: nil ) diff --git a/Sources/FuzzilliCli/Profiles/V8HoleFuzzingProfile.swift b/Sources/FuzzilliCli/Profiles/V8HoleFuzzingProfile.swift index 8d6b75f6a..d292c4cba 100644 --- a/Sources/FuzzilliCli/Profiles/V8HoleFuzzingProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8HoleFuzzingProfile.swift @@ -35,13 +35,19 @@ let v8HoleFuzzingProfile = Profile( ] return args }, + processEnv: [:], + maxExecsBeforeRespawn: 1000, + timeout: 250, + codePrefix: """ """, + codeSuffix: """ """, + ecmaVersion: ECMAScriptVersion.es6, startupTests: [ @@ -67,15 +73,23 @@ let v8HoleFuzzingProfile = Profile( (V8GcGenerator, 10), (HoleLeakGenerator, 25), ], + additionalProgramTemplates: WeightedList([ ]), + disabledCodeGenerators: [], + disabledMutators: [], + additionalBuiltins: [ - "gc" : .function([] => (.undefined | .jsPromise)), + "gc" : .function([.opt(gcOptions.instanceType)] => (.undefined | .jsPromise)), "d8" : .object(), "Worker" : .constructor([.jsAnything, .object()] => .object(withMethods: ["postMessage","getMessage"])), ], - additionalObjectGroups: [], + + additionalObjectGroups: [jsD8, jsD8Test, jsD8FastCAPI, gcOptions], + + additionalEnumerations: [.gcTypeEnum, .gcExecutionEnum], + optionalPostProcessor: nil ) diff --git a/Sources/FuzzilliCli/Profiles/V8Profile.swift b/Sources/FuzzilliCli/Profiles/V8Profile.swift index 8b5ce4da2..313ca249d 100644 --- a/Sources/FuzzilliCli/Profiles/V8Profile.swift +++ b/Sources/FuzzilliCli/Profiles/V8Profile.swift @@ -309,5 +309,7 @@ let v8Profile = Profile( additionalObjectGroups: [jsD8, jsD8Test, jsD8FastCAPI, gcOptions], + additionalEnumerations: [.gcTypeEnum, .gcExecutionEnum], + optionalPostProcessor: nil ) diff --git a/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift b/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift index e3b168ff1..633150068 100644 --- a/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift @@ -493,12 +493,14 @@ let v8SandboxProfile = Profile( disabledMutators: [], additionalBuiltins: [ - "gc" : .function([] => (.undefined | .jsPromise)), + "gc" : .function([.opt(gcOptions.instanceType)] => (.undefined | .jsPromise)), "d8" : .object(), "Worker" : .constructor([.jsAnything, .object()] => .object(withMethods: ["postMessage","getMessage"])), ], - additionalObjectGroups: [], + additionalObjectGroups: [jsD8, jsD8Test, jsD8FastCAPI, gcOptions], + + additionalEnumerations: [.gcTypeEnum, .gcExecutionEnum], optionalPostProcessor: SandboxFuzzingPostProcessor() ) diff --git a/Sources/FuzzilliCli/Profiles/XSProfile.swift b/Sources/FuzzilliCli/Profiles/XSProfile.swift index 3277d0e52..2abb68b2c 100644 --- a/Sources/FuzzilliCli/Profiles/XSProfile.swift +++ b/Sources/FuzzilliCli/Profiles/XSProfile.swift @@ -352,5 +352,7 @@ let xsProfile = Profile( additionalObjectGroups: [jsCompartments, jsCompartmentConstructor, jsModuleSources, jsModuleSourceConstructor], + additionalEnumerations: [], + optionalPostProcessor: nil ) diff --git a/Sources/FuzzilliCli/main.swift b/Sources/FuzzilliCli/main.swift index 11466c6e6..a01f59475 100644 --- a/Sources/FuzzilliCli/main.swift +++ b/Sources/FuzzilliCli/main.swift @@ -459,13 +459,16 @@ func makeFuzzer(with configuration: Configuration) -> Fuzzer { } // The environment containing available builtins, property names, and method names. - let environment = JavaScriptEnvironment(additionalBuiltins: profile.additionalBuiltins, additionalObjectGroups: profile.additionalObjectGroups) + let environment = JavaScriptEnvironment(additionalBuiltins: profile.additionalBuiltins, additionalObjectGroups: profile.additionalObjectGroups, additionalEnumerations: profile.additionalEnumerations) if !profile.additionalBuiltins.isEmpty { logger.verbose("Loaded additional builtins from profile: \(profile.additionalBuiltins.map { $0.key })") } if !profile.additionalObjectGroups.isEmpty { logger.verbose("Loaded additional ObjectGroups from profile: \(profile.additionalObjectGroups.map { $0.name })") } + if !profile.additionalEnumerations.isEmpty { + logger.verbose("Loaded additional Enumerations from profile: \(profile.additionalEnumerations.map { $0.group! })") + } // A lifter to translate FuzzIL programs to JavaScript. let lifter = JavaScriptLifter(prefix: profile.codePrefix, From af50fa4f53fb06373accd6d37cc5562284bd9963 Mon Sep 17 00:00:00 2001 From: Carl Smith Date: Mon, 22 Sep 2025 16:40:17 +0200 Subject: [PATCH 10/35] Fix compile error in release build. The BuildLog in the ProgramBuilder needs to be initialized in the initializer. Change-Id: I236abcea185b89de3df09abdc777914e7dd8f619 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8609377 Commit-Queue: Matthias Liedtke Reviewed-by: Matthias Liedtke Auto-Submit: Carl Smith Commit-Queue: Carl Smith --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 67a0a9ff0..46860dd9a 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -193,6 +193,10 @@ public class ProgramBuilder { self.fuzzer = fuzzer self.jsTyper = JSTyper(for: fuzzer.environment) self.parent = parent + + if fuzzer.config.logLevel.isAtLeast(.verbose) { + self.buildLog = BuildLog() + } } /// Resets this builder. @@ -1790,7 +1794,8 @@ public class ProgramBuilder { // We definitely want to have the BuildLog in DEBUG builds. var buildLog: BuildLog? = BuildLog() #else - var buildLog: BuildLog? = fuzzer.config.logLevel.isAtLeast(.verbose) ? BuildLog() : nil + // We initialize this depending on the LogLevel in the initializer. + var buildLog: BuildLog? = nil #endif /// Build random code at the current position in the program. From c88625162b23af3d1d6e44a163c02a43dbfcfa7f Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Tue, 23 Sep 2025 09:02:38 +0200 Subject: [PATCH 11/35] Add explicit resource management to the fuzzIL compiler This enables parsing and importing some of the syntax provided by the explicit-resource-management feature. The first stage is to parse `using` and `await using` statements and add them to the AST for the fuzzIL compiler. The second stage is to represent them with named variables, similar to other (const, let, var) named variables. We cannot just extend the existing named variables with a few more declaration types, since (async) disposable variables are also separate instructions, to enforce their respective syntactic constraints. This does not yet enable many useful examples though. We do not yet support disposable variables in loops. Nor does the fuzzIL compiler support computed properties yet, which are typically used in conjunction with disposable variables. Bug: 441669678 Change-Id: I63bcf8a8e2ed250c23a3017aab396d777ef9c900 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8552137 Reviewed-by: Matthias Liedtke Commit-Queue: Michael Achenbach --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 10 ++ Sources/Fuzzilli/Compiler/Compiler.swift | 33 +++-- Sources/Fuzzilli/Compiler/Parser/parser.js | 21 +++- Sources/Fuzzilli/FuzzIL/Instruction.swift | 12 ++ Sources/Fuzzilli/FuzzIL/JSTyper.swift | 6 + Sources/Fuzzilli/FuzzIL/JsOperations.swift | 23 ++++ Sources/Fuzzilli/FuzzIL/Opcodes.swift | 2 + Sources/Fuzzilli/Lifting/FuzzILLifter.swift | 6 + .../Fuzzilli/Lifting/JavaScriptLifter.swift | 8 ++ .../Fuzzilli/Mutators/OperationMutator.swift | 2 + Sources/Fuzzilli/Protobuf/ast.pb.swift | 115 +++++++++++++++++- Sources/Fuzzilli/Protobuf/ast.proto | 11 ++ Sources/Fuzzilli/Protobuf/operations.pb.swift | 84 +++++++++++++ Sources/Fuzzilli/Protobuf/operations.proto | 8 ++ Sources/Fuzzilli/Protobuf/program.pb.swift | 54 +++++++- Sources/Fuzzilli/Protobuf/program.proto | 2 + .../explicit_resource_management.js | 56 +++++++++ Tests/FuzzilliTests/LifterTest.swift | 44 +++++-- 18 files changed, 476 insertions(+), 21 deletions(-) create mode 100644 Tests/FuzzilliTests/CompilerTests/explicit_resource_management.js diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 46860dd9a..b87f4dfe0 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -3093,6 +3093,16 @@ public class ProgramBuilder { return createNamedVariable(builtinName, declarationMode: .none) } + @discardableResult + public func createNamedDisposableVariable(_ name: String, _ initialValue: Variable) -> Variable { + return emit(CreateNamedDisposableVariable(name), withInputs: [initialValue]).output + } + + @discardableResult + public func createNamedAsyncDisposableVariable(_ name: String, _ initialValue: Variable) -> Variable { + return emit(CreateNamedAsyncDisposableVariable(name), withInputs: [initialValue]).output + } + @discardableResult public func eval(_ string: String, with arguments: [Variable] = [], hasOutput: Bool = false) -> Variable? { let instr = emit(Eval(string, numArguments: arguments.count, hasOutput: hasOutput), withInputs: arguments) diff --git a/Sources/Fuzzilli/Compiler/Compiler.swift b/Sources/Fuzzilli/Compiler/Compiler.swift index 43e8e58ff..8866f63f8 100644 --- a/Sources/Fuzzilli/Compiler/Compiler.swift +++ b/Sources/Fuzzilli/Compiler/Compiler.swift @@ -238,6 +238,14 @@ public class JavaScriptCompiler { return classDecl.output } + private func compileInitialDeclarationValue(_ decl: Compiler_Protobuf_VariableDeclarator) throws -> Variable { + if decl.hasValue { + return try compileExpression(decl.value) + } else { + // TODO(saelo): consider caching the `undefined` value for future uses + return emit(LoadUndefined()).output + } + } private func compileStatement(_ node: StatementNode) throws { guard let stmt = node.statement else { @@ -260,13 +268,7 @@ public class JavaScriptCompiler { case .variableDeclaration(let variableDeclaration): for decl in variableDeclaration.declarations { - let initialValue: Variable - if decl.hasValue { - initialValue = try compileExpression(decl.value) - } else { - // TODO(saelo): consider caching the `undefined` value for future uses - initialValue = emit(LoadUndefined()).output - } + let initialValue = try compileInitialDeclarationValue(decl) let declarationMode: NamedVariableDeclarationMode switch variableDeclaration.kind { @@ -286,6 +288,23 @@ public class JavaScriptCompiler { mapOrRemap(decl.name, to: v) } + case .disposableVariableDeclaration(let variableDeclaration): + for decl in variableDeclaration.declarations { + let initialValue = try compileInitialDeclarationValue(decl) + + let v: Variable; + switch variableDeclaration.kind { + case .using: + v = emit(CreateNamedDisposableVariable(decl.name), withInputs: [initialValue]).output + case .awaitUsing: + v = emit(CreateNamedAsyncDisposableVariable(decl.name), withInputs: [initialValue]).output + case .UNRECOGNIZED(let type): + throw CompilerError.invalidNodeError("invalid disposable variable declaration type \(type)") + } + assert(!currentScope.keys.contains(decl.name)) + mapOrRemap(decl.name, to: v) + } + case .functionDeclaration(let functionDeclaration): let parameters = convertParameters(functionDeclaration.parameters) let functionBegin, functionEnd: Operation diff --git a/Sources/Fuzzilli/Compiler/Parser/parser.js b/Sources/Fuzzilli/Compiler/Parser/parser.js index f42fe00dd..fb9a30543 100644 --- a/Sources/Fuzzilli/Compiler/Parser/parser.js +++ b/Sources/Fuzzilli/Compiler/Parser/parser.js @@ -34,7 +34,7 @@ function tryReadFile(path) { // Parse the given JavaScript script and return an AST compatible with Fuzzilli's protobuf-based AST format. function parse(script, proto) { - let ast = Parser.parse(script, { plugins: ["v8intrinsic"] }); + let ast = Parser.parse(script, { plugins: ["explicitResourceManagement", "v8intrinsic"] }); function assertNoError(err) { if (err) throw err; @@ -98,12 +98,22 @@ function parse(script, proto) { function visitVariableDeclaration(node) { let kind; + let disposable; if (node.kind === "var") { kind = 0; + disposable = false; } else if (node.kind === "let") { kind = 1; + disposable = false; } else if (node.kind === "const") { kind = 2; + disposable = false; + } else if (node.kind === "using") { + kind = 0; + disposable = true; + } else if (node.kind === "await using") { + kind = 1; + disposable = true; } else { throw "Unknown variable declaration kind: " + node.kind; } @@ -118,7 +128,8 @@ function parse(script, proto) { declarations.push(make('VariableDeclarator', outDecl)); } - return { kind, declarations }; + const type = disposable ? 'DisposableVariableDeclaration' : 'VariableDeclaration' + return [type, { kind, declarations }]; } function visitClass(node, isExpression) { @@ -222,7 +233,7 @@ function parse(script, proto) { return makeStatement('ExpressionStatement', { expression }); } case 'VariableDeclaration': { - return makeStatement('VariableDeclaration', visitVariableDeclaration(node)); + return makeStatement(...visitVariableDeclaration(node)); } case 'FunctionDeclaration': { assert(node.id.type === 'Identifier', "Expected an identifier as function declaration name"); @@ -275,7 +286,9 @@ function parse(script, proto) { let forLoop = {}; if (node.init !== null) { if (node.init.type === 'VariableDeclaration') { - forLoop.declaration = make('VariableDeclaration', visitVariableDeclaration(node.init)); + let [type, config] = visitVariableDeclaration(node.init); + assert(type != 'DisposableVariableDeclaration', 'Disposable variables in for loops are not yet supported') + forLoop.declaration = make(type, config); } else { forLoop.expression = visitExpression(node.init); } diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index d2db55c62..1a003c7f0 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -918,6 +918,14 @@ extension Instruction: ProtobufConvertible { $0.variableName = op.variableName $0.declarationMode = convertEnum(op.declarationMode, NamedVariableDeclarationMode.allCases) } + case .createNamedDisposableVariable(let op): + $0.createNamedDisposableVariable = Fuzzilli_Protobuf_CreateNamedDisposableVariable.with { + $0.variableName = op.variableName + } + case .createNamedAsyncDisposableVariable(let op): + $0.createNamedAsyncDisposableVariable = Fuzzilli_Protobuf_CreateNamedAsyncDisposableVariable.with { + $0.variableName = op.variableName + } case .eval(let op): $0.eval = Fuzzilli_Protobuf_Eval.with { $0.code = op.code @@ -2084,6 +2092,10 @@ extension Instruction: ProtobufConvertible { op = Compare(try convertEnum(p.op, Comparator.allCases)) case .createNamedVariable(let p): op = CreateNamedVariable(p.variableName, declarationMode: try convertEnum(p.declarationMode, NamedVariableDeclarationMode.allCases)) + case .createNamedDisposableVariable(let p): + op = CreateNamedDisposableVariable(p.variableName) + case .createNamedAsyncDisposableVariable(let p): + op = CreateNamedAsyncDisposableVariable(p.variableName) case .eval(let p): let numArguments = inouts.count - (p.hasOutput_p ? 1 : 0) op = Eval(p.code, numArguments: numArguments, hasOutput: p.hasOutput_p) diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index e2301d0fd..879e5255f 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -1459,6 +1459,12 @@ public struct JSTyper: Analyzer { case .loadAsyncDisposableVariable: set(instr.output, type(ofInput: 0)) + case .createNamedDisposableVariable: + set(instr.output, type(ofInput: 0)) + + case .createNamedAsyncDisposableVariable: + set(instr.output, type(ofInput: 0)) + case .loadNewTarget: set(instr.output, .function() | .undefined) diff --git a/Sources/Fuzzilli/FuzzIL/JsOperations.swift b/Sources/Fuzzilli/FuzzIL/JsOperations.swift index 1bf2a4e0c..929a6852d 100644 --- a/Sources/Fuzzilli/FuzzIL/JsOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/JsOperations.swift @@ -324,6 +324,18 @@ final class LoadDisposableVariable: JsOperation { } } +final class CreateNamedDisposableVariable: JsOperation { + override var opcode: Opcode { .createNamedDisposableVariable(self) } + + let variableName: String + + init(_ name: String) { + self.variableName = name + // TODO: Add support for block context, see details above. + super.init(numInputs: 1, numOutputs: 1, requiredContext: [.javascript, .subroutine]) + } +} + final class LoadAsyncDisposableVariable: JsOperation { override var opcode: Opcode { .loadAsyncDisposableVariable(self) } @@ -332,6 +344,17 @@ final class LoadAsyncDisposableVariable: JsOperation { } } +final class CreateNamedAsyncDisposableVariable: JsOperation { + override var opcode: Opcode { .createNamedAsyncDisposableVariable(self) } + + let variableName: String + + init(_ name: String) { + self.variableName = name + super.init(numInputs: 1, numOutputs: 1, requiredContext: [.javascript, .asyncFunction]) + } +} + public struct RegExpFlags: OptionSet, Hashable { public let rawValue: UInt32 diff --git a/Sources/Fuzzilli/FuzzIL/Opcodes.swift b/Sources/Fuzzilli/FuzzIL/Opcodes.swift index c067e9678..e3f1ff3f5 100644 --- a/Sources/Fuzzilli/FuzzIL/Opcodes.swift +++ b/Sources/Fuzzilli/FuzzIL/Opcodes.swift @@ -357,4 +357,6 @@ enum Opcode { case wasmDropElementSegment(WasmDropElementSegment) case wasmTableCopy(WasmTableCopy) case wasmDefineSignatureType(WasmDefineSignatureType) + case createNamedDisposableVariable(CreateNamedDisposableVariable) + case createNamedAsyncDisposableVariable(CreateNamedAsyncDisposableVariable) } diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift index 8a1ef5709..b78df09c6 100644 --- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift +++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift @@ -78,6 +78,12 @@ public class FuzzILLifter: Lifter { w.emit("\(output()) <- CreateNamedVariable '\(op.variableName)', '\(op.declarationMode)'") } + case .createNamedDisposableVariable(let op): + w.emit("\(output()) <- CreateNamedDisposableVariable '\(op.variableName)', \(input(0))") + + case .createNamedAsyncDisposableVariable(let op): + w.emit("\(output()) <- CreateNamedAsyncDisposableVariable '\(op.variableName)', \(input(0))") + case .loadDisposableVariable: w.emit("\(output()) <- LoadDisposableVariable \(input(0))") diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift index 76f5fba08..f4041ecff 100644 --- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift +++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift @@ -323,6 +323,14 @@ public class JavaScriptLifter: Lifter { } w.declare(instr.output, as: op.variableName) + case .createNamedDisposableVariable(let op): + w.emit("using \(op.variableName) = \(input(0));"); + w.declare(instr.output, as: op.variableName) + + case .createNamedAsyncDisposableVariable(let op): + w.emit("await using \(op.variableName) = \(input(0));"); + w.declare(instr.output, as: op.variableName) + case .loadDisposableVariable: let V = w.declare(instr.output); w.emit("using \(V) = \(input(0));"); diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift index fac287942..61fc36f6f 100644 --- a/Sources/Fuzzilli/Mutators/OperationMutator.swift +++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift @@ -617,6 +617,8 @@ public class OperationMutator: BaseInstructionMutator { .explore(_), .probe(_), .fixup(_), + .createNamedDisposableVariable(_), + .createNamedAsyncDisposableVariable(_), .loadDisposableVariable(_), .loadAsyncDisposableVariable(_), .void(_), diff --git a/Sources/Fuzzilli/Protobuf/ast.pb.swift b/Sources/Fuzzilli/Protobuf/ast.pb.swift index 2d83a4153..92144a983 100644 --- a/Sources/Fuzzilli/Protobuf/ast.pb.swift +++ b/Sources/Fuzzilli/Protobuf/ast.pb.swift @@ -72,6 +72,40 @@ public enum Compiler_Protobuf_VariableDeclarationKind: SwiftProtobuf.Enum, Swift } +public enum Compiler_Protobuf_DisposableVariableDeclarationKind: SwiftProtobuf.Enum, Swift.CaseIterable { + public typealias RawValue = Int + case using // = 0 + case awaitUsing // = 1 + case UNRECOGNIZED(Int) + + public init() { + self = .using + } + + public init?(rawValue: Int) { + switch rawValue { + case 0: self = .using + case 1: self = .awaitUsing + default: self = .UNRECOGNIZED(rawValue) + } + } + + public var rawValue: Int { + switch self { + case .using: return 0 + case .awaitUsing: return 1 + case .UNRECOGNIZED(let i): return i + } + } + + // The compiler won't synthesize support with the UNRECOGNIZED case. + public static let allCases: [Compiler_Protobuf_DisposableVariableDeclarationKind] = [ + .using, + .awaitUsing, + ] + +} + public enum Compiler_Protobuf_FunctionType: SwiftProtobuf.Enum, Swift.CaseIterable { public typealias RawValue = Int case plain // = 0 @@ -202,6 +236,20 @@ public struct Compiler_Protobuf_VariableDeclaration: Sendable { public init() {} } +public struct Compiler_Protobuf_DisposableVariableDeclaration: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var kind: Compiler_Protobuf_DisposableVariableDeclarationKind = .using + + public var declarations: [Compiler_Protobuf_VariableDeclarator] = [] + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + public struct Compiler_Protobuf_FunctionDeclaration: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -1169,6 +1217,14 @@ public struct Compiler_Protobuf_Statement: @unchecked Sendable { set {_uniqueStorage()._statement = .switchStatement(newValue)} } + public var disposableVariableDeclaration: Compiler_Protobuf_DisposableVariableDeclaration { + get { + if case .disposableVariableDeclaration(let v)? = _storage._statement {return v} + return Compiler_Protobuf_DisposableVariableDeclaration() + } + set {_uniqueStorage()._statement = .disposableVariableDeclaration(newValue)} + } + public var unknownFields = SwiftProtobuf.UnknownStorage() public enum OneOf_Statement: Equatable, Sendable { @@ -1192,6 +1248,7 @@ public struct Compiler_Protobuf_Statement: @unchecked Sendable { case throwStatement(Compiler_Protobuf_ThrowStatement) case withStatement(Compiler_Protobuf_WithStatement) case switchStatement(Compiler_Protobuf_SwitchStatement) + case disposableVariableDeclaration(Compiler_Protobuf_DisposableVariableDeclaration) } @@ -2359,6 +2416,10 @@ extension Compiler_Protobuf_VariableDeclarationKind: SwiftProtobuf._ProtoNamePro public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0VAR\0\u{1}LET\0\u{1}CONST\0") } +extension Compiler_Protobuf_DisposableVariableDeclarationKind: SwiftProtobuf._ProtoNameProviding { + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0USING\0\u{1}AWAIT_USING\0") +} + extension Compiler_Protobuf_FunctionType: SwiftProtobuf._ProtoNameProviding { public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0PLAIN\0\u{1}GENERATOR\0\u{1}ASYNC\0\u{1}ASYNC_GENERATOR\0") } @@ -2584,6 +2645,41 @@ extension Compiler_Protobuf_VariableDeclaration: SwiftProtobuf.Message, SwiftPro } } +extension Compiler_Protobuf_DisposableVariableDeclaration: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".DisposableVariableDeclaration" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}kind\0\u{1}declarations\0") + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularEnumField(value: &self.kind) }() + case 2: try { try decoder.decodeRepeatedMessageField(value: &self.declarations) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if self.kind != .using { + try visitor.visitSingularEnumField(value: self.kind, fieldNumber: 1) + } + if !self.declarations.isEmpty { + try visitor.visitRepeatedMessageField(value: self.declarations, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Compiler_Protobuf_DisposableVariableDeclaration, rhs: Compiler_Protobuf_DisposableVariableDeclaration) -> Bool { + if lhs.kind != rhs.kind {return false} + if lhs.declarations != rhs.declarations {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension Compiler_Protobuf_FunctionDeclaration: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".FunctionDeclaration" public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}name\0\u{1}type\0\u{1}parameters\0\u{1}body\0") @@ -4331,7 +4427,7 @@ extension Compiler_Protobuf_SwitchCase: SwiftProtobuf.Message, SwiftProtobuf._Me extension Compiler_Protobuf_Statement: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Statement" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}emptyStatement\0\u{1}blockStatement\0\u{1}variableDeclaration\0\u{1}functionDeclaration\0\u{1}classDeclaration\0\u{1}returnStatement\0\u{1}directiveStatement\0\u{1}expressionStatement\0\u{1}ifStatement\0\u{1}whileLoop\0\u{1}doWhileLoop\0\u{1}forLoop\0\u{1}forInLoop\0\u{1}forOfLoop\0\u{1}breakStatement\0\u{1}continueStatement\0\u{1}tryStatement\0\u{1}throwStatement\0\u{1}withStatement\0\u{1}switchStatement\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}emptyStatement\0\u{1}blockStatement\0\u{1}variableDeclaration\0\u{1}functionDeclaration\0\u{1}classDeclaration\0\u{1}returnStatement\0\u{1}directiveStatement\0\u{1}expressionStatement\0\u{1}ifStatement\0\u{1}whileLoop\0\u{1}doWhileLoop\0\u{1}forLoop\0\u{1}forInLoop\0\u{1}forOfLoop\0\u{1}breakStatement\0\u{1}continueStatement\0\u{1}tryStatement\0\u{1}throwStatement\0\u{1}withStatement\0\u{1}switchStatement\0\u{1}disposableVariableDeclaration\0") fileprivate class _StorageClass { var _statement: Compiler_Protobuf_Statement.OneOf_Statement? @@ -4624,6 +4720,19 @@ extension Compiler_Protobuf_Statement: SwiftProtobuf.Message, SwiftProtobuf._Mes _storage._statement = .switchStatement(v) } }() + case 21: try { + var v: Compiler_Protobuf_DisposableVariableDeclaration? + var hadOneofValue = false + if let current = _storage._statement { + hadOneofValue = true + if case .disposableVariableDeclaration(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + _storage._statement = .disposableVariableDeclaration(v) + } + }() default: break } } @@ -4717,6 +4826,10 @@ extension Compiler_Protobuf_Statement: SwiftProtobuf.Message, SwiftProtobuf._Mes guard case .switchStatement(let v)? = _storage._statement else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 20) }() + case .disposableVariableDeclaration?: try { + guard case .disposableVariableDeclaration(let v)? = _storage._statement else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 21) + }() case nil: break } } diff --git a/Sources/Fuzzilli/Protobuf/ast.proto b/Sources/Fuzzilli/Protobuf/ast.proto index bac50eb8a..3d49b7b8d 100644 --- a/Sources/Fuzzilli/Protobuf/ast.proto +++ b/Sources/Fuzzilli/Protobuf/ast.proto @@ -48,6 +48,16 @@ message VariableDeclaration { repeated VariableDeclarator declarations = 2; } +enum DisposableVariableDeclarationKind { + USING = 0; + AWAIT_USING = 1; +} + +message DisposableVariableDeclaration { + DisposableVariableDeclarationKind kind = 1; + repeated VariableDeclarator declarations = 2; +} + enum FunctionType { PLAIN = 0; GENERATOR = 1; @@ -251,6 +261,7 @@ message Statement { ThrowStatement throwStatement = 18; WithStatement withStatement = 19; SwitchStatement switchStatement = 20; + DisposableVariableDeclaration disposableVariableDeclaration = 21; } } diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index 8c49f9596..bd079d427 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -3190,6 +3190,30 @@ public struct Fuzzilli_Protobuf_CreateNamedVariable: Sendable { public init() {} } +public struct Fuzzilli_Protobuf_CreateNamedDisposableVariable: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var variableName: String = String() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +public struct Fuzzilli_Protobuf_CreateNamedAsyncDisposableVariable: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var variableName: String = String() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + public struct Fuzzilli_Protobuf_Eval: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -9519,6 +9543,66 @@ extension Fuzzilli_Protobuf_CreateNamedVariable: SwiftProtobuf.Message, SwiftPro } } +extension Fuzzilli_Protobuf_CreateNamedDisposableVariable: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".CreateNamedDisposableVariable" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}variableName\0") + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularStringField(value: &self.variableName) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.variableName.isEmpty { + try visitor.visitSingularStringField(value: self.variableName, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Fuzzilli_Protobuf_CreateNamedDisposableVariable, rhs: Fuzzilli_Protobuf_CreateNamedDisposableVariable) -> Bool { + if lhs.variableName != rhs.variableName {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Fuzzilli_Protobuf_CreateNamedAsyncDisposableVariable: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".CreateNamedAsyncDisposableVariable" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}variableName\0") + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularStringField(value: &self.variableName) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.variableName.isEmpty { + try visitor.visitSingularStringField(value: self.variableName, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Fuzzilli_Protobuf_CreateNamedAsyncDisposableVariable, rhs: Fuzzilli_Protobuf_CreateNamedAsyncDisposableVariable) -> Bool { + if lhs.variableName != rhs.variableName {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension Fuzzilli_Protobuf_Eval: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Eval" public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}code\0\u{1}hasOutput\0") diff --git a/Sources/Fuzzilli/Protobuf/operations.proto b/Sources/Fuzzilli/Protobuf/operations.proto index 7ea8d0d94..2bb208943 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -602,6 +602,14 @@ message CreateNamedVariable { NamedVariableDeclarationMode declarationMode = 2; } +message CreateNamedDisposableVariable { + string variableName = 1; +} + +message CreateNamedAsyncDisposableVariable { + string variableName = 1; +} + message Eval { string code = 1; bool hasOutput = 2; diff --git a/Sources/Fuzzilli/Protobuf/program.pb.swift b/Sources/Fuzzilli/Protobuf/program.pb.swift index 336123aef..051b50afa 100644 --- a/Sources/Fuzzilli/Protobuf/program.pb.swift +++ b/Sources/Fuzzilli/Protobuf/program.pb.swift @@ -2673,6 +2673,22 @@ public struct Fuzzilli_Protobuf_Instruction: Sendable { set {operation = .wasmDefineSignatureType(newValue)} } + public var createNamedDisposableVariable: Fuzzilli_Protobuf_CreateNamedDisposableVariable { + get { + if case .createNamedDisposableVariable(let v)? = operation {return v} + return Fuzzilli_Protobuf_CreateNamedDisposableVariable() + } + set {operation = .createNamedDisposableVariable(newValue)} + } + + public var createNamedAsyncDisposableVariable: Fuzzilli_Protobuf_CreateNamedAsyncDisposableVariable { + get { + if case .createNamedAsyncDisposableVariable(let v)? = operation {return v} + return Fuzzilli_Protobuf_CreateNamedAsyncDisposableVariable() + } + set {operation = .createNamedAsyncDisposableVariable(newValue)} + } + public var unknownFields = SwiftProtobuf.UnknownStorage() public enum OneOf_Operation: Equatable, Sendable { @@ -3004,6 +3020,8 @@ public struct Fuzzilli_Protobuf_Instruction: Sendable { case wasmDropElementSegment(Fuzzilli_Protobuf_WasmDropElementSegment) case wasmTableCopy(Fuzzilli_Protobuf_WasmTableCopy) case wasmDefineSignatureType(Fuzzilli_Protobuf_WasmDefineSignatureType) + case createNamedDisposableVariable(Fuzzilli_Protobuf_CreateNamedDisposableVariable) + case createNamedAsyncDisposableVariable(Fuzzilli_Protobuf_CreateNamedAsyncDisposableVariable) } @@ -3052,7 +3070,7 @@ fileprivate let _protobuf_package = "fuzzilli.protobuf" extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Instruction" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}inouts\0\u{1}opIdx\0\u{1}nop\0\u{1}loadInteger\0\u{1}loadBigInt\0\u{1}loadFloat\0\u{1}loadString\0\u{1}loadBoolean\0\u{1}loadUndefined\0\u{1}loadNull\0\u{1}loadThis\0\u{1}loadArguments\0\u{1}createNamedVariable\0\u{1}loadDisposableVariable\0\u{1}loadAsyncDisposableVariable\0\u{1}loadRegExp\0\u{1}beginObjectLiteral\0\u{1}objectLiteralAddProperty\0\u{1}objectLiteralAddElement\0\u{1}objectLiteralAddComputedProperty\0\u{1}objectLiteralCopyProperties\0\u{1}objectLiteralSetPrototype\0\u{1}beginObjectLiteralMethod\0\u{1}endObjectLiteralMethod\0\u{1}beginObjectLiteralComputedMethod\0\u{1}endObjectLiteralComputedMethod\0\u{1}beginObjectLiteralGetter\0\u{1}endObjectLiteralGetter\0\u{1}beginObjectLiteralSetter\0\u{1}endObjectLiteralSetter\0\u{1}endObjectLiteral\0\u{1}beginClassDefinition\0\u{1}beginClassConstructor\0\u{1}endClassConstructor\0\u{1}classAddInstanceProperty\0\u{1}classAddInstanceElement\0\u{1}classAddInstanceComputedProperty\0\u{1}beginClassInstanceMethod\0\u{1}endClassInstanceMethod\0\u{1}beginClassInstanceGetter\0\u{1}endClassInstanceGetter\0\u{1}beginClassInstanceSetter\0\u{1}endClassInstanceSetter\0\u{1}classAddStaticProperty\0\u{1}classAddStaticElement\0\u{1}classAddStaticComputedProperty\0\u{1}beginClassStaticInitializer\0\u{1}endClassStaticInitializer\0\u{1}beginClassStaticMethod\0\u{1}endClassStaticMethod\0\u{1}beginClassStaticGetter\0\u{1}endClassStaticGetter\0\u{1}beginClassStaticSetter\0\u{1}endClassStaticSetter\0\u{1}classAddPrivateInstanceProperty\0\u{1}beginClassPrivateInstanceMethod\0\u{1}endClassPrivateInstanceMethod\0\u{1}classAddPrivateStaticProperty\0\u{1}beginClassPrivateStaticMethod\0\u{1}endClassPrivateStaticMethod\0\u{1}endClassDefinition\0\u{1}createArray\0\u{1}createIntArray\0\u{1}createFloatArray\0\u{1}createArrayWithSpread\0\u{1}createTemplateString\0\u{1}getProperty\0\u{1}setProperty\0\u{1}updateProperty\0\u{1}deleteProperty\0\u{1}configureProperty\0\u{1}getElement\0\u{1}setElement\0\u{1}updateElement\0\u{1}deleteElement\0\u{1}configureElement\0\u{1}getComputedProperty\0\u{1}setComputedProperty\0\u{1}updateComputedProperty\0\u{1}deleteComputedProperty\0\u{1}configureComputedProperty\0\u{1}typeOf\0\u{1}void\0\u{1}testInstanceOf\0\u{1}testIn\0\u{1}beginPlainFunction\0\u{1}endPlainFunction\0\u{1}beginArrowFunction\0\u{1}endArrowFunction\0\u{1}beginGeneratorFunction\0\u{1}endGeneratorFunction\0\u{1}beginAsyncFunction\0\u{1}endAsyncFunction\0\u{1}beginAsyncArrowFunction\0\u{1}endAsyncArrowFunction\0\u{1}beginAsyncGeneratorFunction\0\u{1}endAsyncGeneratorFunction\0\u{1}beginConstructor\0\u{1}endConstructor\0\u{1}directive\0\u{1}return\0\u{1}yield\0\u{1}yieldEach\0\u{1}await\0\u{1}callFunction\0\u{1}callFunctionWithSpread\0\u{1}construct\0\u{1}constructWithSpread\0\u{1}callMethod\0\u{1}callMethodWithSpread\0\u{1}callComputedMethod\0\u{1}callComputedMethodWithSpread\0\u{1}unaryOperation\0\u{1}binaryOperation\0\u{1}ternaryOperation\0\u{1}update\0\u{1}dup\0\u{1}reassign\0\u{1}destructArray\0\u{1}destructArrayAndReassign\0\u{1}destructObject\0\u{1}destructObjectAndReassign\0\u{1}compare\0\u{1}eval\0\u{1}beginWith\0\u{1}endWith\0\u{1}callSuperConstructor\0\u{1}callSuperMethod\0\u{1}getPrivateProperty\0\u{1}setPrivateProperty\0\u{1}updatePrivateProperty\0\u{1}callPrivateMethod\0\u{1}getSuperProperty\0\u{1}setSuperProperty\0\u{1}getComputedSuperProperty\0\u{1}setComputedSuperProperty\0\u{1}updateSuperProperty\0\u{1}beginIf\0\u{1}beginElse\0\u{1}endIf\0\u{1}beginWhileLoopHeader\0\u{1}beginWhileLoopBody\0\u{1}endWhileLoop\0\u{1}beginDoWhileLoopBody\0\u{1}beginDoWhileLoopHeader\0\u{1}endDoWhileLoop\0\u{1}beginForLoopInitializer\0\u{1}beginForLoopCondition\0\u{1}beginForLoopAfterthought\0\u{1}beginForLoopBody\0\u{1}endForLoop\0\u{1}beginForInLoop\0\u{1}endForInLoop\0\u{1}beginForOfLoop\0\u{1}beginForOfLoopWithDestruct\0\u{1}endForOfLoop\0\u{1}beginRepeatLoop\0\u{1}endRepeatLoop\0\u{1}loopBreak\0\u{1}loopContinue\0\u{1}beginTry\0\u{1}beginCatch\0\u{1}beginFinally\0\u{1}endTryCatchFinally\0\u{1}throwException\0\u{1}beginCodeString\0\u{1}endCodeString\0\u{1}beginBlockStatement\0\u{1}endBlockStatement\0\u{1}beginSwitch\0\u{1}beginSwitchCase\0\u{1}beginSwitchDefaultCase\0\u{1}endSwitchCase\0\u{1}endSwitch\0\u{1}switchBreak\0\u{1}loadNewTarget\0\u{1}print\0\u{1}explore\0\u{1}probe\0\u{1}fixup\0\u{1}beginWasmModule\0\u{1}endWasmModule\0\u{1}createWasmGlobal\0\u{1}createWasmMemory\0\u{1}createWasmTable\0\u{1}createWasmJSTag\0\u{1}createWasmTag\0\u{1}wrapPromising\0\u{1}wrapSuspending\0\u{1}bindMethod\0\u{1}bindFunction\0\u{1}consti64\0\u{1}consti32\0\u{1}constf32\0\u{1}constf64\0\u{1}wasmReturn\0\u{1}wasmJsCall\0\u{1}wasmi32CompareOp\0\u{1}wasmi64CompareOp\0\u{1}wasmf32CompareOp\0\u{1}wasmf64CompareOp\0\u{1}wasmi32EqualZero\0\u{1}wasmi64EqualZero\0\u{1}wasmi32BinOp\0\u{1}wasmi64BinOp\0\u{1}wasmi32UnOp\0\u{1}wasmi64UnOp\0\u{1}wasmf32BinOp\0\u{1}wasmf64BinOp\0\u{1}wasmf32UnOp\0\u{1}wasmf64UnOp\0\u{1}wasmWrapi64Toi32\0\u{1}wasmTruncatef32Toi32\0\u{1}wasmTruncatef64Toi32\0\u{1}wasmExtendi32Toi64\0\u{1}wasmTruncatef32Toi64\0\u{1}wasmTruncatef64Toi64\0\u{1}wasmConverti32Tof32\0\u{1}wasmConverti64Tof32\0\u{1}wasmDemotef64Tof32\0\u{1}wasmConverti32Tof64\0\u{1}wasmConverti64Tof64\0\u{1}wasmPromotef32Tof64\0\u{1}wasmReinterpretf32Asi32\0\u{1}wasmReinterpretf64Asi64\0\u{1}wasmReinterpreti32Asf32\0\u{1}wasmReinterpreti64Asf64\0\u{1}wasmSignExtend8Intoi32\0\u{1}wasmSignExtend16Intoi32\0\u{1}wasmSignExtend8Intoi64\0\u{1}wasmSignExtend16Intoi64\0\u{1}wasmSignExtend32Intoi64\0\u{1}wasmTruncateSatf32Toi32\0\u{1}wasmTruncateSatf64Toi32\0\u{1}wasmTruncateSatf32Toi64\0\u{1}wasmTruncateSatf64Toi64\0\u{1}wasmReassign\0\u{1}wasmDefineGlobal\0\u{1}wasmDefineTable\0\u{1}wasmDefineMemory\0\u{1}wasmDefineDataSegment\0\u{1}wasmLoadGlobal\0\u{1}wasmStoreGlobal\0\u{1}wasmTableGet\0\u{1}wasmTableSet\0\u{1}wasmTableSize\0\u{1}wasmTableGrow\0\u{1}wasmCallIndirect\0\u{1}wasmCallDirect\0\u{1}wasmReturnCallDirect\0\u{1}wasmReturnCallIndirect\0\u{1}wasmMemoryLoad\0\u{1}wasmMemoryStore\0\u{1}wasmAtomicLoad\0\u{1}wasmAtomicStore\0\u{1}wasmAtomicRMW\0\u{1}wasmAtomicCmpxchg\0\u{1}wasmMemorySize\0\u{1}wasmMemoryGrow\0\u{1}wasmMemoryFill\0\u{1}wasmMemoryInit\0\u{1}wasmDropDataSegment\0\u{1}beginWasmFunction\0\u{1}endWasmFunction\0\u{1}wasmBeginBlock\0\u{1}wasmEndBlock\0\u{1}wasmBeginLoop\0\u{1}wasmEndLoop\0\u{1}wasmBranch\0\u{1}wasmBranchIf\0\u{1}wasmBranchTable\0\u{1}wasmNop\0\u{1}wasmBeginIf\0\u{1}wasmBeginElse\0\u{1}wasmEndIf\0\u{1}wasmBeginTryTable\0\u{1}wasmEndTryTable\0\u{1}wasmBeginTry\0\u{1}wasmBeginCatchAll\0\u{1}wasmBeginCatch\0\u{1}wasmEndTry\0\u{1}wasmBeginTryDelegate\0\u{1}wasmEndTryDelegate\0\u{1}wasmThrow\0\u{1}wasmRethrow\0\u{1}wasmThrowRef\0\u{1}wasmDefineTag\0\u{1}constSimd128\0\u{1}wasmSimd128Compare\0\u{1}wasmSimd128IntegerUnOp\0\u{1}wasmSimd128IntegerBinOp\0\u{1}wasmSimd128IntegerTernaryOp\0\u{1}wasmSimd128FloatUnOp\0\u{1}wasmSimd128FloatBinOp\0\u{1}wasmSimd128FloatTernaryOp\0\u{1}wasmSimdSplat\0\u{1}wasmSimdExtractLane\0\u{1}wasmSimdReplaceLane\0\u{1}wasmSimdStoreLane\0\u{1}wasmSimdLoadLane\0\u{1}wasmSimdLoad\0\u{1}wasmUnreachable\0\u{1}wasmSelect\0\u{1}wasmBeginTypeGroup\0\u{1}wasmEndTypeGroup\0\u{1}wasmDefineArrayType\0\u{1}wasmDefineStructType\0\u{1}wasmDefineForwardOrSelfReference\0\u{1}wasmResolveForwardReference\0\u{1}wasmArrayNewFixed\0\u{1}wasmArrayNewDefault\0\u{1}wasmArrayLen\0\u{1}wasmArrayGet\0\u{1}wasmArraySet\0\u{1}wasmStructNewDefault\0\u{1}wasmStructGet\0\u{1}wasmStructSet\0\u{1}wasmRefNull\0\u{1}wasmRefIsNull\0\u{1}wasmRefI31\0\u{1}wasmI31Get\0\u{1}wasmAnyConvertExtern\0\u{1}wasmExternConvertAny\0\u{1}wasmMemoryCopy\0\u{1}wasmDefineElementSegment\0\u{1}wasmTableInit\0\u{1}wasmDropElementSegment\0\u{1}wasmTableCopy\0\u{1}wasmDefineSignatureType\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}inouts\0\u{1}opIdx\0\u{1}nop\0\u{1}loadInteger\0\u{1}loadBigInt\0\u{1}loadFloat\0\u{1}loadString\0\u{1}loadBoolean\0\u{1}loadUndefined\0\u{1}loadNull\0\u{1}loadThis\0\u{1}loadArguments\0\u{1}createNamedVariable\0\u{1}loadDisposableVariable\0\u{1}loadAsyncDisposableVariable\0\u{1}loadRegExp\0\u{1}beginObjectLiteral\0\u{1}objectLiteralAddProperty\0\u{1}objectLiteralAddElement\0\u{1}objectLiteralAddComputedProperty\0\u{1}objectLiteralCopyProperties\0\u{1}objectLiteralSetPrototype\0\u{1}beginObjectLiteralMethod\0\u{1}endObjectLiteralMethod\0\u{1}beginObjectLiteralComputedMethod\0\u{1}endObjectLiteralComputedMethod\0\u{1}beginObjectLiteralGetter\0\u{1}endObjectLiteralGetter\0\u{1}beginObjectLiteralSetter\0\u{1}endObjectLiteralSetter\0\u{1}endObjectLiteral\0\u{1}beginClassDefinition\0\u{1}beginClassConstructor\0\u{1}endClassConstructor\0\u{1}classAddInstanceProperty\0\u{1}classAddInstanceElement\0\u{1}classAddInstanceComputedProperty\0\u{1}beginClassInstanceMethod\0\u{1}endClassInstanceMethod\0\u{1}beginClassInstanceGetter\0\u{1}endClassInstanceGetter\0\u{1}beginClassInstanceSetter\0\u{1}endClassInstanceSetter\0\u{1}classAddStaticProperty\0\u{1}classAddStaticElement\0\u{1}classAddStaticComputedProperty\0\u{1}beginClassStaticInitializer\0\u{1}endClassStaticInitializer\0\u{1}beginClassStaticMethod\0\u{1}endClassStaticMethod\0\u{1}beginClassStaticGetter\0\u{1}endClassStaticGetter\0\u{1}beginClassStaticSetter\0\u{1}endClassStaticSetter\0\u{1}classAddPrivateInstanceProperty\0\u{1}beginClassPrivateInstanceMethod\0\u{1}endClassPrivateInstanceMethod\0\u{1}classAddPrivateStaticProperty\0\u{1}beginClassPrivateStaticMethod\0\u{1}endClassPrivateStaticMethod\0\u{1}endClassDefinition\0\u{1}createArray\0\u{1}createIntArray\0\u{1}createFloatArray\0\u{1}createArrayWithSpread\0\u{1}createTemplateString\0\u{1}getProperty\0\u{1}setProperty\0\u{1}updateProperty\0\u{1}deleteProperty\0\u{1}configureProperty\0\u{1}getElement\0\u{1}setElement\0\u{1}updateElement\0\u{1}deleteElement\0\u{1}configureElement\0\u{1}getComputedProperty\0\u{1}setComputedProperty\0\u{1}updateComputedProperty\0\u{1}deleteComputedProperty\0\u{1}configureComputedProperty\0\u{1}typeOf\0\u{1}void\0\u{1}testInstanceOf\0\u{1}testIn\0\u{1}beginPlainFunction\0\u{1}endPlainFunction\0\u{1}beginArrowFunction\0\u{1}endArrowFunction\0\u{1}beginGeneratorFunction\0\u{1}endGeneratorFunction\0\u{1}beginAsyncFunction\0\u{1}endAsyncFunction\0\u{1}beginAsyncArrowFunction\0\u{1}endAsyncArrowFunction\0\u{1}beginAsyncGeneratorFunction\0\u{1}endAsyncGeneratorFunction\0\u{1}beginConstructor\0\u{1}endConstructor\0\u{1}directive\0\u{1}return\0\u{1}yield\0\u{1}yieldEach\0\u{1}await\0\u{1}callFunction\0\u{1}callFunctionWithSpread\0\u{1}construct\0\u{1}constructWithSpread\0\u{1}callMethod\0\u{1}callMethodWithSpread\0\u{1}callComputedMethod\0\u{1}callComputedMethodWithSpread\0\u{1}unaryOperation\0\u{1}binaryOperation\0\u{1}ternaryOperation\0\u{1}update\0\u{1}dup\0\u{1}reassign\0\u{1}destructArray\0\u{1}destructArrayAndReassign\0\u{1}destructObject\0\u{1}destructObjectAndReassign\0\u{1}compare\0\u{1}eval\0\u{1}beginWith\0\u{1}endWith\0\u{1}callSuperConstructor\0\u{1}callSuperMethod\0\u{1}getPrivateProperty\0\u{1}setPrivateProperty\0\u{1}updatePrivateProperty\0\u{1}callPrivateMethod\0\u{1}getSuperProperty\0\u{1}setSuperProperty\0\u{1}getComputedSuperProperty\0\u{1}setComputedSuperProperty\0\u{1}updateSuperProperty\0\u{1}beginIf\0\u{1}beginElse\0\u{1}endIf\0\u{1}beginWhileLoopHeader\0\u{1}beginWhileLoopBody\0\u{1}endWhileLoop\0\u{1}beginDoWhileLoopBody\0\u{1}beginDoWhileLoopHeader\0\u{1}endDoWhileLoop\0\u{1}beginForLoopInitializer\0\u{1}beginForLoopCondition\0\u{1}beginForLoopAfterthought\0\u{1}beginForLoopBody\0\u{1}endForLoop\0\u{1}beginForInLoop\0\u{1}endForInLoop\0\u{1}beginForOfLoop\0\u{1}beginForOfLoopWithDestruct\0\u{1}endForOfLoop\0\u{1}beginRepeatLoop\0\u{1}endRepeatLoop\0\u{1}loopBreak\0\u{1}loopContinue\0\u{1}beginTry\0\u{1}beginCatch\0\u{1}beginFinally\0\u{1}endTryCatchFinally\0\u{1}throwException\0\u{1}beginCodeString\0\u{1}endCodeString\0\u{1}beginBlockStatement\0\u{1}endBlockStatement\0\u{1}beginSwitch\0\u{1}beginSwitchCase\0\u{1}beginSwitchDefaultCase\0\u{1}endSwitchCase\0\u{1}endSwitch\0\u{1}switchBreak\0\u{1}loadNewTarget\0\u{1}print\0\u{1}explore\0\u{1}probe\0\u{1}fixup\0\u{1}beginWasmModule\0\u{1}endWasmModule\0\u{1}createWasmGlobal\0\u{1}createWasmMemory\0\u{1}createWasmTable\0\u{1}createWasmJSTag\0\u{1}createWasmTag\0\u{1}wrapPromising\0\u{1}wrapSuspending\0\u{1}bindMethod\0\u{1}bindFunction\0\u{1}consti64\0\u{1}consti32\0\u{1}constf32\0\u{1}constf64\0\u{1}wasmReturn\0\u{1}wasmJsCall\0\u{1}wasmi32CompareOp\0\u{1}wasmi64CompareOp\0\u{1}wasmf32CompareOp\0\u{1}wasmf64CompareOp\0\u{1}wasmi32EqualZero\0\u{1}wasmi64EqualZero\0\u{1}wasmi32BinOp\0\u{1}wasmi64BinOp\0\u{1}wasmi32UnOp\0\u{1}wasmi64UnOp\0\u{1}wasmf32BinOp\0\u{1}wasmf64BinOp\0\u{1}wasmf32UnOp\0\u{1}wasmf64UnOp\0\u{1}wasmWrapi64Toi32\0\u{1}wasmTruncatef32Toi32\0\u{1}wasmTruncatef64Toi32\0\u{1}wasmExtendi32Toi64\0\u{1}wasmTruncatef32Toi64\0\u{1}wasmTruncatef64Toi64\0\u{1}wasmConverti32Tof32\0\u{1}wasmConverti64Tof32\0\u{1}wasmDemotef64Tof32\0\u{1}wasmConverti32Tof64\0\u{1}wasmConverti64Tof64\0\u{1}wasmPromotef32Tof64\0\u{1}wasmReinterpretf32Asi32\0\u{1}wasmReinterpretf64Asi64\0\u{1}wasmReinterpreti32Asf32\0\u{1}wasmReinterpreti64Asf64\0\u{1}wasmSignExtend8Intoi32\0\u{1}wasmSignExtend16Intoi32\0\u{1}wasmSignExtend8Intoi64\0\u{1}wasmSignExtend16Intoi64\0\u{1}wasmSignExtend32Intoi64\0\u{1}wasmTruncateSatf32Toi32\0\u{1}wasmTruncateSatf64Toi32\0\u{1}wasmTruncateSatf32Toi64\0\u{1}wasmTruncateSatf64Toi64\0\u{1}wasmReassign\0\u{1}wasmDefineGlobal\0\u{1}wasmDefineTable\0\u{1}wasmDefineMemory\0\u{1}wasmDefineDataSegment\0\u{1}wasmLoadGlobal\0\u{1}wasmStoreGlobal\0\u{1}wasmTableGet\0\u{1}wasmTableSet\0\u{1}wasmTableSize\0\u{1}wasmTableGrow\0\u{1}wasmCallIndirect\0\u{1}wasmCallDirect\0\u{1}wasmReturnCallDirect\0\u{1}wasmReturnCallIndirect\0\u{1}wasmMemoryLoad\0\u{1}wasmMemoryStore\0\u{1}wasmAtomicLoad\0\u{1}wasmAtomicStore\0\u{1}wasmAtomicRMW\0\u{1}wasmAtomicCmpxchg\0\u{1}wasmMemorySize\0\u{1}wasmMemoryGrow\0\u{1}wasmMemoryFill\0\u{1}wasmMemoryInit\0\u{1}wasmDropDataSegment\0\u{1}beginWasmFunction\0\u{1}endWasmFunction\0\u{1}wasmBeginBlock\0\u{1}wasmEndBlock\0\u{1}wasmBeginLoop\0\u{1}wasmEndLoop\0\u{1}wasmBranch\0\u{1}wasmBranchIf\0\u{1}wasmBranchTable\0\u{1}wasmNop\0\u{1}wasmBeginIf\0\u{1}wasmBeginElse\0\u{1}wasmEndIf\0\u{1}wasmBeginTryTable\0\u{1}wasmEndTryTable\0\u{1}wasmBeginTry\0\u{1}wasmBeginCatchAll\0\u{1}wasmBeginCatch\0\u{1}wasmEndTry\0\u{1}wasmBeginTryDelegate\0\u{1}wasmEndTryDelegate\0\u{1}wasmThrow\0\u{1}wasmRethrow\0\u{1}wasmThrowRef\0\u{1}wasmDefineTag\0\u{1}constSimd128\0\u{1}wasmSimd128Compare\0\u{1}wasmSimd128IntegerUnOp\0\u{1}wasmSimd128IntegerBinOp\0\u{1}wasmSimd128IntegerTernaryOp\0\u{1}wasmSimd128FloatUnOp\0\u{1}wasmSimd128FloatBinOp\0\u{1}wasmSimd128FloatTernaryOp\0\u{1}wasmSimdSplat\0\u{1}wasmSimdExtractLane\0\u{1}wasmSimdReplaceLane\0\u{1}wasmSimdStoreLane\0\u{1}wasmSimdLoadLane\0\u{1}wasmSimdLoad\0\u{1}wasmUnreachable\0\u{1}wasmSelect\0\u{1}wasmBeginTypeGroup\0\u{1}wasmEndTypeGroup\0\u{1}wasmDefineArrayType\0\u{1}wasmDefineStructType\0\u{1}wasmDefineForwardOrSelfReference\0\u{1}wasmResolveForwardReference\0\u{1}wasmArrayNewFixed\0\u{1}wasmArrayNewDefault\0\u{1}wasmArrayLen\0\u{1}wasmArrayGet\0\u{1}wasmArraySet\0\u{1}wasmStructNewDefault\0\u{1}wasmStructGet\0\u{1}wasmStructSet\0\u{1}wasmRefNull\0\u{1}wasmRefIsNull\0\u{1}wasmRefI31\0\u{1}wasmI31Get\0\u{1}wasmAnyConvertExtern\0\u{1}wasmExternConvertAny\0\u{1}wasmMemoryCopy\0\u{1}wasmDefineElementSegment\0\u{1}wasmTableInit\0\u{1}wasmDropElementSegment\0\u{1}wasmTableCopy\0\u{1}wasmDefineSignatureType\0\u{1}createNamedDisposableVariable\0\u{1}createNamedAsyncDisposableVariable\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -7320,6 +7338,32 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M self.operation = .wasmDefineSignatureType(v) } }() + case 330: try { + var v: Fuzzilli_Protobuf_CreateNamedDisposableVariable? + var hadOneofValue = false + if let current = self.operation { + hadOneofValue = true + if case .createNamedDisposableVariable(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.operation = .createNamedDisposableVariable(v) + } + }() + case 331: try { + var v: Fuzzilli_Protobuf_CreateNamedAsyncDisposableVariable? + var hadOneofValue = false + if let current = self.operation { + hadOneofValue = true + if case .createNamedAsyncDisposableVariable(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.operation = .createNamedAsyncDisposableVariable(v) + } + }() default: break } } @@ -8646,6 +8690,14 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M guard case .wasmDefineSignatureType(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 329) }() + case .createNamedDisposableVariable?: try { + guard case .createNamedDisposableVariable(let v)? = self.operation else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 330) + }() + case .createNamedAsyncDisposableVariable?: try { + guard case .createNamedAsyncDisposableVariable(let v)? = self.operation else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 331) + }() case nil: break } try unknownFields.traverse(visitor: &visitor) diff --git a/Sources/Fuzzilli/Protobuf/program.proto b/Sources/Fuzzilli/Protobuf/program.proto index 529cb5611..5f3d390ff 100644 --- a/Sources/Fuzzilli/Protobuf/program.proto +++ b/Sources/Fuzzilli/Protobuf/program.proto @@ -353,6 +353,8 @@ message Instruction { WasmDropElementSegment wasmDropElementSegment = 327; WasmTableCopy wasmTableCopy = 328; WasmDefineSignatureType wasmDefineSignatureType = 329; + CreateNamedDisposableVariable createNamedDisposableVariable = 330; + CreateNamedAsyncDisposableVariable createNamedAsyncDisposableVariable = 331; } } diff --git a/Tests/FuzzilliTests/CompilerTests/explicit_resource_management.js b/Tests/FuzzilliTests/CompilerTests/explicit_resource_management.js new file mode 100644 index 000000000..2e0a50637 --- /dev/null +++ b/Tests/FuzzilliTests/CompilerTests/explicit_resource_management.js @@ -0,0 +1,56 @@ +(() => { + function f() { + using x = null; + console.log(x); + } + f(); +})(); + +(() => { + async function f() { + await using x = null; + console.log(x); + } + f(); +})(); + +(() => { + function * f() { + try { + yield 1; + yield 2; + yield 3; + } finally { + console.log("finally"); + } + } + + { + using o = f(), p = f(); + console.log(o.next()); + console.log(p.next()); + console.log(o.next()); + } + console.log("the end") +})(); + +async function test(){ + async function * f() { + try { + yield 1; + yield 2; + yield 3; + } finally { + console.log("finally"); + } + } + + { + await using o = f(), p = f(); + console.log(await o.next()); + console.log(await p.next()); + console.log(await o.next()); + } + console.log("the end") +}; +test(); diff --git a/Tests/FuzzilliTests/LifterTest.swift b/Tests/FuzzilliTests/LifterTest.swift index 14991b65f..4f0892bf0 100644 --- a/Tests/FuzzilliTests/LifterTest.swift +++ b/Tests/FuzzilliTests/LifterTest.swift @@ -3138,7 +3138,9 @@ class LifterTests: XCTestCase { XCTAssertEqual(actual, expected) } - func testLoadDisposableVariableLifting() { + // This test is parameterized for normal and named variables with the + // respective concrete cases below. + func _testDisposableVariableLifting(_ variableName : String, generateVariable: (ProgramBuilder, Variable) -> Void) { let fuzzer = makeMockFuzzer() let b = fuzzer.makeBuilder() @@ -3152,7 +3154,7 @@ class LifterTests: XCTestCase { b.doReturn(v2) } } - b.loadDisposableVariable(disposableVariable) + generateVariable(b, disposableVariable) } b.callFunction(f) @@ -3168,15 +3170,29 @@ class LifterTests: XCTestCase { return 42; }, }; - using v7 = v6; + using %@ = v6; } f0(); """ - XCTAssertEqual(actual, expected) + XCTAssertEqual(actual, String(format: expected, variableName)) } - func testLoadAsyncDisposableVariableLifting() { + func testLoadDisposableVariableLifting() { + _testDisposableVariableLifting("v7", generateVariable: { (b: ProgramBuilder, v: Variable) in + b.loadDisposableVariable(v) + }) + } + + func testCreateNamedDisposableVariableLifting() { + _testDisposableVariableLifting("dis", generateVariable: { (b: ProgramBuilder, v: Variable) in + b.createNamedDisposableVariable("dis", v) + }) + } + + // This test is parameterized for normal and named variables with the + // respective concrete cases below. + func _testAsyncDisposableVariableLifting(_ variableName : String, generateVariable: (ProgramBuilder, Variable) -> Void) { let fuzzer = makeMockFuzzer() let b = fuzzer.makeBuilder() @@ -3190,7 +3206,7 @@ class LifterTests: XCTestCase { b.doReturn(v2) } } - b.loadAsyncDisposableVariable(asyncDisposableVariable) + generateVariable(b, asyncDisposableVariable) } let g = b.buildAsyncFunction(with: .parameters(n: 0)) { args in @@ -3210,7 +3226,7 @@ class LifterTests: XCTestCase { return 42; }, }; - await using v7 = v6; + await using %@ = v6; } async function f8() { await f0(); @@ -3218,7 +3234,19 @@ class LifterTests: XCTestCase { f8(); """ - XCTAssertEqual(actual, expected) + XCTAssertEqual(actual, String(format: expected, variableName)) + } + + func testLoadAsyncDisposableVariableLifting() { + _testAsyncDisposableVariableLifting("v7", generateVariable: { (b: ProgramBuilder, v: Variable) in + b.loadAsyncDisposableVariable(v) + }) + } + + func testCreateNamedAsyncDisposableVariableLifting() { + _testAsyncDisposableVariableLifting("dis", generateVariable: { (b: ProgramBuilder, v: Variable) in + b.createNamedAsyncDisposableVariable("dis", v) + }) } func testImportAnalysisMisTypedJS() { From 3df2febbd39372d7d4c7ceef0b7e21354a85180c Mon Sep 17 00:00:00 2001 From: Dominik Klemba Date: Tue, 23 Sep 2025 13:23:03 +0000 Subject: [PATCH 12/35] [Turbolev] Adjust fuzzing flags probabilities After a discussion, we are adjusting the flag probabilities. The stable version of Turbolev (--turbolev) is now enabled with a 50% probability. In 82% of those cases, the future version (--turbolev-future) is also enabled, as a result it is never used alone. Change-Id: I8b19b0f16414fd7928040020c9b4045820ef6b41 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8609477 Commit-Queue: Dominik Klemba Auto-Submit: Dominik Klemba Reviewed-by: Matthias Liedtke Reviewed-by: Darius Mercadier --- Sources/FuzzilliCli/Profiles/V8Profile.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Sources/FuzzilliCli/Profiles/V8Profile.swift b/Sources/FuzzilliCli/Profiles/V8Profile.swift index 313ca249d..68be00ce2 100644 --- a/Sources/FuzzilliCli/Profiles/V8Profile.swift +++ b/Sources/FuzzilliCli/Profiles/V8Profile.swift @@ -89,10 +89,11 @@ let v8Profile = Profile( args.append("--turboshaft-typed-optimizations") } - if probability(0.4) { + if probability(0.5) { args.append("--turbolev") - } else if probability(0.15) { - args.append("--turbolev-future") + if probability(0.82) { + args.append("--turbolev-future") + } } if probability(0.1) { From 6d4173ab226f2d55fbfd208e176f2dc1dd86b940 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 18 Sep 2025 17:01:25 -0700 Subject: [PATCH 13/35] Have the mutator produce valid values half the time when mutating named strings Not quite sure if this is the best way to do it. For custom strings we might want to register string-producing generators that can be used here. Change-Id: I6a6a69642074dbd56b8ea52c9d8fe6fab07cf514 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8603432 Commit-Queue: Manish Goregaokar Reviewed-by: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 62 ++++++++++--------- Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 2 +- .../Environment/JavaScriptEnvironment.swift | 25 +++++++- .../Fuzzilli/Mutators/OperationMutator.swift | 12 ++++ Tests/FuzzilliTests/JSTyperTests.swift | 10 +-- Tests/FuzzilliTests/MutatorTests.swift | 61 ++++++++++++++++++ 6 files changed, 133 insertions(+), 39 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index b87f4dfe0..dd06cdc97 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -667,11 +667,9 @@ public class ProgramBuilder { if type.isEnumeration { return self.loadEnum(type) } - let producingGenerator = self.fuzzer.environment.getProducingGenerator(ofType: type); - if let producingGenerator { - if probability(producingGenerator.probability) { - return producingGenerator.generator(self) - } + if let typeName = type.group, + let customStringGen = self.fuzzer.environment.getNamedStringGenerator(ofName: typeName) { + return self.loadString(customStringGen(), customName: typeName) } return self.loadString(self.randomString()) }), (.boolean, { return self.loadBool(probability(0.5)) }), @@ -4814,17 +4812,40 @@ public class ProgramBuilder { // Generate a random time zone identifier @discardableResult func randomTimeZone() -> Variable { + return loadString(ProgramBuilder.randomTimeZoneString(), customName: "TemporalTimeZoneString") + } + + @discardableResult + static func randomTimeZoneString() -> String { // Bias towards knownTimeZoneIdentifiers since it's a larger array if probability(0.7) { - return loadString(chooseUniform(from: TimeZone.knownTimeZoneIdentifiers), customName: "TemporalTimeZoneString") + return chooseUniform(from: TimeZone.knownTimeZoneIdentifiers) } else { - return loadString(chooseUniform(from: TimeZone.abbreviationDictionary.keys), customName: "TemporalTimeZoneString") + return chooseUniform(from: TimeZone.abbreviationDictionary.keys) } } @discardableResult - func randomUTCOffset() -> Variable { - return loadString(randomUTCOffsetString(mayHaveSeconds: true), customName: "TemporalTimeZoneString") + func randomUTCOffset(mayHaveSeconds: Bool) -> Variable { + return loadString(ProgramBuilder.randomUTCOffsetString(mayHaveSeconds: mayHaveSeconds), customName: "TemporalTimeZoneString") + } + + @discardableResult + static func randomUTCOffsetString(mayHaveSeconds: Bool) -> String { + let hours = Int.random(in: 0..<24) + // Bias towards zero minutes since that's what most time zones do. + let zeroMinutes = probability(0.8) + let minutes = zeroMinutes ? 0 : Int.random(in: 0..<60) + let plusminus = Bool.random() ? "+" : "-"; + var offset = String(format: "%@%02d:%02d", plusminus, hours, minutes) + if !zeroMinutes && mayHaveSeconds && probability(0.3) { + let seconds = Int.random(in: 0..<60) + offset = String(format: "%@:%02d", offset, seconds) + if probability(0.3) { + offset = String(format: "%@:.%09d", offset, Int.random(in: 0...999999999)) + } + } + return offset } // Generate an object with fields from @@ -4954,7 +4975,7 @@ public class ProgramBuilder { if (!forWith) { if Bool.random() { // Time zones can be offsets, but cannot have seconds - generatedOffset = loadString(randomUTCOffsetString(mayHaveSeconds: false)) + generatedOffset = randomUTCOffset(mayHaveSeconds: false) properties["timeZone"] = generatedOffset } else { properties["timeZone"] = randomTimeZone() @@ -4971,7 +4992,7 @@ public class ProgramBuilder { properties["offset"] = generatedOffset! } else if probability(0.3) { // Otherwise, with a low probability, generate a random offset. - properties["offset"] = loadString(randomUTCOffsetString(mayHaveSeconds: true)) + properties["offset"] = randomUTCOffset(mayHaveSeconds: true) } } return createObject(with: properties) @@ -5121,22 +5142,3 @@ public class ProgramBuilder { } } - - - -fileprivate func randomUTCOffsetString(mayHaveSeconds: Bool) -> String { - let hours = Int.random(in: 0..<24) - // Bias towards zero minutes since that's what most time zones do. - let zeroMinutes = probability(0.8) - let minutes = zeroMinutes ? 0 : Int.random(in: 0..<60) - let plusminus = Bool.random() ? "+" : "-"; - var offset = String(format: "%@%02d:%02d", plusminus, hours, minutes) - if !zeroMinutes && mayHaveSeconds && probability(0.3) { - let seconds = Int.random(in: 0..<60) - offset = String(format: "%@:%02d", offset, seconds) - if probability(0.3) { - offset = String(format: "%@:.%09d", offset, Int.random(in: 0...999999999)) - } - } - return offset -} diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 0802e1037..42c787fb2 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -365,7 +365,7 @@ public let CodeGenerators: [CodeGenerator] = [ if Bool.random() { b.randomTimeZone() } else { - b.randomUTCOffset() + b.randomUTCOffset(mayHaveSeconds: true) } }, diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index 779a8db41..be943a1be 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -299,6 +299,8 @@ public class JavaScriptEnvironment: ComponentBase { // Producing generators, keyed on `type.group` private var producingGenerators: [String: (generator: EnvironmentValueGenerator, probability: Double)] = [:] + // Named string generators, keyed on `type.group` + private var namedStringGenerators: [String: () -> String] = [:] private var producingMethods: [ILType: [(group: String, method: String)]] = [:] private var producingProperties: [ILType: [(group: String, property: String)]] = [:] private var subtypes: [ILType: [ILType]] = [:] @@ -453,7 +455,14 @@ public class JavaScriptEnvironment: ComponentBase { // This isn't a normal "temporal fields object" but is similar, and needs a similar producing generator registerObjectGroup(.jsTemporalDurationLikeObject) addProducingGenerator(forType: ObjectGroup.jsTemporalDurationLikeObject.instanceType, with: { b in b.createTemporalDurationFieldsObject() }) - addProducingGenerator(forType: ObjectGroup.jsTemporalTimeZoneLike, with: { b in chooseUniform(from: [b.randomTimeZone, b.randomUTCOffset])() }) + addNamedStringGenerator(forType: ObjectGroup.jsTemporalTimeZoneLike, + with: { + if Bool.random() { + return ProgramBuilder.randomTimeZoneString() + } else { + return ProgramBuilder.randomUTCOffsetString(mayHaveSeconds: true) + } + }) // Temporal types are produced by a large number of methods; which means findOrGenerateType(), when asked to produce // a Temporal type, will tend towards trying to call a method on another Temporal type, which needs more Temporal types, @@ -596,9 +605,16 @@ public class JavaScriptEnvironment: ComponentBase { // a lot of these API calls need more Temporal objects, leading to runaway recursion attempting // to generate a large number of Temporal objects. Instead, we bias heavily towards the producing generator. public func addProducingGenerator(forType type: ILType, with generator: @escaping EnvironmentValueGenerator, probability: Double = 1.0) { + assert(type.Is(.object()), "Producing generators can only be registered for objects, found \(type)") producingGenerators[type.group!] = (generator, probability) } + // Register a generator for a custom named string. + public func addNamedStringGenerator(forType type: ILType, with generator: @escaping () -> String) { + assert(type.Is(.string), "Named string generators can only be registered for strings, found \(type)") + namedStringGenerators[type.group!] = generator + } + private func addProducingMethod(forType type: ILType, by method: String, on group: String) { if producingMethods[type] == nil { producingMethods[type] = [] @@ -789,6 +805,13 @@ public class JavaScriptEnvironment: ComponentBase { type.group.flatMap {producingGenerators[$0]} } + // For named strings, get a generator that is registered as being able to produce this + // named string. + public func getNamedStringGenerator(ofName name: String) -> (() -> String)? { + namedStringGenerators[name] + } + + // If the object group refers to a constructor, get its path. public func getPathIfConstructor(ofGroup groupName: String) -> [String]? { guard let group = groups[groupName] else { diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift index 61fc36f6f..04fbecdec 100644 --- a/Sources/Fuzzilli/Mutators/OperationMutator.swift +++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift @@ -51,6 +51,18 @@ public class OperationMutator: BaseInstructionMutator { case .loadFloat(_): newOp = LoadFloat(value: b.randomFloat()) case .loadString(let op): + if let customName = op.customName { + // Half the time we want to just hit the regular path + if Bool.random() { + if let type = b.fuzzer.environment.getEnum(ofName: customName) { + newOp = LoadString(value: chooseUniform(from: type.enumValues), customName: customName) + break + } else if let gen = b.fuzzer.environment.getNamedStringGenerator(ofName: customName) { + newOp = LoadString(value: gen(), customName: customName) + break + } + } + } let charSetAlNum = Array("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") // TODO(mliedtke): Should we also use some more esoteric characters in initial string // creation, e.g. ProgramBuilder.randomString? diff --git a/Tests/FuzzilliTests/JSTyperTests.swift b/Tests/FuzzilliTests/JSTyperTests.swift index 30d999e15..56ab52d11 100644 --- a/Tests/FuzzilliTests/JSTyperTests.swift +++ b/Tests/FuzzilliTests/JSTyperTests.swift @@ -1849,18 +1849,16 @@ class JSTyperTests: XCTestCase { } let mockNamedString = ILType.namedString(ofName: "NamedString"); - func generateString(builder: ProgramBuilder) -> Variable { + func generateString() -> String { callCount += 1 - let val = builder.loadString("mockStringValue", customName: "NamedString") - returnedVar = val - return val + return "mockStringValue" } let fuzzer = makeMockFuzzer() fuzzer.environment.registerObjectGroup(mockObject) fuzzer.environment.registerEnumeration(mockEnum) fuzzer.environment.addProducingGenerator(forType: mockObject.instanceType, with: generateObject) - fuzzer.environment.addProducingGenerator(forType: mockNamedString, with: generateString) + fuzzer.environment.addNamedStringGenerator(forType: mockNamedString, with: generateString) let b = fuzzer.makeBuilder() b.buildPrefix() @@ -1876,8 +1874,6 @@ class JSTyperTests: XCTestCase { let variable2 = b.findOrGenerateType(mockNamedString) // Test that the generator was invoked XCTAssertEqual(callCount, 2) - // Test that the returned variable matches the generated one - XCTAssertEqual(variable2, returnedVar) // Test that the returned variable gets typed correctly XCTAssert(b.type(of: variable2).Is(mockNamedString)) diff --git a/Tests/FuzzilliTests/MutatorTests.swift b/Tests/FuzzilliTests/MutatorTests.swift index 18cac018c..69137207b 100644 --- a/Tests/FuzzilliTests/MutatorTests.swift +++ b/Tests/FuzzilliTests/MutatorTests.swift @@ -97,4 +97,65 @@ class MutatorTests: XCTestCase { XCTAssertEqual(newEndTypeGroupInstructions.count, 1) XCTAssertGreaterThan(newEndTypeGroupInstructions[0].numInputs, 1) } + + func testCodeGenMutatorNamedStrings() { + // A generator that deterministically generates a different value each time. + var called = false + func generateString() -> String { + if called { + return "newValue" + } else { + called = true + return "originalValue" + } + } + let mockNamedString = ILType.namedString(ofName: "NamedString"); + + let env = JavaScriptEnvironment() + env.addNamedStringGenerator(forType: mockNamedString, with: generateString) + + let config = Configuration(logLevel: .error) + let fuzzer = makeMockFuzzer(config: config, environment: env) + let b = fuzzer.makeBuilder() + + // We need a minimum number of visible variables for codeGeneration. + b.loadInt(1) + b.loadInt(2) + + let _ = b.findOrGenerateType(mockNamedString) + XCTAssert(called) + + let prog = b.finalize() + + let originalLoadInstruction = prog.code.filter { instr in + instr.op is LoadString + } + + XCTAssertEqual(originalLoadInstruction.count, 1) + let originalLoad = originalLoadInstruction[0].op as! LoadString + XCTAssertEqual(originalLoad.value, "originalValue") + + // Mutator is probabalistic, try 10 times to ensure we are very likely + // to hit the generateString call. + let mutator = OperationMutator() + for _ in 1...10 { + let newBuilder = fuzzer.makeBuilder() + newBuilder.adopting(from: prog) { + mutator.mutate(originalLoadInstruction[0], newBuilder) + } + + let mutatedProg = newBuilder.finalize() + + let newLoadInstruction = mutatedProg.code.filter { instr in + instr.op is LoadString + } + + XCTAssertEqual(newLoadInstruction.count, 1) + let newLoad = newLoadInstruction[0].op as! LoadString + if newLoad.value == "newValue" { + return; + } + } + XCTFail("Mutator ran 10 times without rerunning custom string generator") + } } From 39b95c3c75383c996e828fbb984458b541816232 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 18 Sep 2025 19:11:26 -0700 Subject: [PATCH 14/35] [intl] Add fuzz definitions for Intl.DateTimeFormat With all of the Temporal work done, Intl becomes much, much easier to implement following a similar pattern. Intl generally has more complicated options bags, but relatively simple APIs. Most other Intl APIs can be expected to look like DateTimeFormat: a constructor that takes a locale and an options bag, with a small handful of APIs that return simple things like strings and arrays. These types do not have the annoying cyclic generation problems like Temporal. As usual I added a codegenerator for this since by default it takes a couple steps for ApiMethodCallGenerator to figure out how to find these "namespaced" builtins. Change-Id: I6a6a6964c3995facc844c82dbfa69e03da573c2f Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8601526 Commit-Queue: Manish Goregaokar Reviewed-by: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 27 ++++ .../CodeGen/CodeGeneratorWeights.swift | 1 + Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 4 + .../Environment/JavaScriptEnvironment.swift | 141 +++++++++++++++++- 4 files changed, 172 insertions(+), 1 deletion(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index dd06cdc97..a52061110 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -5141,4 +5141,31 @@ public class ProgramBuilder { } } + @discardableResult + static func constructIntlLocaleString() -> String { + // TODO(Manishearth) Generate more interesting locales than just the builtins + return chooseUniform(from: Locale.availableIdentifiers) + } + + // Generic generators for Intl types. + private func constructIntlType(type: String, optionsBag: OptionsBag) -> Variable { + let intl = createNamedVariable(forBuiltin: "Intl") + let constructor = getProperty(type, of: intl) + + var args: [Variable] = [] + if probability(0.7) { + args.append(findOrGenerateType(.jsIntlLocaleLike)) + + if probability(0.7) { + args.append(createOptionsBag(optionsBag)) + } + } + return construct(constructor, withArgs: args) + } + + @discardableResult + func constructIntlDateTimeFormat() -> Variable { + return constructIntlType(type: "DateTimeFormat", optionsBag: .jsIntlDateTimeFormatSettings) + } + } diff --git a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift index 37698cba2..247e1f017 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift @@ -45,6 +45,7 @@ public let codeGeneratorWeights = [ "BuiltinOverwriteGenerator": 3, "BuiltinObjectPrototypeCallGenerator": 5, "BuiltinTemporalGenerator": 4, + "BuiltinIntlGenerator": 4, "LoadNewTargetGenerator": 3, "DisposableVariableGenerator": 5, "AsyncDisposableVariableGenerator": 5, diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 42c787fb2..0a6721d58 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -149,6 +149,10 @@ public let CodeGenerators: [CodeGenerator] = [ b.construct(constructor, withArgs: [size]) }, + CodeGenerator("BuiltinIntlGenerator") { b in + let _ = chooseUniform(from: [b.constructIntlDateTimeFormat])() + }, + CodeGenerator("HexGenerator") { b in let hexValues = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "A", "B", "C", "D", "E", "F"] diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index be943a1be..71c8a1718 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +import Foundation + public class JavaScriptEnvironment: ComponentBase { // Possible return values of the 'typeof' operator. public static let jsTypeNames = ["undefined", "boolean", "number", "string", "symbol", "function", "object", "bigint"] @@ -411,6 +413,10 @@ public class JavaScriptEnvironment: ComponentBase { registerObjectGroup(.jsTemporalZonedDateTime) registerObjectGroup(.jsTemporalZonedDateTimeConstructor) registerObjectGroup(.jsTemporalZonedDateTimePrototype) + registerObjectGroup(.jsIntlObject) + registerObjectGroup(.jsIntlDateTimeFormat) + registerObjectGroup(.jsIntlDateTimeFormatConstructor) + registerObjectGroup(.jsIntlDateTimeFormatPrototype) for group in additionalObjectGroups { registerObjectGroup(group) @@ -426,6 +432,15 @@ public class JavaScriptEnvironment: ComponentBase { registerEnumeration(OptionsBag.jsTemporalOverflowEnum) registerEnumeration(OptionsBag.jsTemporalDisambiguationEnum) registerEnumeration(OptionsBag.jsTemporalOffsetEnum) + registerEnumeration(OptionsBag.jsIntlLocaleMatcherEnum) + registerEnumeration(OptionsBag.jsIntlNumberingSystemEnum) + registerEnumeration(OptionsBag.jsIntlHourCycleEnum) + registerEnumeration(OptionsBag.jsIntlLongShortNarrowEnum) + registerEnumeration(OptionsBag.jsIntlNumeric2DigitEnum) + registerEnumeration(OptionsBag.jsIntlMonthEnum) + registerEnumeration(OptionsBag.jsIntlTimeZoneNameEnum) + registerEnumeration(OptionsBag.jsIntlFormatMatcherEnum) + registerEnumeration(OptionsBag.jsIntlFullLongMediumShort) registerEnumeration(OptionsBag.base64Alphabet) registerEnumeration(OptionsBag.base64LastChunkHandling) @@ -443,6 +458,8 @@ public class JavaScriptEnvironment: ComponentBase { registerOptionsBag(.toBase64Settings) registerOptionsBag(.fromBase64Settings) registerOptionsBag(.jsTemporalPlainDateToZDTSettings) + registerOptionsBag(.jsIntlDateTimeFormatSettings) + registerOptionsBag(.jsIntlLocaleMatcherSettings) registerTemporalFieldsObject(.jsTemporalPlainTimeLikeObject, forWith: false, dateFields: false, timeFields: true, zonedFields: false) registerTemporalFieldsObject(.jsTemporalPlainDateLikeObject, forWith: false, dateFields: true, timeFields: false, zonedFields: false) @@ -463,6 +480,7 @@ public class JavaScriptEnvironment: ComponentBase { return ProgramBuilder.randomUTCOffsetString(mayHaveSeconds: true) } }) + addNamedStringGenerator(forType: .jsIntlLocaleLike, with: { ProgramBuilder.constructIntlLocaleString() }) // Temporal types are produced by a large number of methods; which means findOrGenerateType(), when asked to produce // a Temporal type, will tend towards trying to call a method on another Temporal type, which needs more Temporal types, @@ -528,6 +546,7 @@ public class JavaScriptEnvironment: ComponentBase { registerBuiltin("NaN", ofType: .jsNaN) registerBuiltin("Infinity", ofType: .jsInfinity) registerBuiltin("Temporal", ofType: .jsTemporalObject) + registerBuiltin("Intl", ofType: .jsIntlObject) registerBuiltin("WebAssembly", ofType: ObjectGroup.jsWebAssembly.instanceType) for (builtin, type) in additionalBuiltins { @@ -1233,8 +1252,15 @@ public extension ObjectGroup { // so Fuzzilli can generate sequences like `Date.prototype.getTime.call(new Date())`. static func createPrototypeObjectGroup(_ receiver: ObjectGroup) -> ObjectGroup { let name = receiver.name + ".prototype" - let properties = Dictionary(uniqueKeysWithValues: receiver.methods.map { + var properties = Dictionary(uniqueKeysWithValues: receiver.methods.map { ($0.0, ILType.unboundFunction($0.1.first, receiver: receiver.instanceType)) }) + if receiver.name == "Intl.DateTimeFormat" { + // DateTimeFormat.format is a getter instead of regular function, and errors + // when called on the prototype. To avoid test failures, we omit + // it from the prototype. + // https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype.format + properties.removeValue(forKey: "format") + } return ObjectGroup( name: name, instanceType: .object(ofGroup: name, withProperties: properties.map {$0.0}, withMethods: []), @@ -2866,6 +2892,119 @@ extension OptionsBag { ) } +// Intl +extension ILType { + // Intl types + static let jsIntlObject = ILType.object(ofGroup: "Intl", withProperties: ["DateTimeFormat"]) + static let jsIntlDateTimeFormat = ILType.object(ofGroup: "Intl.DateTimeFormat", withProperties: [], withMethods: ["format", "formatRange", "formatRangeToParts", "formatToParts", "resolvedOptions"]) + static let jsIntlDateTimeFormatConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlDateTimeFormatSettings.group.instanceType)] => .jsIntlDateTimeFormat) + .object(ofGroup: "IntlDateTimeFormatConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) + + static let jsIntlLocaleLike = ILType.namedString(ofName: "IntlLocaleString") +} + +extension ObjectGroup { + static let jsIntlObject = ObjectGroup( + name: "Intl", + instanceType: .jsIntlObject, + properties: [ + "DateTimeFormat" : .jsIntlDateTimeFormatConstructor, + ], + methods: [:] + ) + fileprivate static func dateTimeFormatSignature(_ returnType: ILType) -> [Signature] { + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/format#date + // No ZonedDateTime as stated in the docs. + [ILType.jsDate, .jsTemporalPlainDate, .jsTemporalPlainTime, .jsTemporalPlainMonthDay, .jsTemporalPlainYearMonth, .jsTemporalPlainDateTime].map { + [.opt($0)] => returnType + } + } + fileprivate static func dateTimeFormatRangeSignature(_ returnType: ILType) -> [Signature] { + // Docs list all Temporal types except ZDT, but we don't wish to explode the signatures so we just pick + // the two main relevant ones. + [ILType.jsDate, .jsTemporalPlainDateTime].flatMap { firstParam in + [ILType.jsDate, .jsTemporalPlainDateTime].map { + [.opt(firstParam), .opt($0)] => returnType + } + } + } + static let jsIntlDateTimeFormat = ObjectGroup( + name: "Intl.DateTimeFormat", + instanceType: .jsIntlDateTimeFormat, + properties: [:], + overloads: [ + "format": dateTimeFormatSignature(.string), + "formatRange": dateTimeFormatRangeSignature(.string), + "formatRangeToParts": dateTimeFormatRangeSignature(.jsArray), + "formatToParts": dateTimeFormatSignature(.jsArray), + "resolvedOptions": [[] => .object()], + ] + ) + + static let jsIntlDateTimeFormatPrototype = createPrototypeObjectGroup(jsIntlDateTimeFormat) + + static let jsIntlDateTimeFormatConstructor = ObjectGroup( + name: "IntlDateTimeFormatConstructor", + constructorPath: "Intl.DateTimeFormat", + instanceType: .jsIntlDateTimeFormatConstructor, + properties: [ + "prototype" : jsIntlDateTimeFormatPrototype.instanceType + ], + methods: [ + // TODO(manishearth) this also accepts arrays of locale-likes + "supportedLocalesOf": [.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlLocaleMatcherSettings.group.instanceType)] => .jsArray, + ] + ) +} + +extension OptionsBag { + fileprivate static let jsIntlLocaleMatcherEnum = ILType.enumeration(ofName: "IntlLocaleMatcher", withValues: ["lookup", "best fit"]) + fileprivate static let jsIntlNumberingSystemEnum = ILType.enumeration(ofName: "IntlNumberingSystem", withValues: Locale.NumberingSystem.availableNumberingSystems.map { $0.identifier }) + fileprivate static let jsIntlHourCycleEnum = ILType.enumeration(ofName: "IntlHourCycle", withValues: ["h11", "h12", "h23", "h24"]) + fileprivate static let jsIntlLongShortNarrowEnum = ILType.enumeration(ofName: "IntlLongShortNarrow", withValues: ["long", "short", "narrow"]) + fileprivate static let jsIntlNumeric2DigitEnum = ILType.enumeration(ofName: "IntlNumeric2Digit", withValues: ["numeric", "2-digit"]) + fileprivate static let jsIntlMonthEnum = ILType.enumeration(ofName: "IntlMonth", withValues: ["numeric", "2-digit", "long", "short", "narrow"]) + fileprivate static let jsIntlTimeZoneNameEnum = ILType.enumeration(ofName: "IntlTimeZoneName", withValues: ["long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric"]) + fileprivate static let jsIntlFormatMatcherEnum = ILType.enumeration(ofName: "IntlFormatMatcher", withValues: ["basic", "best fit"]) + fileprivate static let jsIntlFullLongMediumShort = ILType.enumeration(ofName: "IntlFullLongMediumShort", withValues: ["full", "long", "medium", "short"]) + + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#parameters + static let jsIntlDateTimeFormatSettings = OptionsBag( + name: "IntlLocaleSettings", + properties: [ + // Locale options + "localeMatcher": jsIntlLocaleMatcherEnum, + "calendar": .jsTemporalCalendarEnum, + "numberingSystem": jsIntlNumberingSystemEnum, + "hour12": .boolean, + "hourCycle": jsIntlHourCycleEnum, + "timeZone": ObjectGroup.jsTemporalTimeZoneLike, + // datetime options + "weekday": jsIntlLongShortNarrowEnum, + "era": jsIntlLongShortNarrowEnum, + "year": jsIntlNumeric2DigitEnum, + "month": jsIntlMonthEnum, + "day": jsIntlNumeric2DigitEnum, + "dayPeriod": jsIntlLongShortNarrowEnum, + "hour": jsIntlNumeric2DigitEnum, + "minute": jsIntlNumeric2DigitEnum, + "second": jsIntlNumeric2DigitEnum, + "fractionalSecondDigits": .integer, // Technically only 1-3 + "timeZoneName": jsIntlTimeZoneNameEnum, + "formatMatcher": jsIntlFormatMatcherEnum, + // style options + "dateStyle": jsIntlFullLongMediumShort, + "timeStyle": jsIntlFullLongMediumShort, + ] + ) + + static let jsIntlLocaleMatcherSettings = OptionsBag( + name: "IntlLocaleMatcherSettings", + properties: [ + "localeMatcher": jsIntlLocaleMatcherEnum, + ] + ) +} + // Base64 extension OptionsBag { fileprivate static let base64Alphabet = From 177973b127549c1e2c653784cef67e049d9736d5 Mon Sep 17 00:00:00 2001 From: Omer Katz Date: Wed, 24 Sep 2025 13:11:06 +0200 Subject: [PATCH 15/35] Add --handle-weak-ref-weakly-in-minor-gc to Fuzzilli Bug: 340777103 Change-Id: I0946794f9517b4839db6f8a303c63bd8ceea3ad3 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8614796 Auto-Submit: Omer Katz Reviewed-by: Matthias Liedtke Commit-Queue: Omer Katz --- Sources/FuzzilliCli/Profiles/V8Profile.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/FuzzilliCli/Profiles/V8Profile.swift b/Sources/FuzzilliCli/Profiles/V8Profile.swift index 68be00ce2..d31ac2088 100644 --- a/Sources/FuzzilliCli/Profiles/V8Profile.swift +++ b/Sources/FuzzilliCli/Profiles/V8Profile.swift @@ -120,6 +120,10 @@ let v8Profile = Profile( args.append("--precise-object-pinning") } + if probability(0.1) { + args.append("--handle-weak-ref-weakly-in-minor-gc") + } + // Temporarily enable the three flags below with high probability to // stress-test JSPI. // Lower the probabilities once we have enough coverage. From 36735772052ecbeeb6ab4bb1eb2f959930be6064 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 23 Sep 2025 09:46:58 -0700 Subject: [PATCH 16/35] [intl] Add type definitions for Intl.Collator Change-Id: I6a6a69646624421925b8d0af081e41b41b3676c1 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8613470 Reviewed-by: Matthias Liedtke Commit-Queue: Manish Goregaokar --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 5 ++ Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 2 +- .../Environment/JavaScriptEnvironment.swift | 66 ++++++++++++++++++- 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index a52061110..2cb38a6c7 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -5168,4 +5168,9 @@ public class ProgramBuilder { return constructIntlType(type: "DateTimeFormat", optionsBag: .jsIntlDateTimeFormatSettings) } + @discardableResult + func constructIntlCollator() -> Variable { + return constructIntlType(type: "Collator", optionsBag: .jsIntlCollatorSettings) + } + } diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 0a6721d58..6f1c1576b 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -150,7 +150,7 @@ public let CodeGenerators: [CodeGenerator] = [ }, CodeGenerator("BuiltinIntlGenerator") { b in - let _ = chooseUniform(from: [b.constructIntlDateTimeFormat])() + let _ = chooseUniform(from: [b.constructIntlDateTimeFormat, b.constructIntlCollator])() }, CodeGenerator("HexGenerator") { b in diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index 71c8a1718..08f0b5b8d 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -414,6 +414,9 @@ public class JavaScriptEnvironment: ComponentBase { registerObjectGroup(.jsTemporalZonedDateTimeConstructor) registerObjectGroup(.jsTemporalZonedDateTimePrototype) registerObjectGroup(.jsIntlObject) + registerObjectGroup(.jsIntlCollator) + registerObjectGroup(.jsIntlCollatorConstructor) + registerObjectGroup(.jsIntlCollatorPrototype) registerObjectGroup(.jsIntlDateTimeFormat) registerObjectGroup(.jsIntlDateTimeFormatConstructor) registerObjectGroup(.jsIntlDateTimeFormatPrototype) @@ -441,6 +444,10 @@ public class JavaScriptEnvironment: ComponentBase { registerEnumeration(OptionsBag.jsIntlTimeZoneNameEnum) registerEnumeration(OptionsBag.jsIntlFormatMatcherEnum) registerEnumeration(OptionsBag.jsIntlFullLongMediumShort) + registerEnumeration(OptionsBag.jsIntlCollatorUsageEnum) + registerEnumeration(OptionsBag.jsIntlCollationEnum) + registerEnumeration(OptionsBag.jsIntlCaseFirstEnum) + registerEnumeration(OptionsBag.jsIntlCollatorSensitivityEnum) registerEnumeration(OptionsBag.base64Alphabet) registerEnumeration(OptionsBag.base64LastChunkHandling) @@ -459,6 +466,7 @@ public class JavaScriptEnvironment: ComponentBase { registerOptionsBag(.fromBase64Settings) registerOptionsBag(.jsTemporalPlainDateToZDTSettings) registerOptionsBag(.jsIntlDateTimeFormatSettings) + registerOptionsBag(.jsIntlCollatorSettings) registerOptionsBag(.jsIntlLocaleMatcherSettings) registerTemporalFieldsObject(.jsTemporalPlainTimeLikeObject, forWith: false, dateFields: false, timeFields: true, zonedFields: false) @@ -1256,10 +1264,12 @@ public extension ObjectGroup { ($0.0, ILType.unboundFunction($0.1.first, receiver: receiver.instanceType)) }) if receiver.name == "Intl.DateTimeFormat" { // DateTimeFormat.format is a getter instead of regular function, and errors - // when called on the prototype. To avoid test failures, we omit - // it from the prototype. + // when called on the prototype. We hide this from the prototype object to avoid + // generating `let v0 = Intl.DateTimeFormat.prototype.format`. // https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype.format properties.removeValue(forKey: "format") + } else if receiver.name == "Intl.Collator" { + properties.removeValue(forKey: "compare") } return ObjectGroup( name: name, @@ -2895,7 +2905,11 @@ extension OptionsBag { // Intl extension ILType { // Intl types - static let jsIntlObject = ILType.object(ofGroup: "Intl", withProperties: ["DateTimeFormat"]) + static let jsIntlObject = ILType.object(ofGroup: "Intl", withProperties: ["DateTimeFormat", "Collator"]) + + static let jsIntlCollator = ILType.object(ofGroup: "Intl.Collator", withProperties: [], withMethods: ["compare", "resolvedOptions"]) + static let jsIntlCollatorConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlCollatorSettings.group.instanceType)] => .jsIntlCollator) + .object(ofGroup: "IntlCollatorConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) + static let jsIntlDateTimeFormat = ILType.object(ofGroup: "Intl.DateTimeFormat", withProperties: [], withMethods: ["format", "formatRange", "formatRangeToParts", "formatToParts", "resolvedOptions"]) static let jsIntlDateTimeFormatConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlDateTimeFormatSettings.group.instanceType)] => .jsIntlDateTimeFormat) + .object(ofGroup: "IntlDateTimeFormatConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) @@ -2907,10 +2921,37 @@ extension ObjectGroup { name: "Intl", instanceType: .jsIntlObject, properties: [ + "Collator" : .jsIntlCollatorConstructor, "DateTimeFormat" : .jsIntlDateTimeFormatConstructor, ], methods: [:] ) + + static let jsIntlCollator = ObjectGroup( + name: "Intl.Collator", + instanceType: .jsIntlCollator, + properties: [:], + methods: [ + "compare": [.string, .string] => .integer, + "resolvedOptions": [] => .object(), + ] + ) + + static let jsIntlCollatorPrototype = createPrototypeObjectGroup(jsIntlCollator) + + static let jsIntlCollatorConstructor = ObjectGroup( + name: "IntlCollatorConstructor", + constructorPath: "Intl.Collator", + instanceType: .jsIntlCollatorConstructor, + properties: [ + "prototype" : jsIntlCollatorPrototype.instanceType + ], + methods: [ + // TODO(manishearth) this also accepts arrays of locale-likes + "supportedLocalesOf": [.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlLocaleMatcherSettings.group.instanceType)] => .jsArray, + ] + ) + fileprivate static func dateTimeFormatSignature(_ returnType: ILType) -> [Signature] { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/format#date // No ZonedDateTime as stated in the docs. @@ -2954,6 +2995,7 @@ extension ObjectGroup { "supportedLocalesOf": [.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlLocaleMatcherSettings.group.instanceType)] => .jsArray, ] ) + } extension OptionsBag { @@ -2966,6 +3008,10 @@ extension OptionsBag { fileprivate static let jsIntlTimeZoneNameEnum = ILType.enumeration(ofName: "IntlTimeZoneName", withValues: ["long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric"]) fileprivate static let jsIntlFormatMatcherEnum = ILType.enumeration(ofName: "IntlFormatMatcher", withValues: ["basic", "best fit"]) fileprivate static let jsIntlFullLongMediumShort = ILType.enumeration(ofName: "IntlFullLongMediumShort", withValues: ["full", "long", "medium", "short"]) + fileprivate static let jsIntlCollatorUsageEnum = ILType.enumeration(ofName: "IntlCollatorUsage", withValues: ["sort", "search"]) + fileprivate static let jsIntlCollationEnum = ILType.enumeration(ofName: "IntlCollation", withValues: ["emoji", "pinyin", "stroke"]) + fileprivate static let jsIntlCaseFirstEnum = ILType.enumeration(ofName: "IntlCaseFirst", withValues: ["upper", "lower", "false"]) + fileprivate static let jsIntlCollatorSensitivityEnum = ILType.enumeration(ofName: "IntlCollatorSensitivity", withValues: ["base", "accent", "case", "variant"]) // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#parameters static let jsIntlDateTimeFormatSettings = OptionsBag( @@ -2997,6 +3043,20 @@ extension OptionsBag { ] ) + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/Collator#options + static let jsIntlCollatorSettings = OptionsBag( + name: "IntlCollatorSettings", + properties: [ + "usage": jsIntlCollatorUsageEnum, + "localeMatcher": jsIntlLocaleMatcherEnum, + "collation": jsIntlCollationEnum, + "numeric": .boolean, + "caseFirst": jsIntlCaseFirstEnum, + "sensitivity": jsIntlCollatorSensitivityEnum, + "ignorePunctuation": .boolean, + ] + ) + static let jsIntlLocaleMatcherSettings = OptionsBag( name: "IntlLocaleMatcherSettings", properties: [ From 245cc71a7c6310e122d2090a6aba9126010bcd3a Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 23 Sep 2025 20:05:25 -0700 Subject: [PATCH 17/35] [intl] Add type definitions for Intl.ListFormat Change-Id: I6a6a6964caab8ab9a9c97b37949bd90cd5bb1561 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8613471 Auto-Submit: Manish Goregaokar Commit-Queue: Manish Goregaokar Reviewed-by: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 4 ++ Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 2 +- .../Environment/JavaScriptEnvironment.swift | 48 ++++++++++++++++++- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 2cb38a6c7..7e759aaad 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -5173,4 +5173,8 @@ public class ProgramBuilder { return constructIntlType(type: "Collator", optionsBag: .jsIntlCollatorSettings) } + @discardableResult + func constructIntlListFormat() -> Variable { + return constructIntlType(type: "ListFormat", optionsBag: .jsIntlListFormatSettings) + } } diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 6f1c1576b..943e1b766 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -150,7 +150,7 @@ public let CodeGenerators: [CodeGenerator] = [ }, CodeGenerator("BuiltinIntlGenerator") { b in - let _ = chooseUniform(from: [b.constructIntlDateTimeFormat, b.constructIntlCollator])() + let _ = chooseUniform(from: [b.constructIntlDateTimeFormat, b.constructIntlCollator, b.constructIntlListFormat])() }, CodeGenerator("HexGenerator") { b in diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index 08f0b5b8d..a83f8bda8 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -420,6 +420,9 @@ public class JavaScriptEnvironment: ComponentBase { registerObjectGroup(.jsIntlDateTimeFormat) registerObjectGroup(.jsIntlDateTimeFormatConstructor) registerObjectGroup(.jsIntlDateTimeFormatPrototype) + registerObjectGroup(.jsIntlListFormat) + registerObjectGroup(.jsIntlListFormatConstructor) + registerObjectGroup(.jsIntlListFormatPrototype) for group in additionalObjectGroups { registerObjectGroup(group) @@ -448,6 +451,7 @@ public class JavaScriptEnvironment: ComponentBase { registerEnumeration(OptionsBag.jsIntlCollationEnum) registerEnumeration(OptionsBag.jsIntlCaseFirstEnum) registerEnumeration(OptionsBag.jsIntlCollatorSensitivityEnum) + registerEnumeration(OptionsBag.jsIntlListFormatTypeEnum) registerEnumeration(OptionsBag.base64Alphabet) registerEnumeration(OptionsBag.base64LastChunkHandling) @@ -467,6 +471,7 @@ public class JavaScriptEnvironment: ComponentBase { registerOptionsBag(.jsTemporalPlainDateToZDTSettings) registerOptionsBag(.jsIntlDateTimeFormatSettings) registerOptionsBag(.jsIntlCollatorSettings) + registerOptionsBag(.jsIntlListFormatSettings) registerOptionsBag(.jsIntlLocaleMatcherSettings) registerTemporalFieldsObject(.jsTemporalPlainTimeLikeObject, forWith: false, dateFields: false, timeFields: true, zonedFields: false) @@ -2905,7 +2910,7 @@ extension OptionsBag { // Intl extension ILType { // Intl types - static let jsIntlObject = ILType.object(ofGroup: "Intl", withProperties: ["DateTimeFormat", "Collator"]) + static let jsIntlObject = ILType.object(ofGroup: "Intl", withProperties: ["DateTimeFormat", "Collator", "ListFormat"]) static let jsIntlCollator = ILType.object(ofGroup: "Intl.Collator", withProperties: [], withMethods: ["compare", "resolvedOptions"]) static let jsIntlCollatorConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlCollatorSettings.group.instanceType)] => .jsIntlCollator) + .object(ofGroup: "IntlCollatorConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) @@ -2913,6 +2918,9 @@ extension ILType { static let jsIntlDateTimeFormat = ILType.object(ofGroup: "Intl.DateTimeFormat", withProperties: [], withMethods: ["format", "formatRange", "formatRangeToParts", "formatToParts", "resolvedOptions"]) static let jsIntlDateTimeFormatConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlDateTimeFormatSettings.group.instanceType)] => .jsIntlDateTimeFormat) + .object(ofGroup: "IntlDateTimeFormatConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) + static let jsIntlListFormat = ILType.object(ofGroup: "Intl.ListFormat", withProperties: [], withMethods: ["format", "formatToParts", "resolvedOptions"]) + static let jsIntlListFormatConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlListFormatSettings.group.instanceType)] => .jsIntlListFormat) + .object(ofGroup: "IntlListFormatConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) + static let jsIntlLocaleLike = ILType.namedString(ofName: "IntlLocaleString") } @@ -2923,6 +2931,7 @@ extension ObjectGroup { properties: [ "Collator" : .jsIntlCollatorConstructor, "DateTimeFormat" : .jsIntlDateTimeFormatConstructor, + "ListFormat" : .jsIntlListFormatConstructor, ], methods: [:] ) @@ -2996,6 +3005,32 @@ extension ObjectGroup { ] ) + static let jsIntlListFormat = ObjectGroup( + name: "Intl.ListFormat", + instanceType: .jsIntlListFormat, + properties: [:], + methods: [ + "format": [.plain(.jsArray)] => .string, + "formatToParts": [.plain(.jsArray)] => .jsArray, + "resolvedOptions": [] => .object(), + ] + ) + + static let jsIntlListFormatPrototype = createPrototypeObjectGroup(jsIntlListFormat) + + static let jsIntlListFormatConstructor = ObjectGroup( + name: "IntlListFormatConstructor", + constructorPath: "Intl.ListFormat", + instanceType: .jsIntlListFormatConstructor, + properties: [ + "prototype" : jsIntlListFormatPrototype.instanceType + ], + methods: [ + // TODO(manishearth) this also accepts arrays of locale-likes + "supportedLocalesOf": [.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlLocaleMatcherSettings.group.instanceType)] => .jsArray, + ] + ) + } extension OptionsBag { @@ -3012,6 +3047,7 @@ extension OptionsBag { fileprivate static let jsIntlCollationEnum = ILType.enumeration(ofName: "IntlCollation", withValues: ["emoji", "pinyin", "stroke"]) fileprivate static let jsIntlCaseFirstEnum = ILType.enumeration(ofName: "IntlCaseFirst", withValues: ["upper", "lower", "false"]) fileprivate static let jsIntlCollatorSensitivityEnum = ILType.enumeration(ofName: "IntlCollatorSensitivity", withValues: ["base", "accent", "case", "variant"]) + fileprivate static let jsIntlListFormatTypeEnum = ILType.enumeration(ofName: "IntlListFormatTypeEnum", withValues: ["conjunction", "disjunction", "unit"]) // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#parameters static let jsIntlDateTimeFormatSettings = OptionsBag( @@ -3057,6 +3093,16 @@ extension OptionsBag { ] ) + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/ListFormat#options + static let jsIntlListFormatSettings = OptionsBag( + name: "IntlListFormatSettings", + properties: [ + "localeMatcher": jsIntlLocaleMatcherEnum, + "type": jsIntlListFormatTypeEnum, + "style": jsIntlLongShortNarrowEnum, + ] + ) + static let jsIntlLocaleMatcherSettings = OptionsBag( name: "IntlLocaleMatcherSettings", properties: [ From f64007be6e8bd4d15f698b26ba08fe9f7747510c Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Tue, 23 Sep 2025 21:53:45 -0700 Subject: [PATCH 18/35] [intl] Add type definitions for Intl.NumberFormat Change-Id: I6a6a696400523afcfcd69f958b9b4c80e624abd2 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8613685 Reviewed-by: Matthias Liedtke Auto-Submit: Manish Goregaokar Commit-Queue: Manish Goregaokar --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 20 +++ Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 2 +- .../Environment/JavaScriptEnvironment.swift | 120 ++++++++++++++++-- 3 files changed, 133 insertions(+), 9 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 7e759aaad..bfba707a7 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -5147,6 +5147,20 @@ public class ProgramBuilder { return chooseUniform(from: Locale.availableIdentifiers) } + // Obtained by calling Intl.supportedValuesOf("unit") in a browser + fileprivate static let allUnits = ["acre", "bit", "byte", "celsius", "centimeter", "day", "degree", "fahrenheit", "fluid-ounce", "foot", "gallon", "gigabit", "gigabyte", "gram", "hectare", "hour", "inch", "kilobit", "kilobyte", "kilogram", "kilometer", "liter", "megabit", "megabyte", "meter", "microsecond", "mile", "mile-scandinavian", "milliliter", "millimeter", "millisecond", "minute", "month", "nanosecond", "ounce", "percent", "petabyte", "pound", "second", "stone", "terabit", "terabyte", "week", "yard", "year"] + + @discardableResult + static func constructIntlUnit() -> String { + let firstUnit = chooseUniform(from: allUnits) + // Intl is able to format combinations of units too, like hectares-per-gallon + if probability(0.7) { + return firstUnit + } else { + return "\(firstUnit)-per-\(chooseUniform(from: allUnits))" + } + } + // Generic generators for Intl types. private func constructIntlType(type: String, optionsBag: OptionsBag) -> Variable { let intl = createNamedVariable(forBuiltin: "Intl") @@ -5177,4 +5191,10 @@ public class ProgramBuilder { func constructIntlListFormat() -> Variable { return constructIntlType(type: "ListFormat", optionsBag: .jsIntlListFormatSettings) } + + @discardableResult + func constructIntlNumberFormat() -> Variable { + return constructIntlType(type: "NumberFormat", optionsBag: .jsIntlNumberFormatSettings) + } } + diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 943e1b766..d2f96fa2b 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -150,7 +150,7 @@ public let CodeGenerators: [CodeGenerator] = [ }, CodeGenerator("BuiltinIntlGenerator") { b in - let _ = chooseUniform(from: [b.constructIntlDateTimeFormat, b.constructIntlCollator, b.constructIntlListFormat])() + let _ = chooseUniform(from: [b.constructIntlDateTimeFormat, b.constructIntlCollator, b.constructIntlListFormat, b.constructIntlNumberFormat])() }, CodeGenerator("HexGenerator") { b in diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index a83f8bda8..556a1b3bc 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -423,6 +423,9 @@ public class JavaScriptEnvironment: ComponentBase { registerObjectGroup(.jsIntlListFormat) registerObjectGroup(.jsIntlListFormatConstructor) registerObjectGroup(.jsIntlListFormatPrototype) + registerObjectGroup(.jsIntlNumberFormat) + registerObjectGroup(.jsIntlNumberFormatConstructor) + registerObjectGroup(.jsIntlNumberFormatPrototype) for group in additionalObjectGroups { registerObjectGroup(group) @@ -442,6 +445,7 @@ public class JavaScriptEnvironment: ComponentBase { registerEnumeration(OptionsBag.jsIntlNumberingSystemEnum) registerEnumeration(OptionsBag.jsIntlHourCycleEnum) registerEnumeration(OptionsBag.jsIntlLongShortNarrowEnum) + registerEnumeration(OptionsBag.jsIntlLongShortEnum) registerEnumeration(OptionsBag.jsIntlNumeric2DigitEnum) registerEnumeration(OptionsBag.jsIntlMonthEnum) registerEnumeration(OptionsBag.jsIntlTimeZoneNameEnum) @@ -452,6 +456,16 @@ public class JavaScriptEnvironment: ComponentBase { registerEnumeration(OptionsBag.jsIntlCaseFirstEnum) registerEnumeration(OptionsBag.jsIntlCollatorSensitivityEnum) registerEnumeration(OptionsBag.jsIntlListFormatTypeEnum) + registerEnumeration(OptionsBag.jsIntlNumberFormatStyleEnum) + registerEnumeration(OptionsBag.jsIntlCurrencySystemEnum) + registerEnumeration(OptionsBag.jsIntlCurrencyDisplayEnum) + registerEnumeration(OptionsBag.jsIntlCurrencySignEnum) + registerEnumeration(OptionsBag.jsIntlRoundingPriorityEnum) + registerEnumeration(OptionsBag.jsIntlRoundingModeEnum) + registerEnumeration(OptionsBag.jsIntlTrailingZeroDisplayEnum) + registerEnumeration(OptionsBag.jsIntlNumberFormatNotationEnum) + registerEnumeration(OptionsBag.jsIntlNumberFormatGroupingEnum) + registerEnumeration(OptionsBag.jsIntlSignDisplayEnum) registerEnumeration(OptionsBag.base64Alphabet) registerEnumeration(OptionsBag.base64LastChunkHandling) @@ -472,6 +486,7 @@ public class JavaScriptEnvironment: ComponentBase { registerOptionsBag(.jsIntlDateTimeFormatSettings) registerOptionsBag(.jsIntlCollatorSettings) registerOptionsBag(.jsIntlListFormatSettings) + registerOptionsBag(.jsIntlNumberFormatSettings) registerOptionsBag(.jsIntlLocaleMatcherSettings) registerTemporalFieldsObject(.jsTemporalPlainTimeLikeObject, forWith: false, dateFields: false, timeFields: true, zonedFields: false) @@ -494,6 +509,7 @@ public class JavaScriptEnvironment: ComponentBase { } }) addNamedStringGenerator(forType: .jsIntlLocaleLike, with: { ProgramBuilder.constructIntlLocaleString() }) + addNamedStringGenerator(forType: .jsIntlUnit, with: { ProgramBuilder.constructIntlUnit() }) // Temporal types are produced by a large number of methods; which means findOrGenerateType(), when asked to produce // a Temporal type, will tend towards trying to call a method on another Temporal type, which needs more Temporal types, @@ -671,17 +687,17 @@ public class JavaScriptEnvironment: ComponentBase { public func registerEnumeration(_ type: ILType) { assert(type.isEnumeration) let group = type.group! - assert(enums[group] == nil) + assert(enums[group] == nil, "Registered duplicate enum \(group)") enums[group] = type } public func registerObjectGroup(_ group: ObjectGroup) { - assert(groups[group.name] == nil) + assert(groups[group.name] == nil, "Registered duplicate enum \(group.name)") groups[group.name] = group builtinProperties.formUnion(group.properties.keys) builtinMethods.formUnion(group.methods.keys) - // + //func register // Step 1: Initialize `subtypes` // subtypes[group.instanceType] = [group.instanceType] @@ -1267,11 +1283,11 @@ public extension ObjectGroup { let name = receiver.name + ".prototype" var properties = Dictionary(uniqueKeysWithValues: receiver.methods.map { ($0.0, ILType.unboundFunction($0.1.first, receiver: receiver.instanceType)) }) - if receiver.name == "Intl.DateTimeFormat" { - // DateTimeFormat.format is a getter instead of regular function, and errors - // when called on the prototype. We hide this from the prototype object to avoid - // generating `let v0 = Intl.DateTimeFormat.prototype.format`. - // https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype.format + // These functions are getters instead of regular functions, and error when called + // on the prototype. We hide them from the prototype object to avoid + // generating `let v0 = Intl.DateTimeFormat.prototype.format`. + // https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype.format + if receiver.name == "Intl.DateTimeFormat" || receiver.name == "Intl.NumberFormat" { properties.removeValue(forKey: "format") } else if receiver.name == "Intl.Collator" { properties.removeValue(forKey: "compare") @@ -2921,7 +2937,11 @@ extension ILType { static let jsIntlListFormat = ILType.object(ofGroup: "Intl.ListFormat", withProperties: [], withMethods: ["format", "formatToParts", "resolvedOptions"]) static let jsIntlListFormatConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlListFormatSettings.group.instanceType)] => .jsIntlListFormat) + .object(ofGroup: "IntlListFormatConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) + static let jsIntlNumberFormat = ILType.object(ofGroup: "Intl.NumberFormat", withProperties: [], withMethods: ["format", "formatRange", "formatRangeToParts", "formatToParts", "resolvedOptions"]) + static let jsIntlNumberFormatConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlNumberFormatSettings.group.instanceType)] => .jsIntlNumberFormat) + .object(ofGroup: "IntlNumberFormatConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) + static let jsIntlLocaleLike = ILType.namedString(ofName: "IntlLocaleString") + static let jsIntlUnit = ILType.namedString(ofName: "IntlUnitString") } extension ObjectGroup { @@ -3031,6 +3051,47 @@ extension ObjectGroup { ] ) + fileprivate static func numberFormatSignature(_ returnType: ILType) -> [Signature] { + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/format + [ILType.number, .bigint, .string].map { + [.opt($0)] => returnType + } + } + fileprivate static func numberFormatRangeSignature(_ returnType: ILType) -> [Signature] { + [ILType.number, .bigint, .string].flatMap { firstParam in + [ILType.number, .bigint, .string].map { + [.opt(firstParam), .opt($0)] => returnType + } + } + } + + static let jsIntlNumberFormat = ObjectGroup( + name: "Intl.NumberFormat", + instanceType: .jsIntlNumberFormat, + properties: [:], + overloads: [ + "format": numberFormatSignature(.string), + "formatRange": numberFormatRangeSignature(.string), + "formatRangeToParts": numberFormatRangeSignature(.jsArray), + "formatToParts": numberFormatSignature(.jsArray), + "resolvedOptions": [[] => .object()], + ] + ) + + static let jsIntlNumberFormatPrototype = createPrototypeObjectGroup(jsIntlNumberFormat) + + static let jsIntlNumberFormatConstructor = ObjectGroup( + name: "IntlNumberFormatConstructor", + constructorPath: "Intl.NumberFormat", + instanceType: .jsIntlNumberFormatConstructor, + properties: [ + "prototype" : jsIntlNumberFormatPrototype.instanceType + ], + methods: [ + // TODO(manishearth) this also accepts arrays of locale-likes + "supportedLocalesOf": [.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlLocaleMatcherSettings.group.instanceType)] => .jsArray, + ] + ) } extension OptionsBag { @@ -3038,6 +3099,7 @@ extension OptionsBag { fileprivate static let jsIntlNumberingSystemEnum = ILType.enumeration(ofName: "IntlNumberingSystem", withValues: Locale.NumberingSystem.availableNumberingSystems.map { $0.identifier }) fileprivate static let jsIntlHourCycleEnum = ILType.enumeration(ofName: "IntlHourCycle", withValues: ["h11", "h12", "h23", "h24"]) fileprivate static let jsIntlLongShortNarrowEnum = ILType.enumeration(ofName: "IntlLongShortNarrow", withValues: ["long", "short", "narrow"]) + fileprivate static let jsIntlLongShortEnum = ILType.enumeration(ofName: "IntlLongShort", withValues: ["long", "short"]) fileprivate static let jsIntlNumeric2DigitEnum = ILType.enumeration(ofName: "IntlNumeric2Digit", withValues: ["numeric", "2-digit"]) fileprivate static let jsIntlMonthEnum = ILType.enumeration(ofName: "IntlMonth", withValues: ["numeric", "2-digit", "long", "short", "narrow"]) fileprivate static let jsIntlTimeZoneNameEnum = ILType.enumeration(ofName: "IntlTimeZoneName", withValues: ["long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric"]) @@ -3048,6 +3110,16 @@ extension OptionsBag { fileprivate static let jsIntlCaseFirstEnum = ILType.enumeration(ofName: "IntlCaseFirst", withValues: ["upper", "lower", "false"]) fileprivate static let jsIntlCollatorSensitivityEnum = ILType.enumeration(ofName: "IntlCollatorSensitivity", withValues: ["base", "accent", "case", "variant"]) fileprivate static let jsIntlListFormatTypeEnum = ILType.enumeration(ofName: "IntlListFormatTypeEnum", withValues: ["conjunction", "disjunction", "unit"]) + fileprivate static let jsIntlNumberFormatStyleEnum = ILType.enumeration(ofName: "IntlNumberFormatStyleEnum", withValues: ["decimal", "currency", "percent", "unit"]) + fileprivate static let jsIntlCurrencySystemEnum = ILType.enumeration(ofName: "IntlCurrency", withValues: Locale.Currency.isoCurrencies.map { $0.identifier }) + fileprivate static let jsIntlCurrencyDisplayEnum = ILType.enumeration(ofName: "IntlCurrencyDisplayEnum", withValues: ["code", "symbol", "narrowSymbol", "name"]) + fileprivate static let jsIntlCurrencySignEnum = ILType.enumeration(ofName: "IntlCurrencySignEnum", withValues: ["standard", "accounting"]) + fileprivate static let jsIntlRoundingPriorityEnum = ILType.enumeration(ofName: "IntlRoundingPriority", withValues: ["auto", "morePrecision", "lessPrecision"]) + fileprivate static let jsIntlRoundingModeEnum = ILType.enumeration(ofName: "IntlRoundingMode", withValues: ["ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor", "halfExpand", "halfTrunc", "halfEven"]) + fileprivate static let jsIntlTrailingZeroDisplayEnum = ILType.enumeration(ofName: "IntlTrailingZeroDisplay", withValues: ["auto", "stripIfInteger"]) + fileprivate static let jsIntlNumberFormatNotationEnum = ILType.enumeration(ofName: "IntlNumberFormatNotation", withValues: ["standard", "scientific", "engineering", "compact"]) + fileprivate static let jsIntlNumberFormatGroupingEnum = ILType.enumeration(ofName: "IntlNumberFormatGrouping", withValues: ["always", "auto", "min2", "true", "false"]) + fileprivate static let jsIntlSignDisplayEnum = ILType.enumeration(ofName: "IntlSignDisplay", withValues: ["auto", "always", "exceptZero", "negative", "never"]) // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#parameters static let jsIntlDateTimeFormatSettings = OptionsBag( @@ -3103,6 +3175,38 @@ extension OptionsBag { ] ) + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options + static let jsIntlNumberFormatSettings = OptionsBag( + name: "IntlNumberFormatSettings", + properties: [ + // locale options + "localeMatcher": jsIntlLocaleMatcherEnum, + "numberingSystem": jsIntlNumberingSystemEnum, + // style options + "style": jsIntlNumberFormatStyleEnum, + "currency": jsIntlCurrencySystemEnum, + "currencyDisplay": jsIntlCurrencyDisplayEnum, + "currencySign": jsIntlCurrencySignEnum, + "unit": .jsIntlUnit, + "unitDisplay": jsIntlLongShortNarrowEnum, + // digit options + "minimumIntegerDigits": .integer, + "minimumFractionDigits": .integer, + "maximumFractionDigits": .integer, + "minimumSignificantDigits": .integer, + "maximumSignificantDigits": .integer, + "roundingPriority": jsIntlRoundingPriorityEnum, + "roundingIncrement": .integer, + "roundingMode": jsIntlRoundingModeEnum, + "trailingZeroDisplay": jsIntlTrailingZeroDisplayEnum, + // other options + "notation": jsIntlNumberFormatNotationEnum, + "compactDisplay": jsIntlLongShortEnum, + "useGrouping": jsIntlNumberFormatGroupingEnum, + "signDisplay": jsIntlSignDisplayEnum, + ] + ) + static let jsIntlLocaleMatcherSettings = OptionsBag( name: "IntlLocaleMatcherSettings", properties: [ From 04f20e023b695f15ee9a31a052b78445b843ed8b Mon Sep 17 00:00:00 2001 From: Omer Katz Date: Fri, 26 Sep 2025 14:56:01 +0200 Subject: [PATCH 19/35] Reduce MinorMS probability in Fuzzilli MinorMS is on hold for the foreseeable future, there is no active work on it, and the fuzzers haven't found a new issue in it in a ling time. Therefore MinorMS no longer requires extensive fuzzer coverage. Change-Id: I35743813569287ae2bf3fbd52443aadff64ee884 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8620756 Reviewed-by: Matthias Liedtke Auto-Submit: Omer Katz Commit-Queue: Omer Katz --- Sources/FuzzilliCli/Profiles/V8Profile.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/FuzzilliCli/Profiles/V8Profile.swift b/Sources/FuzzilliCli/Profiles/V8Profile.swift index d31ac2088..67438f9d9 100644 --- a/Sources/FuzzilliCli/Profiles/V8Profile.swift +++ b/Sources/FuzzilliCli/Profiles/V8Profile.swift @@ -73,7 +73,7 @@ let v8Profile = Profile( // // Future features that should sometimes be enabled. // - if probability(0.25) { + if probability(0.1) { args.append("--minor-ms") } From c40f869fd552430801e1f732cf4606ff25b6bd01 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 24 Sep 2025 15:54:24 -0700 Subject: [PATCH 20/35] [intl] Add type definitions for Intl.PluralRules Change-Id: I6a6a6964954f0f5cf274273760551698663c2b7b Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8616123 Auto-Submit: Manish Goregaokar Reviewed-by: Matthias Liedtke Commit-Queue: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 5 ++ Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 2 +- .../Environment/JavaScriptEnvironment.swift | 59 ++++++++++++++++++- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index bfba707a7..fca464450 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -5196,5 +5196,10 @@ public class ProgramBuilder { func constructIntlNumberFormat() -> Variable { return constructIntlType(type: "NumberFormat", optionsBag: .jsIntlNumberFormatSettings) } + + @discardableResult + func constructIntlPluralRules() -> Variable { + return constructIntlType(type: "PluralRules", optionsBag: .jsIntlPluralRulesSettings) + } } diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index d2f96fa2b..194cf9448 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -150,7 +150,7 @@ public let CodeGenerators: [CodeGenerator] = [ }, CodeGenerator("BuiltinIntlGenerator") { b in - let _ = chooseUniform(from: [b.constructIntlDateTimeFormat, b.constructIntlCollator, b.constructIntlListFormat, b.constructIntlNumberFormat])() + let _ = chooseUniform(from: [b.constructIntlDateTimeFormat, b.constructIntlCollator, b.constructIntlListFormat, b.constructIntlNumberFormat, b.constructIntlPluralRules])() }, CodeGenerator("HexGenerator") { b in diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index 556a1b3bc..a21b3bc45 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -426,6 +426,9 @@ public class JavaScriptEnvironment: ComponentBase { registerObjectGroup(.jsIntlNumberFormat) registerObjectGroup(.jsIntlNumberFormatConstructor) registerObjectGroup(.jsIntlNumberFormatPrototype) + registerObjectGroup(.jsIntlPluralRules) + registerObjectGroup(.jsIntlPluralRulesConstructor) + registerObjectGroup(.jsIntlPluralRulesPrototype) for group in additionalObjectGroups { registerObjectGroup(group) @@ -466,6 +469,7 @@ public class JavaScriptEnvironment: ComponentBase { registerEnumeration(OptionsBag.jsIntlNumberFormatNotationEnum) registerEnumeration(OptionsBag.jsIntlNumberFormatGroupingEnum) registerEnumeration(OptionsBag.jsIntlSignDisplayEnum) + registerEnumeration(OptionsBag.jsIntlPluralRulesTypeEnum) registerEnumeration(OptionsBag.base64Alphabet) registerEnumeration(OptionsBag.base64LastChunkHandling) @@ -487,6 +491,7 @@ public class JavaScriptEnvironment: ComponentBase { registerOptionsBag(.jsIntlCollatorSettings) registerOptionsBag(.jsIntlListFormatSettings) registerOptionsBag(.jsIntlNumberFormatSettings) + registerOptionsBag(.jsIntlPluralRulesSettings) registerOptionsBag(.jsIntlLocaleMatcherSettings) registerTemporalFieldsObject(.jsTemporalPlainTimeLikeObject, forWith: false, dateFields: false, timeFields: true, zonedFields: false) @@ -2926,7 +2931,7 @@ extension OptionsBag { // Intl extension ILType { // Intl types - static let jsIntlObject = ILType.object(ofGroup: "Intl", withProperties: ["DateTimeFormat", "Collator", "ListFormat"]) + static let jsIntlObject = ILType.object(ofGroup: "Intl", withProperties: ["DateTimeFormat", "Collator", "ListFormat", "NumberFormat", "PluralRules"]) static let jsIntlCollator = ILType.object(ofGroup: "Intl.Collator", withProperties: [], withMethods: ["compare", "resolvedOptions"]) static let jsIntlCollatorConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlCollatorSettings.group.instanceType)] => .jsIntlCollator) + .object(ofGroup: "IntlCollatorConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) @@ -2940,6 +2945,9 @@ extension ILType { static let jsIntlNumberFormat = ILType.object(ofGroup: "Intl.NumberFormat", withProperties: [], withMethods: ["format", "formatRange", "formatRangeToParts", "formatToParts", "resolvedOptions"]) static let jsIntlNumberFormatConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlNumberFormatSettings.group.instanceType)] => .jsIntlNumberFormat) + .object(ofGroup: "IntlNumberFormatConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) + static let jsIntlPluralRules = ILType.object(ofGroup: "Intl.PluralRules", withProperties: [], withMethods: ["select", "selectRange", "resolvedOptions"]) + static let jsIntlPluralRulesConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlPluralRulesSettings.group.instanceType)] => .jsIntlPluralRules) + .object(ofGroup: "IntlPluralRulesConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) + static let jsIntlLocaleLike = ILType.namedString(ofName: "IntlLocaleString") static let jsIntlUnit = ILType.namedString(ofName: "IntlUnitString") } @@ -2952,6 +2960,8 @@ extension ObjectGroup { "Collator" : .jsIntlCollatorConstructor, "DateTimeFormat" : .jsIntlDateTimeFormatConstructor, "ListFormat" : .jsIntlListFormatConstructor, + "NumberFormat" : .jsIntlNumberFormatConstructor, + "PluralRules" : .jsIntlPluralRulesConstructor, ], methods: [:] ) @@ -3092,6 +3102,32 @@ extension ObjectGroup { "supportedLocalesOf": [.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlLocaleMatcherSettings.group.instanceType)] => .jsArray, ] ) + + static let jsIntlPluralRules = ObjectGroup( + name: "Intl.PluralRules", + instanceType: .jsIntlPluralRules, + properties: [:], + methods: [ + "select": [.number] => .string, + "selectRange": [.number, .number] => .string, + "resolvedOptions": [] => .object(), + ] + ) + + static let jsIntlPluralRulesPrototype = createPrototypeObjectGroup(jsIntlPluralRules) + + static let jsIntlPluralRulesConstructor = ObjectGroup( + name: "IntlPluralRulesConstructor", + constructorPath: "Intl.PluralRules", + instanceType: .jsIntlPluralRulesConstructor, + properties: [ + "prototype" : jsIntlPluralRulesPrototype.instanceType + ], + methods: [ + // TODO(manishearth) this also accepts arrays of locale-likes + "supportedLocalesOf": [.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlLocaleMatcherSettings.group.instanceType)] => .jsArray, + ] + ) } extension OptionsBag { @@ -3120,6 +3156,7 @@ extension OptionsBag { fileprivate static let jsIntlNumberFormatNotationEnum = ILType.enumeration(ofName: "IntlNumberFormatNotation", withValues: ["standard", "scientific", "engineering", "compact"]) fileprivate static let jsIntlNumberFormatGroupingEnum = ILType.enumeration(ofName: "IntlNumberFormatGrouping", withValues: ["always", "auto", "min2", "true", "false"]) fileprivate static let jsIntlSignDisplayEnum = ILType.enumeration(ofName: "IntlSignDisplay", withValues: ["auto", "always", "exceptZero", "negative", "never"]) + fileprivate static let jsIntlPluralRulesTypeEnum = ILType.enumeration(ofName: "IntlPluralRulesTypeEnum", withValues: ["cardinal", "ordinal"]) // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#parameters static let jsIntlDateTimeFormatSettings = OptionsBag( @@ -3207,6 +3244,26 @@ extension OptionsBag { ] ) + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules/PluralRules#options + static let jsIntlPluralRulesSettings = OptionsBag( + name: "IntlPluralRulesSettings", + properties: [ + "localeMatcher": jsIntlLocaleMatcherEnum, + "type": jsIntlPluralRulesTypeEnum, + + // NumberFormat digit options + "minimumIntegerDigits": .integer, + "minimumFractionDigits": .integer, + "maximumFractionDigits": .integer, + "minimumSignificantDigits": .integer, + "maximumSignificantDigits": .integer, + "roundingPriority": jsIntlRoundingPriorityEnum, + "roundingIncrement": .integer, + "roundingMode": jsIntlRoundingModeEnum, + + ] + ) + static let jsIntlLocaleMatcherSettings = OptionsBag( name: "IntlLocaleMatcherSettings", properties: [ From 684538efff5cba6e493c5bcd36b1113de66c8a10 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 24 Sep 2025 15:59:59 -0700 Subject: [PATCH 21/35] [intl] Add type definitions for Intl.RelativeTimeFormat Change-Id: I6a6a69649cf1fd43510fa9a501bf0addb27c2414 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8616124 Auto-Submit: Manish Goregaokar Commit-Queue: Manish Goregaokar Reviewed-by: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 5 ++ Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 2 +- .../Environment/JavaScriptEnvironment.swift | 52 ++++++++++++++++++- 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index fca464450..2e2e79e46 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -5201,5 +5201,10 @@ public class ProgramBuilder { func constructIntlPluralRules() -> Variable { return constructIntlType(type: "PluralRules", optionsBag: .jsIntlPluralRulesSettings) } + + @discardableResult + func constructIntlRelativeTimeFormat() -> Variable { + return constructIntlType(type: "RelativeTimeFormat", optionsBag: .jsIntlRelativeTimeFormatSettings) + } } diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 194cf9448..387f32ff5 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -150,7 +150,7 @@ public let CodeGenerators: [CodeGenerator] = [ }, CodeGenerator("BuiltinIntlGenerator") { b in - let _ = chooseUniform(from: [b.constructIntlDateTimeFormat, b.constructIntlCollator, b.constructIntlListFormat, b.constructIntlNumberFormat, b.constructIntlPluralRules])() + let _ = chooseUniform(from: [b.constructIntlDateTimeFormat, b.constructIntlCollator, b.constructIntlListFormat, b.constructIntlNumberFormat, b.constructIntlPluralRules, b.constructIntlRelativeTimeFormat])() }, CodeGenerator("HexGenerator") { b in diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index a21b3bc45..8d8a83d05 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -429,6 +429,9 @@ public class JavaScriptEnvironment: ComponentBase { registerObjectGroup(.jsIntlPluralRules) registerObjectGroup(.jsIntlPluralRulesConstructor) registerObjectGroup(.jsIntlPluralRulesPrototype) + registerObjectGroup(.jsIntlRelativeTimeFormat) + registerObjectGroup(.jsIntlRelativeTimeFormatConstructor) + registerObjectGroup(.jsIntlRelativeTimeFormatPrototype) for group in additionalObjectGroups { registerObjectGroup(group) @@ -436,6 +439,7 @@ public class JavaScriptEnvironment: ComponentBase { registerEnumeration(.jsTemporalCalendarEnum) registerEnumeration(ObjectGroup.jsTemporalDirectionParam) + registerEnumeration(ObjectGroup.jsIntlRelativeTimeFormatUnitEnum) registerEnumeration(OptionsBag.jsTemporalUnitEnum) registerEnumeration(OptionsBag.jsTemporalRoundingModeEnum) registerEnumeration(OptionsBag.jsTemporalShowCalendarEnum) @@ -449,6 +453,7 @@ public class JavaScriptEnvironment: ComponentBase { registerEnumeration(OptionsBag.jsIntlHourCycleEnum) registerEnumeration(OptionsBag.jsIntlLongShortNarrowEnum) registerEnumeration(OptionsBag.jsIntlLongShortEnum) + registerEnumeration(OptionsBag.jsIntlAutoAlwaysEnum) registerEnumeration(OptionsBag.jsIntlNumeric2DigitEnum) registerEnumeration(OptionsBag.jsIntlMonthEnum) registerEnumeration(OptionsBag.jsIntlTimeZoneNameEnum) @@ -492,6 +497,7 @@ public class JavaScriptEnvironment: ComponentBase { registerOptionsBag(.jsIntlListFormatSettings) registerOptionsBag(.jsIntlNumberFormatSettings) registerOptionsBag(.jsIntlPluralRulesSettings) + registerOptionsBag(.jsIntlRelativeTimeFormatSettings) registerOptionsBag(.jsIntlLocaleMatcherSettings) registerTemporalFieldsObject(.jsTemporalPlainTimeLikeObject, forWith: false, dateFields: false, timeFields: true, zonedFields: false) @@ -2931,7 +2937,7 @@ extension OptionsBag { // Intl extension ILType { // Intl types - static let jsIntlObject = ILType.object(ofGroup: "Intl", withProperties: ["DateTimeFormat", "Collator", "ListFormat", "NumberFormat", "PluralRules"]) + static let jsIntlObject = ILType.object(ofGroup: "Intl", withProperties: ["DateTimeFormat", "Collator", "ListFormat", "NumberFormat", "PluralRules", "RelativeTimeFormat"]) static let jsIntlCollator = ILType.object(ofGroup: "Intl.Collator", withProperties: [], withMethods: ["compare", "resolvedOptions"]) static let jsIntlCollatorConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlCollatorSettings.group.instanceType)] => .jsIntlCollator) + .object(ofGroup: "IntlCollatorConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) @@ -2948,6 +2954,9 @@ extension ILType { static let jsIntlPluralRules = ILType.object(ofGroup: "Intl.PluralRules", withProperties: [], withMethods: ["select", "selectRange", "resolvedOptions"]) static let jsIntlPluralRulesConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlPluralRulesSettings.group.instanceType)] => .jsIntlPluralRules) + .object(ofGroup: "IntlPluralRulesConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) + static let jsIntlRelativeTimeFormat = ILType.object(ofGroup: "Intl.RelativeTimeFormat", withProperties: [], withMethods: ["format", "formatToParts", "resolvedOptions"]) + static let jsIntlRelativeTimeFormatConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlRelativeTimeFormatSettings.group.instanceType)] => .jsIntlRelativeTimeFormat) + .object(ofGroup: "IntlRelativeTimeFormatConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) + static let jsIntlLocaleLike = ILType.namedString(ofName: "IntlLocaleString") static let jsIntlUnit = ILType.namedString(ofName: "IntlUnitString") } @@ -2962,6 +2971,7 @@ extension ObjectGroup { "ListFormat" : .jsIntlListFormatConstructor, "NumberFormat" : .jsIntlNumberFormatConstructor, "PluralRules" : .jsIntlPluralRulesConstructor, + "RelativeTimeFormat" : .jsIntlRelativeTimeFormatConstructor, ], methods: [:] ) @@ -3128,6 +3138,34 @@ extension ObjectGroup { "supportedLocalesOf": [.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlLocaleMatcherSettings.group.instanceType)] => .jsArray, ] ) + + static let jsIntlRelativeTimeFormatUnitEnum = ILType.enumeration(ofName: "IntlRelativeTimeFormatUnit", withValues: ["year", "quarter", "month", "week", "day", "hour", "minute", "second"]) + + static let jsIntlRelativeTimeFormat = ObjectGroup( + name: "Intl.RelativeTimeFormat", + instanceType: .jsIntlRelativeTimeFormat, + properties: [:], + methods: [ + "format": [.number, .plain(jsIntlRelativeTimeFormatUnitEnum)] => .string, + "formatToParts": [.number, .plain(jsIntlRelativeTimeFormatUnitEnum)] => .jsArray, + "resolvedOptions": [] => .object(), + ] + ) + + static let jsIntlRelativeTimeFormatPrototype = createPrototypeObjectGroup(jsIntlRelativeTimeFormat) + + static let jsIntlRelativeTimeFormatConstructor = ObjectGroup( + name: "IntlRelativeTimeFormatConstructor", + constructorPath: "Intl.RelativeTimeFormat", + instanceType: .jsIntlRelativeTimeFormatConstructor, + properties: [ + "prototype" : jsIntlRelativeTimeFormatPrototype.instanceType + ], + methods: [ + // TODO(manishearth) this also accepts arrays of locale-likes + "supportedLocalesOf": [.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlLocaleMatcherSettings.group.instanceType)] => .jsArray, + ] + ) } extension OptionsBag { @@ -3136,6 +3174,7 @@ extension OptionsBag { fileprivate static let jsIntlHourCycleEnum = ILType.enumeration(ofName: "IntlHourCycle", withValues: ["h11", "h12", "h23", "h24"]) fileprivate static let jsIntlLongShortNarrowEnum = ILType.enumeration(ofName: "IntlLongShortNarrow", withValues: ["long", "short", "narrow"]) fileprivate static let jsIntlLongShortEnum = ILType.enumeration(ofName: "IntlLongShort", withValues: ["long", "short"]) + fileprivate static let jsIntlAutoAlwaysEnum = ILType.enumeration(ofName: "IntlAutoAlways", withValues: ["auto", "always"]) fileprivate static let jsIntlNumeric2DigitEnum = ILType.enumeration(ofName: "IntlNumeric2Digit", withValues: ["numeric", "2-digit"]) fileprivate static let jsIntlMonthEnum = ILType.enumeration(ofName: "IntlMonth", withValues: ["numeric", "2-digit", "long", "short", "narrow"]) fileprivate static let jsIntlTimeZoneNameEnum = ILType.enumeration(ofName: "IntlTimeZoneName", withValues: ["long", "short", "shortOffset", "longOffset", "shortGeneric", "longGeneric"]) @@ -3264,6 +3303,17 @@ extension OptionsBag { ] ) + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat/RelativeTimeFormat#options + static let jsIntlRelativeTimeFormatSettings = OptionsBag( + name: "IntlRelativeTimeFormatSettings", + properties: [ + "localeMatcher": jsIntlLocaleMatcherEnum, + "numberingSystem": jsIntlNumberingSystemEnum, + "style": jsIntlLongShortNarrowEnum, + "numeric": jsIntlAutoAlwaysEnum, + ] + ) + static let jsIntlLocaleMatcherSettings = OptionsBag( name: "IntlLocaleMatcherSettings", properties: [ From 88a84c7c3d2a5974c65c78f5b379e93ea7f73c30 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 24 Sep 2025 16:10:22 -0700 Subject: [PATCH 22/35] [intl] Add type definitions for Intl.Segmenter Change-Id: I6a6a696485ee44adbbbe1b5c7e137e7164d477f9 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8616074 Auto-Submit: Manish Goregaokar Reviewed-by: Matthias Liedtke Commit-Queue: Manish Goregaokar --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 5 ++ Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 2 +- .../Environment/JavaScriptEnvironment.swift | 57 ++++++++++++++++++- 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 2e2e79e46..a2c137a05 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -5206,5 +5206,10 @@ public class ProgramBuilder { func constructIntlRelativeTimeFormat() -> Variable { return constructIntlType(type: "RelativeTimeFormat", optionsBag: .jsIntlRelativeTimeFormatSettings) } + + @discardableResult + func constructIntlSegmenter() -> Variable { + return constructIntlType(type: "Segmenter", optionsBag: .jsIntlSegmenterSettings) + } } diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 387f32ff5..df11024c9 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -150,7 +150,7 @@ public let CodeGenerators: [CodeGenerator] = [ }, CodeGenerator("BuiltinIntlGenerator") { b in - let _ = chooseUniform(from: [b.constructIntlDateTimeFormat, b.constructIntlCollator, b.constructIntlListFormat, b.constructIntlNumberFormat, b.constructIntlPluralRules, b.constructIntlRelativeTimeFormat])() + let _ = chooseUniform(from: [b.constructIntlDateTimeFormat, b.constructIntlCollator, b.constructIntlListFormat, b.constructIntlNumberFormat, b.constructIntlPluralRules, b.constructIntlRelativeTimeFormat, b.constructIntlSegmenter])() }, CodeGenerator("HexGenerator") { b in diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index 8d8a83d05..f46f316a8 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -432,6 +432,10 @@ public class JavaScriptEnvironment: ComponentBase { registerObjectGroup(.jsIntlRelativeTimeFormat) registerObjectGroup(.jsIntlRelativeTimeFormatConstructor) registerObjectGroup(.jsIntlRelativeTimeFormatPrototype) + registerObjectGroup(.jsIntlSegmenter) + registerObjectGroup(.jsIntlSegmenterConstructor) + registerObjectGroup(.jsIntlSegmenterPrototype) + registerObjectGroup(.jsIntlSegmenterSegments) for group in additionalObjectGroups { registerObjectGroup(group) @@ -475,6 +479,7 @@ public class JavaScriptEnvironment: ComponentBase { registerEnumeration(OptionsBag.jsIntlNumberFormatGroupingEnum) registerEnumeration(OptionsBag.jsIntlSignDisplayEnum) registerEnumeration(OptionsBag.jsIntlPluralRulesTypeEnum) + registerEnumeration(OptionsBag.jsIntlSegmenterGranularityEnum) registerEnumeration(OptionsBag.base64Alphabet) registerEnumeration(OptionsBag.base64LastChunkHandling) @@ -498,6 +503,7 @@ public class JavaScriptEnvironment: ComponentBase { registerOptionsBag(.jsIntlNumberFormatSettings) registerOptionsBag(.jsIntlPluralRulesSettings) registerOptionsBag(.jsIntlRelativeTimeFormatSettings) + registerOptionsBag(.jsIntlSegmenterSettings) registerOptionsBag(.jsIntlLocaleMatcherSettings) registerTemporalFieldsObject(.jsTemporalPlainTimeLikeObject, forWith: false, dateFields: false, timeFields: true, zonedFields: false) @@ -2937,7 +2943,7 @@ extension OptionsBag { // Intl extension ILType { // Intl types - static let jsIntlObject = ILType.object(ofGroup: "Intl", withProperties: ["DateTimeFormat", "Collator", "ListFormat", "NumberFormat", "PluralRules", "RelativeTimeFormat"]) + static let jsIntlObject = ILType.object(ofGroup: "Intl", withProperties: ["DateTimeFormat", "Collator", "ListFormat", "NumberFormat", "PluralRules", "RelativeTimeFormat", "Segmenter"]) static let jsIntlCollator = ILType.object(ofGroup: "Intl.Collator", withProperties: [], withMethods: ["compare", "resolvedOptions"]) static let jsIntlCollatorConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlCollatorSettings.group.instanceType)] => .jsIntlCollator) + .object(ofGroup: "IntlCollatorConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) @@ -2957,6 +2963,11 @@ extension ILType { static let jsIntlRelativeTimeFormat = ILType.object(ofGroup: "Intl.RelativeTimeFormat", withProperties: [], withMethods: ["format", "formatToParts", "resolvedOptions"]) static let jsIntlRelativeTimeFormatConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlRelativeTimeFormatSettings.group.instanceType)] => .jsIntlRelativeTimeFormat) + .object(ofGroup: "IntlRelativeTimeFormatConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) + static let jsIntlSegmenter = ILType.object(ofGroup: "Intl.Segmenter", withProperties: [], withMethods: ["segment", "resolvedOptions"]) + static let jsIntlSegmenterConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlSegmenterSettings.group.instanceType)] => .jsIntlSegmenter) + .object(ofGroup: "IntlSegmenterConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) + + static let jsIntlSegmenterSegments = ILType.object(ofGroup: "IntlSegmenterSegments", withProperties: [], withMethods: ["containing"]) + static let jsIntlLocaleLike = ILType.namedString(ofName: "IntlLocaleString") static let jsIntlUnit = ILType.namedString(ofName: "IntlUnitString") } @@ -2972,6 +2983,7 @@ extension ObjectGroup { "NumberFormat" : .jsIntlNumberFormatConstructor, "PluralRules" : .jsIntlPluralRulesConstructor, "RelativeTimeFormat" : .jsIntlRelativeTimeFormatConstructor, + "Segmenter" : .jsIntlSegmenterConstructor, ], methods: [:] ) @@ -3166,6 +3178,39 @@ extension ObjectGroup { "supportedLocalesOf": [.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlLocaleMatcherSettings.group.instanceType)] => .jsArray, ] ) + + static let jsIntlSegmenter = ObjectGroup( + name: "Intl.Segmenter", + instanceType: .jsIntlSegmenter, + properties: [:], + methods: [ + "segment": [.string] => .jsIntlSegmenterSegments, + "resolvedOptions": [] => .object(), + ] + ) + + static let jsIntlSegmenterPrototype = createPrototypeObjectGroup(jsIntlSegmenter) + + static let jsIntlSegmenterConstructor = ObjectGroup( + name: "IntlSegmenterConstructor", + constructorPath: "Intl.Segmenter", + instanceType: .jsIntlSegmenterConstructor, + properties: [ + "prototype" : jsIntlSegmenterPrototype.instanceType + ], + methods: [ + // TODO(manishearth) this also accepts arrays of locale-likes + "supportedLocalesOf": [.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlLocaleMatcherSettings.group.instanceType)] => .jsArray, + ] + ) + static let jsIntlSegmenterSegments = ObjectGroup( + name: "IntlSegmenterSegments", + instanceType: .jsIntlSegmenterSegments, + properties: [:], + methods: [ + "containing": [.number] => .object(), + ] + ) } extension OptionsBag { @@ -3196,6 +3241,7 @@ extension OptionsBag { fileprivate static let jsIntlNumberFormatGroupingEnum = ILType.enumeration(ofName: "IntlNumberFormatGrouping", withValues: ["always", "auto", "min2", "true", "false"]) fileprivate static let jsIntlSignDisplayEnum = ILType.enumeration(ofName: "IntlSignDisplay", withValues: ["auto", "always", "exceptZero", "negative", "never"]) fileprivate static let jsIntlPluralRulesTypeEnum = ILType.enumeration(ofName: "IntlPluralRulesTypeEnum", withValues: ["cardinal", "ordinal"]) + fileprivate static let jsIntlSegmenterGranularityEnum = ILType.enumeration(ofName: "IntlSegmenterGranularityEnum", withValues: ["grapheme", "word", "sentence"]) // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#parameters static let jsIntlDateTimeFormatSettings = OptionsBag( @@ -3314,6 +3360,15 @@ extension OptionsBag { ] ) + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter/Segmenter#options + static let jsIntlSegmenterSettings = OptionsBag( + name: "IntlSegmenterSettings", + properties: [ + "localeMatcher": jsIntlLocaleMatcherEnum, + "granularity": jsIntlSegmenterGranularityEnum, + ] + ) + static let jsIntlLocaleMatcherSettings = OptionsBag( name: "IntlLocaleMatcherSettings", properties: [ From 38486439c6b5546f9601aa7102ccc4459b6d0ade Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 24 Sep 2025 16:17:55 -0700 Subject: [PATCH 23/35] [intl] Fill in missing Intl methods After this we just need to do DisplayNames, which unfortunately needs another custom string generator so I saved it for last. Change-Id: I6a6a69642b4316bd94ea39e92a675bf517202b48 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8616075 Auto-Submit: Manish Goregaokar Commit-Queue: Manish Goregaokar Reviewed-by: Matthias Liedtke --- .../Fuzzilli/Environment/JavaScriptEnvironment.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index f46f316a8..488c1cdd3 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -444,6 +444,7 @@ public class JavaScriptEnvironment: ComponentBase { registerEnumeration(.jsTemporalCalendarEnum) registerEnumeration(ObjectGroup.jsTemporalDirectionParam) registerEnumeration(ObjectGroup.jsIntlRelativeTimeFormatUnitEnum) + registerEnumeration(ObjectGroup.jsIntlSupportedValuesEnum) registerEnumeration(OptionsBag.jsTemporalUnitEnum) registerEnumeration(OptionsBag.jsTemporalRoundingModeEnum) registerEnumeration(OptionsBag.jsTemporalShowCalendarEnum) @@ -2943,7 +2944,7 @@ extension OptionsBag { // Intl extension ILType { // Intl types - static let jsIntlObject = ILType.object(ofGroup: "Intl", withProperties: ["DateTimeFormat", "Collator", "ListFormat", "NumberFormat", "PluralRules", "RelativeTimeFormat", "Segmenter"]) + static let jsIntlObject = ILType.object(ofGroup: "Intl", withProperties: ["DateTimeFormat", "Collator", "ListFormat", "NumberFormat", "PluralRules", "RelativeTimeFormat", "Segmenter"], withMethods: ["getCanonicalLocales", "supportedValuesOf"]) static let jsIntlCollator = ILType.object(ofGroup: "Intl.Collator", withProperties: [], withMethods: ["compare", "resolvedOptions"]) static let jsIntlCollatorConstructor = ILType.functionAndConstructor([.opt(.jsIntlLocaleLike), .opt(OptionsBag.jsIntlCollatorSettings.group.instanceType)] => .jsIntlCollator) + .object(ofGroup: "IntlCollatorConstructor", withProperties: ["prototype"], withMethods: ["supportedLocalesOf"]) @@ -2973,6 +2974,8 @@ extension ILType { } extension ObjectGroup { + static let jsIntlSupportedValuesEnum = ILType.enumeration(ofName: "IntlSupportedValues", withValues: ["calendar", "collation", "currency", "numberingSystem", "timeZone"]) + static let jsIntlObject = ObjectGroup( name: "Intl", instanceType: .jsIntlObject, @@ -2985,7 +2988,10 @@ extension ObjectGroup { "RelativeTimeFormat" : .jsIntlRelativeTimeFormatConstructor, "Segmenter" : .jsIntlSegmenterConstructor, ], - methods: [:] + methods: [ + "getCanonicalLocales": [] => .jsArray, + "supportedValuesOf": [.plain(jsIntlSupportedValuesEnum)] => .jsArray, + ] ) static let jsIntlCollator = ObjectGroup( From 80ad045556fdd065752cd1f1733b0c627b7f6b4b Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Fri, 12 Sep 2025 18:19:39 +0200 Subject: [PATCH 24/35] Simplify constrained string generation in code generators Change-Id: I0ed4abed4a3ef0c7e150971ec58f0aae7e5b0982 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8591236 Auto-Submit: Matthias Liedtke Reviewed-by: Carl Smith Commit-Queue: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 30 +++ Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 238 +++--------------- 2 files changed, 70 insertions(+), 198 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index a2c137a05..7d8b569c4 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -483,6 +483,36 @@ public class ProgramBuilder { return probability(0.5) ? randomBuiltinMethodName() : randomCustomMethodName() } + private static func generateConstrained( + _ generator: () -> T, + notIn: any Collection, + fallback: () -> T) -> T { + var result: T + var attempts = 0 + repeat { + if attempts >= 10 { + return fallback() + } + result = generator() + attempts += 1 + } while notIn.contains(result) + return result + } + + // Generate a string not already contained in `notIn` using the provided `generator`. If it fails + // repeatedly, return a random string instead. + public func generateString(_ generator: () -> String, notIn: any Collection) -> String { + Self.generateConstrained(generator, notIn: notIn, + fallback: {String.random(ofLength: Int.random(in: 1...5))}) + } + + // Find a random variable to use as a string that isn't contained in `notIn`. If it fails + // repeatedly, create a random string literal instead. + public func findOrGenerateStringLikeVariable(notIn: any Collection) -> Variable { + return Self.generateConstrained(randomJsVariable, notIn: notIn, + fallback: {loadString(String.random(ofLength: Int.random(in: 1...5)))}) + } + // Settings and constants controlling the behavior of randomParameters() below. // This determines how many variables of a given type need to be visible before // that type is considered a candidate for a parameter type. For example, if this diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index df11024c9..c033a9273 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -476,17 +476,8 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added to this literal. - var propertyName: String - var attempts = 0 - repeat { - if attempts >= 10 { - propertyName = String.random(ofLength: Int.random(in: 1...5)) - break - } - propertyName = b.randomCustomPropertyName() - attempts += 1 - } while b.currentObjectLiteral.properties.contains(propertyName) - + let propertyName = b.generateString(b.randomCustomPropertyName, + notIn: b.currentObjectLiteral.properties) b.currentObjectLiteral.addProperty( propertyName, as: b.randomJsVariable()) }, @@ -528,7 +519,6 @@ public let CodeGenerators: [CodeGenerator] = [ propertyName = b.randomJsVariable() attempts += 1 } while b.currentObjectLiteral.computedProperties.contains(propertyName) - b.currentObjectLiteral.addComputedProperty(propertyName, as: value) }, @@ -567,17 +557,8 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a method that hasn't already been added to this literal. - var methodName: String - var attempts = 0 - repeat { - if attempts >= 10 { - methodName = String.random( - ofLength: Int.random(in: 1...5)) - break - } - methodName = b.randomCustomMethodName() - attempts += 1 - } while b.currentObjectLiteral.methods.contains(methodName) + let methodName = b.generateString(b.randomCustomMethodName, + notIn: b.currentObjectLiteral.methods) let randomParameters = b.randomParameters() b.setParameterTypesForNextSubroutine( @@ -605,15 +586,16 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a computed method name that hasn't already been added to this literal. + var methodName: Variable var attempts = 0 repeat { + methodName = b.randomJsVariable() if attempts >= 10 { - methodName = b.loadString( - String.random(ofLength: Int.random(in: 1...5))) + // This might lead to having two computed methods with the same name (so one + // will overwrite the other). break } - methodName = b.randomJsVariable() attempts += 1 } while b.currentObjectLiteral.computedMethods.contains( methodName) @@ -648,20 +630,8 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added and for which a getter has not yet been installed. - var propertyName: String - var attempts = 0 - repeat { - if attempts >= 10 { - // We should not fail here but also don't produce a syntax error. - propertyName = String.random( - ofLength: Int.random(in: 1...5)) - break - } - propertyName = b.randomCustomPropertyName() - attempts += 1 - } while b.currentObjectLiteral.properties.contains(propertyName) - || b.currentObjectLiteral.getters.contains(propertyName) - + let propertyName = b.generateString(b.randomCustomPropertyName, + notIn: b.currentObjectLiteral.properties + b.currentObjectLiteral.getters) b.emit(BeginObjectLiteralGetter(propertyName: propertyName)) }, @@ -688,20 +658,8 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added and for which a setter has not yet been installed. - var propertyName: String - var attempts = 0 - repeat { - if attempts >= 10 { - // We should not fail here but also don't produce a syntax error. - propertyName = String.random( - ofLength: Int.random(in: 1...5)) - break - } - propertyName = b.randomCustomPropertyName() - attempts += 1 - } while b.currentObjectLiteral.properties.contains(propertyName) - || b.currentObjectLiteral.setters.contains(propertyName) - + let propertyName = b.generateString(b.randomCustomPropertyName, + notIn: b.currentObjectLiteral.properties + b.currentObjectLiteral.setters) b.emit(BeginObjectLiteralSetter(propertyName: propertyName)) }, GeneratorStub( @@ -772,14 +730,8 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added to this literal. - var propertyName: String - var attempts = 0 - repeat { - guard attempts < 10 else { return } - propertyName = b.randomCustomPropertyName() - attempts += 1 - } while b.currentClassDefinition.instanceProperties.contains( - propertyName) + let propertyName = b.generateString(b.randomCustomPropertyName, + notIn: b.currentClassDefinition.instanceProperties) var value: Variable? = probability(0.5) ? b.randomJsVariable() : nil b.currentClassDefinition.addInstanceProperty(propertyName, value: value) @@ -837,18 +789,8 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a method that hasn't already been added to this class. - var methodName: String - var attempts = 0 - repeat { - if attempts >= 10 { - methodName = String.random( - ofLength: Int.random(in: 1...5)) - break - } - methodName = b.randomCustomMethodName() - attempts += 1 - } while b.currentClassDefinition.instanceMethods.contains( - methodName) + let methodName = b.generateString(b.randomCustomMethodName, + notIn: b.currentClassDefinition.instanceMethods) let parameters = b.randomParameters() b.setParameterTypesForNextSubroutine(parameters.parameterTypes) @@ -880,21 +822,9 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added and for which a getter has not yet been installed. - var propertyName: String - var attempts = 0 - repeat { - if attempts >= 10 { - propertyName = String.random( - ofLength: Int.random(in: 1...5)) - break - } - propertyName = b.randomCustomPropertyName() - attempts += 1 - } while b.currentClassDefinition.instanceProperties.contains( - propertyName) - || b.currentClassDefinition.instanceGetters.contains( - propertyName) - + let propertyName = b.generateString(b.randomCustomPropertyName, + notIn: b.currentClassDefinition.instanceProperties + + b.currentClassDefinition.instanceGetters) b.emit(BeginClassInstanceGetter(propertyName: propertyName)) }, GeneratorStub( @@ -918,21 +848,9 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added and for which a setter has not yet been installed. - var propertyName: String - var attempts = 0 - repeat { - if attempts >= 10 { - propertyName = String.random( - ofLength: Int.random(in: 1...5)) - break - } - propertyName = b.randomCustomPropertyName() - attempts += 1 - } while b.currentClassDefinition.instanceProperties.contains( - propertyName) - || b.currentClassDefinition.instanceSetters.contains( - propertyName) - + let propertyName = b.generateString(b.randomCustomPropertyName, + notIn: b.currentClassDefinition.instanceProperties + + b.currentClassDefinition.instanceSetters) b.emit(BeginClassInstanceSetter(propertyName: propertyName)) }, GeneratorStub( @@ -951,17 +869,8 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added to this literal. - var propertyName: String - var attempts = 0 - repeat { - if attempts >= 10 { - propertyName = String.random( - ofLength: Int.random(in: 1...5)) - break - } - propertyName = b.randomCustomPropertyName() - attempts += 1 - } while b.currentClassDefinition.staticProperties.contains(propertyName) + let propertyName = b.generateString(b.randomCustomPropertyName, + notIn: b.currentClassDefinition.staticProperties) var value: Variable? = probability(0.5) ? b.randomJsVariable() : nil b.currentClassDefinition.addStaticProperty(propertyName, value: value) @@ -1044,19 +953,8 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a method that hasn't already been added to this class. - var methodName: String - var attempts = 0 - repeat { - if attempts >= 10 { - methodName = String.random( - ofLength: Int.random(in: 1...5)) - break - } - methodName = b.randomCustomMethodName() - attempts += 1 - } while b.currentClassDefinition.staticMethods.contains( - methodName) - + let methodName = b.generateString(b.randomCustomMethodName, + notIn: b.currentClassDefinition.staticMethods) let parameters = b.randomParameters() b.setParameterTypesForNextSubroutine(parameters.parameterTypes) @@ -1088,21 +986,9 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added and for which a getter has not yet been installed. - var propertyName: String - var attempts = 0 - repeat { - if attempts >= 10 { - propertyName = String.random( - ofLength: Int.random(in: 1...5)) - break - } - propertyName = b.randomCustomPropertyName() - attempts += 1 - } while b.currentClassDefinition.staticProperties.contains( - propertyName) - || b.currentClassDefinition.staticGetters.contains( - propertyName) - + let propertyName = b.generateString(b.randomCustomPropertyName, + notIn: b.currentClassDefinition.staticProperties + + b.currentClassDefinition.staticGetters) b.emit(BeginClassStaticGetter(propertyName: propertyName)) }, GeneratorStub( @@ -1127,22 +1013,10 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added and for which a setter has not yet been installed. - var propertyName: String - var attempts = 0 - repeat { - if attempts >= 10 { - propertyName = String.random(ofLength: Int.random(in: 1...5)) - break - } - propertyName = b.randomCustomPropertyName() - attempts += 1 - } while b.currentClassDefinition.staticProperties.contains( - propertyName) - || b.currentClassDefinition.staticSetters.contains( - propertyName) - + let propertyName = b.generateString(b.randomCustomPropertyName, + notIn: b.currentClassDefinition.staticProperties + + b.currentClassDefinition.staticSetters) b.emit(BeginClassStaticSetter(propertyName: propertyName)) - }, GeneratorStub( "ClassStaticSetterEndGenerator", @@ -1160,13 +1034,8 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a private field that hasn't already been added to this literal. - var propertyName: String - var attempts = 0 - repeat { - guard attempts < 10 else { return } - propertyName = b.randomCustomPropertyName() - attempts += 1 - } while b.currentClassDefinition.privateFields.contains(propertyName) + let propertyName = b.generateString(b.randomCustomPropertyName, + notIn: b.currentClassDefinition.privateFields) var value = probability(0.5) ? b.randomJsVariable() : nil b.currentClassDefinition.addPrivateInstanceProperty( @@ -1186,18 +1055,8 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a private field that hasn't already been added to this class. - var methodName: String - var attempts = 0 - repeat { - if attempts >= 10 { - methodName = String.random(ofLength: Int.random(in: 1...5)) - break - } - methodName = b.randomCustomMethodName() - attempts += 1 - } while b.currentClassDefinition.privateFields.contains( - methodName) - + let methodName = b.generateString(b.randomCustomMethodName, + notIn: b.currentClassDefinition.privateFields) let parameters = b.randomParameters() b.emit( BeginClassPrivateInstanceMethod( @@ -1221,14 +1080,8 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a private field that hasn't already been added to this literal. - var propertyName: String - var attempts = 0 - repeat { - guard attempts < 10 else { return } - propertyName = b.randomCustomPropertyName() - attempts += 1 - } while b.currentClassDefinition.privateFields.contains(propertyName) - + let propertyName = b.generateString(b.randomCustomPropertyName, + notIn: b.currentClassDefinition.privateFields) var value = probability(0.5) ? b.randomJsVariable() : nil b.currentClassDefinition.addPrivateStaticProperty( propertyName, value: value) @@ -1247,19 +1100,8 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a private field that hasn't already been added to this class. - var methodName: String - var attempts = 0 - repeat { - if attempts >= 10 { - methodName = String.random( - ofLength: Int.random(in: 1...5)) - break - } - methodName = b.randomCustomMethodName() - attempts += 1 - } while b.currentClassDefinition.privateFields.contains( - methodName) - + let methodName = b.generateString(b.randomCustomMethodName, + notIn: b.currentClassDefinition.privateFields) let parameters = b.randomParameters() b.emit( BeginClassPrivateStaticMethod( From ac0aa7c0688cbdf1ebab27975414de46c1699918 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Wed, 1 Oct 2025 03:15:10 -0700 Subject: [PATCH 25/35] Revert "Simplify constrained string generation in code generators" This reverts commit 80ad045556fdd065752cd1f1733b0c627b7f6b4b. Reason for revert: Crashes when compiling with -c release on 6.0.3. Passes with 6.1.2, so this is likely a swift compiler bug. Reverting for now. Original change's description: > Simplify constrained string generation in code generators > > Change-Id: I0ed4abed4a3ef0c7e150971ec58f0aae7e5b0982 > Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8591236 > Auto-Submit: Matthias Liedtke > Reviewed-by: Carl Smith > Commit-Queue: Matthias Liedtke No-Presubmit: true No-Tree-Checks: true No-Try: true Change-Id: I9a247d5e37c858633ba02d3a540de42853f68a53 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8632856 Commit-Queue: Matthias Liedtke Auto-Submit: Matthias Liedtke Bot-Commit: Rubber Stamper --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 30 --- Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 238 +++++++++++++++--- 2 files changed, 198 insertions(+), 70 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 7d8b569c4..a2c137a05 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -483,36 +483,6 @@ public class ProgramBuilder { return probability(0.5) ? randomBuiltinMethodName() : randomCustomMethodName() } - private static func generateConstrained( - _ generator: () -> T, - notIn: any Collection, - fallback: () -> T) -> T { - var result: T - var attempts = 0 - repeat { - if attempts >= 10 { - return fallback() - } - result = generator() - attempts += 1 - } while notIn.contains(result) - return result - } - - // Generate a string not already contained in `notIn` using the provided `generator`. If it fails - // repeatedly, return a random string instead. - public func generateString(_ generator: () -> String, notIn: any Collection) -> String { - Self.generateConstrained(generator, notIn: notIn, - fallback: {String.random(ofLength: Int.random(in: 1...5))}) - } - - // Find a random variable to use as a string that isn't contained in `notIn`. If it fails - // repeatedly, create a random string literal instead. - public func findOrGenerateStringLikeVariable(notIn: any Collection) -> Variable { - return Self.generateConstrained(randomJsVariable, notIn: notIn, - fallback: {loadString(String.random(ofLength: Int.random(in: 1...5)))}) - } - // Settings and constants controlling the behavior of randomParameters() below. // This determines how many variables of a given type need to be visible before // that type is considered a candidate for a parameter type. For example, if this diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index c033a9273..df11024c9 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -476,8 +476,17 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added to this literal. - let propertyName = b.generateString(b.randomCustomPropertyName, - notIn: b.currentObjectLiteral.properties) + var propertyName: String + var attempts = 0 + repeat { + if attempts >= 10 { + propertyName = String.random(ofLength: Int.random(in: 1...5)) + break + } + propertyName = b.randomCustomPropertyName() + attempts += 1 + } while b.currentObjectLiteral.properties.contains(propertyName) + b.currentObjectLiteral.addProperty( propertyName, as: b.randomJsVariable()) }, @@ -519,6 +528,7 @@ public let CodeGenerators: [CodeGenerator] = [ propertyName = b.randomJsVariable() attempts += 1 } while b.currentObjectLiteral.computedProperties.contains(propertyName) + b.currentObjectLiteral.addComputedProperty(propertyName, as: value) }, @@ -557,8 +567,17 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a method that hasn't already been added to this literal. - let methodName = b.generateString(b.randomCustomMethodName, - notIn: b.currentObjectLiteral.methods) + var methodName: String + var attempts = 0 + repeat { + if attempts >= 10 { + methodName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + methodName = b.randomCustomMethodName() + attempts += 1 + } while b.currentObjectLiteral.methods.contains(methodName) let randomParameters = b.randomParameters() b.setParameterTypesForNextSubroutine( @@ -586,16 +605,15 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a computed method name that hasn't already been added to this literal. - var methodName: Variable var attempts = 0 repeat { - methodName = b.randomJsVariable() if attempts >= 10 { - // This might lead to having two computed methods with the same name (so one - // will overwrite the other). + methodName = b.loadString( + String.random(ofLength: Int.random(in: 1...5))) break } + methodName = b.randomJsVariable() attempts += 1 } while b.currentObjectLiteral.computedMethods.contains( methodName) @@ -630,8 +648,20 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added and for which a getter has not yet been installed. - let propertyName = b.generateString(b.randomCustomPropertyName, - notIn: b.currentObjectLiteral.properties + b.currentObjectLiteral.getters) + var propertyName: String + var attempts = 0 + repeat { + if attempts >= 10 { + // We should not fail here but also don't produce a syntax error. + propertyName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + propertyName = b.randomCustomPropertyName() + attempts += 1 + } while b.currentObjectLiteral.properties.contains(propertyName) + || b.currentObjectLiteral.getters.contains(propertyName) + b.emit(BeginObjectLiteralGetter(propertyName: propertyName)) }, @@ -658,8 +688,20 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added and for which a setter has not yet been installed. - let propertyName = b.generateString(b.randomCustomPropertyName, - notIn: b.currentObjectLiteral.properties + b.currentObjectLiteral.setters) + var propertyName: String + var attempts = 0 + repeat { + if attempts >= 10 { + // We should not fail here but also don't produce a syntax error. + propertyName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + propertyName = b.randomCustomPropertyName() + attempts += 1 + } while b.currentObjectLiteral.properties.contains(propertyName) + || b.currentObjectLiteral.setters.contains(propertyName) + b.emit(BeginObjectLiteralSetter(propertyName: propertyName)) }, GeneratorStub( @@ -730,8 +772,14 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added to this literal. - let propertyName = b.generateString(b.randomCustomPropertyName, - notIn: b.currentClassDefinition.instanceProperties) + var propertyName: String + var attempts = 0 + repeat { + guard attempts < 10 else { return } + propertyName = b.randomCustomPropertyName() + attempts += 1 + } while b.currentClassDefinition.instanceProperties.contains( + propertyName) var value: Variable? = probability(0.5) ? b.randomJsVariable() : nil b.currentClassDefinition.addInstanceProperty(propertyName, value: value) @@ -789,8 +837,18 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a method that hasn't already been added to this class. - let methodName = b.generateString(b.randomCustomMethodName, - notIn: b.currentClassDefinition.instanceMethods) + var methodName: String + var attempts = 0 + repeat { + if attempts >= 10 { + methodName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + methodName = b.randomCustomMethodName() + attempts += 1 + } while b.currentClassDefinition.instanceMethods.contains( + methodName) let parameters = b.randomParameters() b.setParameterTypesForNextSubroutine(parameters.parameterTypes) @@ -822,9 +880,21 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added and for which a getter has not yet been installed. - let propertyName = b.generateString(b.randomCustomPropertyName, - notIn: b.currentClassDefinition.instanceProperties - + b.currentClassDefinition.instanceGetters) + var propertyName: String + var attempts = 0 + repeat { + if attempts >= 10 { + propertyName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + propertyName = b.randomCustomPropertyName() + attempts += 1 + } while b.currentClassDefinition.instanceProperties.contains( + propertyName) + || b.currentClassDefinition.instanceGetters.contains( + propertyName) + b.emit(BeginClassInstanceGetter(propertyName: propertyName)) }, GeneratorStub( @@ -848,9 +918,21 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added and for which a setter has not yet been installed. - let propertyName = b.generateString(b.randomCustomPropertyName, - notIn: b.currentClassDefinition.instanceProperties - + b.currentClassDefinition.instanceSetters) + var propertyName: String + var attempts = 0 + repeat { + if attempts >= 10 { + propertyName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + propertyName = b.randomCustomPropertyName() + attempts += 1 + } while b.currentClassDefinition.instanceProperties.contains( + propertyName) + || b.currentClassDefinition.instanceSetters.contains( + propertyName) + b.emit(BeginClassInstanceSetter(propertyName: propertyName)) }, GeneratorStub( @@ -869,8 +951,17 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added to this literal. - let propertyName = b.generateString(b.randomCustomPropertyName, - notIn: b.currentClassDefinition.staticProperties) + var propertyName: String + var attempts = 0 + repeat { + if attempts >= 10 { + propertyName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + propertyName = b.randomCustomPropertyName() + attempts += 1 + } while b.currentClassDefinition.staticProperties.contains(propertyName) var value: Variable? = probability(0.5) ? b.randomJsVariable() : nil b.currentClassDefinition.addStaticProperty(propertyName, value: value) @@ -953,8 +1044,19 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a method that hasn't already been added to this class. - let methodName = b.generateString(b.randomCustomMethodName, - notIn: b.currentClassDefinition.staticMethods) + var methodName: String + var attempts = 0 + repeat { + if attempts >= 10 { + methodName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + methodName = b.randomCustomMethodName() + attempts += 1 + } while b.currentClassDefinition.staticMethods.contains( + methodName) + let parameters = b.randomParameters() b.setParameterTypesForNextSubroutine(parameters.parameterTypes) @@ -986,9 +1088,21 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added and for which a getter has not yet been installed. - let propertyName = b.generateString(b.randomCustomPropertyName, - notIn: b.currentClassDefinition.staticProperties - + b.currentClassDefinition.staticGetters) + var propertyName: String + var attempts = 0 + repeat { + if attempts >= 10 { + propertyName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + propertyName = b.randomCustomPropertyName() + attempts += 1 + } while b.currentClassDefinition.staticProperties.contains( + propertyName) + || b.currentClassDefinition.staticGetters.contains( + propertyName) + b.emit(BeginClassStaticGetter(propertyName: propertyName)) }, GeneratorStub( @@ -1013,10 +1127,22 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a property that hasn't already been added and for which a setter has not yet been installed. - let propertyName = b.generateString(b.randomCustomPropertyName, - notIn: b.currentClassDefinition.staticProperties - + b.currentClassDefinition.staticSetters) + var propertyName: String + var attempts = 0 + repeat { + if attempts >= 10 { + propertyName = String.random(ofLength: Int.random(in: 1...5)) + break + } + propertyName = b.randomCustomPropertyName() + attempts += 1 + } while b.currentClassDefinition.staticProperties.contains( + propertyName) + || b.currentClassDefinition.staticSetters.contains( + propertyName) + b.emit(BeginClassStaticSetter(propertyName: propertyName)) + }, GeneratorStub( "ClassStaticSetterEndGenerator", @@ -1034,8 +1160,13 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a private field that hasn't already been added to this literal. - let propertyName = b.generateString(b.randomCustomPropertyName, - notIn: b.currentClassDefinition.privateFields) + var propertyName: String + var attempts = 0 + repeat { + guard attempts < 10 else { return } + propertyName = b.randomCustomPropertyName() + attempts += 1 + } while b.currentClassDefinition.privateFields.contains(propertyName) var value = probability(0.5) ? b.randomJsVariable() : nil b.currentClassDefinition.addPrivateInstanceProperty( @@ -1055,8 +1186,18 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a private field that hasn't already been added to this class. - let methodName = b.generateString(b.randomCustomMethodName, - notIn: b.currentClassDefinition.privateFields) + var methodName: String + var attempts = 0 + repeat { + if attempts >= 10 { + methodName = String.random(ofLength: Int.random(in: 1...5)) + break + } + methodName = b.randomCustomMethodName() + attempts += 1 + } while b.currentClassDefinition.privateFields.contains( + methodName) + let parameters = b.randomParameters() b.emit( BeginClassPrivateInstanceMethod( @@ -1080,8 +1221,14 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a private field that hasn't already been added to this literal. - let propertyName = b.generateString(b.randomCustomPropertyName, - notIn: b.currentClassDefinition.privateFields) + var propertyName: String + var attempts = 0 + repeat { + guard attempts < 10 else { return } + propertyName = b.randomCustomPropertyName() + attempts += 1 + } while b.currentClassDefinition.privateFields.contains(propertyName) + var value = probability(0.5) ? b.randomJsVariable() : nil b.currentClassDefinition.addPrivateStaticProperty( propertyName, value: value) @@ -1100,8 +1247,19 @@ public let CodeGenerators: [CodeGenerator] = [ && !b.context.contains(.javascript)) // Try to find a private field that hasn't already been added to this class. - let methodName = b.generateString(b.randomCustomMethodName, - notIn: b.currentClassDefinition.privateFields) + var methodName: String + var attempts = 0 + repeat { + if attempts >= 10 { + methodName = String.random( + ofLength: Int.random(in: 1...5)) + break + } + methodName = b.randomCustomMethodName() + attempts += 1 + } while b.currentClassDefinition.privateFields.contains( + methodName) + let parameters = b.randomParameters() b.emit( BeginClassPrivateStaticMethod( From 76e3cd6c7fe4afe7b487f7f5533789d773c86e0e Mon Sep 17 00:00:00 2001 From: SUZUKI Sosuke Date: Thu, 2 Oct 2025 01:44:10 +0900 Subject: [PATCH 26/35] Update README for JSC (#529) --- Targets/JavaScriptCore/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Targets/JavaScriptCore/README.md b/Targets/JavaScriptCore/README.md index f54e3eedf..97c689aa3 100644 --- a/Targets/JavaScriptCore/README.md +++ b/Targets/JavaScriptCore/README.md @@ -6,4 +6,4 @@ To build JavaScriptCore (jsc) for fuzzing: 2. Apply Patches/\*. The patches should apply cleanly to the git revision specified in [./REVISION](./REVISION) (_Note_: If you clone WebKit from `git.webkit.org`, the commit hash will differ) 3. Run the fuzzbuild.sh script in the webkit root directory -4. FuzzBuild/Debug/bin/jsc will be the JavaScript shell for the fuzzer +4. WebKitBuild/Fuzzilli/bin/jsc will be the JavaScript shell for the fuzzer From 435935004a8353133a08ed984dc18790b80a6d6d Mon Sep 17 00:00:00 2001 From: Dominik Klemba Date: Wed, 1 Oct 2025 15:40:24 +0000 Subject: [PATCH 27/35] [heap]: Add PretenureAllocationSiteGenerator This introduces the PretenureAllocationSiteGenerator to stress V8's garbage collector. The new generator calls the %PretenureAllocationSite intrinsic which promotes the passed object into the old generation heap. Bug: 441469179 Change-Id: Ie764c7e9d2391f319b138c6f0f4f4f8eff529bd9 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8633736 Reviewed-by: Matthias Liedtke Commit-Queue: Dominik Klemba --- Sources/FuzzilliCli/Profiles/V8CommonProfile.swift | 4 ++++ Sources/FuzzilliCli/Profiles/V8Profile.swift | 1 + 2 files changed, 5 insertions(+) diff --git a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift index aee92edd2..fb06b05d0 100644 --- a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift @@ -135,6 +135,10 @@ public let WasmArrayGenerator = CodeGenerator("WasmArrayGenerator") { b in b.eval("%WasmArray()", hasOutput: true); } +public let PretenureAllocationSiteGenerator = CodeGenerator("PretenureAllocationSiteGenerator", inputs: .required(.object())) { b, obj in + b.eval("%PretenureAllocationSite(%@)", with: [obj]); +} + public let MapTransitionFuzzer = ProgramTemplate("MapTransitionFuzzer") { b in // This template is meant to stress the v8 Map transition mechanisms. // Basically, it generates a bunch of CreateObject, GetProperty, SetProperty, FunctionDefinition, diff --git a/Sources/FuzzilliCli/Profiles/V8Profile.swift b/Sources/FuzzilliCli/Profiles/V8Profile.swift index 67438f9d9..c107390e8 100644 --- a/Sources/FuzzilliCli/Profiles/V8Profile.swift +++ b/Sources/FuzzilliCli/Profiles/V8Profile.swift @@ -289,6 +289,7 @@ let v8Profile = Profile( (WasmStructGenerator, 15), (WasmArrayGenerator, 15), + (PretenureAllocationSiteGenerator, 5), ], additionalProgramTemplates: WeightedList([ From adfa084a75e58e71a1877c3ea2c430a3f114e7ef Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Tue, 30 Sep 2025 14:40:06 +0200 Subject: [PATCH 28/35] Support computed class methods This adds support for computed class method (static and instance) to fuzzIL and to the compiler for imports. Supporting computed getters/setters and indexed methods + indexed getters/setters is still future work. However, this adds a compiler test file with the full array of cases that we could support, not-yet-supported cases commented out for now. This slightly refactors the compiler AST and shares property keys for methods and properties, which can have the same structure. In general, most of the boiler-plate code is similar to the existing code for BeginClass{Instance|Static}Method with changes for computed properties similar to BeginClassComputedProperty or BeginObjectLiteralComputedMethod. Similar to computed object methods, a caveat is that the typer might not be able to compute available method names to be called. Bug: 446634535 BYPASS_LARGE_CHANGE_WARNING: tests for completeness are large Change-Id: I30a85eadbc0fe466a9fbc7dbfa3bb7aad5b51f7d Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8614819 Reviewed-by: Matthias Liedtke Commit-Queue: Michael Achenbach --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 20 + .../CodeGen/CodeGeneratorWeights.swift | 2 + Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 75 ++++ Sources/Fuzzilli/Compiler/Compiler.swift | 71 +++- Sources/Fuzzilli/Compiler/Parser/parser.js | 46 ++- Sources/Fuzzilli/FuzzIL/Instruction.swift | 20 + Sources/Fuzzilli/FuzzIL/JSTyper.swift | 14 + Sources/Fuzzilli/FuzzIL/JsOperations.swift | 26 ++ Sources/Fuzzilli/FuzzIL/Opcodes.swift | 4 + Sources/Fuzzilli/FuzzIL/Semantics.swift | 4 + Sources/Fuzzilli/Lifting/FuzzILLifter.swift | 18 + .../Fuzzilli/Lifting/JavaScriptLifter.swift | 18 + .../Fuzzilli/Minimization/BlockReducer.swift | 2 + .../Minimization/InliningReducer.swift | 4 + .../Fuzzilli/Mutators/OperationMutator.swift | 4 + Sources/Fuzzilli/Protobuf/ast.pb.swift | 153 +++++-- Sources/Fuzzilli/Protobuf/ast.proto | 14 +- Sources/Fuzzilli/Protobuf/operations.pb.swift | 168 ++++++++ Sources/Fuzzilli/Protobuf/operations.proto | 14 + Sources/Fuzzilli/Protobuf/program.pb.swift | 106 ++++- Sources/Fuzzilli/Protobuf/program.proto | 4 + .../computed_and_indexed_properties.js | 387 ++++++++++++++++++ Tests/FuzzilliTests/LifterTest.swift | 55 ++- Tests/FuzzilliTests/ProgramBuilderTest.swift | 12 +- 24 files changed, 1135 insertions(+), 106 deletions(-) create mode 100644 Tests/FuzzilliTests/CompilerTests/computed_and_indexed_properties.js diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index a2c137a05..46c8de95c 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -2513,6 +2513,7 @@ public class ProgramBuilder { public fileprivate(set) var instanceElements: [Int64] = [] public fileprivate(set) var instanceComputedProperties: [Variable] = [] public fileprivate(set) var instanceMethods: [String] = [] + public fileprivate(set) var instanceComputedMethods: [Variable] = [] public fileprivate(set) var instanceGetters: [String] = [] public fileprivate(set) var instanceSetters: [String] = [] @@ -2520,6 +2521,7 @@ public class ProgramBuilder { public fileprivate(set) var staticElements: [Int64] = [] public fileprivate(set) var staticComputedProperties: [Variable] = [] public fileprivate(set) var staticMethods: [String] = [] + public fileprivate(set) var staticComputedMethods: [Variable] = [] public fileprivate(set) var staticGetters: [String] = [] public fileprivate(set) var staticSetters: [String] = [] @@ -2571,6 +2573,13 @@ public class ProgramBuilder { b.emit(EndClassInstanceMethod()) } + public func addInstanceComputedMethod(_ name: Variable, with descriptor: SubroutineDescriptor, _ body: ([Variable]) -> ()) { + b.setParameterTypesForNextSubroutine(descriptor.parameterTypes) + let instr = b.emit(BeginClassInstanceComputedMethod(parameters: descriptor.parameters), withInputs: [name]) + body(Array(instr.innerOutputs)) + b.emit(EndClassInstanceComputedMethod()) + } + public func addInstanceGetter(for name: String, _ body: (_ this: Variable) -> ()) { let instr = b.emit(BeginClassInstanceGetter(propertyName: name)) body(instr.innerOutput) @@ -2611,6 +2620,13 @@ public class ProgramBuilder { b.emit(EndClassStaticMethod()) } + public func addStaticComputedMethod(_ name: Variable, with descriptor: SubroutineDescriptor, _ body: ([Variable]) -> ()) { + b.setParameterTypesForNextSubroutine(descriptor.parameterTypes) + let instr = b.emit(BeginClassStaticComputedMethod(parameters: descriptor.parameters), withInputs: [name]) + body(Array(instr.innerOutputs)) + b.emit(EndClassStaticComputedMethod()) + } + public func addStaticGetter(for name: String, _ body: (_ this: Variable) -> ()) { let instr = b.emit(BeginClassStaticGetter(propertyName: name)) body(instr.innerOutput) @@ -4668,6 +4684,8 @@ public class ProgramBuilder { activeClassDefinitions.top.instanceComputedProperties.append(instr.input(0)) case .beginClassInstanceMethod(let op): activeClassDefinitions.top.instanceMethods.append(op.methodName) + case .beginClassInstanceComputedMethod: + activeClassDefinitions.top.instanceComputedMethods.append(instr.input(0)) case .beginClassInstanceGetter(let op): activeClassDefinitions.top.instanceGetters.append(op.propertyName) case .beginClassInstanceSetter(let op): @@ -4680,6 +4698,8 @@ public class ProgramBuilder { activeClassDefinitions.top.staticComputedProperties.append(instr.input(0)) case .beginClassStaticMethod(let op): activeClassDefinitions.top.staticMethods.append(op.methodName) + case .beginClassStaticComputedMethod: + activeClassDefinitions.top.staticComputedMethods.append(instr.input(0)) case .beginClassStaticGetter(let op): activeClassDefinitions.top.staticGetters.append(op.propertyName) case .beginClassStaticSetter(let op): diff --git a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift index 247e1f017..79543bb19 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift @@ -72,6 +72,7 @@ public let codeGeneratorWeights = [ "ClassInstanceElementGenerator": 5, "ClassInstanceComputedPropertyGenerator": 5, "ClassInstanceMethodGenerator": 10, + "ClassInstanceComputedMethodGenerator": 5, "ClassInstanceGetterGenerator": 3, "ClassInstanceSetterGenerator": 3, "ClassStaticPropertyGenerator": 3, @@ -79,6 +80,7 @@ public let codeGeneratorWeights = [ "ClassStaticComputedPropertyGenerator": 3, "ClassStaticInitializerGenerator": 3, "ClassStaticMethodGenerator": 5, + "ClassStaticComputedMethodGenerator": 3, "ClassStaticGetterGenerator": 2, "ClassStaticSetterGenerator": 2, "ClassPrivateInstancePropertyGenerator": 5, diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index df11024c9..aaeb788c8 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -864,7 +864,44 @@ public let CodeGenerators: [CodeGenerator] = [ b.doReturn(b.randomJsVariable()) b.emit(EndClassInstanceMethod()) }, + ]), + + CodeGenerator( + "ClassInstanceComputedMethodGenerator", + [ + GeneratorStub( + "ClassInstanceComputedMethodBeginGenerator", + inContext: .single(.classDefinition), + provides: [.javascript, .subroutine, .method, .classMethod] + ) { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) + + // Try to find a method that hasn't already been added to this class. + var methodName = b.randomJsVariable() + var attempts = 0 + repeat { + guard attempts < 10 else { break } + methodName = b.randomJsVariable() + attempts += 1 + } while b.currentClassDefinition.instanceComputedMethods.contains( + methodName) + let parameters = b.randomParameters() + b.setParameterTypesForNextSubroutine(parameters.parameterTypes) + b.emit( + BeginClassInstanceComputedMethod( + parameters: parameters.parameters), + withInputs: [methodName]) + }, + GeneratorStub( + "ClassInstanceComputedMethodEndGenerator", + inContext: .single([.javascript, .subroutine, .method, .classMethod]) + ) { b in + b.doReturn(b.randomJsVariable()) + b.emit(EndClassInstanceComputedMethod()) + }, ]), CodeGenerator( @@ -1075,6 +1112,44 @@ public let CodeGenerators: [CodeGenerator] = [ }, ]), + CodeGenerator( + "ClassStaticComputedMethodGenerator", + [ + GeneratorStub( + "ClassStaticComputedMethodBeginGenerator", + inContext: .single(.classDefinition), + provides: [.javascript, .subroutine, .method, .classMethod] + ) { b in + assert( + b.context.contains(.classDefinition) + && !b.context.contains(.javascript)) + + // Try to find a method that hasn't already been added to this class. + var methodName = b.randomJsVariable() + var attempts = 0 + repeat { + guard attempts < 10 else { break } + methodName = b.randomJsVariable() + attempts += 1 + } while b.currentClassDefinition.staticComputedMethods.contains( + methodName) + + let parameters = b.randomParameters() + b.setParameterTypesForNextSubroutine(parameters.parameterTypes) + b.emit( + BeginClassStaticComputedMethod( + parameters: parameters.parameters), + withInputs: [methodName]) + }, + GeneratorStub( + "ClassStaticComputedMethodEndGenerator", + inContext: .single([.javascript, .subroutine, .method, .classMethod]) + ) { b in + b.doReturn(b.randomJsVariable()) + b.emit(EndClassStaticComputedMethod()) + }, + ]), + CodeGenerator( "ClassStaticGetterGenerator", [ diff --git a/Sources/Fuzzilli/Compiler/Compiler.swift b/Sources/Fuzzilli/Compiler/Compiler.swift index 8866f63f8..21264dc68 100644 --- a/Sources/Fuzzilli/Compiler/Compiler.swift +++ b/Sources/Fuzzilli/Compiler/Compiler.swift @@ -71,7 +71,7 @@ public class JavaScriptCompiler { private func compileClass(_ name : String, superClass: ExpressionNode?, fields : [ClassFieldNode], isExpression : Bool) throws -> Variable { // The expressions for property values and computed properties need to be emitted before the class declaration is opened. var propertyValues = [Variable]() - var computedPropertyKeys = [Variable]() + var computedKeys = [Variable]() for field in fields { guard let field = field.field else { throw CompilerError.invalidNodeError("missing concrete field in class declaration") @@ -80,15 +80,20 @@ public class JavaScriptCompiler { if property.hasValue { propertyValues.append(try compileExpression(property.value)) } - if case .expression(let key) = property.key { - computedPropertyKeys.append(try compileExpression(key)) + if case .expression(let expression) = property.key.body { + computedKeys.append(try compileExpression(expression)) + } + } + if case .method(let method) = field { + if case .expression(let expression) = method.key.body { + computedKeys.append(try compileExpression(expression)) } } } // Reverse the arrays since we'll remove the elements in FIFO order. propertyValues.reverse() - computedPropertyKeys.reverse() + computedKeys.reverse() let classDecl: Instruction if let superClass = superClass { @@ -104,7 +109,7 @@ public class JavaScriptCompiler { for field in fields { switch field.field! { case .property(let property): - guard let key = property.key else { + guard let key = property.key.body else { throw CompilerError.invalidNodeError("Missing key in class property") } @@ -124,7 +129,7 @@ public class JavaScriptCompiler { op = ClassAddInstanceElement(index: index, hasValue: property.hasValue) } case .expression: - inputs.append(computedPropertyKeys.removeLast()) + inputs.append(computedKeys.removeLast()) if property.isStatic { op = ClassAddStaticComputedProperty(hasValue: property.hasValue) } else { @@ -154,10 +159,25 @@ public class JavaScriptCompiler { case .method(let method): let parameters = convertParameters(method.parameters) let head: Instruction - if method.isStatic { - head = emit(BeginClassStaticMethod(methodName: method.name, parameters: parameters)) - } else { - head = emit(BeginClassInstanceMethod(methodName: method.name, parameters: parameters)) + + guard let key = method.key.body else { + throw CompilerError.invalidNodeError("Missing key in class property") + } + switch key { + case .name(let name): + if method.isStatic { + head = emit(BeginClassStaticMethod(methodName: name, parameters: parameters)) + } else { + head = emit(BeginClassInstanceMethod(methodName: name, parameters: parameters)) + } + case .index: + throw CompilerError.invalidNodeError("Not supported") + case .expression: + if method.isStatic { + head = emit(BeginClassStaticComputedMethod(parameters: parameters), withInputs: [computedKeys.removeLast()]) + } else { + head = emit(BeginClassInstanceComputedMethod(parameters: parameters), withInputs: [computedKeys.removeLast()]) + } } try enterNewScope { @@ -169,10 +189,21 @@ public class JavaScriptCompiler { } } - if method.isStatic { - emit(EndClassStaticMethod()) - } else { - emit(EndClassInstanceMethod()) + switch key { + case .name: + if method.isStatic { + emit(EndClassStaticMethod()) + } else { + emit(EndClassInstanceMethod()) + } + case .index: + throw CompilerError.invalidNodeError("Not supported") + case .expression: + if method.isStatic { + emit(EndClassStaticComputedMethod()) + } else { + emit(EndClassInstanceComputedMethod()) + } } case .getter(let getter): @@ -783,7 +814,7 @@ public class JavaScriptCompiler { case .objectExpression(let objectExpression): // The expressions for property values and computed properties need to be emitted before the object literal is opened. var propertyValues = [Variable]() - var computedPropertyKeys = [Variable]() + var computedKeys = [Variable]() for field in objectExpression.fields { guard let field = field.field else { throw CompilerError.invalidNodeError("missing concrete field in object expression") @@ -791,18 +822,18 @@ public class JavaScriptCompiler { if case .property(let property) = field { propertyValues.append(try compileExpression(property.value)) if case .expression(let expression) = property.key { - computedPropertyKeys.append(try compileExpression(expression)) + computedKeys.append(try compileExpression(expression)) } } else if case .method(let method) = field { if case .expression(let expression) = method.key { - computedPropertyKeys.append(try compileExpression(expression)) + computedKeys.append(try compileExpression(expression)) } } } // Reverse the arrays since we'll remove the elements in FIFO order. propertyValues.reverse() - computedPropertyKeys.reverse() + computedKeys.reverse() // Now build the object literal. emit(BeginObjectLiteral()) @@ -819,7 +850,7 @@ public class JavaScriptCompiler { case .index(let index): emit(ObjectLiteralAddElement(index: index), withInputs: inputs) case .expression: - emit(ObjectLiteralAddComputedProperty(), withInputs: [computedPropertyKeys.removeLast()] + inputs) + emit(ObjectLiteralAddComputedProperty(), withInputs: [computedKeys.removeLast()] + inputs) } case .method(let method): let parameters = convertParameters(method.parameters) @@ -828,7 +859,7 @@ public class JavaScriptCompiler { if case .name(let name) = method.key { instr = emit(BeginObjectLiteralMethod(methodName: name, parameters: parameters)) } else { - instr = emit(BeginObjectLiteralComputedMethod(parameters: parameters), withInputs: [computedPropertyKeys.removeLast()]) + instr = emit(BeginObjectLiteralComputedMethod(parameters: parameters), withInputs: [computedKeys.removeLast()]) } try enterNewScope { diff --git a/Sources/Fuzzilli/Compiler/Parser/parser.js b/Sources/Fuzzilli/Compiler/Parser/parser.js index fb9a30543..081449c34 100644 --- a/Sources/Fuzzilli/Compiler/Parser/parser.js +++ b/Sources/Fuzzilli/Compiler/Parser/parser.js @@ -132,6 +132,24 @@ function parse(script, proto) { return [type, { kind, declarations }]; } + function visitMemberKey(member) { + let body = {} + if (member.computed) { + body.expression = visitExpression(member.key); + } else { + if (member.key.type === 'Identifier') { + body.name = member.key.name; + } else if (member.key.type === 'NumericLiteral') { + body.index = member.key.value; + } else if (member.key.type === 'StringLiteral') { + body.name = member.key.value; + } else { + throw "Unknown member key type: " + member.key.type + " in class declaration"; + } + } + return make('PropertyKey', body); + } + function visitClass(node, isExpression) { let cls = {}; if (node.id) { @@ -150,34 +168,19 @@ function parse(script, proto) { if (field.value !== null) { property.value = visitExpression(field.value); } - if (field.computed) { - property.expression = visitExpression(field.key); - } else { - if (field.key.type === 'Identifier') { - property.name = field.key.name; - } else if (field.key.type === 'NumericLiteral') { - property.index = field.key.value; - } else if (field.key.type === 'StringLiteral') { - property.name = field.key.value; - } else { - throw "Unknown property key type: " + field.key.type + " in class declaration"; - } - } + property.key = visitMemberKey(field); cls.fields.push(make('ClassField', { property: make('ClassProperty', property) })); } else if (field.type === 'ClassMethod') { assert(!field.shorthand, 'Expected field.shorthand to be false'); - assert(!field.computed, 'Expected field.computed to be false'); assert(!field.generator, 'Expected field.generator to be false'); assert(!field.async, 'Expected field.async to be false'); - assert(field.key.type === 'Identifier', "Expected field.key.type to be exactly 'Identifier'"); let method = field; field = {}; - let name = method.key.name; let isStatic = method.static; if (method.kind === 'constructor') { assert(method.body.type === 'BlockStatement', "Expected method.body.type to be exactly 'BlockStatement'"); - assert(name === 'constructor', "Expected name to be exactly 'constructor'"); + assert(method.key.name === 'constructor', "Expected name to be exactly 'constructor'"); assert(!isStatic, "Expected isStatic to be false"); let parameters = visitParameters(method.params); @@ -188,21 +191,28 @@ function parse(script, proto) { let parameters = visitParameters(method.params); let body = visitBody(method.body); - field.method = make('ClassMethod', { name, isStatic, parameters, body }); + let key = visitMemberKey(method); + field.method = make('ClassMethod', { key, isStatic, parameters, body }); } else if (method.kind === 'get') { + assert(!method.computed, 'Expected method.computed to be false'); + assert(method.key.type === 'Identifier', "Expected method.key.type to be exactly 'Identifier'"); assert(method.params.length === 0, "Expected method.params.length to be exactly 0"); assert(!method.generator && !method.async, "Expected both conditions to hold: !method.generator and !method.async"); assert(method.body.type === 'BlockStatement', "Expected method.body.type to be exactly 'BlockStatement'"); let body = visitBody(method.body); + const name = method.key.name; field.getter = make('ClassGetter', { name, isStatic, body }); } else if (method.kind === 'set') { + assert(!method.computed, 'Expected method.computed to be false'); + assert(method.key.type === 'Identifier', "Expected method.key.type to be exactly 'Identifier'"); assert(method.params.length === 1, "Expected method.params.length to be exactly 1"); assert(!method.generator && !method.async, "Expected both conditions to hold: !method.generator and !method.async"); assert(method.body.type === 'BlockStatement', "Expected method.body.type to be exactly 'BlockStatement'"); let parameter = visitParameter(method.params[0]); let body = visitBody(method.body); + const name = method.key.name; field.setter = make('ClassSetter', { name, isStatic, parameter, body }); } else { throw "Unknown method kind: " + method.kind; diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index 1a003c7f0..ee017f20a 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -632,6 +632,12 @@ extension Instruction: ProtobufConvertible { } case .endClassInstanceMethod: $0.endClassInstanceMethod = Fuzzilli_Protobuf_EndClassInstanceMethod() + case .beginClassInstanceComputedMethod(let op): + $0.beginClassInstanceComputedMethod = Fuzzilli_Protobuf_BeginClassInstanceComputedMethod.with { + $0.parameters = convertParameters(op.parameters) + } + case .endClassInstanceComputedMethod: + $0.endClassInstanceComputedMethod = Fuzzilli_Protobuf_EndClassInstanceComputedMethod() case .beginClassInstanceGetter(let op): $0.beginClassInstanceGetter = Fuzzilli_Protobuf_BeginClassInstanceGetter.with { $0.propertyName = op.propertyName } case .endClassInstanceGetter: @@ -663,6 +669,12 @@ extension Instruction: ProtobufConvertible { } case .endClassStaticMethod: $0.endClassStaticMethod = Fuzzilli_Protobuf_EndClassStaticMethod() + case .beginClassStaticComputedMethod(let op): + $0.beginClassStaticComputedMethod = Fuzzilli_Protobuf_BeginClassStaticComputedMethod.with { + $0.parameters = convertParameters(op.parameters) + } + case .endClassStaticComputedMethod: + $0.endClassStaticComputedMethod = Fuzzilli_Protobuf_EndClassStaticComputedMethod() case .beginClassStaticGetter(let op): $0.beginClassStaticGetter = Fuzzilli_Protobuf_BeginClassStaticGetter.with { $0.propertyName = op.propertyName } case .endClassStaticGetter: @@ -1897,6 +1909,10 @@ extension Instruction: ProtobufConvertible { op = BeginClassInstanceMethod(methodName: p.methodName, parameters: convertParameters(p.parameters)) case .endClassInstanceMethod: op = EndClassInstanceMethod() + case .beginClassInstanceComputedMethod(let p): + op = BeginClassInstanceComputedMethod(parameters: convertParameters(p.parameters)) + case .endClassInstanceComputedMethod: + op = EndClassInstanceComputedMethod() case .beginClassInstanceGetter(let p): op = BeginClassInstanceGetter(propertyName: p.propertyName) case .endClassInstanceGetter: @@ -1919,6 +1935,10 @@ extension Instruction: ProtobufConvertible { op = BeginClassStaticMethod(methodName: p.methodName, parameters: convertParameters(p.parameters)) case .endClassStaticMethod: op = EndClassStaticMethod() + case .beginClassStaticComputedMethod(let p): + op = BeginClassStaticComputedMethod(parameters: convertParameters(p.parameters)) + case .endClassStaticComputedMethod: + op = EndClassStaticComputedMethod() case .beginClassStaticGetter(let p): op = BeginClassStaticGetter(propertyName: p.propertyName) case .endClassStaticGetter: diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index 879e5255f..6157ea39d 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -1218,9 +1218,11 @@ public struct JSTyper: Analyzer { .beginConstructor, .beginClassConstructor, .beginClassInstanceMethod, + .beginClassInstanceComputedMethod, .beginClassInstanceGetter, .beginClassInstanceSetter, .beginClassStaticMethod, + .beginClassStaticComputedMethod, .beginClassStaticGetter, .beginClassStaticSetter, .beginClassPrivateInstanceMethod, @@ -1240,9 +1242,11 @@ public struct JSTyper: Analyzer { .endConstructor, .endClassConstructor, .endClassInstanceMethod, + .endClassInstanceComputedMethod, .endClassInstanceGetter, .endClassInstanceSetter, .endClassStaticMethod, + .endClassStaticComputedMethod, .endClassStaticGetter, .endClassStaticSetter, .endClassPrivateInstanceMethod, @@ -1528,6 +1532,11 @@ public struct JSTyper: Analyzer { processParameterDeclarations(instr.innerOutputs(1...), parameters: inferSubroutineParameterList(of: op, at: instr.index)) dynamicObjectGroupManager.addMethod(methodName: op.methodName, of: .jsClass) + case .beginClassInstanceComputedMethod(let op): + // The first inner output is the explicit |this| + set(instr.innerOutput(0), dynamicObjectGroupManager.top.instanceType) + processParameterDeclarations(instr.innerOutputs(1...), parameters: inferSubroutineParameterList(of: op, at: instr.index)) + case .beginClassInstanceGetter(let op): // The first inner output is the explicit |this| parameter for the constructor set(instr.innerOutput(0), dynamicObjectGroupManager.top.instanceType) @@ -1555,6 +1564,11 @@ public struct JSTyper: Analyzer { processParameterDeclarations(instr.innerOutputs(1...), parameters: inferSubroutineParameterList(of: op, at: instr.index)) dynamicObjectGroupManager.addClassStaticMethod(methodName: op.methodName) + case .beginClassStaticComputedMethod(let op): + // The first inner output is the explicit |this| + set(instr.innerOutput(0), dynamicObjectGroupManager.activeClasses.top.objectGroup.instanceType) + processParameterDeclarations(instr.innerOutputs(1...), parameters: inferSubroutineParameterList(of: op, at: instr.index)) + case .beginClassStaticGetter(let op): // The first inner output is the explicit |this| parameter for the constructor set(instr.innerOutput(0), dynamicObjectGroupManager.activeClasses.top.objectGroup.instanceType) diff --git a/Sources/Fuzzilli/FuzzIL/JsOperations.swift b/Sources/Fuzzilli/FuzzIL/JsOperations.swift index 929a6852d..93c834d93 100644 --- a/Sources/Fuzzilli/FuzzIL/JsOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/JsOperations.swift @@ -752,6 +752,19 @@ final class EndClassInstanceMethod: EndAnySubroutine { override var opcode: Opcode { .endClassInstanceMethod(self) } } +final class BeginClassInstanceComputedMethod: BeginAnySubroutine { + override var opcode: Opcode { .beginClassInstanceComputedMethod(self) } + + init(parameters: Parameters) { + // First inner output is the explicit |this| parameter + super.init(parameters: parameters, numInputs: 1, numInnerOutputs: parameters.count + 1, attributes: [.isMutable, .isBlockStart], requiredContext: .classDefinition, contextOpened: [.javascript, .subroutine, .method, .classMethod]) + } +} + +final class EndClassInstanceComputedMethod: EndAnySubroutine { + override var opcode: Opcode { .endClassInstanceComputedMethod(self) } +} + final class BeginClassInstanceGetter: BeginAnySubroutine { override var opcode: Opcode { .beginClassInstanceGetter(self) } @@ -858,6 +871,19 @@ final class EndClassStaticMethod: EndAnySubroutine { override var opcode: Opcode { .endClassStaticMethod(self) } } +final class BeginClassStaticComputedMethod: BeginAnySubroutine { + override var opcode: Opcode { .beginClassStaticComputedMethod(self) } + + init(parameters: Parameters) { + // First inner output is the explicit |this| parameter + super.init(parameters: parameters, numInputs: 1, numInnerOutputs: parameters.count + 1, attributes: [.isMutable, .isBlockStart], requiredContext: .classDefinition, contextOpened: [.javascript, .subroutine, .method, .classMethod]) + } +} + +final class EndClassStaticComputedMethod: EndAnySubroutine { + override var opcode: Opcode { .endClassStaticComputedMethod(self) } +} + final class BeginClassStaticGetter: BeginAnySubroutine { override var opcode: Opcode { .beginClassStaticGetter(self) } diff --git a/Sources/Fuzzilli/FuzzIL/Opcodes.swift b/Sources/Fuzzilli/FuzzIL/Opcodes.swift index e3f1ff3f5..f9fda2f00 100644 --- a/Sources/Fuzzilli/FuzzIL/Opcodes.swift +++ b/Sources/Fuzzilli/FuzzIL/Opcodes.swift @@ -67,6 +67,8 @@ enum Opcode { case classAddInstanceComputedProperty(ClassAddInstanceComputedProperty) case beginClassInstanceMethod(BeginClassInstanceMethod) case endClassInstanceMethod(EndClassInstanceMethod) + case beginClassInstanceComputedMethod(BeginClassInstanceComputedMethod) + case endClassInstanceComputedMethod(EndClassInstanceComputedMethod) case beginClassInstanceGetter(BeginClassInstanceGetter) case endClassInstanceGetter(EndClassInstanceGetter) case beginClassInstanceSetter(BeginClassInstanceSetter) @@ -78,6 +80,8 @@ enum Opcode { case endClassStaticInitializer(EndClassStaticInitializer) case beginClassStaticMethod(BeginClassStaticMethod) case endClassStaticMethod(EndClassStaticMethod) + case beginClassStaticComputedMethod(BeginClassStaticComputedMethod) + case endClassStaticComputedMethod(EndClassStaticComputedMethod) case beginClassStaticGetter(BeginClassStaticGetter) case endClassStaticGetter(EndClassStaticGetter) case beginClassStaticSetter(BeginClassStaticSetter) diff --git a/Sources/Fuzzilli/FuzzIL/Semantics.swift b/Sources/Fuzzilli/FuzzIL/Semantics.swift index ba9b93ca5..968003082 100644 --- a/Sources/Fuzzilli/FuzzIL/Semantics.swift +++ b/Sources/Fuzzilli/FuzzIL/Semantics.swift @@ -146,6 +146,8 @@ extension Operation { return endOp is EndClassConstructor case .beginClassInstanceMethod: return endOp is EndClassInstanceMethod + case .beginClassInstanceComputedMethod: + return endOp is EndClassInstanceComputedMethod case .beginClassInstanceGetter: return endOp is EndClassInstanceGetter case .beginClassInstanceSetter: @@ -154,6 +156,8 @@ extension Operation { return endOp is EndClassStaticInitializer case .beginClassStaticMethod: return endOp is EndClassStaticMethod + case .beginClassStaticComputedMethod: + return endOp is EndClassStaticComputedMethod case .beginClassStaticGetter: return endOp is EndClassStaticGetter case .beginClassStaticSetter: diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift index b78df09c6..9a4e77785 100644 --- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift +++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift @@ -197,6 +197,15 @@ public class FuzzILLifter: Lifter { w.decreaseIndentionLevel() w.emit("EndClassInstanceMethod") + case .beginClassInstanceComputedMethod: + let params = instr.innerOutputs.map(lift).joined(separator: ", ") + w.emit("BeginClassInstanceComputedMethod \(input(0)) -> \(params)") + w.increaseIndentionLevel() + + case .endClassInstanceComputedMethod: + w.decreaseIndentionLevel() + w.emit("EndClassInstanceComputedMethod") + case .beginClassInstanceGetter(let op): let params = instr.innerOutputs.map(lift).joined(separator: ", ") w.emit("BeginClassInstanceGetter `\(op.propertyName)` -> \(params)") @@ -253,6 +262,15 @@ public class FuzzILLifter: Lifter { w.decreaseIndentionLevel() w.emit("EndClassStaticMethod") + case .beginClassStaticComputedMethod: + let params = instr.innerOutputs.map(lift).joined(separator: ", ") + w.emit("BeginClassStaticComputedMethod \(input(0)) -> \(params)") + w.increaseIndentionLevel() + + case .endClassStaticComputedMethod: + w.decreaseIndentionLevel() + w.emit("EndClassStaticComputedMethod") + case .beginClassStaticGetter(let op): let params = instr.innerOutputs.map(lift).joined(separator: ", ") w.emit("BeginClassStaticGetter `\(op.propertyName)` -> \(params)") diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift index f4041ecff..1cf8b75b9 100644 --- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift +++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift @@ -500,6 +500,14 @@ public class JavaScriptLifter: Lifter { w.enterNewBlock() bindVariableToThis(instr.innerOutput(0)) + case .beginClassInstanceComputedMethod(let op): + let vars = w.declareAll(instr.innerOutputs.dropFirst(), usePrefix: "a") + let PARAMS = liftParameters(op.parameters, as: vars) + let METHOD = input(0) + w.emit("[\(METHOD)](\(PARAMS)) {") + w.enterNewBlock() + bindVariableToThis(instr.innerOutput(0)) + case .beginClassInstanceGetter(let op): let PROPERTY = op.propertyName w.emit("get \(PROPERTY)() {") @@ -516,6 +524,7 @@ public class JavaScriptLifter: Lifter { bindVariableToThis(instr.innerOutput(0)) case .endClassInstanceMethod, + .endClassInstanceComputedMethod, .endClassInstanceGetter, .endClassInstanceSetter: w.leaveCurrentBlock() @@ -561,6 +570,14 @@ public class JavaScriptLifter: Lifter { w.enterNewBlock() bindVariableToThis(instr.innerOutput(0)) + case .beginClassStaticComputedMethod(let op): + let vars = w.declareAll(instr.innerOutputs.dropFirst(), usePrefix: "a") + let PARAMS = liftParameters(op.parameters, as: vars) + let METHOD = input(0) + w.emit("static [\(METHOD)](\(PARAMS)) {") + w.enterNewBlock() + bindVariableToThis(instr.innerOutput(0)) + case .beginClassStaticGetter(let op): assert(instr.numInnerOutputs == 1) let PROPERTY = op.propertyName @@ -579,6 +596,7 @@ public class JavaScriptLifter: Lifter { case .endClassStaticInitializer, .endClassStaticMethod, + .endClassStaticComputedMethod, .endClassStaticGetter, .endClassStaticSetter: w.leaveCurrentBlock() diff --git a/Sources/Fuzzilli/Minimization/BlockReducer.swift b/Sources/Fuzzilli/Minimization/BlockReducer.swift index d86f0c453..5a1efac08 100644 --- a/Sources/Fuzzilli/Minimization/BlockReducer.swift +++ b/Sources/Fuzzilli/Minimization/BlockReducer.swift @@ -38,10 +38,12 @@ struct BlockReducer: Reducer { case .beginClassConstructor, .beginClassInstanceMethod, + .beginClassInstanceComputedMethod, .beginClassInstanceGetter, .beginClassInstanceSetter, .beginClassStaticInitializer, .beginClassStaticMethod, + .beginClassStaticComputedMethod, .beginClassStaticGetter, .beginClassStaticSetter, .beginClassPrivateInstanceMethod, diff --git a/Sources/Fuzzilli/Minimization/InliningReducer.swift b/Sources/Fuzzilli/Minimization/InliningReducer.swift index 1876b8634..be6bed5d6 100644 --- a/Sources/Fuzzilli/Minimization/InliningReducer.swift +++ b/Sources/Fuzzilli/Minimization/InliningReducer.swift @@ -62,10 +62,12 @@ struct InliningReducer: Reducer { .beginObjectLiteralSetter, .beginClassConstructor, .beginClassInstanceMethod, + .beginClassInstanceComputedMethod, .beginClassInstanceGetter, .beginClassInstanceSetter, .beginClassStaticInitializer, .beginClassStaticMethod, + .beginClassStaticComputedMethod, .beginClassStaticGetter, .beginClassStaticSetter, .beginClassPrivateInstanceMethod, @@ -84,10 +86,12 @@ struct InliningReducer: Reducer { .endObjectLiteralSetter, .endClassConstructor, .endClassInstanceMethod, + .endClassInstanceComputedMethod, .endClassInstanceGetter, .endClassInstanceSetter, .endClassStaticInitializer, .endClassStaticMethod, + .endClassStaticComputedMethod, .endClassStaticGetter, .endClassStaticSetter, .endClassPrivateInstanceMethod, diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift index 04fbecdec..4e24a03e2 100644 --- a/Sources/Fuzzilli/Mutators/OperationMutator.swift +++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift @@ -530,6 +530,10 @@ public class OperationMutator: BaseInstructionMutator { .beginClassConstructor(_), .endClassConstructor(_), .classAddInstanceComputedProperty(_), + .beginClassInstanceComputedMethod(_), + .endClassInstanceComputedMethod(_), + .beginClassStaticComputedMethod(_), + .endClassStaticComputedMethod(_), .endClassInstanceMethod(_), .endClassInstanceGetter(_), .endClassInstanceSetter(_), diff --git a/Sources/Fuzzilli/Protobuf/ast.pb.swift b/Sources/Fuzzilli/Protobuf/ast.pb.swift index 92144a983..61f091cb1 100644 --- a/Sources/Fuzzilli/Protobuf/ast.pb.swift +++ b/Sources/Fuzzilli/Protobuf/ast.pb.swift @@ -268,39 +268,68 @@ public struct Compiler_Protobuf_FunctionDeclaration: Sendable { public init() {} } -public struct Compiler_Protobuf_ClassProperty: Sendable { +public struct Compiler_Protobuf_PropertyKey: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var key: Compiler_Protobuf_ClassProperty.OneOf_Key? = nil + public var body: Compiler_Protobuf_PropertyKey.OneOf_Body? = nil /// A "regular" property. public var name: String { get { - if case .name(let v)? = key {return v} + if case .name(let v)? = body {return v} return String() } - set {key = .name(newValue)} + set {body = .name(newValue)} } /// An element. public var index: Int64 { get { - if case .index(let v)? = key {return v} + if case .index(let v)? = body {return v} return 0 } - set {key = .index(newValue)} + set {body = .index(newValue)} } /// A computed property. public var expression: Compiler_Protobuf_Expression { get { - if case .expression(let v)? = key {return v} + if case .expression(let v)? = body {return v} return Compiler_Protobuf_Expression() } - set {key = .expression(newValue)} + set {body = .expression(newValue)} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public enum OneOf_Body: Equatable, Sendable { + /// A "regular" property. + case name(String) + /// An element. + case index(Int64) + /// A computed property. + case expression(Compiler_Protobuf_Expression) + + } + + public init() {} +} + +public struct Compiler_Protobuf_ClassProperty: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var key: Compiler_Protobuf_PropertyKey { + get {return _key ?? Compiler_Protobuf_PropertyKey()} + set {_key = newValue} } + /// Returns true if `key` has been explicitly set. + public var hasKey: Bool {return self._key != nil} + /// Clears the value of `key`. Subsequent reads from it will return its default value. + public mutating func clearKey() {self._key = nil} public var isStatic: Bool = false @@ -316,18 +345,9 @@ public struct Compiler_Protobuf_ClassProperty: Sendable { public var unknownFields = SwiftProtobuf.UnknownStorage() - public enum OneOf_Key: Equatable, Sendable { - /// A "regular" property. - case name(String) - /// An element. - case index(Int64) - /// A computed property. - case expression(Compiler_Protobuf_Expression) - - } - public init() {} + fileprivate var _key: Compiler_Protobuf_PropertyKey? = nil fileprivate var _value: Compiler_Protobuf_Expression? = nil } @@ -350,7 +370,14 @@ public struct Compiler_Protobuf_ClassMethod: Sendable { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var name: String = String() + public var key: Compiler_Protobuf_PropertyKey { + get {return _key ?? Compiler_Protobuf_PropertyKey()} + set {_key = newValue} + } + /// Returns true if `key` has been explicitly set. + public var hasKey: Bool {return self._key != nil} + /// Clears the value of `key`. Subsequent reads from it will return its default value. + public mutating func clearKey() {self._key = nil} public var isStatic: Bool = false @@ -361,6 +388,8 @@ public struct Compiler_Protobuf_ClassMethod: Sendable { public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} + + fileprivate var _key: Compiler_Protobuf_PropertyKey? = nil } public struct Compiler_Protobuf_ClassGetter: Sendable { @@ -2725,9 +2754,9 @@ extension Compiler_Protobuf_FunctionDeclaration: SwiftProtobuf.Message, SwiftPro } } -extension Compiler_Protobuf_ClassProperty: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".ClassProperty" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}name\0\u{1}index\0\u{1}expression\0\u{1}isStatic\0\u{1}value\0") +extension Compiler_Protobuf_PropertyKey: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".PropertyKey" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}name\0\u{1}index\0\u{1}expression\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2739,33 +2768,31 @@ extension Compiler_Protobuf_ClassProperty: SwiftProtobuf.Message, SwiftProtobuf. var v: String? try decoder.decodeSingularStringField(value: &v) if let v = v { - if self.key != nil {try decoder.handleConflictingOneOf()} - self.key = .name(v) + if self.body != nil {try decoder.handleConflictingOneOf()} + self.body = .name(v) } }() case 2: try { var v: Int64? try decoder.decodeSingularInt64Field(value: &v) if let v = v { - if self.key != nil {try decoder.handleConflictingOneOf()} - self.key = .index(v) + if self.body != nil {try decoder.handleConflictingOneOf()} + self.body = .index(v) } }() case 3: try { var v: Compiler_Protobuf_Expression? var hadOneofValue = false - if let current = self.key { + if let current = self.body { hadOneofValue = true if case .expression(let m) = current {v = m} } try decoder.decodeSingularMessageField(value: &v) if let v = v { if hadOneofValue {try decoder.handleConflictingOneOf()} - self.key = .expression(v) + self.body = .expression(v) } }() - case 4: try { try decoder.decodeSingularBoolField(value: &self.isStatic) }() - case 5: try { try decoder.decodeSingularMessageField(value: &self._value) }() default: break } } @@ -2776,32 +2803,68 @@ extension Compiler_Protobuf_ClassProperty: SwiftProtobuf.Message, SwiftProtobuf. // allocates stack space for every if/case branch local when no optimizations // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and // https://github.com/apple/swift-protobuf/issues/1182 - switch self.key { + switch self.body { case .name?: try { - guard case .name(let v)? = self.key else { preconditionFailure() } + guard case .name(let v)? = self.body else { preconditionFailure() } try visitor.visitSingularStringField(value: v, fieldNumber: 1) }() case .index?: try { - guard case .index(let v)? = self.key else { preconditionFailure() } + guard case .index(let v)? = self.body else { preconditionFailure() } try visitor.visitSingularInt64Field(value: v, fieldNumber: 2) }() case .expression?: try { - guard case .expression(let v)? = self.key else { preconditionFailure() } + guard case .expression(let v)? = self.body else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 3) }() case nil: break } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Compiler_Protobuf_PropertyKey, rhs: Compiler_Protobuf_PropertyKey) -> Bool { + if lhs.body != rhs.body {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Compiler_Protobuf_ClassProperty: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".ClassProperty" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}key\0\u{1}isStatic\0\u{1}value\0") + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularMessageField(value: &self._key) }() + case 2: try { try decoder.decodeSingularBoolField(value: &self.isStatic) }() + case 3: try { try decoder.decodeSingularMessageField(value: &self._value) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._key { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() if self.isStatic != false { - try visitor.visitSingularBoolField(value: self.isStatic, fieldNumber: 4) + try visitor.visitSingularBoolField(value: self.isStatic, fieldNumber: 2) } try { if let v = self._value { - try visitor.visitSingularMessageField(value: v, fieldNumber: 5) + try visitor.visitSingularMessageField(value: v, fieldNumber: 3) } }() try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Compiler_Protobuf_ClassProperty, rhs: Compiler_Protobuf_ClassProperty) -> Bool { - if lhs.key != rhs.key {return false} + if lhs._key != rhs._key {return false} if lhs.isStatic != rhs.isStatic {return false} if lhs._value != rhs._value {return false} if lhs.unknownFields != rhs.unknownFields {return false} @@ -2846,7 +2909,7 @@ extension Compiler_Protobuf_ClassConstructor: SwiftProtobuf.Message, SwiftProtob extension Compiler_Protobuf_ClassMethod: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".ClassMethod" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}name\0\u{1}isStatic\0\u{1}parameters\0\u{1}body\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}key\0\u{1}isStatic\0\u{1}parameters\0\u{1}body\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2854,7 +2917,7 @@ extension Compiler_Protobuf_ClassMethod: SwiftProtobuf.Message, SwiftProtobuf._M // allocates stack space for every case branch when no optimizations are // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch fieldNumber { - case 1: try { try decoder.decodeSingularStringField(value: &self.name) }() + case 1: try { try decoder.decodeSingularMessageField(value: &self._key) }() case 2: try { try decoder.decodeSingularBoolField(value: &self.isStatic) }() case 3: try { try decoder.decodeRepeatedMessageField(value: &self.parameters) }() case 4: try { try decoder.decodeRepeatedMessageField(value: &self.body) }() @@ -2864,9 +2927,13 @@ extension Compiler_Protobuf_ClassMethod: SwiftProtobuf.Message, SwiftProtobuf._M } public func traverse(visitor: inout V) throws { - if !self.name.isEmpty { - try visitor.visitSingularStringField(value: self.name, fieldNumber: 1) - } + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._key { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() if self.isStatic != false { try visitor.visitSingularBoolField(value: self.isStatic, fieldNumber: 2) } @@ -2880,7 +2947,7 @@ extension Compiler_Protobuf_ClassMethod: SwiftProtobuf.Message, SwiftProtobuf._M } public static func ==(lhs: Compiler_Protobuf_ClassMethod, rhs: Compiler_Protobuf_ClassMethod) -> Bool { - if lhs.name != rhs.name {return false} + if lhs._key != rhs._key {return false} if lhs.isStatic != rhs.isStatic {return false} if lhs.parameters != rhs.parameters {return false} if lhs.body != rhs.body {return false} diff --git a/Sources/Fuzzilli/Protobuf/ast.proto b/Sources/Fuzzilli/Protobuf/ast.proto index 3d49b7b8d..56fd5fbdc 100644 --- a/Sources/Fuzzilli/Protobuf/ast.proto +++ b/Sources/Fuzzilli/Protobuf/ast.proto @@ -72,8 +72,8 @@ message FunctionDeclaration { repeated Statement body = 4; } -message ClassProperty { - oneof key { +message PropertyKey { + oneof body { // A "regular" property. string name = 1; // An element. @@ -81,9 +81,13 @@ message ClassProperty { // A computed property. Expression expression = 3; } - bool isStatic = 4; +} + +message ClassProperty { + PropertyKey key = 1; + bool isStatic = 2; // The value is optional - Expression value = 5; + Expression value = 3; } message ClassConstructor { @@ -92,7 +96,7 @@ message ClassConstructor { } message ClassMethod { - string name = 1; + PropertyKey key = 1; bool isStatic = 2; repeated Parameter parameters = 3; repeated Statement body = 4; diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index bd079d427..7a1549674 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -2056,6 +2056,37 @@ public struct Fuzzilli_Protobuf_EndClassInstanceMethod: Sendable { public init() {} } +public struct Fuzzilli_Protobuf_BeginClassInstanceComputedMethod: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var parameters: Fuzzilli_Protobuf_Parameters { + get {return _parameters ?? Fuzzilli_Protobuf_Parameters()} + set {_parameters = newValue} + } + /// Returns true if `parameters` has been explicitly set. + public var hasParameters: Bool {return self._parameters != nil} + /// Clears the value of `parameters`. Subsequent reads from it will return its default value. + public mutating func clearParameters() {self._parameters = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _parameters: Fuzzilli_Protobuf_Parameters? = nil +} + +public struct Fuzzilli_Protobuf_EndClassInstanceComputedMethod: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + public struct Fuzzilli_Protobuf_BeginClassInstanceGetter: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -2193,6 +2224,37 @@ public struct Fuzzilli_Protobuf_EndClassStaticMethod: Sendable { public init() {} } +public struct Fuzzilli_Protobuf_BeginClassStaticComputedMethod: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var parameters: Fuzzilli_Protobuf_Parameters { + get {return _parameters ?? Fuzzilli_Protobuf_Parameters()} + set {_parameters = newValue} + } + /// Returns true if `parameters` has been explicitly set. + public var hasParameters: Bool {return self._parameters != nil} + /// Clears the value of `parameters`. Subsequent reads from it will return its default value. + public mutating func clearParameters() {self._parameters = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _parameters: Fuzzilli_Protobuf_Parameters? = nil +} + +public struct Fuzzilli_Protobuf_EndClassStaticComputedMethod: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + public struct Fuzzilli_Protobuf_BeginClassStaticGetter: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -7083,6 +7145,59 @@ extension Fuzzilli_Protobuf_EndClassInstanceMethod: SwiftProtobuf.Message, Swift } } +extension Fuzzilli_Protobuf_BeginClassInstanceComputedMethod: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".BeginClassInstanceComputedMethod" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameters\0") + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularMessageField(value: &self._parameters) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._parameters { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Fuzzilli_Protobuf_BeginClassInstanceComputedMethod, rhs: Fuzzilli_Protobuf_BeginClassInstanceComputedMethod) -> Bool { + if lhs._parameters != rhs._parameters {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Fuzzilli_Protobuf_EndClassInstanceComputedMethod: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".EndClassInstanceComputedMethod" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap() + + public mutating func decodeMessage(decoder: inout D) throws { + // Load everything into unknown fields + while try decoder.nextFieldNumber() != nil {} + } + + public func traverse(visitor: inout V) throws { + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Fuzzilli_Protobuf_EndClassInstanceComputedMethod, rhs: Fuzzilli_Protobuf_EndClassInstanceComputedMethod) -> Bool { + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension Fuzzilli_Protobuf_BeginClassInstanceGetter: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".BeginClassInstanceGetter" public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}propertyName\0") @@ -7377,6 +7492,59 @@ extension Fuzzilli_Protobuf_EndClassStaticMethod: SwiftProtobuf.Message, SwiftPr } } +extension Fuzzilli_Protobuf_BeginClassStaticComputedMethod: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".BeginClassStaticComputedMethod" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameters\0") + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularMessageField(value: &self._parameters) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._parameters { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Fuzzilli_Protobuf_BeginClassStaticComputedMethod, rhs: Fuzzilli_Protobuf_BeginClassStaticComputedMethod) -> Bool { + if lhs._parameters != rhs._parameters {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Fuzzilli_Protobuf_EndClassStaticComputedMethod: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".EndClassStaticComputedMethod" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap() + + public mutating func decodeMessage(decoder: inout D) throws { + // Load everything into unknown fields + while try decoder.nextFieldNumber() != nil {} + } + + public func traverse(visitor: inout V) throws { + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Fuzzilli_Protobuf_EndClassStaticComputedMethod, rhs: Fuzzilli_Protobuf_EndClassStaticComputedMethod) -> Bool { + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension Fuzzilli_Protobuf_BeginClassStaticGetter: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".BeginClassStaticGetter" public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}propertyName\0") diff --git a/Sources/Fuzzilli/Protobuf/operations.proto b/Sources/Fuzzilli/Protobuf/operations.proto index 2bb208943..72266a1fb 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -151,6 +151,13 @@ message BeginClassInstanceMethod { message EndClassInstanceMethod { } +message BeginClassInstanceComputedMethod { + Parameters parameters = 1; +} + +message EndClassInstanceComputedMethod { +} + message BeginClassInstanceGetter { string propertyName = 1; } @@ -193,6 +200,13 @@ message BeginClassStaticMethod { message EndClassStaticMethod { } +message BeginClassStaticComputedMethod { + Parameters parameters = 1; +} + +message EndClassStaticComputedMethod { +} + message BeginClassStaticGetter { string propertyName = 1; } diff --git a/Sources/Fuzzilli/Protobuf/program.pb.swift b/Sources/Fuzzilli/Protobuf/program.pb.swift index 051b50afa..e13009f1f 100644 --- a/Sources/Fuzzilli/Protobuf/program.pb.swift +++ b/Sources/Fuzzilli/Protobuf/program.pb.swift @@ -2689,6 +2689,38 @@ public struct Fuzzilli_Protobuf_Instruction: Sendable { set {operation = .createNamedAsyncDisposableVariable(newValue)} } + public var beginClassInstanceComputedMethod: Fuzzilli_Protobuf_BeginClassInstanceComputedMethod { + get { + if case .beginClassInstanceComputedMethod(let v)? = operation {return v} + return Fuzzilli_Protobuf_BeginClassInstanceComputedMethod() + } + set {operation = .beginClassInstanceComputedMethod(newValue)} + } + + public var endClassInstanceComputedMethod: Fuzzilli_Protobuf_EndClassInstanceComputedMethod { + get { + if case .endClassInstanceComputedMethod(let v)? = operation {return v} + return Fuzzilli_Protobuf_EndClassInstanceComputedMethod() + } + set {operation = .endClassInstanceComputedMethod(newValue)} + } + + public var beginClassStaticComputedMethod: Fuzzilli_Protobuf_BeginClassStaticComputedMethod { + get { + if case .beginClassStaticComputedMethod(let v)? = operation {return v} + return Fuzzilli_Protobuf_BeginClassStaticComputedMethod() + } + set {operation = .beginClassStaticComputedMethod(newValue)} + } + + public var endClassStaticComputedMethod: Fuzzilli_Protobuf_EndClassStaticComputedMethod { + get { + if case .endClassStaticComputedMethod(let v)? = operation {return v} + return Fuzzilli_Protobuf_EndClassStaticComputedMethod() + } + set {operation = .endClassStaticComputedMethod(newValue)} + } + public var unknownFields = SwiftProtobuf.UnknownStorage() public enum OneOf_Operation: Equatable, Sendable { @@ -3022,6 +3054,10 @@ public struct Fuzzilli_Protobuf_Instruction: Sendable { case wasmDefineSignatureType(Fuzzilli_Protobuf_WasmDefineSignatureType) case createNamedDisposableVariable(Fuzzilli_Protobuf_CreateNamedDisposableVariable) case createNamedAsyncDisposableVariable(Fuzzilli_Protobuf_CreateNamedAsyncDisposableVariable) + case beginClassInstanceComputedMethod(Fuzzilli_Protobuf_BeginClassInstanceComputedMethod) + case endClassInstanceComputedMethod(Fuzzilli_Protobuf_EndClassInstanceComputedMethod) + case beginClassStaticComputedMethod(Fuzzilli_Protobuf_BeginClassStaticComputedMethod) + case endClassStaticComputedMethod(Fuzzilli_Protobuf_EndClassStaticComputedMethod) } @@ -3070,7 +3106,7 @@ fileprivate let _protobuf_package = "fuzzilli.protobuf" extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Instruction" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}inouts\0\u{1}opIdx\0\u{1}nop\0\u{1}loadInteger\0\u{1}loadBigInt\0\u{1}loadFloat\0\u{1}loadString\0\u{1}loadBoolean\0\u{1}loadUndefined\0\u{1}loadNull\0\u{1}loadThis\0\u{1}loadArguments\0\u{1}createNamedVariable\0\u{1}loadDisposableVariable\0\u{1}loadAsyncDisposableVariable\0\u{1}loadRegExp\0\u{1}beginObjectLiteral\0\u{1}objectLiteralAddProperty\0\u{1}objectLiteralAddElement\0\u{1}objectLiteralAddComputedProperty\0\u{1}objectLiteralCopyProperties\0\u{1}objectLiteralSetPrototype\0\u{1}beginObjectLiteralMethod\0\u{1}endObjectLiteralMethod\0\u{1}beginObjectLiteralComputedMethod\0\u{1}endObjectLiteralComputedMethod\0\u{1}beginObjectLiteralGetter\0\u{1}endObjectLiteralGetter\0\u{1}beginObjectLiteralSetter\0\u{1}endObjectLiteralSetter\0\u{1}endObjectLiteral\0\u{1}beginClassDefinition\0\u{1}beginClassConstructor\0\u{1}endClassConstructor\0\u{1}classAddInstanceProperty\0\u{1}classAddInstanceElement\0\u{1}classAddInstanceComputedProperty\0\u{1}beginClassInstanceMethod\0\u{1}endClassInstanceMethod\0\u{1}beginClassInstanceGetter\0\u{1}endClassInstanceGetter\0\u{1}beginClassInstanceSetter\0\u{1}endClassInstanceSetter\0\u{1}classAddStaticProperty\0\u{1}classAddStaticElement\0\u{1}classAddStaticComputedProperty\0\u{1}beginClassStaticInitializer\0\u{1}endClassStaticInitializer\0\u{1}beginClassStaticMethod\0\u{1}endClassStaticMethod\0\u{1}beginClassStaticGetter\0\u{1}endClassStaticGetter\0\u{1}beginClassStaticSetter\0\u{1}endClassStaticSetter\0\u{1}classAddPrivateInstanceProperty\0\u{1}beginClassPrivateInstanceMethod\0\u{1}endClassPrivateInstanceMethod\0\u{1}classAddPrivateStaticProperty\0\u{1}beginClassPrivateStaticMethod\0\u{1}endClassPrivateStaticMethod\0\u{1}endClassDefinition\0\u{1}createArray\0\u{1}createIntArray\0\u{1}createFloatArray\0\u{1}createArrayWithSpread\0\u{1}createTemplateString\0\u{1}getProperty\0\u{1}setProperty\0\u{1}updateProperty\0\u{1}deleteProperty\0\u{1}configureProperty\0\u{1}getElement\0\u{1}setElement\0\u{1}updateElement\0\u{1}deleteElement\0\u{1}configureElement\0\u{1}getComputedProperty\0\u{1}setComputedProperty\0\u{1}updateComputedProperty\0\u{1}deleteComputedProperty\0\u{1}configureComputedProperty\0\u{1}typeOf\0\u{1}void\0\u{1}testInstanceOf\0\u{1}testIn\0\u{1}beginPlainFunction\0\u{1}endPlainFunction\0\u{1}beginArrowFunction\0\u{1}endArrowFunction\0\u{1}beginGeneratorFunction\0\u{1}endGeneratorFunction\0\u{1}beginAsyncFunction\0\u{1}endAsyncFunction\0\u{1}beginAsyncArrowFunction\0\u{1}endAsyncArrowFunction\0\u{1}beginAsyncGeneratorFunction\0\u{1}endAsyncGeneratorFunction\0\u{1}beginConstructor\0\u{1}endConstructor\0\u{1}directive\0\u{1}return\0\u{1}yield\0\u{1}yieldEach\0\u{1}await\0\u{1}callFunction\0\u{1}callFunctionWithSpread\0\u{1}construct\0\u{1}constructWithSpread\0\u{1}callMethod\0\u{1}callMethodWithSpread\0\u{1}callComputedMethod\0\u{1}callComputedMethodWithSpread\0\u{1}unaryOperation\0\u{1}binaryOperation\0\u{1}ternaryOperation\0\u{1}update\0\u{1}dup\0\u{1}reassign\0\u{1}destructArray\0\u{1}destructArrayAndReassign\0\u{1}destructObject\0\u{1}destructObjectAndReassign\0\u{1}compare\0\u{1}eval\0\u{1}beginWith\0\u{1}endWith\0\u{1}callSuperConstructor\0\u{1}callSuperMethod\0\u{1}getPrivateProperty\0\u{1}setPrivateProperty\0\u{1}updatePrivateProperty\0\u{1}callPrivateMethod\0\u{1}getSuperProperty\0\u{1}setSuperProperty\0\u{1}getComputedSuperProperty\0\u{1}setComputedSuperProperty\0\u{1}updateSuperProperty\0\u{1}beginIf\0\u{1}beginElse\0\u{1}endIf\0\u{1}beginWhileLoopHeader\0\u{1}beginWhileLoopBody\0\u{1}endWhileLoop\0\u{1}beginDoWhileLoopBody\0\u{1}beginDoWhileLoopHeader\0\u{1}endDoWhileLoop\0\u{1}beginForLoopInitializer\0\u{1}beginForLoopCondition\0\u{1}beginForLoopAfterthought\0\u{1}beginForLoopBody\0\u{1}endForLoop\0\u{1}beginForInLoop\0\u{1}endForInLoop\0\u{1}beginForOfLoop\0\u{1}beginForOfLoopWithDestruct\0\u{1}endForOfLoop\0\u{1}beginRepeatLoop\0\u{1}endRepeatLoop\0\u{1}loopBreak\0\u{1}loopContinue\0\u{1}beginTry\0\u{1}beginCatch\0\u{1}beginFinally\0\u{1}endTryCatchFinally\0\u{1}throwException\0\u{1}beginCodeString\0\u{1}endCodeString\0\u{1}beginBlockStatement\0\u{1}endBlockStatement\0\u{1}beginSwitch\0\u{1}beginSwitchCase\0\u{1}beginSwitchDefaultCase\0\u{1}endSwitchCase\0\u{1}endSwitch\0\u{1}switchBreak\0\u{1}loadNewTarget\0\u{1}print\0\u{1}explore\0\u{1}probe\0\u{1}fixup\0\u{1}beginWasmModule\0\u{1}endWasmModule\0\u{1}createWasmGlobal\0\u{1}createWasmMemory\0\u{1}createWasmTable\0\u{1}createWasmJSTag\0\u{1}createWasmTag\0\u{1}wrapPromising\0\u{1}wrapSuspending\0\u{1}bindMethod\0\u{1}bindFunction\0\u{1}consti64\0\u{1}consti32\0\u{1}constf32\0\u{1}constf64\0\u{1}wasmReturn\0\u{1}wasmJsCall\0\u{1}wasmi32CompareOp\0\u{1}wasmi64CompareOp\0\u{1}wasmf32CompareOp\0\u{1}wasmf64CompareOp\0\u{1}wasmi32EqualZero\0\u{1}wasmi64EqualZero\0\u{1}wasmi32BinOp\0\u{1}wasmi64BinOp\0\u{1}wasmi32UnOp\0\u{1}wasmi64UnOp\0\u{1}wasmf32BinOp\0\u{1}wasmf64BinOp\0\u{1}wasmf32UnOp\0\u{1}wasmf64UnOp\0\u{1}wasmWrapi64Toi32\0\u{1}wasmTruncatef32Toi32\0\u{1}wasmTruncatef64Toi32\0\u{1}wasmExtendi32Toi64\0\u{1}wasmTruncatef32Toi64\0\u{1}wasmTruncatef64Toi64\0\u{1}wasmConverti32Tof32\0\u{1}wasmConverti64Tof32\0\u{1}wasmDemotef64Tof32\0\u{1}wasmConverti32Tof64\0\u{1}wasmConverti64Tof64\0\u{1}wasmPromotef32Tof64\0\u{1}wasmReinterpretf32Asi32\0\u{1}wasmReinterpretf64Asi64\0\u{1}wasmReinterpreti32Asf32\0\u{1}wasmReinterpreti64Asf64\0\u{1}wasmSignExtend8Intoi32\0\u{1}wasmSignExtend16Intoi32\0\u{1}wasmSignExtend8Intoi64\0\u{1}wasmSignExtend16Intoi64\0\u{1}wasmSignExtend32Intoi64\0\u{1}wasmTruncateSatf32Toi32\0\u{1}wasmTruncateSatf64Toi32\0\u{1}wasmTruncateSatf32Toi64\0\u{1}wasmTruncateSatf64Toi64\0\u{1}wasmReassign\0\u{1}wasmDefineGlobal\0\u{1}wasmDefineTable\0\u{1}wasmDefineMemory\0\u{1}wasmDefineDataSegment\0\u{1}wasmLoadGlobal\0\u{1}wasmStoreGlobal\0\u{1}wasmTableGet\0\u{1}wasmTableSet\0\u{1}wasmTableSize\0\u{1}wasmTableGrow\0\u{1}wasmCallIndirect\0\u{1}wasmCallDirect\0\u{1}wasmReturnCallDirect\0\u{1}wasmReturnCallIndirect\0\u{1}wasmMemoryLoad\0\u{1}wasmMemoryStore\0\u{1}wasmAtomicLoad\0\u{1}wasmAtomicStore\0\u{1}wasmAtomicRMW\0\u{1}wasmAtomicCmpxchg\0\u{1}wasmMemorySize\0\u{1}wasmMemoryGrow\0\u{1}wasmMemoryFill\0\u{1}wasmMemoryInit\0\u{1}wasmDropDataSegment\0\u{1}beginWasmFunction\0\u{1}endWasmFunction\0\u{1}wasmBeginBlock\0\u{1}wasmEndBlock\0\u{1}wasmBeginLoop\0\u{1}wasmEndLoop\0\u{1}wasmBranch\0\u{1}wasmBranchIf\0\u{1}wasmBranchTable\0\u{1}wasmNop\0\u{1}wasmBeginIf\0\u{1}wasmBeginElse\0\u{1}wasmEndIf\0\u{1}wasmBeginTryTable\0\u{1}wasmEndTryTable\0\u{1}wasmBeginTry\0\u{1}wasmBeginCatchAll\0\u{1}wasmBeginCatch\0\u{1}wasmEndTry\0\u{1}wasmBeginTryDelegate\0\u{1}wasmEndTryDelegate\0\u{1}wasmThrow\0\u{1}wasmRethrow\0\u{1}wasmThrowRef\0\u{1}wasmDefineTag\0\u{1}constSimd128\0\u{1}wasmSimd128Compare\0\u{1}wasmSimd128IntegerUnOp\0\u{1}wasmSimd128IntegerBinOp\0\u{1}wasmSimd128IntegerTernaryOp\0\u{1}wasmSimd128FloatUnOp\0\u{1}wasmSimd128FloatBinOp\0\u{1}wasmSimd128FloatTernaryOp\0\u{1}wasmSimdSplat\0\u{1}wasmSimdExtractLane\0\u{1}wasmSimdReplaceLane\0\u{1}wasmSimdStoreLane\0\u{1}wasmSimdLoadLane\0\u{1}wasmSimdLoad\0\u{1}wasmUnreachable\0\u{1}wasmSelect\0\u{1}wasmBeginTypeGroup\0\u{1}wasmEndTypeGroup\0\u{1}wasmDefineArrayType\0\u{1}wasmDefineStructType\0\u{1}wasmDefineForwardOrSelfReference\0\u{1}wasmResolveForwardReference\0\u{1}wasmArrayNewFixed\0\u{1}wasmArrayNewDefault\0\u{1}wasmArrayLen\0\u{1}wasmArrayGet\0\u{1}wasmArraySet\0\u{1}wasmStructNewDefault\0\u{1}wasmStructGet\0\u{1}wasmStructSet\0\u{1}wasmRefNull\0\u{1}wasmRefIsNull\0\u{1}wasmRefI31\0\u{1}wasmI31Get\0\u{1}wasmAnyConvertExtern\0\u{1}wasmExternConvertAny\0\u{1}wasmMemoryCopy\0\u{1}wasmDefineElementSegment\0\u{1}wasmTableInit\0\u{1}wasmDropElementSegment\0\u{1}wasmTableCopy\0\u{1}wasmDefineSignatureType\0\u{1}createNamedDisposableVariable\0\u{1}createNamedAsyncDisposableVariable\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}inouts\0\u{1}opIdx\0\u{1}nop\0\u{1}loadInteger\0\u{1}loadBigInt\0\u{1}loadFloat\0\u{1}loadString\0\u{1}loadBoolean\0\u{1}loadUndefined\0\u{1}loadNull\0\u{1}loadThis\0\u{1}loadArguments\0\u{1}createNamedVariable\0\u{1}loadDisposableVariable\0\u{1}loadAsyncDisposableVariable\0\u{1}loadRegExp\0\u{1}beginObjectLiteral\0\u{1}objectLiteralAddProperty\0\u{1}objectLiteralAddElement\0\u{1}objectLiteralAddComputedProperty\0\u{1}objectLiteralCopyProperties\0\u{1}objectLiteralSetPrototype\0\u{1}beginObjectLiteralMethod\0\u{1}endObjectLiteralMethod\0\u{1}beginObjectLiteralComputedMethod\0\u{1}endObjectLiteralComputedMethod\0\u{1}beginObjectLiteralGetter\0\u{1}endObjectLiteralGetter\0\u{1}beginObjectLiteralSetter\0\u{1}endObjectLiteralSetter\0\u{1}endObjectLiteral\0\u{1}beginClassDefinition\0\u{1}beginClassConstructor\0\u{1}endClassConstructor\0\u{1}classAddInstanceProperty\0\u{1}classAddInstanceElement\0\u{1}classAddInstanceComputedProperty\0\u{1}beginClassInstanceMethod\0\u{1}endClassInstanceMethod\0\u{1}beginClassInstanceGetter\0\u{1}endClassInstanceGetter\0\u{1}beginClassInstanceSetter\0\u{1}endClassInstanceSetter\0\u{1}classAddStaticProperty\0\u{1}classAddStaticElement\0\u{1}classAddStaticComputedProperty\0\u{1}beginClassStaticInitializer\0\u{1}endClassStaticInitializer\0\u{1}beginClassStaticMethod\0\u{1}endClassStaticMethod\0\u{1}beginClassStaticGetter\0\u{1}endClassStaticGetter\0\u{1}beginClassStaticSetter\0\u{1}endClassStaticSetter\0\u{1}classAddPrivateInstanceProperty\0\u{1}beginClassPrivateInstanceMethod\0\u{1}endClassPrivateInstanceMethod\0\u{1}classAddPrivateStaticProperty\0\u{1}beginClassPrivateStaticMethod\0\u{1}endClassPrivateStaticMethod\0\u{1}endClassDefinition\0\u{1}createArray\0\u{1}createIntArray\0\u{1}createFloatArray\0\u{1}createArrayWithSpread\0\u{1}createTemplateString\0\u{1}getProperty\0\u{1}setProperty\0\u{1}updateProperty\0\u{1}deleteProperty\0\u{1}configureProperty\0\u{1}getElement\0\u{1}setElement\0\u{1}updateElement\0\u{1}deleteElement\0\u{1}configureElement\0\u{1}getComputedProperty\0\u{1}setComputedProperty\0\u{1}updateComputedProperty\0\u{1}deleteComputedProperty\0\u{1}configureComputedProperty\0\u{1}typeOf\0\u{1}void\0\u{1}testInstanceOf\0\u{1}testIn\0\u{1}beginPlainFunction\0\u{1}endPlainFunction\0\u{1}beginArrowFunction\0\u{1}endArrowFunction\0\u{1}beginGeneratorFunction\0\u{1}endGeneratorFunction\0\u{1}beginAsyncFunction\0\u{1}endAsyncFunction\0\u{1}beginAsyncArrowFunction\0\u{1}endAsyncArrowFunction\0\u{1}beginAsyncGeneratorFunction\0\u{1}endAsyncGeneratorFunction\0\u{1}beginConstructor\0\u{1}endConstructor\0\u{1}directive\0\u{1}return\0\u{1}yield\0\u{1}yieldEach\0\u{1}await\0\u{1}callFunction\0\u{1}callFunctionWithSpread\0\u{1}construct\0\u{1}constructWithSpread\0\u{1}callMethod\0\u{1}callMethodWithSpread\0\u{1}callComputedMethod\0\u{1}callComputedMethodWithSpread\0\u{1}unaryOperation\0\u{1}binaryOperation\0\u{1}ternaryOperation\0\u{1}update\0\u{1}dup\0\u{1}reassign\0\u{1}destructArray\0\u{1}destructArrayAndReassign\0\u{1}destructObject\0\u{1}destructObjectAndReassign\0\u{1}compare\0\u{1}eval\0\u{1}beginWith\0\u{1}endWith\0\u{1}callSuperConstructor\0\u{1}callSuperMethod\0\u{1}getPrivateProperty\0\u{1}setPrivateProperty\0\u{1}updatePrivateProperty\0\u{1}callPrivateMethod\0\u{1}getSuperProperty\0\u{1}setSuperProperty\0\u{1}getComputedSuperProperty\0\u{1}setComputedSuperProperty\0\u{1}updateSuperProperty\0\u{1}beginIf\0\u{1}beginElse\0\u{1}endIf\0\u{1}beginWhileLoopHeader\0\u{1}beginWhileLoopBody\0\u{1}endWhileLoop\0\u{1}beginDoWhileLoopBody\0\u{1}beginDoWhileLoopHeader\0\u{1}endDoWhileLoop\0\u{1}beginForLoopInitializer\0\u{1}beginForLoopCondition\0\u{1}beginForLoopAfterthought\0\u{1}beginForLoopBody\0\u{1}endForLoop\0\u{1}beginForInLoop\0\u{1}endForInLoop\0\u{1}beginForOfLoop\0\u{1}beginForOfLoopWithDestruct\0\u{1}endForOfLoop\0\u{1}beginRepeatLoop\0\u{1}endRepeatLoop\0\u{1}loopBreak\0\u{1}loopContinue\0\u{1}beginTry\0\u{1}beginCatch\0\u{1}beginFinally\0\u{1}endTryCatchFinally\0\u{1}throwException\0\u{1}beginCodeString\0\u{1}endCodeString\0\u{1}beginBlockStatement\0\u{1}endBlockStatement\0\u{1}beginSwitch\0\u{1}beginSwitchCase\0\u{1}beginSwitchDefaultCase\0\u{1}endSwitchCase\0\u{1}endSwitch\0\u{1}switchBreak\0\u{1}loadNewTarget\0\u{1}print\0\u{1}explore\0\u{1}probe\0\u{1}fixup\0\u{1}beginWasmModule\0\u{1}endWasmModule\0\u{1}createWasmGlobal\0\u{1}createWasmMemory\0\u{1}createWasmTable\0\u{1}createWasmJSTag\0\u{1}createWasmTag\0\u{1}wrapPromising\0\u{1}wrapSuspending\0\u{1}bindMethod\0\u{1}bindFunction\0\u{1}consti64\0\u{1}consti32\0\u{1}constf32\0\u{1}constf64\0\u{1}wasmReturn\0\u{1}wasmJsCall\0\u{1}wasmi32CompareOp\0\u{1}wasmi64CompareOp\0\u{1}wasmf32CompareOp\0\u{1}wasmf64CompareOp\0\u{1}wasmi32EqualZero\0\u{1}wasmi64EqualZero\0\u{1}wasmi32BinOp\0\u{1}wasmi64BinOp\0\u{1}wasmi32UnOp\0\u{1}wasmi64UnOp\0\u{1}wasmf32BinOp\0\u{1}wasmf64BinOp\0\u{1}wasmf32UnOp\0\u{1}wasmf64UnOp\0\u{1}wasmWrapi64Toi32\0\u{1}wasmTruncatef32Toi32\0\u{1}wasmTruncatef64Toi32\0\u{1}wasmExtendi32Toi64\0\u{1}wasmTruncatef32Toi64\0\u{1}wasmTruncatef64Toi64\0\u{1}wasmConverti32Tof32\0\u{1}wasmConverti64Tof32\0\u{1}wasmDemotef64Tof32\0\u{1}wasmConverti32Tof64\0\u{1}wasmConverti64Tof64\0\u{1}wasmPromotef32Tof64\0\u{1}wasmReinterpretf32Asi32\0\u{1}wasmReinterpretf64Asi64\0\u{1}wasmReinterpreti32Asf32\0\u{1}wasmReinterpreti64Asf64\0\u{1}wasmSignExtend8Intoi32\0\u{1}wasmSignExtend16Intoi32\0\u{1}wasmSignExtend8Intoi64\0\u{1}wasmSignExtend16Intoi64\0\u{1}wasmSignExtend32Intoi64\0\u{1}wasmTruncateSatf32Toi32\0\u{1}wasmTruncateSatf64Toi32\0\u{1}wasmTruncateSatf32Toi64\0\u{1}wasmTruncateSatf64Toi64\0\u{1}wasmReassign\0\u{1}wasmDefineGlobal\0\u{1}wasmDefineTable\0\u{1}wasmDefineMemory\0\u{1}wasmDefineDataSegment\0\u{1}wasmLoadGlobal\0\u{1}wasmStoreGlobal\0\u{1}wasmTableGet\0\u{1}wasmTableSet\0\u{1}wasmTableSize\0\u{1}wasmTableGrow\0\u{1}wasmCallIndirect\0\u{1}wasmCallDirect\0\u{1}wasmReturnCallDirect\0\u{1}wasmReturnCallIndirect\0\u{1}wasmMemoryLoad\0\u{1}wasmMemoryStore\0\u{1}wasmAtomicLoad\0\u{1}wasmAtomicStore\0\u{1}wasmAtomicRMW\0\u{1}wasmAtomicCmpxchg\0\u{1}wasmMemorySize\0\u{1}wasmMemoryGrow\0\u{1}wasmMemoryFill\0\u{1}wasmMemoryInit\0\u{1}wasmDropDataSegment\0\u{1}beginWasmFunction\0\u{1}endWasmFunction\0\u{1}wasmBeginBlock\0\u{1}wasmEndBlock\0\u{1}wasmBeginLoop\0\u{1}wasmEndLoop\0\u{1}wasmBranch\0\u{1}wasmBranchIf\0\u{1}wasmBranchTable\0\u{1}wasmNop\0\u{1}wasmBeginIf\0\u{1}wasmBeginElse\0\u{1}wasmEndIf\0\u{1}wasmBeginTryTable\0\u{1}wasmEndTryTable\0\u{1}wasmBeginTry\0\u{1}wasmBeginCatchAll\0\u{1}wasmBeginCatch\0\u{1}wasmEndTry\0\u{1}wasmBeginTryDelegate\0\u{1}wasmEndTryDelegate\0\u{1}wasmThrow\0\u{1}wasmRethrow\0\u{1}wasmThrowRef\0\u{1}wasmDefineTag\0\u{1}constSimd128\0\u{1}wasmSimd128Compare\0\u{1}wasmSimd128IntegerUnOp\0\u{1}wasmSimd128IntegerBinOp\0\u{1}wasmSimd128IntegerTernaryOp\0\u{1}wasmSimd128FloatUnOp\0\u{1}wasmSimd128FloatBinOp\0\u{1}wasmSimd128FloatTernaryOp\0\u{1}wasmSimdSplat\0\u{1}wasmSimdExtractLane\0\u{1}wasmSimdReplaceLane\0\u{1}wasmSimdStoreLane\0\u{1}wasmSimdLoadLane\0\u{1}wasmSimdLoad\0\u{1}wasmUnreachable\0\u{1}wasmSelect\0\u{1}wasmBeginTypeGroup\0\u{1}wasmEndTypeGroup\0\u{1}wasmDefineArrayType\0\u{1}wasmDefineStructType\0\u{1}wasmDefineForwardOrSelfReference\0\u{1}wasmResolveForwardReference\0\u{1}wasmArrayNewFixed\0\u{1}wasmArrayNewDefault\0\u{1}wasmArrayLen\0\u{1}wasmArrayGet\0\u{1}wasmArraySet\0\u{1}wasmStructNewDefault\0\u{1}wasmStructGet\0\u{1}wasmStructSet\0\u{1}wasmRefNull\0\u{1}wasmRefIsNull\0\u{1}wasmRefI31\0\u{1}wasmI31Get\0\u{1}wasmAnyConvertExtern\0\u{1}wasmExternConvertAny\0\u{1}wasmMemoryCopy\0\u{1}wasmDefineElementSegment\0\u{1}wasmTableInit\0\u{1}wasmDropElementSegment\0\u{1}wasmTableCopy\0\u{1}wasmDefineSignatureType\0\u{1}createNamedDisposableVariable\0\u{1}createNamedAsyncDisposableVariable\0\u{1}beginClassInstanceComputedMethod\0\u{1}endClassInstanceComputedMethod\0\u{1}beginClassStaticComputedMethod\0\u{1}endClassStaticComputedMethod\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -7364,6 +7400,58 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M self.operation = .createNamedAsyncDisposableVariable(v) } }() + case 332: try { + var v: Fuzzilli_Protobuf_BeginClassInstanceComputedMethod? + var hadOneofValue = false + if let current = self.operation { + hadOneofValue = true + if case .beginClassInstanceComputedMethod(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.operation = .beginClassInstanceComputedMethod(v) + } + }() + case 333: try { + var v: Fuzzilli_Protobuf_EndClassInstanceComputedMethod? + var hadOneofValue = false + if let current = self.operation { + hadOneofValue = true + if case .endClassInstanceComputedMethod(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.operation = .endClassInstanceComputedMethod(v) + } + }() + case 334: try { + var v: Fuzzilli_Protobuf_BeginClassStaticComputedMethod? + var hadOneofValue = false + if let current = self.operation { + hadOneofValue = true + if case .beginClassStaticComputedMethod(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.operation = .beginClassStaticComputedMethod(v) + } + }() + case 335: try { + var v: Fuzzilli_Protobuf_EndClassStaticComputedMethod? + var hadOneofValue = false + if let current = self.operation { + hadOneofValue = true + if case .endClassStaticComputedMethod(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.operation = .endClassStaticComputedMethod(v) + } + }() default: break } } @@ -8698,6 +8786,22 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M guard case .createNamedAsyncDisposableVariable(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 331) }() + case .beginClassInstanceComputedMethod?: try { + guard case .beginClassInstanceComputedMethod(let v)? = self.operation else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 332) + }() + case .endClassInstanceComputedMethod?: try { + guard case .endClassInstanceComputedMethod(let v)? = self.operation else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 333) + }() + case .beginClassStaticComputedMethod?: try { + guard case .beginClassStaticComputedMethod(let v)? = self.operation else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 334) + }() + case .endClassStaticComputedMethod?: try { + guard case .endClassStaticComputedMethod(let v)? = self.operation else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 335) + }() case nil: break } try unknownFields.traverse(visitor: &visitor) diff --git a/Sources/Fuzzilli/Protobuf/program.proto b/Sources/Fuzzilli/Protobuf/program.proto index 5f3d390ff..46c3f7a9c 100644 --- a/Sources/Fuzzilli/Protobuf/program.proto +++ b/Sources/Fuzzilli/Protobuf/program.proto @@ -355,6 +355,10 @@ message Instruction { WasmDefineSignatureType wasmDefineSignatureType = 329; CreateNamedDisposableVariable createNamedDisposableVariable = 330; CreateNamedAsyncDisposableVariable createNamedAsyncDisposableVariable = 331; + BeginClassInstanceComputedMethod beginClassInstanceComputedMethod = 332; + EndClassInstanceComputedMethod endClassInstanceComputedMethod = 333; + BeginClassStaticComputedMethod beginClassStaticComputedMethod = 334; + EndClassStaticComputedMethod endClassStaticComputedMethod = 335; } } diff --git a/Tests/FuzzilliTests/CompilerTests/computed_and_indexed_properties.js b/Tests/FuzzilliTests/CompilerTests/computed_and_indexed_properties.js new file mode 100644 index 000000000..369b93090 --- /dev/null +++ b/Tests/FuzzilliTests/CompilerTests/computed_and_indexed_properties.js @@ -0,0 +1,387 @@ +// TODO(https://crbug.com/446634535): Not yet supported cases are commented +// out below. + +console.log("Computed object property"); +(() => { + const p = 'theAnswerIs'; + const obj = { [p] : 42 }; + console.log(obj.theAnswerIs); +})(); + +console.log("Computed object method"); +(() => { + const p = 'theAnswerIs'; + const obj = { [p]() { return 42; } }; + console.log(obj.theAnswerIs()); +})(); + +console.log("Computed class property (field)"); +(() => { + function classify (name) { + return class { + [name] = 42; + } + } + + console.log(new (classify("theAnswerIs"))().theAnswerIs); +})(); + +console.log("Computed static class property (field)"); +(() => { + function classify (name) { + return class { + static [name] = 42; + } + } + + console.log((classify("theAnswerIs")).theAnswerIs); +})(); + +/* +console.log("Computed class property (getter/setter)"); +(() => { + function classify (name) { + return class { + answer = 7; + + get [name]() { + console.log("Heavy calculations"); + return this.answer; + } + + set [name](answer) { + console.log(`The answer was ${this.answer}`); + this.answer = answer; + console.log(`Now the answer is ${this.answer}`); + } + } + } + + const c = new (classify("theAnswerIs"))(); + console.log(c.theAnswerIs); + c.theAnswerIs = 42; + console.log(c.theAnswerIs); +})(); +*/ + +/* +console.log("Computed static class property (getter/setter)"); +(() => { + function classify (name) { + return class { + static answer = 7; + + static get [name]() { + console.log("Heavy calculations"); + return this.answer; + } + + static set [name](answer) { + console.log(`The answer was ${this.answer}`); + this.answer = answer; + console.log(`Now the answer is ${this.answer}`); + } + } + } + + const c = classify("theAnswerIs"); + console.log(c.theAnswerIs); + c.theAnswerIs = 42; + console.log(c.theAnswerIs); +})(); +*/ + +console.log("Computed class property (method)"); +(() => { + function classify (name) { + return class { + [name]() { + console.log("Heavy calculations"); + return 42; + } + } + } + + console.log(new (classify("theAnswerIs"))().theAnswerIs()); +})(); + +console.log("Computed static class property (method)"); +(() => { + function classify (name) { + return class { + static [name]() { + console.log("Heavy calculations"); + return 42; + } + } + } + + console.log(classify("theAnswerIs").theAnswerIs()); +})(); + +console.log("Indexed class property (field)"); +(() => { + class C { + 42 = 42; + } + const c = new C(); + console.log(c[42]); +})(); + +console.log("Indexed static class property (field)"); +(() => { + class C { + static 42 = 42; + } + console.log(C[42]); +})(); + +/* +console.log("Indexed class property (method)"); +(() => { + class C { + 42() { + console.log("Heavy calculations"); + return 42; + } + } + const c = new C(); + console.log(c[42]()); +})(); +*/ + +/* +console.log("Indexed static class property (method)"); +(() => { + class C { + static 42() { + console.log("Heavy calculations"); + return 42; + } + } + console.log(C[42]()); +})(); +*/ + +/* +console.log("Indexed class property (getter/setter)"); +(() => { + class C { + answer = 7; + + get 42() { + console.log("Heavy calculations"); + return this.answer; + } + + set 42(answer) { + console.log(`The answer was ${this.answer}`); + this.answer = answer; + console.log(`Now the answer is ${this.answer}`); + } + } + const c = new C(); + console.log(c[42]); + c[42] = 42; + console.log(c[42]); +})(); +*/ + +/* +console.log("Indexed static class property (getter/setter)"); +(() => { + class C { + static answer = 7; + + static get 42() { + console.log("Heavy calculations"); + return this.answer; + } + + static set 42(answer) { + console.log(`The answer was ${this.answer}`); + this.answer = answer; + console.log(`Now the answer is ${this.answer}`); + } + } + console.log(C[42]); + C[42] = 42; + console.log(C[42]); +})(); +*/ + +console.log("String-indexed class property (field)"); +(() => { + class C { + "42" = 42; + } + const c = new C(); + console.log(c["42"]); +})(); + +console.log("String-indexed static class property (field)"); +(() => { + class C { + static "42" = 42; + } + console.log(C["42"]); +})(); + +console.log("String-indexed class property (method)"); +(() => { + class C { + "42"() { + console.log("Heavy calculations"); + return 42; + } + } + const c = new C(); + console.log(c["42"]()); +})(); + +console.log("String-indexed static class property (method)"); +(() => { + class C { + static "42"() { + console.log("Heavy calculations"); + return 42; + } + } + console.log(C["42"]()); +})(); + +/* +console.log("String-indexed class property (getter/setter)"); +(() => { + class C { + constructor() { + this.answer = 7; + } + + get "42"() { + console.log("Heavy calculations"); + return this.answer; + } + + set "42"(answer) { + console.log(`The answer was ${this.answer}`); + this.answer = answer; + console.log(`Now the answer is ${this.answer}`); + } + } + const c = new C(); + console.log(c["42"]); + c["42"] = 42; + console.log(c["42"]); +})(); +*/ + +/* +console.log("String-indexed static class property (getter/setter)"); +(() => { + class C { + static answer = 7; + + static get "42"() { + console.log("Heavy calculations"); + return this.answer; + } + + static set "42"(answer) { + console.log(`The answer was ${this.answer}`); + this.answer = answer; + console.log(`Now the answer is ${this.answer}`); + } + } + console.log(C["42"]); + C["42"] = 42; + console.log(C["42"]); +})(); +*/ + +console.log("String-literal class property (field)"); +(() => { + class C { + "theAnswerIs" = 42; + } + const c = new C(); + console.log(c.theAnswerIs); +})(); + +console.log("String-literal static class property (field)"); +(() => { + class C { + static "theAnswerIs" = 42; + } + console.log(C.theAnswerIs); +})(); + +console.log("String-literal class property (method)"); +(() => { + class C { + "theAnswerIs"() { + console.log("Heavy calculations"); + return 42; + } + } + const c = new C(); + console.log(c.theAnswerIs()); +})(); + +console.log("String-literal static class property (method)"); +(() => { + class C { + static "theAnswerIs"() { + console.log("Heavy calculations"); + return 42; + } + } + console.log(C.theAnswerIs()); +})(); + +/* +console.log("String-literal class property (getter/setter)"); +(() => { + class C { + answer = 7; + + get "theAnswerIs"() { + console.log("Heavy calculations"); + return this.answer; + } + + set "theAnswerIs"(answer) { + console.log(`The answer was ${this.answer}`); + this.answer = answer; + console.log(`Now the answer is ${this.answer}`); + } + } + const c = new C(); + console.log(c.theAnswerIs); + c.theAnswerIs = 42; + console.log(c.theAnswerIs); +})(); +*/ + +/* +console.log("String-literal static class property (getter/setter)"); +(() => { + class C { + static answer = 7; + + static get "theAnswerIs"() { + console.log("Heavy calculations"); + return this.answer; + } + + static set "theAnswerIs"(answer) { + console.log(`The answer was ${this.answer}`); + this.answer = answer; + console.log(`Now the answer is ${this.answer}`); + } + } + console.log(C.theAnswerIs); + C.theAnswerIs = 42; + console.log(C.theAnswerIs); +})(); +*/ diff --git a/Tests/FuzzilliTests/LifterTest.swift b/Tests/FuzzilliTests/LifterTest.swift index 4f0892bf0..8d020d82d 100644 --- a/Tests/FuzzilliTests/LifterTest.swift +++ b/Tests/FuzzilliTests/LifterTest.swift @@ -653,6 +653,10 @@ class LifterTests: XCTestCase { let two = b.loadInt(2) let baz = b.loadString("baz") let baz42 = b.binary(baz, i, with: .Add) + let toPrimitive = b.getProperty( + "toPrimitive", + of: b.createNamedVariable(forBuiltin: "Symbol")) + let sm = b.loadString("sm") let C = b.buildClassDefinition() { cls in cls.addInstanceProperty("foo") cls.addInstanceProperty("bar", value: baz) @@ -670,6 +674,11 @@ class LifterTests: XCTestCase { let foo = b.getProperty("foo", of: this) b.doReturn(foo) } + cls.addInstanceComputedMethod(toPrimitive, with: .parameters(n: 0)) { params in + let this = params[0] + let foo = b.getProperty("foo", of: this) + b.doReturn(foo) + } cls.addInstanceGetter(for: "baz") { this in b.doReturn(b.loadInt(1337)) } @@ -691,6 +700,11 @@ class LifterTests: XCTestCase { let foo = b.getProperty("foo", of: this) b.doReturn(foo) } + cls.addInstanceComputedMethod(sm, with: .parameters(n: 0)) { params in + let this = params[0] + let foo = b.getProperty("foo", of: this) + b.doReturn(foo) + } cls.addStaticGetter(for: "baz") { this in b.doReturn(b.loadInt(1337)) } @@ -732,7 +746,8 @@ class LifterTests: XCTestCase { let expected = """ const v3 = "baz" + 42; - class C4 { + const v5 = Symbol.toPrimitive; + class C7 { foo; bar = "baz"; 0 = 42; @@ -740,16 +755,19 @@ class LifterTests: XCTestCase { [-1]; [v3]; [2] = v3; - constructor(a6) { - this.foo = a6; + constructor(a9) { + this.foo = a9; } m() { return this.foo; } + [v5]() { + return this.foo; + } get baz() { return 1337; } - set baz(a12) { + set baz(a17) { } static foo; static { @@ -764,36 +782,39 @@ class LifterTests: XCTestCase { static m() { return this.foo; } + ["sm"]() { + return this.foo; + } static get baz() { return 1337; } - static set baz(a19) { + static set baz(a26) { } #ifoo; #ibar = "baz"; #im() { - const v21 = this.#ifoo; - this.#ibar = v21; - return v21; + const v28 = this.#ifoo; + this.#ibar = v28; + return v28; } - #in(a23) { + #in(a30) { this.#im(); - this.#ibar += a23; + this.#ibar += a30; } static #sfoo; static #sbar = "baz"; static #sm() { - const v26 = this.#sfoo; - this.#sbar = v26; - return v26; + const v33 = this.#sfoo; + this.#sbar = v33; + return v33; } - static #sn(a28) { + static #sn(a35) { this.#sm(); - this.#sbar += a28; + this.#sbar += a35; } } - new C4(42); - C4 = Uint8Array; + new C7(42); + C7 = Uint8Array; """ diff --git a/Tests/FuzzilliTests/ProgramBuilderTest.swift b/Tests/FuzzilliTests/ProgramBuilderTest.swift index 4467636a8..ad5977ab0 100644 --- a/Tests/FuzzilliTests/ProgramBuilderTest.swift +++ b/Tests/FuzzilliTests/ProgramBuilderTest.swift @@ -46,7 +46,7 @@ class ProgramBuilderTests: XCTestCase { let fuzzer = makeMockFuzzer() let b = fuzzer.makeBuilder() let numPrograms = 100 - let maxExpectedProgramSize = 1000 + // let maxExpectedProgramSize = 1000 var sumOfProgramSizes = 0 for _ in 0.. Date: Thu, 2 Oct 2025 10:18:57 +0200 Subject: [PATCH 29/35] Lower the stack-size in the V8 profile in 10% of the cases Some stack-limit-sensitive issues might also run into timeouts more likely. This reduces the stack limit in 10% of the runs to tickle out these situations faster. Change-Id: I5e7c2d4b52a71d34055bb77ce55d8112bc960fdd Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8635956 Commit-Queue: Michael Achenbach Reviewed-by: Matthias Liedtke --- Sources/FuzzilliCli/Profiles/V8Profile.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/FuzzilliCli/Profiles/V8Profile.swift b/Sources/FuzzilliCli/Profiles/V8Profile.swift index c107390e8..f85c88470 100644 --- a/Sources/FuzzilliCli/Profiles/V8Profile.swift +++ b/Sources/FuzzilliCli/Profiles/V8Profile.swift @@ -124,6 +124,11 @@ let v8Profile = Profile( args.append("--handle-weak-ref-weakly-in-minor-gc") } + if probability(0.1) { + let stackSize = Int.random(in: 54...863) + args.append("--stack-size=\(stackSize)") + } + // Temporarily enable the three flags below with high probability to // stress-test JSPI. // Lower the probabilities once we have enough coverage. From 12507b3b6130cc725fb63f3712c699237d26a7d3 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Thu, 2 Oct 2025 10:26:42 +0200 Subject: [PATCH 30/35] Make computed class method immutable Bug: 446634535 Change-Id: I74620e37096f98a1fc9d9e0e71d5ec1d2fd8baca Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8634697 Reviewed-by: Matthias Liedtke Auto-Submit: Michael Achenbach Commit-Queue: Michael Achenbach Commit-Queue: Matthias Liedtke --- Sources/Fuzzilli/FuzzIL/JsOperations.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Fuzzilli/FuzzIL/JsOperations.swift b/Sources/Fuzzilli/FuzzIL/JsOperations.swift index 93c834d93..d8dcbe154 100644 --- a/Sources/Fuzzilli/FuzzIL/JsOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/JsOperations.swift @@ -757,7 +757,7 @@ final class BeginClassInstanceComputedMethod: BeginAnySubroutine { init(parameters: Parameters) { // First inner output is the explicit |this| parameter - super.init(parameters: parameters, numInputs: 1, numInnerOutputs: parameters.count + 1, attributes: [.isMutable, .isBlockStart], requiredContext: .classDefinition, contextOpened: [.javascript, .subroutine, .method, .classMethod]) + super.init(parameters: parameters, numInputs: 1, numInnerOutputs: parameters.count + 1, attributes: [.isBlockStart], requiredContext: .classDefinition, contextOpened: [.javascript, .subroutine, .method, .classMethod]) } } @@ -876,7 +876,7 @@ final class BeginClassStaticComputedMethod: BeginAnySubroutine { init(parameters: Parameters) { // First inner output is the explicit |this| parameter - super.init(parameters: parameters, numInputs: 1, numInnerOutputs: parameters.count + 1, attributes: [.isMutable, .isBlockStart], requiredContext: .classDefinition, contextOpened: [.javascript, .subroutine, .method, .classMethod]) + super.init(parameters: parameters, numInputs: 1, numInnerOutputs: parameters.count + 1, attributes: [.isBlockStart], requiredContext: .classDefinition, contextOpened: [.javascript, .subroutine, .method, .classMethod]) } } From 3e9962ba65f6a5f34a8458d4fe45e8fc5c1293d5 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Thu, 2 Oct 2025 10:29:55 +0200 Subject: [PATCH 31/35] OperationMutator: Report the misconfigured opcode on crash Change-Id: I899267f93d6eefc6fe77de094987c5ce091816fb Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8634698 Reviewed-by: Michael Achenbach Commit-Queue: Matthias Liedtke Auto-Submit: Matthias Liedtke Commit-Queue: Michael Achenbach --- Sources/Fuzzilli/Mutators/OperationMutator.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift index 4e24a03e2..dfe3438df 100644 --- a/Sources/Fuzzilli/Mutators/OperationMutator.swift +++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift @@ -729,8 +729,8 @@ public class OperationMutator: BaseInstructionMutator { .wasmDropElementSegment(_), .wasmTableInit(_), .wasmTableCopy(_): - assert(!instr.isOperationMutable) - fatalError("Unexpected Operation") + let mutability = instr.isOperationMutable ? "mutable" : "immutable" + fatalError("Unexpected operation \(instr.op.opcode), marked as \(mutability)") } // This assert is here to prevent subtle bugs if we ever decide to add flags that are "alive" during program building / mutation. From 5827a552b876ee83748c649446f310846e0ebf68 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Wed, 1 Oct 2025 23:57:42 +0200 Subject: [PATCH 32/35] [js] Test that all global builtins are registered and document those globals that aren't registered, yet. Change-Id: Iea9be27be5f2f7cdeebb1621d98fe3949ab7b7c5 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8635957 Reviewed-by: Michael Achenbach Commit-Queue: Matthias Liedtke --- Tests/FuzzilliTests/EnvironmentTest.swift | 57 +++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/Tests/FuzzilliTests/EnvironmentTest.swift b/Tests/FuzzilliTests/EnvironmentTest.swift index 3bbf52ba9..d8dbcf271 100644 --- a/Tests/FuzzilliTests/EnvironmentTest.swift +++ b/Tests/FuzzilliTests/EnvironmentTest.swift @@ -73,6 +73,63 @@ class EnvironmentTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "") } + /// Test that all interesting properties on the globalThis object are registered as builtins. + func testJSEnvironmentRegisteredBuiltins() throws { + let runner = try GetJavaScriptExecutorOrSkipTest(type: .user, withArguments: []) + let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) + let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) + let b = fuzzer.makeBuilder() + + let globalThis = b.createNamedVariable(forBuiltin: "globalThis") + let object = b.createNamedVariable(forBuiltin: "Object") + let names = b.callMethod("getOwnPropertyNames", on: object, withArgs: [globalThis]) + let namesString = b.callMethod("join", on: names, withArgs: [b.loadString(",")]) + b.callFunction(b.createNamedVariable(forBuiltin: "output"), withArgs: [namesString]) + + let prog = b.finalize() + let jsProg = fuzzer.lifter.lift(prog, withOptions: []) + let jsEnvironment = b.fuzzer.environment + let result = testExecuteScript(program: jsProg, runner: runner) + XCTAssert(result.isSuccess, "\(result.error)\n\(result.output)") + var output = result.output + XCTAssertEqual(output.removeLast(), "\n") + + // Global builtins available in d8 that should not be fuzzed. + let skipped = [ + "fuzzilli", "testRunner", "quit", "load", "read", "readline", "readbuffer", + "writeFile", "write", "print", "printErr", "version", "os", "d8", "arguments", "Realm" + ] + // Global builtins that we probably should register but haven't done so, yet. + let TODO = [ + "globalThis", + "Iterator", + "setTimeout", + "console", + "escape", + "unescape", + "encodeURIComponent", + "encodeURI", + "decodeURIComponent", + "decodeURI", + // https://github.com/tc39/proposal-ecmascript-sharedmem/tree/main + "Atomics", + // https://github.com/tc39/proposal-explicit-resource-management + "DisposableStack", + "AsyncDisposableStack", + // https://github.com/tc39/proposal-float16array + "Float16Array", + // Web APIs + "performance", + "Worker", + ] + let ignore = Set(skipped + TODO) + + for builtin in output.split(separator: ",") where !ignore.contains(String(builtin)) { + XCTAssert(jsEnvironment.builtins.contains(String(builtin)), + "Unregistered builtin \(builtin)") + } + } + func convertTypedArrayToHex(_ b: ProgramBuilder, _ array: Variable) -> Variable { let toHex = b.buildArrowFunction(with: .parameters(n: 1)) { args in let hex = b.callMethod("toString", on: args[0], withArgs: [b.loadInt(16)]) From ba9eb8f373ba96ee46cb24e4e32d1902840ceb87 Mon Sep 17 00:00:00 2001 From: Danylo Mocherniuk Date: Thu, 2 Oct 2025 14:37:00 +0000 Subject: [PATCH 33/35] Upgrade required macOS version to 13. Also upgrade necesary dependencies. Change-Id: I814feab19cf152bef72fe2f71694d4139eaa76f7 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8636576 Reviewed-by: Matthias Liedtke Commit-Queue: Danylo Mocherniuk --- Package.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Package.swift b/Package.swift index e7198b332..216cf67c3 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.7 // // Copyright 2019 Google LLC // @@ -19,7 +19,7 @@ import PackageDescription let package = Package( name: "Fuzzilli", platforms: [ - .macOS(.v11), + .macOS(.v13), ], products: [ .library(name: "Fuzzilli",targets: ["Fuzzilli"]), @@ -61,13 +61,13 @@ let package = Package( .copy("Protobuf/ast.proto"), .copy("Compiler/Parser")]), - .target(name: "REPRLRun", + .executableTarget(name: "REPRLRun", dependencies: ["libreprl"]), - .target(name: "FuzzilliCli", + .executableTarget(name: "FuzzilliCli", dependencies: ["Fuzzilli"]), - .target(name: "FuzzILTool", + .executableTarget(name: "FuzzILTool", dependencies: ["Fuzzilli"]), .testTarget(name: "FuzzilliTests", From 2a585ad841d0b56c36d349f50a4354a270ed8c5e Mon Sep 17 00:00:00 2001 From: "Thomas (Aeshus)" Date: Sun, 5 Oct 2025 00:48:31 -0400 Subject: [PATCH 34/35] Fix missing aditionalEnumerations field --- Sources/FuzzilliCli/Profiles/V8DebugProfile.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/FuzzilliCli/Profiles/V8DebugProfile.swift b/Sources/FuzzilliCli/Profiles/V8DebugProfile.swift index 42cc7a1bc..02b36285f 100644 --- a/Sources/FuzzilliCli/Profiles/V8DebugProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8DebugProfile.swift @@ -95,5 +95,8 @@ let v8DebugProfile = Profile( additionalObjectGroups: [jsD8, jsD8Test, jsD8FastCAPI, gcOptions], + // The other v8 configs have this as well + additionalEnumerations: [.gcTypeEnum, .gcExecutionEnum], + optionalPostProcessor: nil ) From 8d75c179e9134bb2d92ea38709dd723b71dbecf3 Mon Sep 17 00:00:00 2001 From: "Thomas (Aeshus)" Date: Sun, 5 Oct 2025 19:19:10 -0400 Subject: [PATCH 35/35] Test possible fix --- .github/workflows/swift.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index ea0f69ec6..cc7b8617f 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -18,13 +18,19 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 25-nightly - - uses: swift-actions/setup-swift@v2 + + # Is it failing to run tests b/c we're overriding + # what swift binary to use? + - name: Setup Swift for Ubuntu + if: runner.os == 'Linux' + uses: swift-actions/setup-swift@v2 with: swift-version: "6.0.3" - - name: Swift Version - run: swift --version + - uses: actions/checkout@v2 + - name: Build run: swift build -v + - name: Run tests run: swift test -v