From cafa284b70e2f681d1e77d1c0f03e50ce9cf2b59 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Mon, 10 Nov 2025 13:51:41 +0100 Subject: [PATCH 01/89] [v8] Register %GetHoleNaN() and %GetUndefinedNaN() V8 side change: https://crrev.com/c/7137442 Bug: 457866804 Change-Id: Id01597d3194e4c88d38623f646c1671330e63b43 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8753396 Reviewed-by: Michael Achenbach Auto-Submit: Matthias Liedtke Commit-Queue: Michael Achenbach --- Sources/FuzzilliCli/Profiles/V8CommonProfile.swift | 8 ++++++++ Sources/FuzzilliCli/Profiles/V8Profile.swift | 2 ++ Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift | 2 ++ 3 files changed, 12 insertions(+) diff --git a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift index 4c83c2b31..ff5eb1df6 100644 --- a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift @@ -143,6 +143,14 @@ public let PretenureAllocationSiteGenerator = CodeGenerator("PretenureAllocation b.eval("%PretenureAllocationSite(%@)", with: [obj]); } +public let HoleNanGenerator = CodeGenerator("HoleNanGenerator") { b in + b.eval("%GetHoleNaN()", hasOutput: true); +} + +public let UndefinedNanGenerator = CodeGenerator("UndefinedNanGenerator") { b in + b.eval("%GetUndefinedNaN()", hasOutput: true); +} + 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 b239caf55..abc30bdcf 100644 --- a/Sources/FuzzilliCli/Profiles/V8Profile.swift +++ b/Sources/FuzzilliCli/Profiles/V8Profile.swift @@ -66,6 +66,8 @@ let v8Profile = Profile( (WasmArrayGenerator, 15), (SharedObjectGenerator, 5), (PretenureAllocationSiteGenerator, 5), + (HoleNanGenerator, 5), + (UndefinedNanGenerator, 5), ], additionalProgramTemplates: WeightedList([ diff --git a/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift b/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift index 91ff6d8ea..daeb71525 100644 --- a/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift @@ -476,6 +476,8 @@ let v8SandboxProfile = Profile( (WasmArrayGenerator, 5), (SharedObjectGenerator, 5), (PretenureAllocationSiteGenerator, 5), + (HoleNanGenerator, 5), + (UndefinedNanGenerator, 5), ], additionalProgramTemplates: WeightedList([ From 6218da5b87677d333beb66abcd93e7cc7b0f4d91 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Wed, 12 Nov 2025 15:39:46 +0100 Subject: [PATCH 02/89] Refactoring: Use stub-state storage also for functions This replaces the separate logic for lastFunctionVariable with the generic runtimeData approach. This doesn't change behavior. Change-Id: I9cc988879638b423dabc99d4598028caacb6a3de Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8714836 Commit-Queue: Michael Achenbach Reviewed-by: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 28 ---------- Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 55 +++++++++++-------- 2 files changed, 31 insertions(+), 52 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 940b8d4af..7d5e3ed08 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -134,17 +134,6 @@ 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 { @@ -4883,23 +4872,6 @@ 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/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index c31dfab81..ea2ff12cb 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -462,15 +462,16 @@ public let CodeGenerators: [CodeGenerator] = [ let randomParameters = probability(0.5) ? .parameters(n: 0) : b.randomParameters() b.setParameterTypesForNextSubroutine( randomParameters.parameterTypes) - b.emit( - BeginPlainFunction(parameters: randomParameters.parameters, functionName: nil)) + let f = b.emit( + BeginPlainFunction(parameters: randomParameters.parameters, functionName: nil)).output + b.runtimeData.push("functionArgsAccess", f) b.loadArguments() }, GeneratorStub("FunctionWithArgumentsAccessEndGenerator", inContext: .single([.javascript, .subroutine])) { b in // Ideally we would like to return the arguments Variable from above here. b.doReturn(b.randomJsVariable()) - let f = b.lastFunctionVariable b.emit(EndPlainFunction()) + let f = b.runtimeData.pop("functionArgsAccess") let args = b.randomJsVariables(n: Int.random(in: 0...5)) b.callFunction(f, withArgs: args) }, @@ -1200,13 +1201,14 @@ public let CodeGenerators: [CodeGenerator] = [ let randomParameters = b.randomParameters() b.setParameterTypesForNextSubroutine( randomParameters.parameterTypes) - b.emit( - BeginPlainFunction(parameters: randomParameters.parameters, functionName: nil)) + let f = b.emit( + BeginPlainFunction(parameters: randomParameters.parameters, functionName: nil)).output + b.runtimeData.push("plainFunction", f) }, GeneratorStub("PlainFunctionEndGenerator", inContext: .single([.javascript, .subroutine])) { b in b.doReturn(b.randomJsVariable()) - let f = b.lastFunctionVariable b.emit(EndPlainFunction()) + let f = b.runtimeData.pop("plainFunction") let (arguments, matches) = b.randomArguments(forCallingGuardableFunction: f) b.callFunction(f, withArgs: arguments, guard: !matches) }, @@ -1219,14 +1221,15 @@ public let CodeGenerators: [CodeGenerator] = [ let randomParameters = b.randomParameters() b.setParameterTypesForNextSubroutine( randomParameters.parameterTypes) - b.emit( - BeginPlainFunction(parameters: randomParameters.parameters, functionName: nil)) + let f = b.emit( + BeginPlainFunction(parameters: randomParameters.parameters, functionName: nil)).output + b.runtimeData.push("strictFunction", f) b.directive("use strict") }, GeneratorStub("StrictModeFunctionEndGenerator", inContext: .single([.javascript, .subroutine])) { b in b.doReturn(b.randomJsVariable()) - let f = b.lastFunctionVariable b.emit(EndPlainFunction()) + let f = b.runtimeData.pop("strictFunction") let (arguments, matches) = b.randomArguments(forCallingGuardableFunction: f) b.callFunction(f, withArgs: arguments, guard: !matches) }, @@ -1259,8 +1262,9 @@ public let CodeGenerators: [CodeGenerator] = [ let randomParameters = b.randomParameters() b.setParameterTypesForNextSubroutine( randomParameters.parameterTypes) - b.emit( - BeginGeneratorFunction(parameters: randomParameters.parameters, functionName: nil)) + let f = b.emit( + BeginGeneratorFunction(parameters: randomParameters.parameters, functionName: nil)).output + b.runtimeData.push("generatorFunction", f) }, GeneratorStub("GeneratorFunctionEndGenerator", inContext: .single([.generatorFunction, .subroutine, .javascript])) { b in if probability(0.5) { @@ -1272,8 +1276,8 @@ public let CodeGenerators: [CodeGenerator] = [ b.yieldEach(array) } b.doReturn(b.randomJsVariable()) - let f = b.lastFunctionVariable b.emit(EndGeneratorFunction()) + let f = b.runtimeData.pop("generatorFunction") let (arguments, matches) = b.randomArguments(forCallingGuardableFunction: f) b.callFunction(f, withArgs: arguments, guard: !matches) }, @@ -1284,14 +1288,15 @@ public let CodeGenerators: [CodeGenerator] = [ let randomParameters = b.randomParameters() b.setParameterTypesForNextSubroutine( randomParameters.parameterTypes) - b.emit( - BeginAsyncFunction(parameters: randomParameters.parameters, functionName: nil)) + let f = b.emit( + BeginAsyncFunction(parameters: randomParameters.parameters, functionName: nil)).output + b.runtimeData.push("asyncFunction", f) }, GeneratorStub("AsyncFunctionEndGenerator", inContext: .single([.javascript, .subroutine, .asyncFunction])) { b in b.await(b.randomJsVariable()) b.doReturn(b.randomJsVariable()) - let f = b.lastFunctionVariable b.emit(EndAsyncFunction()) + let f = b.runtimeData.pop("asyncFunction") let (arguments, matches) = b.randomArguments(forCallingGuardableFunction: f) b.callFunction(f, withArgs: arguments, guard: !matches) }, @@ -1335,9 +1340,10 @@ public let CodeGenerators: [CodeGenerator] = [ let randomParameters = b.randomParameters() b.setParameterTypesForNextSubroutine( randomParameters.parameterTypes) - b.emit( + let f = b.emit( BeginAsyncGeneratorFunction( - parameters: randomParameters.parameters, functionName: nil)) + parameters: randomParameters.parameters, functionName: nil)).output + b.runtimeData.push("asyncGeneratorFunction", f) }, GeneratorStub("AsyncGeneratorFunctionEndGenerator", inContext: .single([.javascript, .subroutine, .generatorFunction, .asyncFunction])) { b in b.await(b.randomJsVariable()) @@ -1350,8 +1356,8 @@ public let CodeGenerators: [CodeGenerator] = [ b.yieldEach(array) } b.doReturn(b.randomJsVariable()) - let f = b.lastFunctionVariable b.emit(EndAsyncGeneratorFunction()) + let f = b.runtimeData.pop("asyncGeneratorFunction") let (arguments, matches) = b.randomArguments(forCallingGuardableFunction: f) b.callFunction(f, withArgs: arguments, guard: !matches) }, @@ -2519,13 +2525,13 @@ public let CodeGenerators: [CodeGenerator] = [ let randomParameters = b.randomParameters() b.setParameterTypesForNextSubroutine( randomParameters.parameterTypes) - b.emit( - BeginPlainFunction(parameters: randomParameters.parameters, functionName: nil)) - + let handler = b.emit( + BeginPlainFunction(parameters: randomParameters.parameters, functionName: nil)).output + b.runtimeData.push("promiseHandler", handler) }, GeneratorStub("PromiseEndGenerator", inContext: .single([.subroutine, .javascript])) { b in - let handler = b.lastFunctionVariable b.emit(EndPlainFunction()) + let handler = b.runtimeData.pop("promiseHandler") let Promise = b.createNamedVariable(forBuiltin: "Promise") b.hide(Promise) // We want the promise to be used by following code generators, not the Promise constructor b.construct(Promise, withArgs: [handler]) @@ -2579,11 +2585,12 @@ public let CodeGenerators: [CodeGenerator] = [ CodeGenerator("EvalGenerator", [ GeneratorStub("EvalBeginGenerator", provides: [.javascript]) { b in - b.emit(BeginCodeString()) + let code = b.emit(BeginCodeString()).output + b.runtimeData.push("codeToEval", code) }, GeneratorStub("EvalEndGenerator") { b in - let code = b.lastFunctionVariable b.emit(EndCodeString()) + let code = b.runtimeData.pop("codeToEval") let eval = b.createNamedVariable(forBuiltin: "eval") b.callFunction(eval, withArgs: [code]) }, From 0f911af266d45ce1c76d035a708fab272a1178c0 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Tue, 28 Oct 2025 15:59:12 +0100 Subject: [PATCH 03/89] Enable generating disposable class variables Similarly to objects as disposable variables, this enables generating instances of classes as disposable variables, used with both: `using` and `await using`. The generators have the new style and provide a class with a computed method with Symbol.dispose or Symbol.asyncDispose. As a fly-by, this also makes use of `b.runtimeData` to store the symbol of the existing generator for disposable objects. Bug: 446632644 Change-Id: I433ce357e4649230b803361e6fba15ca2cb954e2 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8715016 Reviewed-by: Danylo Mocherniuk Commit-Queue: Michael Achenbach Reviewed-by: Matthias Liedtke --- .../CodeGen/CodeGeneratorWeights.swift | 6 +- Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 99 +++++++++++++++++-- 2 files changed, 93 insertions(+), 12 deletions(-) diff --git a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift index c5e030303..a387c7097 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift @@ -47,8 +47,10 @@ public let codeGeneratorWeights = [ "BuiltinTemporalGenerator": 4, "BuiltinIntlGenerator": 4, "LoadNewTargetGenerator": 3, - "DisposableVariableGenerator": 5, - "AsyncDisposableVariableGenerator": 5, + "DisposableObjVariableGenerator": 3, + "DisposableClassVariableGenerator": 3, + "AsyncDisposableObjVariableGenerator": 3, + "AsyncDisposableClassVariableGenerator": 3, "HexGenerator": 2, "Base64Generator": 2, diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index ea2ff12cb..dfb78c433 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Generator stubs for disposable and async-disposable variables. -func disposableVariableGeneratorStubs( +// Generator stubs for disposable and async-disposable object variables. +func disposableObjVariableGeneratorStubs( inContext contextRequirement : Context, withSymbol symbolProperty : String, genDisposableVariable : @escaping (ProgramBuilder, Variable) -> Void) -> [GeneratorStub] { @@ -24,7 +24,9 @@ func disposableVariableGeneratorStubs( provides: [.objectLiteral] ) { b in // Ensure we have the desired symbol below. - b.createSymbolProperty(symbolProperty) + let symbol = b.createSymbolProperty(symbolProperty) + b.hide(symbol) + b.runtimeData.push("symbol", symbol) b.emit(BeginObjectLiteral()) }, GeneratorStub( @@ -32,9 +34,7 @@ func disposableVariableGeneratorStubs( inContext: .single(.objectLiteral), provides: [.javascript, .subroutine, .method] ) { b in - // It should be safe to assume that we find at least the - // desired symbol we created above. - let symbol = b.randomVariable(forUseAs: .jsSymbol) + let symbol = b.runtimeData.pop("symbol") let parameters = b.randomParameters() b.setParameterTypesForNextSubroutine(parameters.parameterTypes) b.emit( @@ -60,6 +60,69 @@ func disposableVariableGeneratorStubs( ] } +// Generator stubs for disposable and async-disposable class variables. +func disposableClassVariableGeneratorStubs( + inContext contextRequirement : Context, + withSymbol symbolProperty : String, + genDisposableVariable : @escaping (ProgramBuilder, Variable) -> Void) -> [GeneratorStub] { + return [ + GeneratorStub( + "DisposableClassDefinitionBeginGenerator", + inContext: .single(contextRequirement), + provides: [.classDefinition] + ) { b in + // Ensure we have the desired symbol below. + let symbol = b.createSymbolProperty(symbolProperty) + b.hide(symbol) + b.runtimeData.push("symbol", symbol) + + // Possibly pick a superclass. + // The superclass must be a constructor (or null). + var superclass: Variable? = nil + if probability(0.5) && b.hasVisibleVariables { + superclass = b.randomVariable(ofType: .constructor()) + } + let inputs = superclass != nil ? [superclass!] : [] + let cls = b.emit(BeginClassDefinition( + hasSuperclass: superclass != nil, + isExpression: probability(0.3)), + withInputs: inputs).output + b.runtimeData.push("class", cls) + }, + GeneratorStub( + "DisposableClassInstanceComputedMethodBeginGenerator", + inContext: .single(.classDefinition), + provides: [.javascript, .subroutine, .method, .classMethod] + ) { b in + let symbol = b.runtimeData.pop("symbol") + let parameters = b.randomParameters() + b.setParameterTypesForNextSubroutine(parameters.parameterTypes) + b.emit( + BeginClassInstanceComputedMethod( + parameters: parameters.parameters), + withInputs: [symbol]) + }, + GeneratorStub( + "DisposableClassInstanceComputedMethodEndGenerator", + inContext: .single([.javascript, .subroutine, .method, .classMethod]), + provides: [.classDefinition] + ) { b in + b.maybeReturnRandomJsVariable(0.9) + b.emit(EndClassInstanceComputedMethod()) + }, + GeneratorStub( + "DisposableClassDefinitionEndGenerator", + inContext: .single(.classDefinition) + ) { b in + b.emit(EndClassDefinition()) + let cls = b.runtimeData.pop("class") + let disposableVariable = b.construct( + cls, withArgs: b.randomArguments(forCalling: cls)) + genDisposableVariable(b, disposableVariable) + }, + ] +} + // // Code generators. // @@ -478,16 +541,32 @@ public let CodeGenerators: [CodeGenerator] = [ ]), CodeGenerator( - "DisposableVariableGenerator", - disposableVariableGeneratorStubs( + "DisposableObjVariableGenerator", + disposableObjVariableGeneratorStubs( + inContext: .subroutine, + withSymbol: "dispose") { b, variable in + b.loadDisposableVariable(variable) + }), + + CodeGenerator( + "AsyncDisposableObjVariableGenerator", + disposableObjVariableGeneratorStubs( + inContext: .asyncFunction, + withSymbol: "asyncDispose") { b, variable in + b.loadAsyncDisposableVariable(variable) + }), + + CodeGenerator( + "DisposableClassVariableGenerator", + disposableClassVariableGeneratorStubs( inContext: .subroutine, withSymbol: "dispose") { b, variable in b.loadDisposableVariable(variable) }), CodeGenerator( - "AsyncDisposableVariableGenerator", - disposableVariableGeneratorStubs( + "AsyncDisposableClassVariableGenerator", + disposableClassVariableGeneratorStubs( inContext: .asyncFunction, withSymbol: "asyncDispose") { b, variable in b.loadAsyncDisposableVariable(variable) From 07403150c305ea014adbc8865cf6792ba4dd3a5a Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Tue, 11 Nov 2025 12:44:01 +0100 Subject: [PATCH 04/89] [v8] Add code generator for various string shapes V8-side change: https://crrev.com/c/7137292 Bug: 455552707 Change-Id: Ifd5f44b69ef62f18ecfa03525e988bd2f43253cc Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8756377 Commit-Queue: Matthias Liedtke Reviewed-by: Victor Gomes --- .../Profiles/V8CommonProfile.swift | 27 +++++++++++++++++++ Sources/FuzzilliCli/Profiles/V8Profile.swift | 1 + .../Profiles/V8SandboxProfile.swift | 1 + 3 files changed, 29 insertions(+) diff --git a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift index ff5eb1df6..3018dd6ba 100644 --- a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift @@ -151,6 +151,33 @@ public let UndefinedNanGenerator = CodeGenerator("UndefinedNanGenerator") { b in b.eval("%GetUndefinedNaN()", hasOutput: true); } +public let StringShapeGenerator = CodeGenerator("StringShapeGenerator") { b in + withEqualProbability( + { + // Use one random input string (can be 1-byte or 2-byte etc.) and a predefined second + // string so that the string is guaranteed to be long enough for emitting a cons string. + let lhs = b.randomVariable(forUseAs: .jsString) + let rhs = b.loadString("ConsStringConcatenation") + b.hide(rhs) + b.eval("%ConstructConsString(%@, %@)", with: [lhs, rhs], hasOutput: true) + }, { + let str = b.loadString( + Bool.random() ? "Long enough 1-byte string" : "Long enoµgh 2-byte string") + b.hide(str) + let offset = b.loadInt(Int64.random(in: 1...5)) + b.eval("%ConstructSlicedString(%@, %@)", with: [str, offset], hasOutput: true) + }, { + let str = b.randomVariable(forUseAs: .jsString) + b.eval("%ConstructInternalizedString(%@)", with: [str], hasOutput: true) + }, { + let str = b.loadString( + Bool.random() ? "Long enough 1-byte string" : "Long enoµgh 2-byte string") + b.hide(str) + b.eval("%ConstructThinString(%@)", with: [str], hasOutput: true) + } + ) +} + 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 abc30bdcf..98344b37a 100644 --- a/Sources/FuzzilliCli/Profiles/V8Profile.swift +++ b/Sources/FuzzilliCli/Profiles/V8Profile.swift @@ -68,6 +68,7 @@ let v8Profile = Profile( (PretenureAllocationSiteGenerator, 5), (HoleNanGenerator, 5), (UndefinedNanGenerator, 5), + (StringShapeGenerator, 5), ], additionalProgramTemplates: WeightedList([ diff --git a/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift b/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift index daeb71525..1e96c7762 100644 --- a/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift @@ -478,6 +478,7 @@ let v8SandboxProfile = Profile( (PretenureAllocationSiteGenerator, 5), (HoleNanGenerator, 5), (UndefinedNanGenerator, 5), + (StringShapeGenerator, 5), ], additionalProgramTemplates: WeightedList([ From 87123d56c4309c851b485977fe28b41e40158094 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Tue, 11 Nov 2025 13:54:49 +0100 Subject: [PATCH 05/89] [js] Add code generator for concatenated strings Many (all?) JS engines have optimizations for string concatenations. To make it more likely having such concatenated strings (ConsString in V8), add a code generator for string concatenation. Fixed: 455552707 Change-Id: I0a9bf66a5f721d38f34327f7acd8c5344086cf10 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8756756 Commit-Queue: Matthias Liedtke Reviewed-by: Dominik Klemba Reviewed-by: Victor Gomes --- .../Fuzzilli/CodeGen/CodeGeneratorWeights.swift | 1 + Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift index a387c7097..c6889a0e8 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift @@ -94,6 +94,7 @@ public let codeGeneratorWeights = [ "ObjectWithSpreadGenerator": 2, "ArrayWithSpreadGenerator": 2, "TemplateStringGenerator": 1, + "ConcatenatedStringGenerator": 2, "StringNormalizeGenerator": 1, "PlainFunctionGenerator": 15, "StrictModeFunctionGenerator": 3, diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index dfb78c433..13a20b613 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -161,6 +161,22 @@ public let CodeGenerators: [CodeGenerator] = [ b.loadString(b.randomString()) }, + CodeGenerator("ConcatenatedStringGenerator", inputs: .required(.string), produces: [.string]) { b, inputString in + // Emit a dynamically concatenated string, e.g. something like: + // let select = someVar ? "string a" : "string b"; + // let result = select + "other string"; + let selectLhs = inputString + let selectRhs = b.randomVariable(ofType: .string)! + let cond = b.randomJsVariable() + let select = b.ternary(cond, selectLhs, selectRhs) + let other = b.randomVariable(ofType: .string)! + if Bool.random() { + b.binary(select, other, with: .Add) + } else { + b.binary(other, select, with: .Add) + } + }, + CodeGenerator("BooleanGenerator", produces: [.boolean]) { b in // It's probably not too useful to generate multiple boolean values here. b.loadBool(Bool.random()) From fbfe35c47281bafbd8c1ec2a9bfd35f3d6140b65 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Mon, 10 Nov 2025 14:35:57 +0100 Subject: [PATCH 06/89] [wasm] Add code generators for wasm-gc signatures So far this will only fuzz the definition of these signatures as there aren't any operations registered which would make use of these definitions, yet. Bug: 445356784 Change-Id: I1c6b99e863bf359e4c505605d2d7f64533553f19 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8753596 Reviewed-by: Pawel Krawczyk Commit-Queue: Matthias Liedtke --- .../CodeGen/CodeGeneratorWeights.swift | 1 + .../Fuzzilli/CodeGen/WasmCodeGenerators.swift | 32 +++++++++++++++++++ Sources/Fuzzilli/FuzzIL/JSTyper.swift | 3 +- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift index c6889a0e8..280a13780 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift @@ -347,6 +347,7 @@ public let codeGeneratorWeights = [ "WasmTypeGroupGenerator": 5, "WasmArrayTypeGenerator": 5, "WasmStructTypeGenerator": 5, + "WasmSignatureTypeGenerator": 5, "WasmSelfReferenceGenerator": 5, "WasmForwardReferenceGenerator": 5, diff --git a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift index f63a168e5..7ea893b47 100644 --- a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift @@ -118,6 +118,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ elementType: .wasmRef(.Index(), nullability: nullability), mutability: mutability, indexType: elementType) } else { + // TODO(mliedtke): Extend list with abstract heap types. b.wasmDefineArrayType( elementType: chooseUniform(from: [ .wasmPackedI8, .wasmPackedI16, .wasmi32, .wasmi64, .wasmf32, .wasmf64, .wasmSimd128, @@ -138,6 +139,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ indexTypes.append(elementType) type = .wasmRef(.Index(), nullability: nullability) } else { + // TODO(mliedtke): Extend list with abstract heap types. type = chooseUniform(from: [ .wasmPackedI8, .wasmPackedI16, .wasmi32, .wasmi64, .wasmf32, .wasmf64, .wasmSimd128, ]) @@ -149,6 +151,36 @@ public let WasmCodeGenerators: [CodeGenerator] = [ b.wasmDefineStructType(fields: fields, indexTypes: indexTypes) }, + CodeGenerator( + "WasmSignatureTypeGenerator", + inContext: .single(.wasmTypeGroup), + produces: [.wasmTypeDef()] + ) { b in + let typeCount = Int.random(in: 0...10) + let returnCount = Int.random(in: 0...typeCount) + let parameterCount = typeCount - returnCount + + var indexTypes: [Variable] = [] + let chooseType = { + var type: ILType + if let elementType = b.randomVariable(ofType: .wasmTypeDef()), + probability(0.25) + { + let nullability = + b.type(of: elementType).wasmTypeDefinition!.description + == .selfReference || probability(0.5) + indexTypes.append(elementType) + return ILType.wasmRef(.Index(), nullability: nullability) + } else { + // TODO(mliedtke): Extend list with abstract heap types. + return chooseUniform(from: [.wasmi32, .wasmi64, .wasmf32, .wasmf64, .wasmSimd128]) + } + } + let signature = (0.. (0.. desc.signature.outputTypes }) } else { From 363ed2b23a59e695e34a860d8277d0776f182f9f Mon Sep 17 00:00:00 2001 From: Pawel Krawczyk Date: Tue, 18 Nov 2025 10:31:47 +0000 Subject: [PATCH 07/89] Remove unused ValueGeneratorFunc Change-Id: Ib70851f9cd9d11f39501815280d6ea641c6df40e Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8764020 Reviewed-by: Carl Smith Commit-Queue: Carl Smith Auto-Submit: Pawel Krawczyk --- Sources/Fuzzilli/CodeGen/CodeGenerator.swift | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerator.swift b/Sources/Fuzzilli/CodeGen/CodeGenerator.swift index 07be36f35..a3409b41b 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerator.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerator.swift @@ -17,16 +17,6 @@ protocol GeneratorAdapter { func run(in b: ProgramBuilder, with inputs: [Variable]) } -public typealias ValueGeneratorFunc = (ProgramBuilder, Int) -> () -fileprivate struct ValueGeneratorAdapter: GeneratorAdapter { - let expectedNumberOfInputs = 0 - let f: ValueGeneratorFunc - func run(in b: ProgramBuilder, with inputs: [Variable]) { - assert(inputs.isEmpty) - f(b, GeneratorStub.numberOfValuesToGenerateByValueGenerators) - } -} - public typealias GeneratorFuncNoArgs = (ProgramBuilder) -> () fileprivate struct GeneratorAdapterNoArgs: GeneratorAdapter { let expectedNumberOfInputs = 0 From 2e3e639b1db60851ad678d6f7361e3fe33386899 Mon Sep 17 00:00:00 2001 From: Darius Mercadier Date: Thu, 20 Nov 2025 12:33:39 +0100 Subject: [PATCH 08/89] [turboshaft] Fuzz --turboshaft-verify-load-store-taggedness Bug: 458429784 Change-Id: If21b4e7bd0670939f0413c11e8d6c8ef1b5e5823 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8783156 Reviewed-by: Michael Achenbach Commit-Queue: Michael Achenbach Auto-Submit: Darius Mercadier --- Sources/FuzzilliCli/Profiles/V8CommonProfile.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift index 3018dd6ba..12b466c82 100644 --- a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift @@ -878,6 +878,9 @@ public func v8ProcessArgs(randomize: Bool, forSandbox: Bool) -> [String] { if probability(0.1) { args.append("--turboshaft-verify-reductions") } + if probability(0.2) { + args.append("--turboshaft-verify-load-store-taggedness") + } } if probability(0.1) { From ec80efade6fb320637d090e0150782d6010caf41 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Wed, 12 Nov 2025 14:58:53 +0100 Subject: [PATCH 09/89] Extend new code generation logic to allow specifying more than just a type for input requirements and output guarantees. Bug: 445356784 Change-Id: Ib1319c8e42e33688c7c0921b166e46e50b031748 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8760696 Commit-Queue: Matthias Liedtke Reviewed-by: Carl Smith --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 70 ++++++------ Sources/Fuzzilli/CodeGen/CodeGenerator.swift | 106 +++++++++++++++--- .../Fuzzilli/CodeGen/WasmCodeGenerators.swift | 26 +++-- Sources/Fuzzilli/FuzzIL/TypeSystem.swift | 2 +- Tests/FuzzilliTests/ProgramBuilderTest.swift | 59 +++++++++- 5 files changed, 196 insertions(+), 67 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 7d5e3ed08..17f111300 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -770,8 +770,8 @@ public class ProgramBuilder { (.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()) + gen.produces.contains { produces in + produces.type.Is(.wasmTypeDef()) } } let _ = self.complete(generator: generators.randomElement(), withBudget: 5) @@ -862,8 +862,8 @@ public class ProgramBuilder { // 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) + $0.parts.last!.produces.contains(where: { produces in + produces.type.Is(type) }) }) if generators.count > 0 { @@ -1798,14 +1798,14 @@ public class ProgramBuilder { struct BuildAction { var name: String var outcome = ActionOutcome.started - var produces: [ILType] + var produces: [GeneratorStub.Constraint] } var pendingActions: Stack = Stack() var actions = [(BuildAction, Int)]() var indent = 0 - mutating func startAction(_ actionName: String, produces: [ILType]) { + mutating func startAction(_ actionName: String, produces: [GeneratorStub.Constraint]) { let action = BuildAction(name: actionName, produces: produces) // Mark this action as `.started`. actions.append((action, indent)) @@ -1820,16 +1820,14 @@ public class ProgramBuilder { 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) - }) { + // Now Check that we have variables that match the the specified productions. + for requirement in finishedAction.produces { + if !newlyCreatedVariableTypes.contains(where: requirement.fulfilled) { 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)" + fatalErrorString += "newlyCreatedVariableTypes: \(newlyCreatedVariableTypes) does not contain anything matching \(requirement)" fatalError(fatalErrorString) } } @@ -2113,31 +2111,29 @@ public class ProgramBuilder { var numberOfGeneratedInstructions = 0 // Calculate all input requirements of this CodeGenerator. - let inputTypes = Set(generator.parts.reduce([]) { res, gen in - return res + gen.inputs.types + let inputRequirements = Set(generator.parts.reduce([]) { res, gen in + return res + gen.inputs.constraints }) - var availableTypes = inputTypes.filter { - randomVariable(ofType: $0) != nil + var fulfilledRequirements = inputRequirements.filter { requirement in + findVariable {requirement.fulfilled(by: self.type(of: $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) } + let contextsAndRequirements = generator.parts.map { ($0.providedContext, $0.inputs.constraints) } // Check if the can be produced along this generator, otherwise we need to bail. - for (contexts, types) in contextsAndTypes { + for (contexts, requirements) in contextsAndRequirements { // We've seen the current context. for context in contexts { seenContexts.append(context) } - for type in types { + for requirement in requirements { // 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) - }) { + if !fulfilledRequirements.contains(where: requirement.fulfilled) { // 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]}] } @@ -2151,17 +2147,17 @@ public class ProgramBuilder { // 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) }) + generator.produces.contains(where: { requirement.fulfilled(by: $0) }) }) // 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).") + buildLog?.reportFailure(reason: "Cannot produce type \(requirement) starting in original context \(context).") return 0 } else { // Mark the type as available. - availableTypes.insert(type) + fulfilledRequirements.insert(requirement) } } } @@ -2169,7 +2165,7 @@ public class ProgramBuilder { // 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) + createRequiredInputVariables(for: inputRequirements) // Push the remaining stubs, we need to call them to close all Contexts properly. for part in generator.tail.reversed() { @@ -2191,7 +2187,7 @@ public class ProgramBuilder { while scheduled.count > depth { let codeSizePre = code.count // Check if we need to or can create types here. - createRequiredInputVariables(forTypes: inputTypes) + createRequiredInputVariables(for: inputRequirements) // Build into the block. buildRecursive(n: budgetPerYieldPoint) // Call the next scheduled stub. @@ -2215,8 +2211,9 @@ public class ProgramBuilder { } // Todo, the context graph could also find ideal paths that allow type creation. - private func createRequiredInputVariables(forTypes types: Set) { - for type in types { + private func createRequiredInputVariables(for requirements: Set) { + for requirement in requirements { + let type = requirement.type if type.Is(.jsAnything) && context.contains(.javascript) { let _ = findOrGenerateType(type) } else { @@ -2224,13 +2221,12 @@ public class ProgramBuilder { // Check if we can produce it with findOrGenerateWasmVar let _ = currentWasmFunction.generateRandomWasmVar(ofType: type) } - if randomVariable(ofType: type) == nil { + if (findVariable {requirement.fulfilled(by: self.type(of: $0))} == 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) - } + $0.produces.contains(where: requirement.fulfilled) } // Cannot build type here. @@ -2344,18 +2340,18 @@ public class ProgramBuilder { switch generator.inputs.mode { case .loose: // Find inputs that are probably compatible with the desired input types using randomVariable(forUseAs:) - inputs = generator.inputs.types.map(randomVariable(forUseAs:)) + inputs = generator.inputs.constraints.map {randomVariable(forUseAs: $0.type)} case .strict: // Find inputs of the required type using randomVariable(ofType:) - for inputType in generator.inputs.types { - guard let input = randomVariable(ofType: inputType) else { + for requirement in generator.inputs.constraints { + guard let input = (findVariable {requirement.fulfilled(by: self.type(of: $0))}) 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).") + buildLog?.reportFailure(reason: "Cannot find variable that satifies input constraints \(requirement.type).") return 0 } inputs.append(input) diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerator.swift b/Sources/Fuzzilli/CodeGen/CodeGenerator.swift index a3409b41b..78bef10fb 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerator.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerator.swift @@ -78,6 +78,53 @@ public class GeneratorStub: Contributor { /// How many different values of the same type ValueGenerators should aim to generate. public static let numberOfValuesToGenerateByValueGenerators = 3 + public enum AdditionalConstraints { + case None + case IsWasmArray + case IsWasmStruct + } + + public struct Constraint : Hashable { + let type: ILType + let additional: AdditionalConstraints + + public init(_ type: ILType, _ additional: AdditionalConstraints = .None) { + self.type = type + self.additional = additional + } + + public func fulfilled(by type: ILType) -> Bool { + if !type.Is(self.type) { + return false + } + return switch additional { + case .None: + true + case .IsWasmArray: + if type.Is(.wasmTypeDef()) { + type.wasmTypeDefinition?.description is WasmArrayTypeDescription + } else if type.Is(.anyNonNullableIndexRef) { + type.Is(.wasmArrayRef) + } else { + false + } + case .IsWasmStruct: + if type.Is(.wasmTypeDef()) { + type.wasmTypeDefinition?.description is WasmStructTypeDescription + } else if type.Is(.anyNonNullableIndexRef) { + type.Is(.wasmStructRef) + } else { + false + } + } + } + + public func fulfilled(by other: Constraint) -> Bool { + other.type.Is(self.type) + && (self.additional == .None || self.additional == other.additional) + } + } + /// Describes the inputs expected by a CodeGenerator. public struct Inputs { /// How the inputs should be treated. @@ -92,48 +139,51 @@ public class GeneratorStub: Contributor { case strict } - public let types: [ILType] + public let constraints: [Constraint] public let mode: Mode public var count: Int { - return types.count + return constraints.count } public var isEmpty: Bool { - types.count == 0 + constraints.count == 0 } // No inputs. public static var none: Inputs { - return Inputs(types: [], mode: .loose) + return Inputs(constraints: [], mode: .loose) } // One input of type .jsAnything public static var one: Inputs { - return Inputs(types: [.jsAnything], mode: .loose) + return Inputs(constraints: [.init(.jsAnything)], mode: .loose) } // One input of a wasmPrimitive and it has to be strict to ensure type correctness. public static var oneWasmPrimitive: Inputs { - return Inputs(types: [.wasmPrimitive], mode: .strict) + return Inputs(constraints: [.init(.wasmPrimitive)], mode: .strict) } public static var oneWasmNumericalPrimitive: Inputs { - return Inputs(types: [.wasmNumericalPrimitive], mode: .strict) + return Inputs(constraints: [.init(.wasmNumericalPrimitive)], mode: .strict) } // Two inputs of type .jsAnything public static var two: Inputs { - return Inputs(types: [.jsAnything, .jsAnything], mode: .loose) + let jsAny = Constraint(.jsAnything) + return Inputs(constraints: [jsAny, jsAny], mode: .loose) } // Three inputs of type .jsAnything public static var three: Inputs { - return Inputs(types: [.jsAnything, .jsAnything, .jsAnything], mode: .loose) + let jsAny = Constraint(.jsAnything) + return Inputs(constraints: [jsAny, jsAny, jsAny], mode: .loose) } // Four inputs of type .jsAnything public static var four: Inputs { - return Inputs(types: [.jsAnything, .jsAnything, .jsAnything, .jsAnything], mode: .loose) + let jsAny = Constraint(.jsAnything) + return Inputs(constraints: [jsAny, jsAny, jsAny, jsAny], mode: .loose) } @@ -142,14 +192,19 @@ public class GeneratorStub: Contributor { // be used as inputs during code generation. public static func preferred(_ types: ILType...) -> Inputs { assert(!types.isEmpty) - return Inputs(types: types, mode: .loose) + return Inputs(constraints: types.map {Constraint($0)}, mode: .loose) } // A number of inputs that must have the specified type. // Only use this if the code generator cannot do anything meaningful if it receives a value of the wrong type. public static func required(_ types: ILType...) -> Inputs { assert(!types.isEmpty) - return Inputs(types: types, mode: .strict) + return Inputs(constraints: types.map {Constraint($0)}, mode: .strict) + } + + public static func requiredComplex(_ constraints: Constraint...) -> Inputs { + assert(!constraints.isEmpty) + return Inputs(constraints: constraints, mode: .strict) } } /// The inputs expected by this generator. @@ -157,7 +212,7 @@ public class GeneratorStub: Contributor { /// The types this CodeGenerator produces /// ProgramBuilding will assert that these types are (newly) available after running this CodeGenerator. - public let produces: [ILType] + public let produces: [Constraint] public enum ContextRequirement { // If this GeneratorStub has a single Context requirement, which may still be comprised of multiple Context values. @@ -225,13 +280,13 @@ public class GeneratorStub: Contributor { 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] + /// 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. + /// Wrapper around the actual generator function called. private let adapter: GeneratorAdapter - fileprivate init(name: String, inputs: Inputs, produces: [ILType] = [], context: ContextRequirement, providedContext: [Context] = [], adapter: GeneratorAdapter) { + fileprivate init(name: String, inputs: Inputs, produces: [Constraint] = [], context: ContextRequirement, providedContext: [Context] = [], adapter: GeneratorAdapter) { self.inputs = inputs self.produces = produces @@ -243,6 +298,10 @@ public class GeneratorStub: Contributor { assert(inputs.count == adapter.expectedNumberOfInputs) } + fileprivate convenience init(name: String, inputs: Inputs, produces: [ILType] = [], context: ContextRequirement, providedContext: [Context] = [], adapter: GeneratorAdapter) { + self.init(name: name, inputs: inputs, produces: produces.map {Constraint($0)}, context: context, providedContext: providedContext, adapter: adapter) + } + /// Execute this code generator, generating new code at the current position in the ProgramBuilder. /// Returns the number of generated instructions. public func run(in b: ProgramBuilder, with inputs: [Variable]) -> Int { @@ -259,6 +318,10 @@ public class GeneratorStub: Contributor { self.init(name: name, inputs: .none, produces: produces, context: context, providedContext: provides, adapter: GeneratorAdapterNoArgs(f: f)) } + public convenience init(_ name: String, inContext context: ContextRequirement = .single(.javascript), producesComplex: [Constraint], provides: [Context] = [], _ f: @escaping GeneratorFuncNoArgs) { + self.init(name: name, inputs: .none, produces: producesComplex, context: context, providedContext: provides, adapter: GeneratorAdapterNoArgs(f: f)) + } + 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, inputs: inputs, produces: produces, context: context, providedContext: provides, adapter: GeneratorAdapter1Arg(f: f)) @@ -320,7 +383,7 @@ public class CodeGenerator { // 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] { + public var produces: [GeneratorStub.Constraint] { return self.parts.last!.produces } @@ -345,11 +408,20 @@ public class CodeGenerator { 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), producesComplex: [GeneratorStub.Constraint], provides: [Context] = [], _ f: @escaping GeneratorFuncNoArgs) { + self.init(name, [GeneratorStub(name: name, inputs: .none, produces: producesComplex, 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, producesComplex: [GeneratorStub.Constraint], provides: [Context] = [], _ f: @escaping GeneratorFunc1Arg) { + assert(inputs.count == 1) + self.init(name, [GeneratorStub(name: name, inputs: inputs, produces: producesComplex, 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))]) diff --git a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift index 7ea893b47..c65d55a84 100644 --- a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift @@ -100,11 +100,10 @@ public let WasmCodeGenerators: [CodeGenerator] = [ }, ]), - // TODO: refine this `produces` annotation? CodeGenerator( "WasmArrayTypeGenerator", inContext: .single(.wasmTypeGroup), - produces: [.wasmTypeDef()] + producesComplex: [.init(.wasmTypeDef(), .IsWasmArray)] ) { b in let mutability = probability(0.75) if let elementType = b.randomVariable(ofType: .wasmTypeDef()), @@ -126,6 +125,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ } }, + // TODO(mliedtke): Refine this `produces` annotation. CodeGenerator("WasmStructTypeGenerator", inContext: .single(.wasmTypeGroup), produces: [.wasmTypeDef()]) { b in var indexTypes: [Variable] = [] let fields = (0.. { diff --git a/Tests/FuzzilliTests/ProgramBuilderTest.swift b/Tests/FuzzilliTests/ProgramBuilderTest.swift index 2932757cb..fc05859e3 100644 --- a/Tests/FuzzilliTests/ProgramBuilderTest.swift +++ b/Tests/FuzzilliTests/ProgramBuilderTest.swift @@ -2982,12 +2982,18 @@ class ProgramBuilderTests: XCTestCase { // XCTAssertGreaterThan(numGeneratedInstructions, 30) } - func testWasmCallDirectGeneratorSchedulingTest() { + func testWasmStructNewDefaultGeneratorSchedulingTest() { let fuzzer = makeMockFuzzer() let b = fuzzer.makeBuilder() b.buildPrefix() - // Pick the Branch Generator. + // TODO(mliedtke): The mechanism needs to learn how to resolve nested input dependencies. + b.wasmDefineTypeGroup {[ + b.wasmDefineArrayType(elementType: .wasmi32, mutability: true), + b.wasmDefineStructType(fields: [.init(type: .wasmi32, mutability: true)], indexTypes: []), + ]} + + // Pick the generator. let generator = fuzzer.codeGenerators.filter { $0.name == "WasmStructNewDefaultGenerator" }[0] @@ -2998,11 +3004,18 @@ class ProgramBuilderTests: XCTestCase { let numGeneratedInstructions = b.complete(generator: syntheticGenerator!, withBudget: 30) XCTAssertGreaterThan(numGeneratedInstructions, 0) + XCTAssertTrue(b.finalize().code.contains(where: { instr in + if case .wasmStructNewDefault(_) = instr.op.opcode { + return true + } else { + return false + } + })) } func testWasmMemorySizeSchedulingTest() { let fuzzer = makeMockFuzzer() - let numPrograms = 100 + let numPrograms = 30 for _ in 0...numPrograms { let b = fuzzer.makeBuilder() @@ -3033,6 +3046,46 @@ class ProgramBuilderTests: XCTestCase { } } + func testArrayGetSchedulingTest() { + let fuzzer = makeMockFuzzer() + let numPrograms = 30 + + for _ in 0.. Date: Thu, 20 Nov 2025 15:48:14 +0100 Subject: [PATCH 10/89] [v8] Add ProgramTemplate for --proto-assign-seq-opt pattern Bug: 429332174 Change-Id: Ic644ce211f96e1bd2c3044bc14fa12ee4410fa24 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8783696 Commit-Queue: Matthias Liedtke Reviewed-by: Dominik Klemba --- .../Profiles/V8CommonProfile.swift | 46 +++++++++++++++++++ Sources/FuzzilliCli/Profiles/V8Profile.swift | 23 +++++----- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift index 12b466c82..6a6ca3916 100644 --- a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift @@ -686,6 +686,52 @@ public let FastApiCallFuzzer = ProgramTemplate("FastApiCallFuzzer") { b in b.build(n: 10) } +public let ProtoAssignSeqOptFuzzer = ProgramTemplate("ProtoAssignSeqOptFuzzer") { b in + b.buildPrefix() + + let containingFct = b.buildPlainFunction(with: b.randomParameters()) { args in + // The function to install the prototypes on. + let params = b.randomParameters() + let body = {(args: [Variable]) in + b.build(n: 20) + b.doReturn(b.randomVariable(forUseAs: .object())) + } + let fct = withEqualProbability( + {b.buildPlainFunction(with: params, body)}, + {b.buildArrowFunction(with: params, body)}, + {b.buildGeneratorFunction(with: params, body)}, // not a valid constructor + {b.buildAsyncFunction(with: params, body)}, // not a valid constructor + {b.buildConstructor(with: params, body)}, + {b.buildClassDefinition(withSuperclass: b.randomVariable(forUseAs: .object())) { _ in + b.build(n: 30) + }} + ) + // Explicitly expose the prototype property to make modifications of it more likely. + b.getProperty("prototype", of: fct) + // Allow further modifications on the function. + b.build(n: 10) + // Perform the prototype assignments. + for _ in 0..([ - (MapTransitionFuzzer, 1), - (ValueSerializerFuzzer, 1), - (V8RegExpFuzzer, 1), - (WasmFastCallFuzzer, 1), - (FastApiCallFuzzer, 1), - (LazyDeoptFuzzer, 1), - (WasmDeoptFuzzer, 1), - (WasmTurbofanFuzzer, 1), + (MapTransitionFuzzer, 1), + (ValueSerializerFuzzer, 1), + (V8RegExpFuzzer, 1), + (WasmFastCallFuzzer, 1), + (FastApiCallFuzzer, 1), + (LazyDeoptFuzzer, 1), + (WasmDeoptFuzzer, 1), + (WasmTurbofanFuzzer, 1), + (ProtoAssignSeqOptFuzzer, 1), ]), disabledCodeGenerators: [], @@ -87,9 +88,9 @@ let v8Profile = Profile( disabledMutators: [], additionalBuiltins: [ - "gc" : .function([.opt(gcOptions.instanceType)] => (.undefined | .jsPromise)), - "d8" : .jsD8, - "Worker" : .constructor([.jsAnything, .object()] => .object(withMethods: ["postMessage","getMessage"])), + "gc" : .function([.opt(gcOptions.instanceType)] => (.undefined | .jsPromise)), + "d8" : .jsD8, + "Worker": .constructor([.jsAnything, .object()] => .object(withMethods: ["postMessage","getMessage"])), ], additionalObjectGroups: [jsD8, jsD8Test, jsD8FastCAPI, gcOptions], From 9ccd9ea0757c3d61e23318a9bb65cbc059351e4b Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Mon, 24 Nov 2025 11:00:42 +0100 Subject: [PATCH 11/89] [v8] Add flag for stress-testing prototype assignment optimization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit V8-side-change: https://crrev.com/c/7178541 Right now this probably doesn't change much as the ProgramTemplate from commit 9e2e2a359467d59790a194a34b821da05b684c2d uses multiple assignments and other instructions will never emit the correct bytecode due to how expression inlining is implemented for assignments right now. Still, it doesn't hurt to add this flag to Fuzzilli as well. Bug: 429332174 Change-Id: I7a4318ba434d701c530fef72a31bce1497f51529 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8792496 Auto-Submit: Matthias Liedtke Commit-Queue: Raphaël Hérouart Reviewed-by: Raphaël Hérouart --- Sources/FuzzilliCli/Profiles/V8CommonProfile.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift index 6a6ca3916..5a7c329bf 100644 --- a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift @@ -897,6 +897,9 @@ public func v8ProcessArgs(randomize: Bool, forSandbox: Bool) -> [String] { if probability(0.5) { args.append("--proto-assign-seq-opt") + if probability(0.5) { + args.append("--proto-assign-seq-opt-count=1") + } } // From 9b7e15395193f9d0e97663b46cd9319739e69be8 Mon Sep 17 00:00:00 2001 From: Pawel Krawczyk Date: Mon, 24 Nov 2025 13:43:11 +0000 Subject: [PATCH 12/89] [cleanup] Improve readability, remove dead code, fix comments. Change-Id: Ib196ad69f5a3a09620b82da5e60694777a024aef Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8783856 Reviewed-by: Dominik Klemba Commit-Queue: Pawel Krawczyk Reviewed-by: Matthias Liedtke Auto-Submit: Pawel Krawczyk --- Sources/Fuzzilli/Base/ContextGraph.swift | 50 +++++----- Sources/Fuzzilli/Base/ProgramBuilder.swift | 95 ++++++++----------- Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 21 +--- .../Environment/JavaScriptEnvironment.swift | 1 + Tests/FuzzilliTests/ProgramBuilderTest.swift | 2 - 5 files changed, 76 insertions(+), 93 deletions(-) diff --git a/Sources/Fuzzilli/Base/ContextGraph.swift b/Sources/Fuzzilli/Base/ContextGraph.swift index 38d6992dd..153f76541 100644 --- a/Sources/Fuzzilli/Base/ContextGraph.swift +++ b/Sources/Fuzzilli/Base/ContextGraph.swift @@ -16,7 +16,7 @@ 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. + // Another invariant is that each Generator will keep the original context, i.e. it will return to the `from` context. struct EdgeKey: Hashable { let from: Context let to: Context @@ -55,19 +55,24 @@ public class ContextGraph { 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() + assertBasicConsistency(in: generators) + warnOfSuspiciousContexts(in: generators, withLogger: logger) + + // One can still try to build in a context that doesn't have generators, this will be caught in the build function, if we fail to find any suitable generator. + // Otherwise we could assert here that the sets are equal. + // One example is a GeneratorStub that opens a context, then calls build manually without it being split into two stubs where we have a yield point to assemble a synthetic generator. + self.edges = [:] for generator in generators { - generator.providedContexts.forEach { ctx in - providedContexts.insert(ctx) + for providableContext in generator.providedContexts { + let edge = EdgeKey(from: generator.requiredContext, to: providableContext) + self.edges[edge, default: GeneratorEdge()].addGenerator(generator) } - - 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. + // Check that every part that provides something is used by the next part of the Generator. This is a simple consistency check. + private func assertBasicConsistency(in generators: WeightedList) { for generator in generators where generator.parts.count > 1 { var currentContext = Context(generator.parts[0].providedContext) @@ -82,8 +87,21 @@ public class ContextGraph { fatalError("Inconsistent requires/provides Contexts for \(generator.name)") } - currentContext = Context(stub.providedContext) + currentContext = stub.providedContext.isEmpty ? currentContext : Context(stub.providedContext) + } + } + } + + private func warnOfSuspiciousContexts(in 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) } for generator in generators { @@ -100,18 +118,6 @@ public class ContextGraph { logger.warning("Generator \(generator.name) provides a context that is never required by another generator \(generator.providedContexts)") } } - - // One can still try to build in a context that doesn't have generators, this will be caught in the build function, if we fail to find any suitable generator. - // Otherwise we could assert here that the sets are equal. - // One example is a GeneratorStub that opens a context, then calls build manually without it being split into two stubs where we have a yield point to assemble a synthetic generator. - self.edges = [:] - - for generator in generators { - for providableContext in generator.providedContexts { - let edge = EdgeKey(from: generator.requiredContext, to: providableContext) - self.edges[edge, default: GeneratorEdge()].addGenerator(generator) - } - } } /// Gets all possible paths from the `from` Context to the `to` Context. diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 17f111300..bbef5a758 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -72,34 +72,6 @@ public class ProgramBuilder { return contextAnalyzer.context } - /// If true, the variables containing a function is hidden inside the function's body. - /// - /// For example, in - /// - /// let f = b.buildPlainFunction(with: .parameters(n: 2) { args in - /// // ... - /// } - /// b.callFunction(f, withArgs: b.randomArguments(forCalling: f)) - /// - /// The variable f would *not* be visible inside the body of the plain function during building - /// when this is enabled. However, the variable will be visible during future mutations, it is only - /// hidden when the function is initially created. - /// - /// The same is done for class definitions, which may also cause trivial recursion in the constructor, - /// but where usage of the output inside the class definition's body may also cause other problems, - /// for example since `class C { [C] = 42; }` is invalid. - /// - /// This can make sense for a number of reasons. First, it prevents trivial recursion where a - /// function directly calls itself. Second, it prevents weird code like for example the following: - /// - /// function f1() { - /// let o6 = { x: foo + foo, y() { return foo; } }; - /// } - /// - /// From being generated, which can happen quite frequently during prefix generation as - /// the number of visible variables may be quite small. - public let enableRecursionGuard = true - /// Counter to quickly determine the next free variable. private var numVariables = 0 @@ -166,7 +138,7 @@ public class ProgramBuilder { } /// 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() + private var scheduled: Stack = Stack() // Runtime data that can be shared between different stubs within a CodeGenerator. var runtimeData = GeneratorRuntimeData() @@ -1245,6 +1217,39 @@ public class ProgramBuilder { numberOfHiddenVariables -= 1 } + + /// Hides a variable containing a function from the function's body. + /// + /// For example, in + /// + /// let f = b.buildPlainFunction(with: .parameters(n: 2) { args in + /// // ... + /// } + /// b.callFunction(f, withArgs: b.randomArguments(forCalling: f)) + /// + /// The variable f would *not* be visible inside the body of the plain function during building + /// when this is enabled. However, the variable will be visible during future mutations, it is only + /// hidden when the function is initially created. + /// + /// The same is done for class definitions, which may also cause trivial recursion in the constructor, + /// but where usage of the output inside the class definition's body may also cause other problems, + /// for example since `class C { [C] = 42; }` is invalid. + /// + /// This can make sense for a number of reasons. First, it prevents trivial recursion where a + /// function directly calls itself. Second, it prevents weird code like for example the following: + /// + /// function f1() { + /// let o6 = { x: foo + foo, y() { return foo; } }; + /// } + /// + /// From being generated, which can happen quite frequently during prefix generation as + /// the number of visible variables may be quite small. + private func bodyWithRecursionGuard(_ variableToHide: Variable, body: () -> ()) { + hide(variableToHide) + body() + unhide(variableToHide) + } + private static func matchingWasmTypes(jsType: ILType) -> [ILType] { if jsType.Is(.integer) { return [.wasmi32, .wasmf64, .wasmf32] @@ -2746,9 +2751,7 @@ public class ProgramBuilder { public func buildClassDefinition(withSuperclass superclass: Variable? = nil, isExpression: Bool = false, _ body: (ClassDefinition) -> ()) -> Variable { let inputs = superclass != nil ? [superclass!] : [] let output = emit(BeginClassDefinition(hasSuperclass: superclass != nil, isExpression: isExpression), withInputs: inputs).output - if enableRecursionGuard { hide(output) } - body(currentClassDefinition) - if enableRecursionGuard { unhide(output) } + bodyWithRecursionGuard(output) { body(currentClassDefinition) } emit(EndClassDefinition()) return output } @@ -2963,9 +2966,7 @@ public class ProgramBuilder { public func buildPlainFunction(with descriptor: SubroutineDescriptor, named functionName: String? = nil,_ body: ([Variable]) -> ()) -> Variable { setParameterTypesForNextSubroutine(descriptor.parameterTypes) let instr = emit(BeginPlainFunction(parameters: descriptor.parameters, functionName: functionName)) - if enableRecursionGuard { hide(instr.output) } - body(Array(instr.innerOutputs)) - if enableRecursionGuard { unhide(instr.output) } + bodyWithRecursionGuard(instr.output) { body(Array(instr.innerOutputs)) } emit(EndPlainFunction()) return instr.output } @@ -2974,9 +2975,7 @@ public class ProgramBuilder { public func buildArrowFunction(with descriptor: SubroutineDescriptor, _ body: ([Variable]) -> ()) -> Variable { setParameterTypesForNextSubroutine(descriptor.parameterTypes) let instr = emit(BeginArrowFunction(parameters: descriptor.parameters)) - if enableRecursionGuard { hide(instr.output) } - body(Array(instr.innerOutputs)) - if enableRecursionGuard { unhide(instr.output) } + bodyWithRecursionGuard(instr.output) { body(Array(instr.innerOutputs)) } emit(EndArrowFunction()) return instr.output } @@ -2985,9 +2984,7 @@ public class ProgramBuilder { public func buildGeneratorFunction(with descriptor: SubroutineDescriptor, named functionName: String? = nil, _ body: ([Variable]) -> ()) -> Variable { setParameterTypesForNextSubroutine(descriptor.parameterTypes) let instr = emit(BeginGeneratorFunction(parameters: descriptor.parameters, functionName: functionName)) - if enableRecursionGuard { hide(instr.output) } - body(Array(instr.innerOutputs)) - if enableRecursionGuard { unhide(instr.output) } + bodyWithRecursionGuard(instr.output) { body(Array(instr.innerOutputs)) } emit(EndGeneratorFunction()) return instr.output } @@ -2996,9 +2993,7 @@ public class ProgramBuilder { public func buildAsyncFunction(with descriptor: SubroutineDescriptor, named functionName: String? = nil, _ body: ([Variable]) -> ()) -> Variable { setParameterTypesForNextSubroutine(descriptor.parameterTypes) let instr = emit(BeginAsyncFunction(parameters: descriptor.parameters, functionName: functionName)) - if enableRecursionGuard { hide(instr.output) } - body(Array(instr.innerOutputs)) - if enableRecursionGuard { unhide(instr.output) } + bodyWithRecursionGuard(instr.output) { body(Array(instr.innerOutputs)) } emit(EndAsyncFunction()) return instr.output } @@ -3007,9 +3002,7 @@ public class ProgramBuilder { public func buildAsyncArrowFunction(with descriptor: SubroutineDescriptor, _ body: ([Variable]) -> ()) -> Variable { setParameterTypesForNextSubroutine(descriptor.parameterTypes) let instr = emit(BeginAsyncArrowFunction(parameters: descriptor.parameters)) - if enableRecursionGuard { hide(instr.output) } - body(Array(instr.innerOutputs)) - if enableRecursionGuard { unhide(instr.output) } + bodyWithRecursionGuard(instr.output) { body(Array(instr.innerOutputs)) } emit(EndAsyncArrowFunction()) return instr.output } @@ -3018,9 +3011,7 @@ public class ProgramBuilder { public func buildAsyncGeneratorFunction(with descriptor: SubroutineDescriptor, named functionName: String? = nil, _ body: ([Variable]) -> ()) -> Variable { setParameterTypesForNextSubroutine(descriptor.parameterTypes) let instr = emit(BeginAsyncGeneratorFunction(parameters: descriptor.parameters, functionName: functionName)) - if enableRecursionGuard { hide(instr.output) } - body(Array(instr.innerOutputs)) - if enableRecursionGuard { unhide(instr.output) } + bodyWithRecursionGuard(instr.output) { body(Array(instr.innerOutputs)) } emit(EndAsyncGeneratorFunction()) return instr.output } @@ -3029,9 +3020,7 @@ public class ProgramBuilder { public func buildConstructor(with descriptor: SubroutineDescriptor, _ body: ([Variable]) -> ()) -> Variable { setParameterTypesForNextSubroutine(descriptor.parameterTypes) let instr = emit(BeginConstructor(parameters: descriptor.parameters)) - if enableRecursionGuard { hide(instr.output) } - body(Array(instr.innerOutputs)) - if enableRecursionGuard { unhide(instr.output) } + bodyWithRecursionGuard(instr.output) { body(Array(instr.innerOutputs)) } emit(EndConstructor()) return instr.output } diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 13a20b613..3b64d0903 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -1936,7 +1936,7 @@ public let CodeGenerators: [CodeGenerator] = [ b, arr in // Fuzzilli generated arrays can have a length ranging from 0 to 10 elements, // We want to ensure that 1) when destructing arrays we are usually within this length range - // and 2) The probability with which we select indices allows defining atleast 2-3 variables. + // and 2) the probability with which we select indices allows defining at least 2-3 variables. var indices: [Int64] = [] for idx in 0.. GeneratorStub { - for generator in self { - if generator.name == name { - return generator - } - } - fatalError("Unknown code generator \(name)") - } -} diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index 17888b9f1..9ef54f216 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -912,6 +912,7 @@ public struct ObjectGroup { public let name: String public var properties: [String: ILType] public var methods: [String: [Signature]] + // This may be used in custom profiles of Fuzzilli users' (e.g. ChromiumProfile.swift). public let parent: String? // Path to constructor function from `globalThis` if available (e.g. `["Temporal", "Instant"]`). public let constructorPath: [String]? diff --git a/Tests/FuzzilliTests/ProgramBuilderTest.swift b/Tests/FuzzilliTests/ProgramBuilderTest.swift index fc05859e3..c5c071536 100644 --- a/Tests/FuzzilliTests/ProgramBuilderTest.swift +++ b/Tests/FuzzilliTests/ProgramBuilderTest.swift @@ -431,8 +431,6 @@ class ProgramBuilderTests: XCTestCase { let fuzzer = makeMockFuzzer() let b = fuzzer.makeBuilder() - XCTAssert(b.enableRecursionGuard) - // The recursion guard feature of the ProgramBuilder is meant to prevent trivial recursion // where a newly created function directly calls itself. It's also meant to prevent somewhat // odd code from being generated where operation inside a function's body operate on the function From 47630fad4fa6f9cb233cdfa58833562987319ff2 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Thu, 13 Nov 2025 14:16:06 +0100 Subject: [PATCH 13/89] [wasm] Register code generator that produces all wasm-gc type kinds This way the new code generation logic can resolve dependencies when it requires a Wasm struct, array, or signature type. In theory, these could all be registered as separate code generators, however it seems simpler having one that just generates all 3 types. We need the separate generator and can't rely on the "inner" generators like the "ArrayTypeGenerator" as these can only run inside the `.wasmTypeGroup` context. Bug: 445356784 Change-Id: I5c2b9e37aeb9b3ab50f05a37e49147efff4acaa7 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8767377 Commit-Queue: Matthias Liedtke Auto-Submit: Matthias Liedtke Reviewed-by: Pawel Krawczyk --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 5 + Sources/Fuzzilli/CodeGen/CodeGenerator.swift | 9 + .../CodeGen/CodeGeneratorWeights.swift | 1 + .../Fuzzilli/CodeGen/WasmCodeGenerators.swift | 229 ++++++++++-------- Sources/Fuzzilli/FuzzIL/TypeSystem.swift | 4 +- Tests/FuzzilliTests/ProgramBuilderTest.swift | 79 +++--- 6 files changed, 186 insertions(+), 141 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index bbef5a758..4249fabfc 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -4617,6 +4617,11 @@ public class ProgramBuilder { public func wasmDefineTypeGroup(recursiveGenerator: () -> ()) -> [Variable] { emit(WasmBeginTypeGroup()) recursiveGenerator() + return wasmEndTypeGroup() + } + + @discardableResult + public func wasmEndTypeGroup() -> [Variable] { // Make all type definitions visible. let types = scopes.top.filter { let t = type(of: $0) diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerator.swift b/Sources/Fuzzilli/CodeGen/CodeGenerator.swift index 78bef10fb..174e72924 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerator.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerator.swift @@ -82,6 +82,7 @@ public class GeneratorStub: Contributor { case None case IsWasmArray case IsWasmStruct + case IsWasmFunction // On a type definition this means "signature". } public struct Constraint : Hashable { @@ -116,6 +117,14 @@ public class GeneratorStub: Contributor { } else { false } + case .IsWasmFunction: + if type.Is(.wasmTypeDef()) { + type.wasmTypeDefinition?.description is WasmSignatureTypeDescription + } else if type.Is(.anyNonNullableIndexRef) { + type.Is(.wasmFuncRef) + } else { + false + } } } diff --git a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift index 280a13780..1dff70aed 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift @@ -345,6 +345,7 @@ public let codeGeneratorWeights = [ // Wasm-gc type generators // These run in the javascript context and define types to be used within wasm modules. "WasmTypeGroupGenerator": 5, + "WasmTypeGroupWithAllTypesGenerator": 5, "WasmArrayTypeGenerator": 5, "WasmStructTypeGenerator": 5, "WasmSignatureTypeGenerator": 5, diff --git a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift index c65d55a84..3878e4d4e 100644 --- a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift @@ -88,99 +88,36 @@ public let WasmCodeGenerators: [CodeGenerator] = [ "WasmTypeGroupEndGenerator", inContext: .single(.wasmTypeGroup) ) { b in - // Collect all types that are visible and expose them. - let types = b.scopes.top.filter { - let t = b.type(of: $0) - return t.Is(.wasmTypeDef()) - && t.wasmTypeDefinition?.description != .selfReference - } - b.emit( - WasmEndTypeGroup(typesCount: types.count), withInputs: types - ) + b.wasmEndTypeGroup() }, ]), - CodeGenerator( - "WasmArrayTypeGenerator", - inContext: .single(.wasmTypeGroup), - producesComplex: [.init(.wasmTypeDef(), .IsWasmArray)] - ) { b in - let mutability = probability(0.75) - if let elementType = b.randomVariable(ofType: .wasmTypeDef()), - probability(0.25) - { - // Excluding non-nullable references from referring to a self-reference ensures we do not end up with cycles of non-nullable references. - let nullability = - b.type(of: elementType).wasmTypeDefinition!.description - == .selfReference || probability(0.5) - b.wasmDefineArrayType( - elementType: .wasmRef(.Index(), nullability: nullability), - mutability: mutability, indexType: elementType) - } else { - // TODO(mliedtke): Extend list with abstract heap types. - b.wasmDefineArrayType( - elementType: chooseUniform(from: [ - .wasmPackedI8, .wasmPackedI16, .wasmi32, .wasmi64, .wasmf32, .wasmf64, .wasmSimd128, - ]), mutability: mutability) - } - }, - - // TODO(mliedtke): Refine this `produces` annotation. - CodeGenerator("WasmStructTypeGenerator", inContext: .single(.wasmTypeGroup), produces: [.wasmTypeDef()]) { b in - var indexTypes: [Variable] = [] - let fields = (0.. (0.. (0.. { diff --git a/Tests/FuzzilliTests/ProgramBuilderTest.swift b/Tests/FuzzilliTests/ProgramBuilderTest.swift index c5c071536..5a9c53b63 100644 --- a/Tests/FuzzilliTests/ProgramBuilderTest.swift +++ b/Tests/FuzzilliTests/ProgramBuilderTest.swift @@ -2980,37 +2980,6 @@ class ProgramBuilderTests: XCTestCase { // XCTAssertGreaterThan(numGeneratedInstructions, 30) } - func testWasmStructNewDefaultGeneratorSchedulingTest() { - let fuzzer = makeMockFuzzer() - let b = fuzzer.makeBuilder() - b.buildPrefix() - - // TODO(mliedtke): The mechanism needs to learn how to resolve nested input dependencies. - b.wasmDefineTypeGroup {[ - b.wasmDefineArrayType(elementType: .wasmi32, mutability: true), - b.wasmDefineStructType(fields: [.init(type: .wasmi32, mutability: true)], indexTypes: []), - ]} - - // Pick the 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) - XCTAssertTrue(b.finalize().code.contains(where: { instr in - if case .wasmStructNewDefault(_) = instr.op.opcode { - return true - } else { - return false - } - })) - } - func testWasmMemorySizeSchedulingTest() { let fuzzer = makeMockFuzzer() let numPrograms = 30 @@ -3066,9 +3035,7 @@ class ProgramBuilderTests: XCTestCase { 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 numGeneratedInstructions = b.complete(generator: syntheticGenerator!, withBudget: 30) let program = b.finalize() @@ -3084,6 +3051,50 @@ class ProgramBuilderTests: XCTestCase { } } + func testWasmGCScheduling() { + func test( + _ generatorName: String, + expectAny: repeat (each ExpectedOp).Type, + requiresTypes: Bool = false) { + let fuzzer = makeMockFuzzer() + let numPrograms = 30 + + for _ in 0.. Date: Fri, 14 Nov 2025 16:52:04 +0100 Subject: [PATCH 14/89] [wasm] Slightly increase chance of successful wasm-gc operations Change-Id: I9f502e7d70fcccbb335f424391bebfdb6561f3e0 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8764022 Commit-Queue: Matthias Liedtke Reviewed-by: Pawel Krawczyk --- Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift index 3878e4d4e..697f84d4a 100644 --- a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift @@ -201,8 +201,10 @@ public let WasmCodeGenerators: [CodeGenerator] = [ fatalError("unreachable: array.set input not an array") } guard arrayType.mutability else { return } - guard let element = b.randomVariable(ofType: arrayType.elementType.unpacked()) else { return } + let inputType = arrayType.elementType.unpacked() let function = b.currentWasmModule.currentWasmFunction + guard let element = b.randomVariable(ofType: inputType) ?? function.generateRandomWasmVar(ofType: inputType) + else { return } // TODO(mliedtke): Track array length and use other indices as well. let index = function.consti32(0) function.wasmArraySet(array: array, index: index, element: element) @@ -255,17 +257,14 @@ public let WasmCodeGenerators: [CodeGenerator] = [ guard let structType = desc.get()! as? WasmStructTypeDescription else { fatalError("Invalid type description for \(b.type(of: theStruct))") } - guard - let fieldWithIndex = structType.fields.enumerated().filter({ - (offset, field) in + guard let fieldWithIndex = structType.fields.enumerated().filter({(offset, field) in field.mutability }).randomElement() else { return } - // TODO(mliedtke): Create the input when not available! - guard - let newValue = b.randomVariable(ofType: fieldWithIndex.element.type) - else { return } let function = b.currentWasmModule.currentWasmFunction + let inputType = fieldWithIndex.element.type.unpacked() + guard let newValue = b.randomVariable(ofType: inputType) ?? function.generateRandomWasmVar(ofType: inputType) + else { return } function.wasmStructSet( theStruct: theStruct, fieldIndex: fieldWithIndex.offset, value: newValue) From 633c823b41e285cec7590148297b69c4d7362d03 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Mon, 24 Nov 2025 17:43:50 +0100 Subject: [PATCH 15/89] [v8] Add code generator for %MajorGCForCompilerTesting() V8-side-change: https://crrev.com/c/7198340 Change-Id: I423361da98643dcde469b8a13c6b7df44114d8c6 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8793536 Reviewed-by: Dominik Klemba Auto-Submit: Matthias Liedtke Commit-Queue: Dominik Klemba --- Sources/FuzzilliCli/Profiles/V8CommonProfile.swift | 6 ++++++ Sources/FuzzilliCli/Profiles/V8Profile.swift | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift index 5a7c329bf..967210b97 100644 --- a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift @@ -51,6 +51,12 @@ public let V8GcGenerator = CodeGenerator("GcGenerator") { b in b.callFunction(gc, withArgs: b.findOrGenerateArguments(forSignature: b.fuzzer.environment.type(ofBuiltin: "gc").signature!)) } +public let V8MajorGcGenerator = CodeGenerator("MajorGcGenerator") { b in + // Differently to `gc()`, this intrinsic is registered with less effects, preventing fewer + // optimizations in V8's optimizing compilers. + b.eval("%MajorGCForCompilerTesting()") +} + public let ForceJITCompilationThroughLoopGenerator = CodeGenerator("ForceJITCompilationThroughLoopGenerator", inputs: .required(.function())) { b, f in assert(b.type(of: f).Is(.function())) let arguments = b.randomArguments(forCalling: f) diff --git a/Sources/FuzzilliCli/Profiles/V8Profile.swift b/Sources/FuzzilliCli/Profiles/V8Profile.swift index b1eb26365..0a858aee8 100644 --- a/Sources/FuzzilliCli/Profiles/V8Profile.swift +++ b/Sources/FuzzilliCli/Profiles/V8Profile.swift @@ -60,7 +60,8 @@ let v8Profile = Profile( (TurbofanVerifyTypeGenerator, 10), (WorkerGenerator, 10), - (V8GcGenerator, 10), + (V8GcGenerator, 5), + (V8MajorGcGenerator, 5), (WasmStructGenerator, 15), (WasmArrayGenerator, 15), From 72dd5d7d9fcb648881543cd31bbac0d1baaa106f Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Fri, 21 Nov 2025 12:03:00 +0100 Subject: [PATCH 16/89] [wasm] Add a new operation for defining signatures directly inside a function To allow defining a block with a wasm-gc signature while already being in the .wasmFunction context, this change adds a new operation WasmDefineAdHocSignature. This way statements requiring a signature type input can directly embed this signature definition inside the function. Bug: 445356784 Change-Id: I56754224551ea82883c71410f4aca957b7bf24d4 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8787096 Reviewed-by: Pawel Krawczyk Commit-Queue: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 7 +++ Sources/Fuzzilli/FuzzIL/Instruction.swift | 7 +++ Sources/Fuzzilli/FuzzIL/JSTyper.swift | 25 +++++----- Sources/Fuzzilli/FuzzIL/Opcodes.swift | 1 + Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 12 +++++ Sources/Fuzzilli/Lifting/FuzzILLifter.swift | 4 ++ .../Fuzzilli/Lifting/JavaScriptLifter.swift | 1 + Sources/Fuzzilli/Lifting/WasmLifter.swift | 33 +++++++------ .../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/WasmTests.swift | 27 +++++++++- 14 files changed, 171 insertions(+), 30 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 4249fabfc..6bc1dbd6b 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -4635,6 +4635,13 @@ public class ProgramBuilder { return emit(WasmDefineSignatureType(signature: signature), withInputs: indexTypes).output } + /// Like wasmDefineSignatureType but instead of within a type group this defines a signature + /// type directly inside a wasm function. + @discardableResult + func wasmDefineAdHocSignatureType(signature: WasmSignature, indexTypes: [Variable]) -> Variable { + return emit(WasmDefineAdHocSignatureType(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 ee017f20a..6486e4603 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -1559,6 +1559,11 @@ extension Instruction: ProtobufConvertible { $0.wasmBeginTypeGroup = Fuzzilli_Protobuf_WasmBeginTypeGroup() case .wasmEndTypeGroup(_): $0.wasmEndTypeGroup = Fuzzilli_Protobuf_WasmEndTypeGroup() + case .wasmDefineAdHocSignatureType(let op): + $0.wasmDefineAdHocSignatureType = Fuzzilli_Protobuf_WasmDefineAdHocSignatureType.with { + $0.parameterTypes = op.signature.parameterTypes.map(ILTypeToWasmTypeEnum) + $0.outputTypes = op.signature.outputTypes.map(ILTypeToWasmTypeEnum) + } case .wasmDefineSignatureType(let op): $0.wasmDefineSignatureType = Fuzzilli_Protobuf_WasmDefineSignatureType.with { $0.parameterTypes = op.signature.parameterTypes.map(ILTypeToWasmTypeEnum) @@ -2566,6 +2571,8 @@ extension Instruction: ProtobufConvertible { 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 .wasmDefineAdHocSignatureType(let p): + op = WasmDefineAdHocSignatureType(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 0ca07622c..054f242a2 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -433,8 +433,9 @@ public struct JSTyper: Analyzer { } mutating func addSignatureType(def: Variable, signature: WasmSignature, inputs: ArraySlice) { + assert(isWithinTypeGroup) var inputs = inputs.makeIterator() - let tgIndex = isWithinTypeGroup ? typeGroups.count - 1 : -1 + let tgIndex = typeGroups.count - 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 @@ -477,13 +478,12 @@ public struct JSTyper: Analyzer { 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) - } + 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 + assert(isWithinTypeGroup) + let tgIndex = typeGroups.count - 1 let resolvedElementType: ILType if let elementRef = elementRef { let elementNullability = elementType.wasmReferenceType!.nullability @@ -519,13 +519,11 @@ public struct JSTyper: Analyzer { elementType: resolvedElementType, mutability: mutability, typeGroupIndex: tgIndex))) - if isWithinTypeGroup { - typeGroups[typeGroups.count - 1].append(def) - } + typeGroups[typeGroups.count - 1].append(def) } mutating func addStructType(def: Variable, fieldsWithRefs: [(WasmStructTypeDescription.Field, Variable?)]) { - let tgIndex = isWithinTypeGroup ? typeGroups.count - 1 : -1 + let tgIndex = typeGroups.count - 1 let resolvedFields = fieldsWithRefs.enumerated().map { (fieldIndex, fieldWithInput) in let (field, fieldTypeRef) = fieldWithInput if let fieldTypeRef { @@ -556,9 +554,7 @@ public struct JSTyper: Analyzer { set(def, .wasmTypeDef(description: WasmStructTypeDescription( fields: resolvedFields, typeGroupIndex: tgIndex))) - if (isWithinTypeGroup) { - typeGroups[typeGroups.count - 1].append(def) - } + typeGroups[typeGroups.count - 1].append(def) } func getTypeGroup(_ index: Int) -> [Variable] { @@ -588,7 +584,6 @@ public struct JSTyper: Analyzer { } } selfReferences.removeAll() - isWithinTypeGroup = false } @@ -904,6 +899,10 @@ public struct JSTyper: Analyzer { // extern.convert_any forwards the nullability bit from the input. let null = type(of: instr.input(0)).wasmReferenceType!.nullability setType(of: instr.output, to: .wasmRef(.Abstract(.WasmExtern), nullability: null)) + case .wasmDefineAdHocSignatureType(let op): + startTypeGroup() + addSignatureType(def: instr.output, signature: op.signature, inputs: instr.inputs) + finishTypeGroup() default: if instr.numInnerOutputs + instr.numOutputs != 0 { fatalError("Missing typing of outputs for \(instr.op.opcode)") diff --git a/Sources/Fuzzilli/FuzzIL/Opcodes.swift b/Sources/Fuzzilli/FuzzIL/Opcodes.swift index f9fda2f00..d28dd189f 100644 --- a/Sources/Fuzzilli/FuzzIL/Opcodes.swift +++ b/Sources/Fuzzilli/FuzzIL/Opcodes.swift @@ -363,4 +363,5 @@ enum Opcode { case wasmDefineSignatureType(WasmDefineSignatureType) case createNamedDisposableVariable(CreateNamedDisposableVariable) case createNamedAsyncDisposableVariable(CreateNamedAsyncDisposableVariable) + case wasmDefineAdHocSignatureType(WasmDefineAdHocSignatureType) } diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index bed8bd6ae..452dd9cc5 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -2391,3 +2391,15 @@ final class WasmAtomicCmpxchg: WasmOperation { super.init(numInputs: 4, numOutputs: 1, attributes: [.isMutable], requiredContext: [.wasmFunction]) } } + +final class WasmDefineAdHocSignatureType: WasmOperation { + override var opcode: Opcode { .wasmDefineAdHocSignatureType(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: [.wasmFunction]) + } +} diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift index 9a4e77785..33d47fa16 100644 --- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift +++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift @@ -1354,6 +1354,10 @@ public class FuzzILLifter: Lifter { let inputs = instr.inputs.map(lift).joined(separator: ", ") w.emit("\(output()) <- WasmDefineSignatureType(\(op.signature)) [\(inputs)]") + case .wasmDefineAdHocSignatureType(let op): + let inputs = instr.inputs.map(lift).joined(separator: ", ") + w.emit("\(output()) <- WasmDefineAdHocSignatureType(\(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 1cf8b75b9..49cfb5a04 100644 --- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift +++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift @@ -1722,6 +1722,7 @@ public class JavaScriptLifter: Lifter { .wasmBeginTypeGroup(_), .wasmEndTypeGroup(_), .wasmDefineSignatureType(_), + .wasmDefineAdHocSignatureType(_), .wasmDefineArrayType(_), .wasmDefineStructType(_), .wasmDefineForwardOrSelfReference(_), diff --git a/Sources/Fuzzilli/Lifting/WasmLifter.swift b/Sources/Fuzzilli/Lifting/WasmLifter.swift index 287d1cc5e..4944ac161 100644 --- a/Sources/Fuzzilli/Lifting/WasmLifter.swift +++ b/Sources/Fuzzilli/Lifting/WasmLifter.swift @@ -294,7 +294,6 @@ public class WasmLifter { // private var tags: VariableMap<[ILType]> = VariableMap() private var typeGroups: Set = [] - private var freeTypes: Set = [] private var typeDescToIndex : [WasmTypeDescription:Int] = [:] private var userDefinedTypesCount = 0 @@ -655,7 +654,6 @@ public class WasmLifter { try buildTypeEntry(for: typer.getTypeDescription(of: typeDef), data: &temp) } } - // TODO(mliedtke): Also add "free types" which aren't in any explicit type group. for signature in self.signatures { temp += [0x60] @@ -1343,8 +1341,10 @@ public class WasmLifter { // requires that the instr has been analyzed before. Maybe assert that? private func emitInputLoadsIfNecessary(forInstruction instr: Instruction) { - // Don't emit loads for reassigns. This is specially handled in the `lift` function for reassigns. - if instr.op is WasmReassign { + // Don't emit loads for reassigns. This is specially handled in the `lift` function for + // reassigns. WasmDefineAdHocSignatureType isn't a wasm instruction and therefore also + // doesn't have any Wasm stack inputs. + if instr.op is WasmReassign || instr.op is WasmDefineAdHocSignatureType { return } @@ -1376,9 +1376,11 @@ public class WasmLifter { } private func emitStackSpillsIfNecessary(forInstruction instr: Instruction) { - // Don't emit spills for reassigns. This is specially handled in the `lift` function for reassigns. - // Similarly, the end of a function doesn't spill anything. - if instr.op is WasmReassign || instr.op is EndWasmFunction { + // Don't emit spills for reassigns. This is specially handled in the `lift` function for + // reassigns. Similarly, the end of a function doesn't spill anything. + // WasmDefineAdHocSignatureType isn't a wasm instruction and therefore also doesn't have any + // Wasm stack inputs. + if instr.op is WasmReassign || instr.op is EndWasmFunction || instr.op is WasmDefineAdHocSignatureType { return } @@ -1455,13 +1457,12 @@ public class WasmLifter { if inputType.Is(.wasmTypeDef()) || inputType.Is(.wasmRef(.Index(), nullability: true)) { let typeDesc = typer.getTypeDescription(of: input) - if typeDesc.typeGroupIndex != -1 { - // Add typegroups and their dependencies. - if typeGroups.insert(typeDesc.typeGroupIndex).inserted { - typeGroups.formUnion(typer.getTypeGroupDependencies(typeGroupIndex: typeDesc.typeGroupIndex)) - } - } else { - freeTypes.insert(input) + guard typeDesc.typeGroupIndex != -1 else { + throw CompileError.fatalError("Missing type group index for \(input)") + } + // Add typegroups and their dependencies. + if typeGroups.insert(typeDesc.typeGroupIndex).inserted { + typeGroups.formUnion(typer.getTypeGroupDependencies(typeGroupIndex: typeDesc.typeGroupIndex)) } } @@ -2196,6 +2197,10 @@ public class WasmLifter { return Data([Prefix.GC.rawValue, 0x1A]) case .wasmExternConvertAny(_): return Data([Prefix.GC.rawValue, 0x1B]) + case .wasmDefineAdHocSignatureType(_): + // Nothing to do here, types are defined inside the typegroups, not inside a wasm + // function. + return Data() default: fatalError("unreachable") diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift index dfe3438df..94e7e297c 100644 --- a/Sources/Fuzzilli/Mutators/OperationMutator.swift +++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift @@ -712,6 +712,7 @@ public class OperationMutator: BaseInstructionMutator { .wasmDefineArrayType(_), .wasmDefineStructType(_), .wasmDefineSignatureType(_), + .wasmDefineAdHocSignatureType(_), .wasmDefineForwardOrSelfReference(_), .wasmResolveForwardReference(_), .wasmArrayNewFixed(_), diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index 7a1549674..2b2248232 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -5709,6 +5709,20 @@ public struct Fuzzilli_Protobuf_WasmDefineSignatureType: Sendable { public init() {} } +public struct Fuzzilli_Protobuf_WasmDefineAdHocSignatureType: 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 @@ -14994,6 +15008,41 @@ extension Fuzzilli_Protobuf_WasmDefineSignatureType: SwiftProtobuf.Message, Swif } } +extension Fuzzilli_Protobuf_WasmDefineAdHocSignatureType: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".WasmDefineAdHocSignatureType" + 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_WasmDefineAdHocSignatureType, rhs: Fuzzilli_Protobuf_WasmDefineAdHocSignatureType) -> 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 72266a1fb..8ed578def 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -1503,6 +1503,11 @@ message WasmDefineSignatureType { repeated WasmILType outputTypes = 2; } +message WasmDefineAdHocSignatureType { + 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 bf4e21f82..293a03607 100644 --- a/Sources/Fuzzilli/Protobuf/program.pb.swift +++ b/Sources/Fuzzilli/Protobuf/program.pb.swift @@ -2721,6 +2721,14 @@ public struct Fuzzilli_Protobuf_Instruction: Sendable { set {operation = .createNamedAsyncDisposableVariable(newValue)} } + public var wasmDefineAdHocSignatureType: Fuzzilli_Protobuf_WasmDefineAdHocSignatureType { + get { + if case .wasmDefineAdHocSignatureType(let v)? = operation {return v} + return Fuzzilli_Protobuf_WasmDefineAdHocSignatureType() + } + set {operation = .wasmDefineAdHocSignatureType(newValue)} + } + public var unknownFields = SwiftProtobuf.UnknownStorage() public enum OneOf_Operation: Equatable, Sendable { @@ -3058,6 +3066,7 @@ public struct Fuzzilli_Protobuf_Instruction: Sendable { case wasmDefineSignatureType(Fuzzilli_Protobuf_WasmDefineSignatureType) case createNamedDisposableVariable(Fuzzilli_Protobuf_CreateNamedDisposableVariable) case createNamedAsyncDisposableVariable(Fuzzilli_Protobuf_CreateNamedAsyncDisposableVariable) + case wasmDefineAdHocSignatureType(Fuzzilli_Protobuf_WasmDefineAdHocSignatureType) } @@ -3106,7 +3115,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}beginClassInstanceComputedMethod\0\u{1}endClassInstanceComputedMethod\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}beginClassStaticComputedMethod\0\u{1}endClassStaticComputedMethod\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}beginClassInstanceComputedMethod\0\u{1}endClassInstanceComputedMethod\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}beginClassStaticComputedMethod\0\u{1}endClassStaticComputedMethod\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}wasmDefineAdHocSignatureType\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -7452,6 +7461,19 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M self.operation = .createNamedAsyncDisposableVariable(v) } }() + case 336: try { + var v: Fuzzilli_Protobuf_WasmDefineAdHocSignatureType? + var hadOneofValue = false + if let current = self.operation { + hadOneofValue = true + if case .wasmDefineAdHocSignatureType(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.operation = .wasmDefineAdHocSignatureType(v) + } + }() default: break } } @@ -8802,6 +8824,10 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M guard case .createNamedAsyncDisposableVariable(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 335) }() + case .wasmDefineAdHocSignatureType?: try { + guard case .wasmDefineAdHocSignatureType(let v)? = self.operation else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 336) + }() case nil: break } try unknownFields.traverse(visitor: &visitor) diff --git a/Sources/Fuzzilli/Protobuf/program.proto b/Sources/Fuzzilli/Protobuf/program.proto index 0582386bf..db2587934 100644 --- a/Sources/Fuzzilli/Protobuf/program.proto +++ b/Sources/Fuzzilli/Protobuf/program.proto @@ -359,6 +359,7 @@ message Instruction { WasmDefineSignatureType wasmDefineSignatureType = 333; CreateNamedDisposableVariable createNamedDisposableVariable = 334; CreateNamedAsyncDisposableVariable createNamedAsyncDisposableVariable = 335; + WasmDefineAdHocSignatureType wasmDefineAdHocSignatureType = 336; } } diff --git a/Tests/FuzzilliTests/WasmTests.swift b/Tests/FuzzilliTests/WasmTests.swift index 05a3c6b61..f7955e212 100644 --- a/Tests/FuzzilliTests/WasmTests.swift +++ b/Tests/FuzzilliTests/WasmTests.swift @@ -4165,7 +4165,7 @@ class WasmFoundationTests: XCTestCase { module.addWasmFunction(with: [] => [.wasmi64, .wasmi64]) { f, _, _ in let tableOffset = { (i: Int) in isTable64 ? f.consti64(Int64(i)) : f.consti32(Int32(i))} f.wasmTableInit(elementSegment: elemSegment2, table: table2, tableOffset: tableOffset(5), elementSegmentOffset: f.consti32(2), nrOfElementsToUpdate: f.consti32(2)) - let callIndirect = { (table: Variable, idx: Int) in + let callIndirect = { (table: Variable, idx: Int) in let idxVar = isTable64 ? f.consti64(Int64(idx)) : f.consti32(Int32(idx)) return f.wasmCallIndirect(signature: [] => [.wasmi64], table: table, functionArgs: [], tableIndex: idxVar) } @@ -4210,7 +4210,7 @@ class WasmFoundationTests: XCTestCase { module.addWasmFunction(with: [] => [.wasmi64, .wasmi64]) { f, _, _ in let const = { (i: Int) in isTable64 ? f.consti64(Int64(i)) : f.consti32(Int32(i))} f.wasmTableCopy(dstTable: table1, srcTable: table2, dstOffset: const(1), srcOffset: const(2), count: const(2)) - let callIndirect = { (table: Variable, idx: Int) in + let callIndirect = { (table: Variable, idx: Int) in let idxVar = isTable64 ? f.consti64(Int64(idx)) : f.consti32(Int32(idx)) return f.wasmCallIndirect(signature: [] => [.wasmi64], table: table, functionArgs: [], tableIndex: idxVar) } @@ -4489,6 +4489,29 @@ class WasmGCTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "null\n") } + func testAdHocSignature() throws { + let runner = try GetJavaScriptExecutorOrSkipTest() + let jsProg = buildAndLiftProgram { b in + + 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. + let signatureType = b.wasmDefineAdHocSignatureType(signature: [.wasmi32] => [.wasmi32], indexTypes: []) + return [function.wasmRefNull(typeDef: signatureType)] + } + } + + 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 9ddaf186a6584967ef156c15707769c069692e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Gro=C3=9F?= Date: Tue, 25 Nov 2025 10:55:40 +0100 Subject: [PATCH 17/89] [v8] Add startup test for new abort_with_sandbox_violation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To ensure that this function is correctly detected as a crash in both regular fuzzing and sandbox fuzzing configurations Change-Id: I22eae385d08d343926624d5e6f33b7e6dbf72993 Bug: 461681036 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8796176 Commit-Queue: Samuel Groß Reviewed-by: Matthias Liedtke --- Sources/FuzzilliCli/Profiles/V8Profile.swift | 2 ++ Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Sources/FuzzilliCli/Profiles/V8Profile.swift b/Sources/FuzzilliCli/Profiles/V8Profile.swift index 0a858aee8..ade2d87ac 100644 --- a/Sources/FuzzilliCli/Profiles/V8Profile.swift +++ b/Sources/FuzzilliCli/Profiles/V8Profile.swift @@ -49,6 +49,8 @@ let v8Profile = Profile( ("fuzzilli('FUZZILLI_CRASH', 3)", .shouldCrash), // Check that DEBUG is defined. ("fuzzilli('FUZZILLI_CRASH', 8)", .shouldCrash), + // Check that abort_with_sandbox_violation works. + ("fuzzilli('FUZZILLI_CRASH', 9)", .shouldCrash), // TODO we could try to check that OOM crashes are ignored here ( with.shouldNotCrash). ], diff --git a/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift b/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift index 1e96c7762..3177c273e 100644 --- a/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift @@ -457,6 +457,8 @@ let v8SandboxProfile = Profile( ("fuzzilli('FUZZILLI_CRASH', 4)", .shouldCrash), // This should crash with an ASan-detectable out-of-bounds write. ("fuzzilli('FUZZILLI_CRASH', 6)", .shouldCrash), + // This should crash due to calling abort_with_sandbox_violation(). + ("fuzzilli('FUZZILLI_CRASH', 9)", .shouldCrash), // Crashes that are not sandbox violations and so should be filtered out by the crash filter. // This triggers an IMMEDIATE_CRASH. From 33ed3cf706ff07cd8692e4fd9ae43ac9760b0956 Mon Sep 17 00:00:00 2001 From: Dominik Klemba Date: Wed, 26 Nov 2025 08:40:13 +0000 Subject: [PATCH 18/89] Improve RestLength parameter fuzzing This change increases the probability of accessing the length of rest parameters and rest elements to improve fuzzing coverage of V8's optimizations for RestLength (rest.length). With a 20% probability, FuzzIL variable is created for the "length" property of a newly created rest parameter or element. This affects all function types and array destructuring generators. For function generators and 'ForOfWithDestructLoopGenerator', we do not need to check if outputs are empty: 'hasRestParameter' implies the existence of parameters, and loop generation logic guarantees non-empty indices. For 'DestructArrayGenerator' and 'DestructArrayAndReassignGenerator', we now ensure that 'lastIsRest' is only true when the variable list is non-empty. Assertions were also added to the DestructArray instructions to enforce this invariant. Bug: 456162872 Change-Id: I37b78cc892aac5bb5e5164864863dc51dba40f51 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8741996 Reviewed-by: Matthias Liedtke Commit-Queue: Dominik Klemba --- Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 83 ++++++++++++++----- Sources/Fuzzilli/FuzzIL/JsOperations.swift | 2 + 2 files changed, 63 insertions(+), 22 deletions(-) diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 3b64d0903..0c5442f4a 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -795,6 +795,10 @@ public let CodeGenerators: [CodeGenerator] = [ parameters: randomParameters.parameters) ).innerOutputs + if randomParameters.parameters.hasRestParameter && probability(0.2) { + b.getProperty("length", of: args.last!) + } + let this = args[0] // Derived classes must call `super()` before accessing this, but non-derived classes must not call `super()`. if b.currentClassDefinition.isDerivedClass { @@ -1296,9 +1300,12 @@ public let CodeGenerators: [CodeGenerator] = [ let randomParameters = b.randomParameters() b.setParameterTypesForNextSubroutine( randomParameters.parameterTypes) - let f = b.emit( - BeginPlainFunction(parameters: randomParameters.parameters, functionName: nil)).output - b.runtimeData.push("plainFunction", f) + let instr = b.emit( + BeginPlainFunction(parameters: randomParameters.parameters, functionName: nil)) + if randomParameters.parameters.hasRestParameter && probability(0.2) { + b.getProperty("length", of: instr.innerOutputs.last!) + } + b.runtimeData.push("plainFunction", instr.output) }, GeneratorStub("PlainFunctionEndGenerator", inContext: .single([.javascript, .subroutine])) { b in b.doReturn(b.randomJsVariable()) @@ -1316,9 +1323,12 @@ public let CodeGenerators: [CodeGenerator] = [ let randomParameters = b.randomParameters() b.setParameterTypesForNextSubroutine( randomParameters.parameterTypes) - let f = b.emit( - BeginPlainFunction(parameters: randomParameters.parameters, functionName: nil)).output - b.runtimeData.push("strictFunction", f) + let instr = b.emit( + BeginPlainFunction(parameters: randomParameters.parameters, functionName: nil)) + if randomParameters.parameters.hasRestParameter && probability(0.2) { + b.getProperty("length", of: instr.innerOutputs.last!) + } + b.runtimeData.push("strictFunction", instr.output) b.directive("use strict") }, GeneratorStub("StrictModeFunctionEndGenerator", inContext: .single([.javascript, .subroutine])) { b in @@ -1340,8 +1350,11 @@ public let CodeGenerators: [CodeGenerator] = [ let randomParameters = b.randomParameters() b.setParameterTypesForNextSubroutine( randomParameters.parameterTypes) - b.emit( + let instr = b.emit( BeginArrowFunction(parameters: randomParameters.parameters)) + if randomParameters.parameters.hasRestParameter && probability(0.2) { + b.getProperty("length", of: instr.innerOutputs.last!) + } }, GeneratorStub( "ArrowFunctionEndGenerator", @@ -1357,9 +1370,12 @@ public let CodeGenerators: [CodeGenerator] = [ let randomParameters = b.randomParameters() b.setParameterTypesForNextSubroutine( randomParameters.parameterTypes) - let f = b.emit( - BeginGeneratorFunction(parameters: randomParameters.parameters, functionName: nil)).output - b.runtimeData.push("generatorFunction", f) + let instr = b.emit( + BeginGeneratorFunction(parameters: randomParameters.parameters, functionName: nil)) + if randomParameters.parameters.hasRestParameter && probability(0.2) { + b.getProperty("length", of: instr.innerOutputs.last!) + } + b.runtimeData.push("generatorFunction", instr.output) }, GeneratorStub("GeneratorFunctionEndGenerator", inContext: .single([.generatorFunction, .subroutine, .javascript])) { b in if probability(0.5) { @@ -1383,9 +1399,12 @@ public let CodeGenerators: [CodeGenerator] = [ let randomParameters = b.randomParameters() b.setParameterTypesForNextSubroutine( randomParameters.parameterTypes) - let f = b.emit( - BeginAsyncFunction(parameters: randomParameters.parameters, functionName: nil)).output - b.runtimeData.push("asyncFunction", f) + let instr = b.emit( + BeginAsyncFunction(parameters: randomParameters.parameters, functionName: nil)) + if randomParameters.parameters.hasRestParameter && probability(0.2) { + b.getProperty("length", of: instr.innerOutputs.last!) + } + b.runtimeData.push("asyncFunction", instr.output) }, GeneratorStub("AsyncFunctionEndGenerator", inContext: .single([.javascript, .subroutine, .asyncFunction])) { b in b.await(b.randomJsVariable()) @@ -1407,9 +1426,12 @@ public let CodeGenerators: [CodeGenerator] = [ let randomParameters = b.randomParameters() b.setParameterTypesForNextSubroutine( randomParameters.parameterTypes) - b.emit( + let instr = b.emit( BeginAsyncArrowFunction( parameters: randomParameters.parameters)) + if randomParameters.parameters.hasRestParameter && probability(0.2) { + b.getProperty("length", of: instr.innerOutputs.last!) + } }, GeneratorStub( "AsyncArrowFunctionAwaitGenerator", @@ -1435,10 +1457,13 @@ public let CodeGenerators: [CodeGenerator] = [ let randomParameters = b.randomParameters() b.setParameterTypesForNextSubroutine( randomParameters.parameterTypes) - let f = b.emit( + let instr = b.emit( BeginAsyncGeneratorFunction( - parameters: randomParameters.parameters, functionName: nil)).output - b.runtimeData.push("asyncGeneratorFunction", f) + parameters: randomParameters.parameters, functionName: nil)) + if randomParameters.parameters.hasRestParameter && probability(0.2) { + b.getProperty("length", of: instr.innerOutputs.last!) + } + b.runtimeData.push("asyncGeneratorFunction", instr.output) }, GeneratorStub("AsyncGeneratorFunctionEndGenerator", inContext: .single([.javascript, .subroutine, .generatorFunction, .asyncFunction])) { b in b.await(b.randomJsVariable()) @@ -1944,7 +1969,12 @@ public let CodeGenerators: [CodeGenerator] = [ } } - b.destruct(arr, selecting: indices, lastIsRest: probability(0.33)) + let lastIsRest = !indices.isEmpty && probability(0.33) + let vars = b.destruct(arr, selecting: indices, lastIsRest: lastIsRest) + + if lastIsRest && probability(0.2) { + b.getProperty("length", of: vars.last!) + } }, CodeGenerator( @@ -1958,9 +1988,14 @@ public let CodeGenerators: [CodeGenerator] = [ candidates.append(b.randomJsVariable()) } } + let lastIsRest = !candidates.isEmpty && probability(0.33) b.destruct( arr, selecting: indices, into: candidates, - lastIsRest: probability(0.33)) + lastIsRest: lastIsRest) + + if lastIsRest && probability(0.2) { + b.getProperty("length", of: candidates.last!) + } }, CodeGenerator("DestructObjectGenerator", inputs: .preferred(.object())) { @@ -2398,10 +2433,14 @@ public let CodeGenerators: [CodeGenerator] = [ indices = [0] } - b.emit( + let hasRestElement = probability(0.2) + let vars = b.emit( BeginForOfLoopWithDestruct( - indices: indices, hasRestElement: probability(0.2)), - withInputs: [obj]) + indices: indices, hasRestElement: hasRestElement), + withInputs: [obj]).innerOutputs + if hasRestElement && probability(0.2) { + b.getProperty("length", of: vars.last!) + } }, GeneratorStub( "ForOfWithDestructLoopEndGenerator", diff --git a/Sources/Fuzzilli/FuzzIL/JsOperations.swift b/Sources/Fuzzilli/FuzzIL/JsOperations.swift index d8dcbe154..bc87ecb2a 100644 --- a/Sources/Fuzzilli/FuzzIL/JsOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/JsOperations.swift @@ -1748,6 +1748,7 @@ final class DestructArray: JsOperation { init(indices: [Int64], lastIsRest: Bool) { assert(indices == indices.sorted(), "Indices must be sorted in ascending order") assert(indices.count == Set(indices).count, "Indices must not have duplicates") + assert(!lastIsRest || !indices.isEmpty, "DestructArray with lastIsRest requires at least one index") self.indices = indices self.lastIsRest = lastIsRest super.init(numInputs: 1, numOutputs: indices.count) @@ -1764,6 +1765,7 @@ final class DestructArrayAndReassign: JsOperation { init(indices: [Int64], lastIsRest:Bool) { assert(indices == indices.sorted(), "Indices must be sorted in ascending order") assert(indices.count == Set(indices).count, "Indices must not have duplicates") + assert(!lastIsRest || !indices.isEmpty, "DestructArray with lastIsRest requires at least one index") self.indices = indices self.lastIsRest = lastIsRest // The first input is the array being destructed From 15f47d074dd4e9044e390ba2d91b0c69f27dc09c Mon Sep 17 00:00:00 2001 From: Pawel Krawczyk Date: Thu, 27 Nov 2025 16:55:33 +0000 Subject: [PATCH 19/89] Small readability improvement. Change-Id: I02ac85b1f90e3a21a6310157457d2e0c0ec364d3 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8796658 Auto-Submit: Pawel Krawczyk Commit-Queue: Dominik Klemba Reviewed-by: Dominik Klemba --- Tests/FuzzilliTests/ContextGraphTest.swift | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/Tests/FuzzilliTests/ContextGraphTest.swift b/Tests/FuzzilliTests/ContextGraphTest.swift index 1ffdbc914..fb068f34f 100644 --- a/Tests/FuzzilliTests/ContextGraphTest.swift +++ b/Tests/FuzzilliTests/ContextGraphTest.swift @@ -26,21 +26,9 @@ class ContextGraphTests: XCTestCase { XCTAssertEqual(reachableContexts, reachableContexts2) - XCTAssertEqual(reachableContexts, - Set([.javascript, - .method, - .classMethod, - .switchCase, - .classDefinition, - .switchBlock, - .asyncFunction, - .wasmFunction, - .wasm, - .loop, - .generatorFunction, - .objectLiteral, - .subroutine, - .wasmTypeGroup])) + var expectedReachedContexts = Set(Context.allCases) + expectedReachedContexts.remove(.empty) + XCTAssertEqual(reachableContexts, expectedReachedContexts) } func testSubsetReachabilityCalculation() { From 8a542afbbf2c6b6dd5f62fdcf8361ec7f6979110 Mon Sep 17 00:00:00 2001 From: Pawel Krawczyk Date: Fri, 28 Nov 2025 09:45:09 +0000 Subject: [PATCH 20/89] Throw exception in TryCatchFinally blocks (with certain probability). Bug: 455512155,455513417 Change-Id: I52dc1b9d27d02ee1e5d905eca3705d9a9c4a6661 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8796096 Commit-Queue: Pawel Krawczyk Reviewed-by: Dominik Klemba --- .../CodeGen/CodeGeneratorWeights.swift | 1 + Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 32 +++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift index 1dff70aed..c60c853ce 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift @@ -186,6 +186,7 @@ public let codeGeneratorWeights = [ "TryCatchGenerator": 5, "TryFinallyGenerator": 5, "ThrowGenerator": 1, + "NondeterministiclyThrowGenerator": 1, "BlockStatementGenerator": 1, // Special generators diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 0c5442f4a..002b46b01 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -2492,6 +2492,8 @@ public let CodeGenerators: [CodeGenerator] = [ inContext: .single(.javascript), provides: [.javascript] ) { b in + if (probability(0.1)) { throwRandomJSVariable(b) } + else if (probability(0.3)) { nondeterministiclyThrowRandomJSVariable(b, withProbability: 0.5)} b.emit(BeginCatch()) }, GeneratorStub( @@ -2524,6 +2526,8 @@ public let CodeGenerators: [CodeGenerator] = [ inContext: .single(.javascript), provides: [.javascript] ) { b in + if (probability(0.1)) { throwRandomJSVariable(b) } + else if (probability(0.3)) { nondeterministiclyThrowRandomJSVariable(b, withProbability: 0.5)} b.emit(BeginCatch()) }, GeneratorStub( @@ -2560,8 +2564,11 @@ public let CodeGenerators: [CodeGenerator] = [ ]), CodeGenerator("ThrowGenerator") { b in - let v = b.randomJsVariable() - b.throwException(v) + throwRandomJSVariable(b) + }, + + CodeGenerator("NondeterministiclyThrowGenerator") { b in + nondeterministiclyThrowRandomJSVariable(b, withProbability: 0.3) }, // @@ -3018,3 +3025,24 @@ public let CodeGenerators: [CodeGenerator] = [ }, catchBody: { _ in }) }, ] + +private func nondeterministiclyThrowRandomJSVariable(_ b: ProgramBuilder, withProbability probability: Double) { + assert(probability >= 0 && probability <= 1) + + let threshold = b.loadFloat(probability) + let Math = b.createNamedVariable(forBuiltin: "Math") + let randomNumber = b.callMethod("random", on: Math, withArgs: []) + // Let's not influence other generators with Math & randomly generated number. + b.hide(Math) + b.hide(randomNumber) + + let condition = b.compare(threshold, with: randomNumber, using: Comparator.greaterThan) + b.buildIf(condition) { + throwRandomJSVariable(b) + } +} + +private func throwRandomJSVariable(_ b: ProgramBuilder) { + let v = b.randomJsVariable() + b.throwException(v) +} From 8adcc7639ffea09e3aa96bd5cdd77f0650a91a88 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Mon, 1 Dec 2025 11:26:51 +0100 Subject: [PATCH 21/89] Add script to transpile existing test cases This adds a stand-alone python script that with the following properties: * Mimic various test configs from V8 (for now test262 without staging) * List all supported tests from a config * Transpile all tests in parallel (i.e. compile to FuzzIL and lift back to JS) * Print statistics and return relevant results as a json file * The results contain stats that we can track as a metric, e.g. the percentage of properly transpiled tests. The script is tested with a Python unit tests that runs the script E2E, also hooked up through a presubmit script so that it's tested on updates. Bug: 442444727 Change-Id: I29c89cede59aef885e45a0ae0821d3388bc51e8f Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8787097 Reviewed-by: Matthias Liedtke Commit-Queue: Michael Achenbach --- Tools/transpile_tests/.gitignore | 1 + Tools/transpile_tests/.vpython3 | 21 ++ Tools/transpile_tests/PRESUBMIT.py | 34 +++ Tools/transpile_tests/test_transpile_tests.py | 111 ++++++++++ .../data/test/folder1/subfolder1/Test1.js | 15 ++ .../data/test/folder1/subfolder1/Test2.js | 15 ++ .../test/test262/data/test/folder2/Test3.js | 15 ++ .../data/test/folder2/Test3_FIXTURE.js | 15 ++ .../test262/data/test/folder2/Test4_fail.js | 15 ++ .../data/test/folder2/Test4_negative.js | 15 ++ .../data/tools/packaging/parseTestRecord.py | 19 ++ Tools/transpile_tests/transpile_tests.py | 204 ++++++++++++++++++ 12 files changed, 480 insertions(+) create mode 100644 Tools/transpile_tests/.gitignore create mode 100644 Tools/transpile_tests/.vpython3 create mode 100644 Tools/transpile_tests/PRESUBMIT.py create mode 100644 Tools/transpile_tests/test_transpile_tests.py create mode 100644 Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder1/subfolder1/Test1.js create mode 100644 Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder1/subfolder1/Test2.js create mode 100644 Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder2/Test3.js create mode 100644 Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder2/Test3_FIXTURE.js create mode 100644 Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder2/Test4_fail.js create mode 100644 Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder2/Test4_negative.js create mode 100644 Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/tools/packaging/parseTestRecord.py create mode 100644 Tools/transpile_tests/transpile_tests.py diff --git a/Tools/transpile_tests/.gitignore b/Tools/transpile_tests/.gitignore new file mode 100644 index 000000000..7e99e367f --- /dev/null +++ b/Tools/transpile_tests/.gitignore @@ -0,0 +1 @@ +*.pyc \ No newline at end of file diff --git a/Tools/transpile_tests/.vpython3 b/Tools/transpile_tests/.vpython3 new file mode 100644 index 000000000..f683b68bc --- /dev/null +++ b/Tools/transpile_tests/.vpython3 @@ -0,0 +1,21 @@ +python_version: "3.11" + +wheel: < + name: "infra/python/wheels/six-py2_py3" + version: "version:1.16.0" +> + +wheel: < + name: "infra/python/wheels/pbr-py2_py3" + version: "version:3.0.0" +> + +wheel: < + name: "infra/python/wheels/mock-py2_py3" + version: "version:2.0.0" +> + +wheel: < + name: "infra/python/wheels/pyfakefs-py3" + version: "version:5.7.3" +> \ No newline at end of file diff --git a/Tools/transpile_tests/PRESUBMIT.py b/Tools/transpile_tests/PRESUBMIT.py new file mode 100644 index 000000000..cea2e358f --- /dev/null +++ b/Tools/transpile_tests/PRESUBMIT.py @@ -0,0 +1,34 @@ +# 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. + +USE_PYTHON3 = True + + +def _CommonChecks(input_api, output_api): + return input_api.RunTests( + input_api.canned_checks.GetUnitTestsRecursively( + input_api, + output_api, + input_api.os_path.join(input_api.PresubmitLocalPath()), + files_to_check=[r'test.+\.py$'], + files_to_skip=[], + run_on_python2=False, + )) + + +def CheckChangeOnUpload(input_api, output_api): + return _CommonChecks(input_api, output_api) + +def CheckChangeOnCommit(input_api, output_api): + return _CommonChecks(input_api, output_api) diff --git a/Tools/transpile_tests/test_transpile_tests.py b/Tools/transpile_tests/test_transpile_tests.py new file mode 100644 index 000000000..cc776ce27 --- /dev/null +++ b/Tools/transpile_tests/test_transpile_tests.py @@ -0,0 +1,111 @@ +# 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 contextlib +import glob +import io +import json +import os +import unittest + +from collections import namedtuple +from mock import patch +from pathlib import Path +from pyfakefs import fake_filesystem_unittest + +import transpile_tests + + +TEST_DATA = Path(__file__).parent / 'testdata' + +class TestTranspileTests(fake_filesystem_unittest.TestCase): + + @fake_filesystem_unittest.patchfs(allow_root_user=True) + def test_full_run(self, fs): + base_dir = TEST_DATA / 'transpile_full_run' / 'v8' + fs.create_dir('/output') + fs.add_real_directory(base_dir) + + Process = namedtuple('Process', 'returncode stdout') + + # Mock out the FuzzIL tool, but mimick writing the desired output file. + # Simulate one compilation failure for the test with "fail" in its name. + def fake_transpile(input_file, output_file): + if 'fail' in str(output_file): + return Process(1, 'Failed!'.encode('utf-8')) + with open(output_file, 'w') as f: + f.write('') + return Process(0, b'') + + # Replace the multiprocessing Pool with a fake that doesn't mess with the + # fake file system. + class FakePool: + def __init__(self, _): + pass + + def __enter__(self): + return self + + def __exit__(self, *_): + pass + + def imap_unordered(self, *args): + return map(*args) + + f = io.StringIO() + with contextlib.redirect_stdout(f): + with patch( + 'transpile_tests.run_transpile_tool', fake_transpile): + with patch( + 'multiprocessing.Pool', FakePool): + transpile_tests.main([ + '--config', 'test262', + '--base-dir', str(base_dir), + '--output-dir', '/output', + '--json-output', '/output.json', + ]) + + # Verify the output. + self.assertEqual( + 'Successfully compiled 75.00% (3 of 4) test cases.', + f.getvalue().strip()) + + # Verify the written output files. + expected_files = [ + '/output/test/test262/data/test/folder1/subfolder1/Test1.js', + '/output/test/test262/data/test/folder1/subfolder1/Test2.js', + '/output/test/test262/data/test/folder2/Test3.js', + ] + self.assertEqual( + expected_files, glob.glob('/output/**/*.*', recursive=True)) + + # Verify the results written to the json output file. + with open('/output.json') as f: + actual_results = json.load(f) + + expected_results = { + 'num_tests': 4, + 'num_successes': 3, + 'percent_successes': 75.0, + 'failures': [ + { + 'output': 'Failed!', + 'path': 'test/test262/data/test/folder2/Test4_fail.js', + }, + ], + } + self.assertEqual(expected_results, actual_results) + +if __name__ == '__main__': + unittest.main() diff --git a/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder1/subfolder1/Test1.js b/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder1/subfolder1/Test1.js new file mode 100644 index 000000000..27cf2af62 --- /dev/null +++ b/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder1/subfolder1/Test1.js @@ -0,0 +1,15 @@ +// 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. + +print(42) diff --git a/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder1/subfolder1/Test2.js b/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder1/subfolder1/Test2.js new file mode 100644 index 000000000..27cf2af62 --- /dev/null +++ b/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder1/subfolder1/Test2.js @@ -0,0 +1,15 @@ +// 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. + +print(42) diff --git a/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder2/Test3.js b/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder2/Test3.js new file mode 100644 index 000000000..27cf2af62 --- /dev/null +++ b/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder2/Test3.js @@ -0,0 +1,15 @@ +// 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. + +print(42) diff --git a/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder2/Test3_FIXTURE.js b/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder2/Test3_FIXTURE.js new file mode 100644 index 000000000..27cf2af62 --- /dev/null +++ b/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder2/Test3_FIXTURE.js @@ -0,0 +1,15 @@ +// 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. + +print(42) diff --git a/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder2/Test4_fail.js b/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder2/Test4_fail.js new file mode 100644 index 000000000..27cf2af62 --- /dev/null +++ b/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder2/Test4_fail.js @@ -0,0 +1,15 @@ +// 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. + +print(42) diff --git a/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder2/Test4_negative.js b/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder2/Test4_negative.js new file mode 100644 index 000000000..27cf2af62 --- /dev/null +++ b/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/test/folder2/Test4_negative.js @@ -0,0 +1,15 @@ +// 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. + +print(42) diff --git a/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/tools/packaging/parseTestRecord.py b/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/tools/packaging/parseTestRecord.py new file mode 100644 index 000000000..5f5e154aa --- /dev/null +++ b/Tools/transpile_tests/testdata/transpile_full_run/v8/test/test262/data/tools/packaging/parseTestRecord.py @@ -0,0 +1,19 @@ +# 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. + + +def parseTestRecord(src, name): + if 'negative' in str(name): + return {'negative'} + return {} diff --git a/Tools/transpile_tests/transpile_tests.py b/Tools/transpile_tests/transpile_tests.py new file mode 100644 index 000000000..8deccb1a9 --- /dev/null +++ b/Tools/transpile_tests/transpile_tests.py @@ -0,0 +1,204 @@ +# 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. + +""" +Script to transpile (compile to FuzzIL and lift again to JS) multiple +tests in parallel with the Fuzzilli FuzzILTool. The output of the +script provides 1) the tests so that those can be used e.g. for execution +in the V8 test framework, and 2) statistics about the overall transpilation +state to track the progress of extending the compiler. +""" + +import argparse +import importlib.machinery +import json +import multiprocessing +import os +import subprocess +import sys + +from pathlib import Path + + +class DefaultMetaDataParser: + """Class instantiated once per test configuration/suite, providing a + method to check for supported tests based on their metadata. + """ + def is_supported(self, abspath, relpath): + return any(relpath.name.endswith(ext) for ext in ['.js', '.mjs']) + + +class Test262MetaDataParser(DefaultMetaDataParser): + def __init__(self, base_dir): + """Metadata parsing for Test262 analog to the V8 test suite definition.""" + tools_abs_path = base_dir / 'test/test262/data/tools/packaging' + loader = importlib.machinery.SourceFileLoader( + 'parseTestRecord', f'{tools_abs_path}/parseTestRecord.py') + self.parse = loader.load_module().parseTestRecord + self.excluded_suffixes = ['_FIXTURE.js'] + self.excluded_dirs = ['staging'] + + def is_supported(self, abspath, relpath): + if not super().is_supported(abspath, relpath): + return False + + if any(relpath.name.endswith(suffix) + for suffix in self.excluded_suffixes): + return False + + if any(str(relpath).startswith(directory) + for directory in self.excluded_dirs): + return False + + with open(abspath, encoding='utf-8') as f: + content = f.read() + record = self.parse(content, relpath) + # We don't support negative tests, which typically exhibit syntax errors. + return 'negative' not in record + + +TEST_CONFIGS = { + 'test262': { + 'path': 'test/test262/data/test', + 'excluded_suffixes': ['_FIXTURE.js'], + # TODO(https://crbug.com/442444727): We might want to track the staging + # tests separately. Those typically address in-progress JS features with + # a high import-failure rate. + 'excluded_dirs': ['staging'], + 'metadata_parser': Test262MetaDataParser, + } +} + + +def list_test_filenames(test_root, is_supported_fun): + """Walk directories and return all absolute test filenames for supported + tests. + """ + for dirname, dirs, files in os.walk(test_root, followlinks=True): + dirs.sort() + files.sort() + for filename in files: + abspath = Path(dirname) / filename + if is_supported_fun(abspath, abspath.relative_to(test_root)): + yield abspath + + +def run_transpile_tool(input_file, output_file): + cmd = [ + '.build/debug/FuzzILTool', + '--compile', + input_file, + f'--outputPathJS={output_file}', + ] + return subprocess.run( + cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + + +def transpile_test(args): + """Transpile one test from JS -> JS with Fuzzilli. + + This method is to be called via the multi-process boundary. + + We rebuild the original directory structure on the output side. + """ + input_file, output_file = args + os.makedirs(output_file.parent, exist_ok=True) + process = run_transpile_tool(input_file, output_file) + return process.returncode, input_file, process.stdout.decode('utf-8') + + +def verbose_print(options, text): + if options.verbose: + print(text) + + +def transpile_suite(options, base_dir, output_dir): + """Transpile all tests from one suite configuration in parallel.""" + test_config = TEST_CONFIGS[options.config] + test_input_dir = base_dir / test_config['path'] + metadata_parser = test_config['metadata_parser'](base_dir) + + # Prepare inputs as a generator over tuples of input/output path. + verbose_print(options, f'Listing tests in {test_input_dir}') + def test_input_gen(): + for abspath in list_test_filenames( + test_input_dir, metadata_parser.is_supported): + yield (abspath, output_dir / abspath.relative_to(base_dir)) + + # Iterate over all tests in parallel and collect stats. + num_tests = 0 + failures = [] + with multiprocessing.Pool(multiprocessing.cpu_count()) as pool: + for exit_code, abspath, stdout in pool.imap_unordered( + transpile_test, test_input_gen()): + num_tests += 1 + if exit_code != 0: + relpath = abspath.relative_to(base_dir) + failures.append({'path': str(relpath), 'output': stdout}) + verbose_print(options, f'Failed to compile {relpath}') + if (num_tests + 1) % 500 == 0: + print(f'Processed {num_tests + 1} test cases.') + + # Render and return results. + assert num_tests, 'Failed to find any tests.' + num_successes = num_tests - len(failures) + ratio = float(num_successes) / num_tests * 100 + print(f'Successfully compiled {ratio:.2f}% ' + f'({num_successes} of {num_tests}) test cases.') + return { + 'num_tests': num_tests, + 'num_successes': num_successes, + 'percent_successes': ratio, + 'failures': failures, + } + + +def write_json_output(path, results): + with open(path, 'w') as f: + json.dump(results, f) + + +def parse_args(args): + parser = argparse.ArgumentParser() + parser.add_argument( + '--base-dir', required=True, + help='Absolute path to the V8 checkout.') + parser.add_argument( + '--config', required=True, choices=TEST_CONFIGS.keys(), + help='Name of the supported test configuration.') + parser.add_argument( + '--output-dir', required=True, + help='Absolute path pointing to an empty directory, ' + 'where this script will place the output files.') + parser.add_argument( + '--json-output', + help='Optional absolute path to a json file, ' + 'where this script will write its stats to.') + parser.add_argument( + '-v', '--verbose', default=False, action='store_true', + help='Print more verbose output.') + return parser.parse_args(args) + + +def main(args): + options = parse_args(args) + base_dir = Path(options.base_dir) + output_dir = Path(options.output_dir) + results = transpile_suite(options, base_dir, output_dir) + if options.json_output: + write_json_output(options.json_output, results) + + +if __name__ == '__main__': + main(sys.argv[1:]) From 7f59fbf78353079f0bb873c0249dd3824fc4ab01 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Thu, 27 Nov 2025 19:14:31 +0100 Subject: [PATCH 22/89] Enable bundling Node.js in the CWD This makes the executor look for Node.js in the CWD, which makes it easy to bundle both together when porting the FuzzILTool to another machine. Bug: 442444727 Change-Id: I80adcde79fb6d773f3f47817da24188bbbe5431e Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8796659 Reviewed-by: Pawel Krawczyk Commit-Queue: Michael Achenbach Reviewed-by: Matthias Liedtke --- Sources/Fuzzilli/Util/JavaScriptExecutor.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/Fuzzilli/Util/JavaScriptExecutor.swift b/Sources/Fuzzilli/Util/JavaScriptExecutor.swift index 4340c67ef..c67e411ea 100644 --- a/Sources/Fuzzilli/Util/JavaScriptExecutor.swift +++ b/Sources/Fuzzilli/Util/JavaScriptExecutor.swift @@ -166,6 +166,10 @@ public class JavaScriptExecutor { var directories = pathVar.split(separator: ":") // Also append the homebrew binary path since it may not be in $PATH, especially inside XCode. directories.append("/opt/homebrew/bin") + // Append the CWD to enable bundling node.js from where the script + // is called. + directories.append( + String.SubSequence(FileManager.default.currentDirectoryPath)) for directory in directories { let path = String(directory + "/node") if FileManager.default.isExecutableFile(atPath: path) { From e35cbb5b7b784cae910776e4edeefc8553316d56 Mon Sep 17 00:00:00 2001 From: Pawel Krawczyk Date: Tue, 2 Dec 2025 11:39:11 +0000 Subject: [PATCH 23/89] Add support for shared references. Generating shared ref variables to be done in following CLs. See https://github.com/WebAssembly/shared-everything-threads/blob/main/proposals/shared-everything-threads/Overview.md. Bug: 448349112 Change-Id: I3358ce9cdd528147b66f1954ef1a008b048e06df Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8734256 Commit-Queue: Matthias Liedtke Reviewed-by: Dominik Klemba Commit-Queue: Pawel Krawczyk --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 99 +++--- Sources/Fuzzilli/CodeGen/CodeGenerator.swift | 6 +- .../Fuzzilli/CodeGen/ProgramTemplates.swift | 11 +- .../Fuzzilli/CodeGen/WasmCodeGenerators.swift | 39 ++- Sources/Fuzzilli/Configuration.swift | 1 + .../Environment/JavaScriptEnvironment.swift | 2 +- Sources/Fuzzilli/FuzzIL/Instruction.swift | 69 +++-- Sources/Fuzzilli/FuzzIL/JSTyper.swift | 11 +- Sources/Fuzzilli/FuzzIL/TypeSystem.swift | 127 ++++++-- Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 45 +-- .../Fuzzilli/Lifting/JavaScriptLifter.swift | 23 +- Sources/Fuzzilli/Lifting/WasmLifter.swift | 81 +++-- .../Fuzzilli/Mutators/OperationMutator.swift | 32 +- Sources/Fuzzilli/Protobuf/operations.pb.swift | 28 +- Sources/Fuzzilli/Protobuf/operations.proto | 3 +- .../Profiles/V8CommonProfile.swift | 5 +- Tests/FuzzilliTests/JSTyperTests.swift | 8 +- Tests/FuzzilliTests/LifterTest.swift | 2 +- Tests/FuzzilliTests/LiveTests.swift | 7 +- Tests/FuzzilliTests/ProgramBuilderTest.swift | 52 ++-- Tests/FuzzilliTests/TypeSystemTest.swift | 108 ++++--- Tests/FuzzilliTests/WasmTableTests.swift | 4 +- Tests/FuzzilliTests/WasmTests.swift | 283 ++++++++++++------ 23 files changed, 643 insertions(+), 403 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 6bc1dbd6b..63ef596ed 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -1217,7 +1217,6 @@ public class ProgramBuilder { numberOfHiddenVariables -= 1 } - /// Hides a variable containing a function from the function's body. /// /// For example, in @@ -1250,19 +1249,20 @@ public class ProgramBuilder { unhide(variableToHide) } + // TODO(pawkra): enable shared types. private static func matchingWasmTypes(jsType: ILType) -> [ILType] { if jsType.Is(.integer) { return [.wasmi32, .wasmf64, .wasmf32] } else if jsType.Is(.number) { - return [.wasmf32, .wasmf64, .wasmi32, .wasmRefI31, .wasmI31Ref] + return [.wasmf32, .wasmf64, .wasmi32, .wasmRefI31(), .wasmI31Ref()] } else if jsType.Is(.bigint) { return [.wasmi64] } else if jsType.Is(.function()) { // TODO(gc): Add support for specific signatures. - return [.wasmFuncRef] + return [.wasmFuncRef()] } else { // TODO(gc): Add support for types of the anyref hierarchy. - return [.wasmExternRef] + return [.wasmExternRef()] } } @@ -1272,6 +1272,7 @@ public class ProgramBuilder { } // Helper that converts a Wasm type to its deterministic known JS counterparts. + // TODO(pawkra): enable shared types. private static func mapWasmToJsType(_ type: ILType) -> ILType { if type.Is(.wasmi32) { return .integer @@ -1288,12 +1289,12 @@ public class ProgramBuilder { return .jsAnything } else if type.Is(.nothing) { return .undefined - } else if type.Is(.wasmFuncRef) { + } else if type.Is(.wasmFuncRef()) { // TODO(cffsmith): refine this type with the signature if we can. return .function() - } else if type.Is(.wasmI31Ref) { + } else if type.Is(.wasmI31Ref()) { return .integer - } else if type.Is(.wasmNullRef) || type.Is(.wasmNullExternRef) || type.Is(.wasmNullFuncRef) { + } else if type.Is(.wasmNullRef()) || type.Is(.wasmNullExternRef()) || type.Is(.wasmNullFuncRef()) { // This is slightly imprecise: The null types only accept null, not undefined but // Fuzzilli doesn't differentiate between null and undefined in its type system. return .nullish @@ -3881,8 +3882,9 @@ public class ProgramBuilder { b.emit(WasmDropElementSegment(), withInputs: [elementSegment], types: [.wasmElementSegment()]) } + // TODO(pawkra): support shared tables and element segments. public func wasmTableInit(elementSegment: Variable, table: Variable, tableOffset: Variable, elementSegmentOffset: Variable, nrOfElementsToUpdate: Variable) { - let elementSegmentType = ILType.wasmFuncRef + let elementSegmentType = ILType.wasmFuncRef() let tableElemType = b.type(of: table).wasmTableType!.elementType assert(elementSegmentType.Is(tableElemType)) @@ -4012,18 +4014,22 @@ public class ProgramBuilder { return Array(b.emit(WasmEndLoop(outputTypes: signature.outputTypes), withInputs: fallthroughResults, types: signature.outputTypes).outputs) } + // TODO(pawkra): enable shared types. @discardableResult func wasmBuildTryTable(with signature: WasmSignature, args: [Variable], catches: [WasmBeginTryTable.CatchKind], body: (Variable, [Variable]) -> [Variable]) -> [Variable] { assert(zip(signature.parameterTypes, args).allSatisfy {b.type(of: $1).Is($0)}) #if DEBUG var argIndex = signature.parameterTypes.count + let assertLabelTypeData: (ILType) -> () = { labelType in + assert(labelType.Is(.anyLabel)) + assert(labelType.wasmLabelType!.parameters.last!.Is(.wasmExnRef())) + } for catchKind in catches { switch catchKind { case .Ref: assert(b.type(of: args[argIndex]).Is(.object(ofGroup: "WasmTag"))) let labelType = b.type(of: args[argIndex + 1]) - assert(labelType.Is(.anyLabel)) - assert(labelType.wasmLabelType!.parameters.last!.Is(.wasmExnRef)) + assertLabelTypeData(labelType) argIndex += 2 case .NoRef: assert(b.type(of: args[argIndex]).Is(.object(ofGroup: "WasmTag"))) @@ -4031,8 +4037,7 @@ public class ProgramBuilder { argIndex += 2 case .AllRef: let labelType = b.type(of: args[argIndex]) - assert(labelType.Is(.anyLabel)) - assert(labelType.wasmLabelType!.parameters.last!.Is(.wasmExnRef)) + assertLabelTypeData(labelType) argIndex += 1 case .AllNoRef: assert(b.type(of: args[argIndex]).Is(.anyLabel)) @@ -4095,8 +4100,8 @@ public class ProgramBuilder { b.emit(WasmThrow(parameterTypes: tagType.parameters), withInputs: [tag] + inputs, types: [.object(ofGroup: "WasmTag")] + tagType.parameters) } - public func wasmBuildThrowRef(exception: Variable) { - b.emit(WasmThrowRef(), withInputs: [exception], types: [.wasmExnRef]) + public func wasmBuildThrowRef(exception: Variable, sharedRef: Bool) { + b.emit(WasmThrowRef(), withInputs: [exception], types: [.wasmExnRef(shared: sharedRef)]) } public func wasmBuildLegacyRethrow(_ exceptionLabel: Variable) { @@ -4137,15 +4142,23 @@ public class ProgramBuilder { // TODO(cffsmith): Can we improve this once we have better support for ad hoc // code generation in other contexts? switch type.wasmReferenceType?.kind { - case .Abstract(let heapType): - if heapType == .WasmI31 { - // Prefer generating a non-null value. - return probability(0.2) && type.wasmReferenceType!.nullability - ? self.wasmRefNull(type: type) - : self.wasmRefI31(self.consti32(Int32(truncatingIfNeeded: b.randomInt()))) + case .Abstract(let heapTypeInfo): + // TODO(pawkra): add support for shared refs. + assert(!heapTypeInfo.shared) + if probability(0.2) && type.wasmReferenceType!.nullability { + return self.wasmRefNull(type: type) + } + // Prefer generating a non-null value. + if heapTypeInfo.heapType == .WasmI31 { + return self.wasmRefI31(self.consti32(Int32(truncatingIfNeeded: b.randomInt()))) + } + // TODO(pawkra): support different types. For now + // fallback to refNull if possible. + if (type.wasmReferenceType!.nullability) { + return self.wasmRefNull(type: type) + } else { + return nil } - assert(type.wasmReferenceType!.nullability) - return self.wasmRefNull(type: type) case .Index(_), .none: break // Unimplemented @@ -4343,18 +4356,18 @@ public class ProgramBuilder { } @discardableResult - public func wasmI31Get(_ refI31: Variable, isSigned: Bool) -> Variable { - return b.emit(WasmI31Get(isSigned: isSigned), withInputs: [refI31], types: [.wasmI31Ref]).output + public func wasmI31Get(_ refI31: Variable, isSigned: Bool, shared: Bool) -> Variable { + return b.emit(WasmI31Get(isSigned: isSigned), withInputs: [refI31], types: [.wasmI31Ref(shared: shared)]).output } @discardableResult - public func wasmAnyConvertExtern(_ ref: Variable) -> Variable { - b.emit(WasmAnyConvertExtern(), withInputs: [ref], types: [.wasmExternRef]).output + public func wasmAnyConvertExtern(_ ref: Variable, shared: Bool) -> Variable { + b.emit(WasmAnyConvertExtern(), withInputs: [ref], types: [.wasmExternRef(shared: shared)]).output } @discardableResult - public func wasmExternConvertAny(_ ref: Variable) -> Variable { - b.emit(WasmExternConvertAny(), withInputs: [ref], types: [.wasmAnyRef]).output + public func wasmExternConvertAny(_ ref: Variable, shared: Bool) -> Variable { + b.emit(WasmExternConvertAny(), withInputs: [ref], types: [.wasmAnyRef(shared: shared)]).output } } @@ -4423,13 +4436,14 @@ public class ProgramBuilder { @discardableResult public func addElementSegment(elements: [Variable]) -> Variable { - let inputTypes = Array(repeating: getEntryTypeForTable(elementType: ILType.wasmFuncRef), count: elements.count) + let inputTypes = Array(repeating: getEntryTypeForTable(elementType: ILType.wasmFuncRef()), count: elements.count) return b.emit(WasmDefineElementSegment(size: UInt32(elements.count)), withInputs: elements, types: inputTypes).output } + // TODO(pawkra): enable shared tables and element segments (and functions?). public func getEntryTypeForTable(elementType: ILType) -> ILType { switch elementType { - case .wasmFuncRef: + case .wasmFuncRef(): return .wasmFunctionDef() | .function() default: return .object() @@ -4505,6 +4519,7 @@ public class ProgramBuilder { /// Produces a WasmGlobal that is valid to create in the given Context. public func randomWasmGlobal(forContext context: Context) -> WasmGlobal { + // TODO(pawkra): enable shared types. switch context { case .javascript: // These are valid in JS according to: https://webassembly.github.io/spec/js-api/#globals. @@ -4514,7 +4529,7 @@ public class ProgramBuilder { {.wasmf64(self.randomFloat())}, {.wasmi32(Int32(truncatingIfNeeded: self.randomInt()))}, {.wasmi64(self.randomInt())}, - {.externref}) + {.externref(shared: false)}) case .wasm: // TODO: Add simd128 and nullrefs. return withEqualProbability( @@ -4522,9 +4537,9 @@ public class ProgramBuilder { {.wasmf64(self.randomFloat())}, {.wasmi32(Int32(truncatingIfNeeded: self.randomInt()))}, {.wasmi64(self.randomInt())}, - {.externref}, - {.exnref}, - {.i31ref}) + {.externref(shared: false)}, + {.exnref(shared: false)}, + {.i31ref(shared: false)}) default: fatalError("Unsupported context \(context) for a WasmGlobal.") } @@ -4534,21 +4549,23 @@ public class ProgramBuilder { // TODO(mliedtke): The list of types should be shared with function signature generation // etc. We should also support non-nullable references but that requires being able // to generate valid ones which currently isn't the case for most of them. + // TODO(pawkra): enable shared types. return (0.. WasmSignature { // TODO: generalize this to support more types. Also add support for simd128 and // (null)exnref, note however that these types raise exceptions when used from JS. + // TODO(pawkra): enable shared types. let valueTypes: [ILType] = [.wasmi32, .wasmi64, .wasmf32, .wasmf64] - let abstractRefTypes: [ILType] = [.wasmExternRef, .wasmAnyRef, .wasmI31Ref] - let nullTypes: [ILType] = [.wasmNullRef, .wasmNullExternRef, .wasmNullFuncRef] + let abstractRefTypes: [ILType] = [.wasmExternRef(), .wasmAnyRef(), .wasmI31Ref()] + let nullTypes: [ILType] = [.wasmNullRef(), .wasmNullExternRef(), .wasmNullFuncRef()] let randomType = { chooseUniform( from: chooseBiased(from: [nullTypes, abstractRefTypes, valueTypes], factor: 1.5)) @@ -4563,9 +4580,10 @@ public class ProgramBuilder { // abstract heap types. To be able to emit them, generateRandomWasmVar() needs to be able // to generate a sequence that produces such a non-nullable value which might be difficult // for some types as of now. + // TODO(pawkra): enable shared types. (0.. [Variable] { @@ -5319,3 +5337,4 @@ public class ProgramBuilder { } } + diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerator.swift b/Sources/Fuzzilli/CodeGen/CodeGenerator.swift index 174e72924..61d9e181e 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerator.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerator.swift @@ -105,7 +105,7 @@ public class GeneratorStub: Contributor { if type.Is(.wasmTypeDef()) { type.wasmTypeDefinition?.description is WasmArrayTypeDescription } else if type.Is(.anyNonNullableIndexRef) { - type.Is(.wasmArrayRef) + type.Is(.wasmArrayRef()) } else { false } @@ -113,7 +113,7 @@ public class GeneratorStub: Contributor { if type.Is(.wasmTypeDef()) { type.wasmTypeDefinition?.description is WasmStructTypeDescription } else if type.Is(.anyNonNullableIndexRef) { - type.Is(.wasmStructRef) + type.Is(.wasmStructRef()) } else { false } @@ -121,7 +121,7 @@ public class GeneratorStub: Contributor { if type.Is(.wasmTypeDef()) { type.wasmTypeDefinition?.description is WasmSignatureTypeDescription } else if type.Is(.anyNonNullableIndexRef) { - type.Is(.wasmFuncRef) + type.Is(.wasmFuncRef()) } else { false } diff --git a/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift b/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift index 126519941..f356a4cc5 100644 --- a/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift +++ b/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift @@ -99,7 +99,7 @@ public let ProgramTemplates = [ let signature = b.type(of: f!).signature ?? Signature.forUnknownFunction // As we do not yet know what types we have in the Wasm module when we try to call this, let Fuzzilli know that it could potentially use all Wasm types here. - let allWasmTypes: WeightedList = WeightedList([(.wasmi32, 1), (.wasmi64, 1), (.wasmf32, 1), (.wasmf64, 1), (.wasmExternRef, 1), (.wasmFuncRef, 1)]) + let allWasmTypes: WeightedList = WeightedList([(.wasmi32, 1), (.wasmi64, 1), (.wasmf32, 1), (.wasmf64, 1), (.wasmExternRef(), 1), (.wasmFuncRef(), 1)]) var wasmSignature = ProgramBuilder.convertJsSignatureToWasmSignature(signature, availableTypes: allWasmTypes) let wrapped = b.wrapSuspending(function: f!) @@ -152,7 +152,10 @@ public let ProgramTemplates = [ let tagToThrow = chooseUniform(from: wasmTags) let throwParamTypes = b.type(of: tagToThrow).wasmTagType!.parameters let tagToCatchForRethrow = chooseUniform(from: tags) - let catchBlockOutputTypes = b.type(of: tagToCatchForRethrow).wasmTagType!.parameters + [.wasmExnRef] + // TODO(pawkra): support shared variant + let sharedRef = false + let wasmExnRefType = ILType.wasmExnRef(shared: sharedRef) + let catchBlockOutputTypes = b.type(of: tagToCatchForRethrow).wasmTagType!.parameters + [wasmExnRefType] let module = b.buildWasmModule { wasmModule in // Wasm function that throws a tag, catches a tag (the same or a different one) to @@ -173,7 +176,7 @@ public let ProgramTemplates = [ return catchBlockOutputTypes.map(function.findOrGenerateWasmVar) } b.build(n: 10) - function.wasmBuildThrowRef(exception: b.randomVariable(ofType: .wasmExnRef)!) + function.wasmBuildThrowRef(exception: b.randomVariable(ofType: wasmExnRefType)!, sharedRef: sharedRef) return [] } } @@ -206,7 +209,7 @@ public let ProgramTemplates = [ return calleeSig.outputTypes.map(function.findOrGenerateWasmVar) }} - let table = wasmModule.addTable(elementType: .wasmFuncRef, + let table = wasmModule.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: callees.enumerated().map { (index, callee) in .init(indexInTable: index, signature: calleeSig) diff --git a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift index 697f84d4a..855bd34c9 100644 --- a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift @@ -270,6 +270,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ value: newValue) }, + // TODO(pawkra): add shared variant. CodeGenerator("WasmRefNullGenerator", inContext: .single(.wasmFunction)) { b in let function = b.currentWasmModule.currentWasmFunction if let typeDef = (b.findVariable { b.type(of: $0).Is(.wasmTypeDef()) }), @@ -280,7 +281,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ function.wasmRefNull( type: .wasmRef( .Abstract( - chooseUniform(from: WasmAbstractHeapType.allCases)), + HeapTypeInfo(chooseUniform(from: WasmAbstractHeapType.allCases), shared: false)), nullability: true)) } }, @@ -296,16 +297,19 @@ public let WasmCodeGenerators: [CodeGenerator] = [ b.currentWasmModule.currentWasmFunction.wasmRefI31(value) }, - CodeGenerator("WasmI31GetGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmI31Ref)) { b, ref in - b.currentWasmModule.currentWasmFunction.wasmI31Get(ref, isSigned: Bool.random()) + // TODO(pawkra): add shared variant. What about non-null case? + CodeGenerator("WasmI31GetGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmI31Ref())) { b, ref in + b.currentWasmModule.currentWasmFunction.wasmI31Get(ref, isSigned: Bool.random(), shared: false) }, - CodeGenerator("WasmAnyConvertExternGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmExternRef)) { b, ref in - b.currentWasmModule.currentWasmFunction.wasmAnyConvertExtern(ref) + // TODO(pawkra): add shared variant. What about non-null case? + CodeGenerator("WasmAnyConvertExternGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmExternRef())) { b, ref in + b.currentWasmModule.currentWasmFunction.wasmAnyConvertExtern(ref, shared: false) }, - CodeGenerator("WasmExternConvertAnyGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmAnyRef)) { b, ref in - b.currentWasmModule.currentWasmFunction.wasmExternConvertAny(ref) + // TODO(pawkra): add shared variant. What about non-null case? + CodeGenerator("WasmExternConvertAnyGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmAnyRef())) { b, ref in + b.currentWasmModule.currentWasmFunction.wasmExternConvertAny(ref, shared: false) }, // Primitive Value Generators @@ -596,7 +600,8 @@ public let WasmCodeGenerators: [CodeGenerator] = [ // TODO(manoskouk): Generalize these. let minSize = 10 let maxSize: Int? = nil - let elementType = ILType.wasmFuncRef + // TODO(pawkra): support shared variant. + let elementType = ILType.wasmFuncRef() let definedEntryIndices: [Int] var definedEntries: [WasmTableType.IndexInTableAndWasmSignature] = [] @@ -609,7 +614,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ // Currently, only generate entries for funcref tables. // TODO(manoskouk): Generalize this. - if elementType == .wasmFuncRef { + if elementType == .wasmFuncRef() { if b.randomVariable(ofType: expectedEntryType) != nil { // There is at least one function in scope. Add some initial entries to the table. // TODO(manoskouk): Generalize this. @@ -638,7 +643,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ }, CodeGenerator("WasmDefineElementSegmentGenerator", inContext: .single(.wasm)) { b in - let expectedEntryType = b.currentWasmModule.getEntryTypeForTable(elementType: ILType.wasmFuncRef) + let expectedEntryType = b.currentWasmModule.getEntryTypeForTable(elementType: .wasmFuncRef()) if b.randomVariable(ofType: expectedEntryType) == nil { return } @@ -669,7 +674,8 @@ public let WasmCodeGenerators: [CodeGenerator] = [ inputs: .required(.object(ofGroup: "WasmTable")) ) { b, table in let tableType = b.type(of: table).wasmTableType! - if !tableType.elementType.Is(.wasmFuncRef) { return } + // TODO(pawkra): support shared variant. + if !tableType.elementType.Is(.wasmFuncRef()) { return } guard let indexedSignature = tableType.knownEntries.randomElement() else { return } @@ -729,7 +735,8 @@ public let WasmCodeGenerators: [CodeGenerator] = [ ) { b, table in let function = b.currentWasmModule.currentWasmFunction let tableType = b.type(of: table).wasmTableType! - if !tableType.elementType.Is(.wasmFuncRef) { return } + // TODO(pawkra): support shared variant. + if !tableType.elementType.Is(.wasmFuncRef()) { return } guard let indexedSignature = (tableType.knownEntries.filter { @@ -1528,10 +1535,11 @@ public let WasmCodeGenerators: [CodeGenerator] = [ CodeGenerator( "WasmThrowRefGenerator", inContext: .single(.wasmFunction), - inputs: .required(.wasmExnRef) + // TODO(pawkra): support shared variant. + inputs: .required(.wasmExnRef()) ) { b, exception in let function = b.currentWasmModule.currentWasmFunction - function.wasmBuildThrowRef(exception: exception) + function.wasmBuildThrowRef(exception: exception, sharedRef: false) }, CodeGenerator( @@ -1624,7 +1632,8 @@ public let WasmCodeGenerators: [CodeGenerator] = [ [] } if withExnRef { - outputTypes.append(.wasmExnRef) + // TODO(pawkra): support shared variant. + outputTypes.append(.wasmExnRef()) } function.wasmBeginBlock(with: [] => outputTypes, args: []) return outputTypes diff --git a/Sources/Fuzzilli/Configuration.swift b/Sources/Fuzzilli/Configuration.swift index 121e9cbb8..9179ae663 100644 --- a/Sources/Fuzzilli/Configuration.swift +++ b/Sources/Fuzzilli/Configuration.swift @@ -14,6 +14,7 @@ public struct Configuration { /// The commandline arguments used by this instance. + /// TODO(pawkra): use these args or remove them (for now the only usage is printing them). public let arguments: [String] /// Timeout in milliseconds after which child processes will be killed. diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index 9ef54f216..6e8e212f8 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -2215,7 +2215,7 @@ public extension ObjectGroup { name: "WebAssembly", instanceType: nil, properties: [ - "JSTag": .object(ofGroup: "WasmTag", withWasmType: WasmTagType([.wasmExternRef], isJSTag: true)), + "JSTag": .object(ofGroup: "WasmTag", withWasmType: WasmTagType([.wasmExternRef()], isJSTag: true)), "Module": .jsWebAssemblyModuleConstructor, "Global": .jsWebAssemblyGlobalConstructor, "Instance": .jsWebAssemblyInstanceConstructor, diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index 6486e4603..6bf1de3e7 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -388,8 +388,8 @@ extension Instruction: ProtobufConvertible { $0.nullability = underlyingWasmType.wasmReferenceType!.nullability } } - case .Abstract(let heapType): - let kind = switch heapType { + case .Abstract(let heapTypeInfo): + let kind = switch heapTypeInfo.heapType { case .WasmExn: Fuzzilli_Protobuf_WasmReferenceTypeKind.exnref case .WasmI31: @@ -420,6 +420,7 @@ extension Instruction: ProtobufConvertible { $0.refType = Fuzzilli_Protobuf_WasmReferenceType.with { $0.kind = kind $0.nullability = underlyingWasmType.wasmReferenceType!.nullability + $0.isShared = heapTypeInfo.shared } } } @@ -498,17 +499,26 @@ extension Instruction: ProtobufConvertible { return Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal.valuef64(val) case .refFunc(let val): return Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal.funcref(Int64(val)) - case .externref: - return Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal.nullref(Fuzzilli_Protobuf_WasmReferenceTypeKind.externref) - case .exnref: - return Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal.nullref(Fuzzilli_Protobuf_WasmReferenceTypeKind.exnref) - case .i31ref: - return Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal.nullref(Fuzzilli_Protobuf_WasmReferenceTypeKind.i31Ref) + case .externref(let shared): + return nullrefGlobal(.externref, shared: shared) + case .exnref(let shared): + return nullrefGlobal(.exnref, shared: shared) + case .i31ref(let shared): + return nullrefGlobal(.i31Ref, shared: shared) case .imported(let ilType): return Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal.imported(ILTypeToWasmTypeEnum(ilType)) } } + func nullrefGlobal(_ kind: Fuzzilli_Protobuf_WasmReferenceTypeKind, shared: Bool) -> Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal { + return Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal.nullref(Fuzzilli_Protobuf_WasmReferenceType.with { + $0.kind = kind + // TODO(gc): set nullability + $0.nullability = false + $0.isShared = shared + }) + } + func convertWasmCatch(catchKind: WasmBeginTryTable.CatchKind) -> Fuzzilli_Protobuf_WasmCatchKind { switch catchKind { case .NoRef: @@ -1684,37 +1694,40 @@ extension Instruction: ProtobufConvertible { fatalError("Unrecognized wasm value type \(value)") } case .refType(_): - let refKind: WasmReferenceType.Kind = switch wasmType.refType.kind { - case .index: - .Index() + if wasmType.refType.kind == .index { + return .wasmRef(.Index(), nullability: wasmType.refType.nullability) + } + let heapType: WasmAbstractHeapType = switch wasmType.refType.kind { case .externref: - .Abstract(.WasmExtern) + .WasmExtern case .funcref: - .Abstract(.WasmFunc) + .WasmFunc case .exnref: - .Abstract(.WasmExn) + .WasmExn case .i31Ref: - .Abstract(.WasmI31) + .WasmI31 case .anyref: - .Abstract(.WasmAny) + .WasmAny case .eqref: - .Abstract(.WasmEq) + .WasmEq case .structref: - .Abstract(.WasmStruct) + .WasmStruct case .arrayref: - .Abstract(.WasmArray) + .WasmArray case .noneref: - .Abstract(.WasmNone) + .WasmNone case .noexternref: - .Abstract(.WasmNoExtern) + .WasmNoExtern case .nofuncref: - .Abstract(.WasmNoFunc) + .WasmNoFunc case .noexnref: - .Abstract(.WasmNoExn) + .WasmNoExn + case .index: + fatalError("Unexpected index type.") case .UNRECOGNIZED(let value): fatalError("Unrecognized wasm reference type \(value)") } - return .wasmRef(refKind, nullability: wasmType.refType.nullability) + return .wasmRef(heapType, shared: wasmType.refType.isShared, nullability: wasmType.refType.nullability) case .none: fatalError("Absent wasm type") } @@ -1785,13 +1798,13 @@ extension Instruction: ProtobufConvertible { func convertWasmGlobal(_ proto: Fuzzilli_Protobuf_WasmGlobal) -> WasmGlobal { switch proto.wasmGlobal { case .nullref(let val): - switch val { + switch val.kind { case .externref: - return .externref + return .externref(shared: val.isShared) case .exnref: - return .exnref + return .exnref(shared: val.isShared) case .i31Ref: - return .i31ref + return .i31ref(shared: val.isShared) default: fatalError("Unrecognized global wasm reference type \(val)") } diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index 054f242a2..65f9689ac 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -888,17 +888,18 @@ public struct JSTyper: Analyzer { case .wasmRefIsNull(_): setType(of: instr.output, to: .wasmi32) case .wasmRefI31(_): - setType(of: instr.output, to: .wasmRefI31) + // TODO(pawkra): support shared variant. + setType(of: instr.output, to: .wasmRefI31()) case .wasmI31Get(_): setType(of: instr.output, to: .wasmi32) case .wasmAnyConvertExtern(_): // any.convert_extern forwards the nullability bit from the input. let null = type(of: instr.input(0)).wasmReferenceType!.nullability - setType(of: instr.output, to: .wasmRef(.Abstract(.WasmAny), nullability: null)) + setType(of: instr.output, to: .wasmRef(.WasmAny, shared: false, nullability: null)) case .wasmExternConvertAny(_): - // extern.convert_any forwards the nullability bit from the input. + // extern.convert_any forwards the nullability from the input. let null = type(of: instr.input(0)).wasmReferenceType!.nullability - setType(of: instr.output, to: .wasmRef(.Abstract(.WasmExtern), nullability: null)) + setType(of: instr.output, to: .wasmRef(.WasmExtern, shared: false, nullability: null)) case .wasmDefineAdHocSignatureType(let op): startTypeGroup() addSignatureType(def: instr.output, signature: op.signature, inputs: instr.inputs) @@ -1834,7 +1835,7 @@ public struct JSTyper: Analyzer { set(instr.output, .wasmTable(wasmTableType: WasmTableType(elementType: op.tableType.elementType, limits: op.tableType.limits, isTable64: op.tableType.isTable64, knownEntries: []))) case .createWasmJSTag(_): - set(instr.output, .object(ofGroup: "WasmTag", withWasmType: WasmTagType([.wasmExternRef], isJSTag: true))) + set(instr.output, .object(ofGroup: "WasmTag", withWasmType: WasmTagType([.wasmExternRef()], isJSTag: true))) case .createWasmTag(let op): set(instr.output, .object(ofGroup: "WasmTag", withWasmType: WasmTagType(op.parameterTypes))) diff --git a/Sources/Fuzzilli/FuzzIL/TypeSystem.swift b/Sources/Fuzzilli/FuzzIL/TypeSystem.swift index 729550e9c..5fe3d7269 100644 --- a/Sources/Fuzzilli/FuzzIL/TypeSystem.swift +++ b/Sources/Fuzzilli/FuzzIL/TypeSystem.swift @@ -247,23 +247,33 @@ public struct ILType: Hashable { public static let wasmi64 = ILType(definiteType: .wasmi64) public static let wasmf32 = ILType(definiteType: .wasmf32) public static let wasmf64 = ILType(definiteType: .wasmf64) - public static let wasmExternRef = ILType.wasmRef(.Abstract(.WasmExtern), nullability: true) - public static let wasmRefExtern = ILType.wasmRef(.Abstract(.WasmExtern), nullability: false) - public static let wasmFuncRef = ILType.wasmRef(.Abstract(.WasmFunc), nullability: true) - public static let wasmExnRef = ILType.wasmRef(.Abstract(.WasmExn), nullability: true) - public static let wasmI31Ref = ILType.wasmRef(.Abstract(.WasmI31), nullability: true) - public static let wasmRefI31 = ILType.wasmRef(.Abstract(.WasmI31), nullability: false) - public static let wasmAnyRef = ILType.wasmRef(.Abstract(.WasmAny), nullability: true) - public static let wasmRefAny = ILType.wasmRef(.Abstract(.WasmAny), nullability: false) - public static let wasmNullRef = ILType.wasmRef(.Abstract(.WasmNone), nullability: true) - public static let wasmNullExternRef = ILType.wasmRef(.Abstract(.WasmNoExtern), nullability: true) - public static let wasmNullFuncRef = ILType.wasmRef(.Abstract(.WasmNoFunc), nullability: true) - public static let wasmEqRef = ILType.wasmRef(.Abstract(.WasmEq), nullability: true) - public static let wasmStructRef = ILType.wasmRef(.Abstract(.WasmStruct), nullability: true) - public static let wasmArrayRef = ILType.wasmRef(.Abstract(.WasmArray), nullability: true) + public static func wasmExternRef(shared: Bool = false) -> ILType { wasmRef(.WasmExtern, shared: shared, nullability: true) } + public static func wasmRefExtern(shared: Bool = false) -> ILType { wasmRef(.WasmExtern, shared: shared, nullability: false) } + public static func wasmFuncRef(shared: Bool = false) -> ILType { wasmRef(.WasmFunc, shared: shared, nullability: true) } + public static func wasmExnRef(shared: Bool = false) -> ILType { wasmRef(.WasmExn, shared: shared, nullability: true) } + public static func wasmI31Ref(shared: Bool = false) -> ILType { wasmRef(.WasmI31, shared: shared, nullability: true) } + public static func wasmRefI31(shared: Bool = false) -> ILType { wasmRef(.WasmI31, shared: shared, nullability: false) } + public static func wasmAnyRef(shared: Bool = false) -> ILType { wasmRef(.WasmAny, shared: shared, nullability: true) } + public static func wasmRefAny(shared: Bool = false) -> ILType { wasmRef(.WasmAny, shared: shared, nullability: false) } + public static func wasmNullRef(shared: Bool = false) -> ILType { wasmRef(.WasmNone, shared: shared, nullability: true) } + public static func wasmNullExternRef(shared: Bool = false) -> ILType { wasmRef(.WasmNoExtern, shared: shared, nullability: true) } + public static func wasmNullFuncRef(shared: Bool = false) -> ILType { wasmRef(.WasmNoFunc, shared: shared, nullability: true) } + public static func wasmEqRef(shared: Bool = false) -> ILType { wasmRef(.WasmEq, shared: shared, nullability: true) } + public static func wasmStructRef(shared: Bool = false) -> ILType { wasmRef(.WasmStruct, shared: shared, nullability: true) } + public static func wasmArrayRef(shared: Bool = false) -> ILType { wasmRef(.WasmArray, shared: shared, nullability: true) } public static let wasmSimd128 = ILType(definiteType: .wasmSimd128) public static let wasmGenericRef = ILType(definiteType: .wasmRef) + public static func allWasmRefTypes() -> [ILType] { + var refTypes: [ILType] = [] + for sharedRef in [true, false] { + for heapType in WasmAbstractHeapType.allCases { + refTypes.append(wasmRef(heapType, shared: sharedRef, nullability: true)) + } + } + return refTypes + } + static func wasmTypeDef(description: WasmTypeDescription? = nil) -> ILType { let typeDef = WasmTypeDefinition() typeDef.description = description @@ -275,6 +285,10 @@ public struct ILType: Hashable { wasmTypeDef(description: .selfReference) } + static func wasmRef(_ heapType: WasmAbstractHeapType, shared: Bool = false, nullability: Bool = true) -> ILType { + ILType.wasmRef(.Abstract(.init(heapType, shared: shared)), nullability: nullability) + } + static func wasmRef(_ kind: WasmReferenceType.Kind, nullability: Bool) -> ILType { return ILType(definiteType: .wasmRef, ext: TypeExtension( properties: [], methods: [], signature: nil, @@ -286,7 +300,7 @@ public struct ILType: Hashable { } // The union of all primitive wasm types - public static let wasmPrimitive = .wasmi32 | .wasmi64 | .wasmf32 | .wasmf64 | .wasmExternRef | .wasmFuncRef | .wasmI31Ref | .wasmSimd128 | .wasmGenericRef + public static let wasmPrimitive = .wasmi32 | .wasmi64 | .wasmf32 | .wasmf64 | .wasmSimd128 | .wasmGenericRef public static let wasmNumericalPrimitive = .wasmi32 | .wasmi64 | .wasmf32 | .wasmf64 @@ -1089,11 +1103,13 @@ extension ILType: CustomStringConvertible { } let nullPrefix = refType.nullability ? "null " : "" switch refType.kind { - case .Abstract(let heapType): - return ".wasmRef(.Abstract(\(nullPrefix)\(heapType)))" + case .Abstract(let heapTypeInfo): + let sharedPrefix = heapTypeInfo.shared ? "shared " : "" + return ".wasmRef(.Abstract(\(nullPrefix)\(sharedPrefix)\(heapTypeInfo.heapType)))" case .Index(let indexRef): if let desc = indexRef.get() { - return ".wasmRef(\(nullPrefix)Index \(desc.format(abbreviate: abbreviate)))" + let sharedPrefix = if desc.abstractHeapSupertype?.shared ?? false { "shared " } else { "" } + return ".wasmRef(\(nullPrefix)Index \(sharedPrefix)\(desc.format(abbreviate: abbreviate)))" } return ".wasmRef(\(nullPrefix)Index)" } @@ -1391,9 +1407,9 @@ public class WasmTypeDefinition: WasmTypeExtension { } // TODO: Add continuation types for core stack switching. -// TODO: Add shared bit for shared-everything-threads. // TODO: Add internal string type for JS string builtins. -enum WasmAbstractHeapType: CaseIterable, Comparable { +// TODO(pawkra): rename to HeapType +public enum WasmAbstractHeapType: CaseIterable, Comparable { // Note: The union, intersection, ... implementations are inspired by Binaryen's implementation, // so when extending the type system, feel free to use that implemenation as an orientation. // https://github.com/WebAssembly/binaryen/blob/main/src/wasm/wasm-type.cpp @@ -1475,8 +1491,8 @@ enum WasmAbstractHeapType: CaseIterable, Comparable { if self == other { return self } - if self.getBottom() != other.getBottom() { - return nil + if !self.inSameHierarchy(other) { + return nil // Incompatible heap types. } if self.subsumes(other) { return other @@ -1490,6 +1506,57 @@ enum WasmAbstractHeapType: CaseIterable, Comparable { func subsumes(_ other: Self) -> Bool { union(other) == self } + + public static func allNonBottomTypes() -> [WasmAbstractHeapType] { + return WasmAbstractHeapType.allCases.filter { !$0.isBottom() } + } +} + +public class HeapTypeInfo : Hashable { + public let heapType: WasmAbstractHeapType + public let shared: Bool + + init(_ heapType: WasmAbstractHeapType, shared: Bool) { + self.heapType = heapType + self.shared = shared + } + + public static func ==(lhs: HeapTypeInfo, rhs: HeapTypeInfo) -> Bool { + return lhs.heapType == rhs.heapType && lhs.shared == rhs.shared + } + + func union(_ other: HeapTypeInfo) -> HeapTypeInfo? { + if (shared != other.shared) { + return nil; + } + if let unionHeapType = heapType.union(other.heapType) { + return HeapTypeInfo(unionHeapType, shared: shared) + } + return nil + } + + func intersection(_ other: HeapTypeInfo) -> HeapTypeInfo? { + if (shared != other.shared) { + return nil; + } + if let intersectionHeapType = heapType.intersection(other.heapType) { + return HeapTypeInfo(intersectionHeapType, shared: shared) + } + return nil + } + + func subsumes(_ other: HeapTypeInfo) -> Bool { + if (shared != other.shared) { + return false; + } + return heapType.subsumes(other.heapType) + } + + + public func hash(into hasher: inout Hasher) { + hasher.combine(heapType) + hasher.combine(shared) + } } // A wrapper around a WasmTypeDescription without owning the WasmTypeDescription. @@ -1514,7 +1581,7 @@ public class WasmReferenceType: WasmTypeExtension { // corresponding WasmTypeDefinition extension attached to the type of the operation // defining the wasm-gc type (and is kept alive by the JSTyper). case Index(UnownedWasmTypeDescription = UnownedWasmTypeDescription()) - case Abstract(WasmAbstractHeapType) + case Abstract(HeapTypeInfo) func union(_ other: Self) -> Self? { switch self { @@ -2068,10 +2135,11 @@ class WasmTypeDescription: Hashable, CustomStringConvertible { // The "closest" super type that is an abstract type (.WasmArray for arrays, .WasmStruct for // structs). It is nil for unresolved forward/self references for which the concrete abstract // super type is still undecided. - public let abstractHeapSupertype: WasmAbstractHeapType? + // TODO(pawkra): rename to heapSupertype + public let abstractHeapSupertype: HeapTypeInfo? // TODO(gc): We will also need to support subtyping of struct and array types at some point. - init(typeGroupIndex: Int, superType: WasmAbstractHeapType? = nil) { + init(typeGroupIndex: Int, superType: HeapTypeInfo? = nil) { self.typeGroupIndex = typeGroupIndex self.abstractHeapSupertype = superType } @@ -2101,7 +2169,8 @@ class WasmSignatureTypeDescription: WasmTypeDescription { init(signature: WasmSignature, typeGroupIndex: Int) { self.signature = signature - super.init(typeGroupIndex: typeGroupIndex, superType: .WasmFunc) + // TODO(pawkra): support shared variant. + super.init(typeGroupIndex: typeGroupIndex, superType: HeapTypeInfo.init(.WasmFunc, shared: false)) } override func format(abbreviate: Bool) -> String { @@ -2122,7 +2191,8 @@ class WasmArrayTypeDescription: WasmTypeDescription { init(elementType: ILType, mutability: Bool, typeGroupIndex: Int) { self.elementType = elementType self.mutability = mutability - super.init(typeGroupIndex: typeGroupIndex, superType: .WasmArray) + // TODO(pawkra): support shared variant. + super.init(typeGroupIndex: typeGroupIndex, superType: HeapTypeInfo.init(.WasmArray, shared: false)) } override func format(abbreviate: Bool) -> String { @@ -2153,7 +2223,8 @@ class WasmStructTypeDescription: WasmTypeDescription { init(fields: [Field], typeGroupIndex: Int) { self.fields = fields - super.init(typeGroupIndex: typeGroupIndex, superType: .WasmStruct) + // TODO(pawkra): support shared variant. + super.init(typeGroupIndex: typeGroupIndex, superType: HeapTypeInfo.init(.WasmStruct, shared: false)) } override func format(abbreviate: Bool) -> String { diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index 452dd9cc5..8a441966f 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -695,9 +695,9 @@ public enum WasmGlobal { case wasmf64(Float64) // Empty reference // TODO(gc): Add support for globals with non-nullable references. - case externref - case exnref - case i31ref + case externref(shared: Bool) + case exnref(shared: Bool) + case i31ref(shared: Bool) // function reference case refFunc(Int) @@ -715,12 +715,12 @@ public enum WasmGlobal { return .wasmf32 case .wasmf64: return .wasmf64 - case .externref: - return .wasmExternRef - case .exnref: - return .wasmExnRef - case .i31ref: - return .wasmI31Ref + case let .externref(shared): + return ILType.wasmExternRef(shared: shared) + case let .exnref(shared): + return ILType.wasmExnRef(shared: shared) + case let .i31ref(shared): + return ILType.wasmI31Ref(shared: shared) case .imported(let type): assert(type.wasmGlobalType != nil) return type.wasmGlobalType!.valueType @@ -729,6 +729,7 @@ public enum WasmGlobal { } } + //TODO(pawkra): rename to jsTypeName func typeString() -> String { switch self { case .wasmi64(_): @@ -739,18 +740,26 @@ public enum WasmGlobal { return "f32" case .wasmf64(_): return "f64" - case .externref: + case let .externref(shared): + assertNotShared(shared) return "externref" - case .exnref: + case let .exnref(shared): + assertNotShared(shared) return "exnref" - case .i31ref: + case let .i31ref(shared): + assertNotShared(shared) return "i31ref" default: fatalError("Unimplemented / unhandled") } } + private func assertNotShared(_ shared: Bool) { + assert(!shared, "Shared references not supported in JS > WASM API") + } + // Returns a JS string representing the initial value. + // TODO(pawkra): rename to valueToJsString func valueToString() -> String { switch self { case .wasmi64(let val): @@ -761,10 +770,10 @@ public enum WasmGlobal { return "\(val)" case .wasmf64(let val): return "\(val)" - case .externref: + case .externref(_): return "" - case .exnref, - .i31ref: + case .exnref(_), + .i31ref(_): return "null" default: fatalError("Unimplemented / unhandled") @@ -801,9 +810,11 @@ final class WasmDefineTable: WasmOperation { self.definedEntries = definedEntries // TODO(manoskouk): Find a way to define non-function tables with initializers. - assert(elementType == .wasmFuncRef || definedEntries.isEmpty) + // TODO(pawkra): support shared refs. + let isWasmFuncRef = elementType == .wasmFuncRef() + assert(isWasmFuncRef || definedEntries.isEmpty) - super.init(numInputs: elementType == .wasmFuncRef ? definedEntries.count : 0, + super.init(numInputs: isWasmFuncRef ? definedEntries.count : 0, numOutputs: 1, attributes: [.isMutable], requiredContext: [.wasm]) diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift index 49cfb5a04..bcd7057b7 100644 --- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift +++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift @@ -1540,11 +1540,12 @@ public class JavaScriptLifter: Lifter { let LET = w.varKeyword let type: String switch op.tableType.elementType { - case .wasmExternRef: + case .wasmExternRef(): type = "externref" - case .wasmFuncRef: + case .wasmFuncRef(): type = "anyfunc" // TODO(mliedtke): add tables for i31ref. + // TODO(pawkra): add shared ref variants. default: fatalError("Unknown table type") } @@ -1579,21 +1580,21 @@ public class JavaScriptLifter: Lifter { return "\"i64\"" case .wasmSimd128: return "\"v128\"" - case .wasmExternRef: - return "\"externref\"" - case .wasmFuncRef: + case ILType.wasmExternRef(): + return "\"externref\"" + case ILType.wasmFuncRef(): return "\"anyfunc\"" - case .wasmAnyRef: + case ILType.wasmAnyRef(): return "\"anyref\"" - case .wasmEqRef: + case ILType.wasmEqRef(): return "\"eqref\"" - case .wasmI31Ref: + case ILType.wasmI31Ref(): return "\"i31ref\"" - case .wasmStructRef: + case ILType.wasmStructRef(): return "\"structref\"" - case .wasmArrayRef: + case ILType.wasmArrayRef(): return "\"arrayref\"" - case .wasmExnRef: + case ILType.wasmExnRef(): return "\"exnref\"" default: diff --git a/Sources/Fuzzilli/Lifting/WasmLifter.swift b/Sources/Fuzzilli/Lifting/WasmLifter.swift index 4944ac161..a11ed0dbd 100644 --- a/Sources/Fuzzilli/Lifting/WasmLifter.swift +++ b/Sources/Fuzzilli/Lifting/WasmLifter.swift @@ -527,39 +527,33 @@ public class WasmLifter { self.bytecode += [0x1, 0x0, 0x0, 0x0] } - private func encodeAbstractHeapType(_ heapType: WasmAbstractHeapType) -> Data { - switch (heapType) { - case .WasmExtern: - return Data([0x6F]) - case .WasmFunc: - return Data([0x70]) - case .WasmAny: - return Data([0x6E]) - case .WasmEq: - return Data([0x6D]) - case .WasmI31: - return Data([0x6C]) - case .WasmStruct: - return Data([0x6B]) - case .WasmArray: - return Data([0x6A]) - case .WasmExn: - return Data([0x69]) - case .WasmNone: - return Data([0x71]) - case .WasmNoExtern: - return Data([0x72]) - case .WasmNoFunc: - return Data([0x73]) - case .WasmNoExn: - return Data([0x74]) - } + private func encodeAbstractHeapType(_ heapTypeInfo: HeapTypeInfo) -> Data { + // Base of v8 implementation. See + // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/wasm-constants.h?q=symbol:ValueTypeCode + let sharedFlagPrefix: [UInt8] = heapTypeInfo.shared ? [0x65] : [] + let opCode: UInt8 = + switch (heapTypeInfo.heapType) { + case .WasmExtern: 0x6F + case .WasmFunc: 0x70 + case .WasmAny: 0x6E + case .WasmEq: 0x6D + case .WasmI31: 0x6C + case .WasmStruct: 0x6B + case .WasmArray: 0x6A + case .WasmExn: 0x69 + case .WasmNone: 0x71 + case .WasmNoExtern: 0x72 + case .WasmNoFunc: 0x73 + case .WasmNoExn: 0x74 + } + return Data(sharedFlagPrefix + [opCode]) } private func encodeWasmGCType(_ description: WasmTypeDescription?) throws -> Data { guard let description else { throw WasmLifter.CompileError.missingTypeInformation } + // TODO(pawkra): encode shared bit return Leb128.unsignedEncode(typeDescToIndex[description]!) } @@ -568,28 +562,22 @@ public class WasmLifter { let isNullable = refType.nullability let nullabilityByte: UInt8 = isNullable ? 0x63 : 0x64 - switch refType.kind { - case .Index(let description): - return try Data([nullabilityByte]) + encodeWasmGCType(description.get()) - case .Abstract(let heapType): - return Data([nullabilityByte]) + encodeAbstractHeapType(heapType) - } + return try Data([nullabilityByte]) + encodeHeapType(type) } // HINT: If you crash here, you might not have specified an encoding for your new type in `ILTypeMapping`. return ILTypeMapping[type] ?? ILTypeMapping[defaultType!]! } - private func encodeHeapType(_ type: ILType, defaultType: ILType? = nil) throws -> Data { + private func encodeHeapType(_ type: ILType) throws -> Data { if let refType = type.wasmReferenceType { switch refType.kind { case .Index(let description): return try encodeWasmGCType(description.get()) - case .Abstract(let heapType): - return encodeAbstractHeapType(heapType) + case .Abstract(let heapTypeInfo): + return encodeAbstractHeapType(heapTypeInfo) } } - // HINT: If you crash here, you might not have specified an encoding for your new type in `ILTypeMapping`. - return ILTypeMapping[type] ?? ILTypeMapping[defaultType!]! + fatalError("This function supports only wasmReferenceType.") } private func buildTypeEntry(for desc: WasmTypeDescription, data: inout Data) throws { @@ -663,7 +651,7 @@ public class WasmLifter { } temp += Leb128.unsignedEncode(signature.outputTypes.count) for outputType in signature.outputTypes { - temp += try encodeType(outputType, defaultType: .wasmExternRef) + temp += try encodeType(outputType, defaultType: .wasmExternRef()) } } @@ -1076,14 +1064,14 @@ public class WasmLifter { temporaryInstruction = Instruction(Consti32(value: val), output: Variable()) case .wasmi64(let val): temporaryInstruction = Instruction(Consti64(value: val), output: Variable()) - case .externref: - temp += try! Data([0xD0]) + encodeHeapType(.wasmExternRef) + Data([0x0B]) + case .externref(let shared): + temp += try! Data([0xD0]) + encodeHeapType(.wasmExternRef(shared: shared)) + Data([0x0B]) continue - case .exnref: - temp += try! Data([0xD0]) + encodeHeapType(.wasmExnRef) + Data([0x0B]) + case .exnref(let shared): + temp += try! Data([0xD0]) + encodeHeapType(.wasmExnRef(shared: shared)) + Data([0x0B]) continue - case .i31ref: - temp += try! Data([0xD0]) + encodeHeapType(.wasmI31Ref) + Data([0x0B]) + case .i31ref(let shared): + temp += try! Data([0xD0]) + encodeHeapType(.wasmI31Ref(shared: shared)) + Data([0x0B]) continue case .refFunc(_), .imported(_): @@ -1506,7 +1494,8 @@ public class WasmLifter { self.exports.append(.global(instr)) case .wasmDefineTable(let tableDef): self.exports.append(.table(instr)) - if tableDef.elementType == .wasmFuncRef { + // TODO(pawkra): support shared refs. + if tableDef.elementType == .wasmFuncRef() { for (value, definedEntry) in zip(instr.inputs, tableDef.definedEntries) { if !typer.type(of: value).Is(.wasmFunctionDef()) { // Check if we need to import the inputs. diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift index 94e7e297c..4f8b7b843 100644 --- a/Sources/Fuzzilli/Mutators/OperationMutator.swift +++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift @@ -369,23 +369,21 @@ public class OperationMutator: BaseInstructionMutator { case .wasmDefineGlobal(let op): // We never change the type of the global, only the value as changing the type will break the following code pretty much instantly. - let wasmGlobal: WasmGlobal - switch op.wasmGlobal.toType() { - case .wasmf32: - wasmGlobal = .wasmf32(Float32(b.randomFloat())) - case .wasmf64: - wasmGlobal = .wasmf64(b.randomFloat()) - case .wasmi32: - wasmGlobal = .wasmi32(Int32(truncatingIfNeeded: b.randomInt())) - case .wasmi64: - wasmGlobal = .wasmi64(b.randomInt()) - case .wasmExternRef, - .wasmExnRef, - .wasmI31Ref: - wasmGlobal = op.wasmGlobal - default: - fatalError("unexpected/unimplemented Value Type!") - } + let wasmGlobal:WasmGlobal = + switch op.wasmGlobal.toType() { + case .wasmf32: + .wasmf32(Float32(b.randomFloat())) + case .wasmf64: + .wasmf64(b.randomFloat()) + case .wasmi32: + .wasmi32(Int32(truncatingIfNeeded: b.randomInt())) + case .wasmi64: + .wasmi64(b.randomInt()) + case ILType.wasmExternRef(), ILType.wasmExnRef(), ILType.wasmI31Ref(): + op.wasmGlobal + default: + fatalError("unexpected/unimplemented Value Type!") + } newOp = WasmDefineGlobal(wasmGlobal: wasmGlobal, isMutable: probability(0.5)) case .wasmDefineTable(let op): // TODO: change table size? diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index 2b2248232..cdca77467 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -4138,6 +4138,8 @@ public struct Fuzzilli_Protobuf_WasmReferenceType: Sendable { public var nullability: Bool = false + public var isShared: Bool = false + public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} @@ -4712,10 +4714,10 @@ public struct Fuzzilli_Protobuf_WasmGlobal: Sendable { set {wasmGlobal = .valuef64(newValue)} } - public var nullref: Fuzzilli_Protobuf_WasmReferenceTypeKind { + public var nullref: Fuzzilli_Protobuf_WasmReferenceType { get { if case .nullref(let v)? = wasmGlobal {return v} - return .index + return Fuzzilli_Protobuf_WasmReferenceType() } set {wasmGlobal = .nullref(newValue)} } @@ -4743,7 +4745,7 @@ public struct Fuzzilli_Protobuf_WasmGlobal: Sendable { case valuei64(Int64) case valuef32(Float) case valuef64(Double) - case nullref(Fuzzilli_Protobuf_WasmReferenceTypeKind) + case nullref(Fuzzilli_Protobuf_WasmReferenceType) case funcref(Int64) case imported(Fuzzilli_Protobuf_WasmILType) @@ -11562,7 +11564,7 @@ extension Fuzzilli_Protobuf_WasmReturn: SwiftProtobuf.Message, SwiftProtobuf._Me extension Fuzzilli_Protobuf_WasmReferenceType: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmReferenceType" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}kind\0\u{1}nullability\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}kind\0\u{1}nullability\0\u{1}isShared\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -11572,6 +11574,7 @@ extension Fuzzilli_Protobuf_WasmReferenceType: SwiftProtobuf.Message, SwiftProto switch fieldNumber { case 1: try { try decoder.decodeSingularEnumField(value: &self.kind) }() case 2: try { try decoder.decodeSingularBoolField(value: &self.nullability) }() + case 3: try { try decoder.decodeSingularBoolField(value: &self.isShared) }() default: break } } @@ -11584,12 +11587,16 @@ extension Fuzzilli_Protobuf_WasmReferenceType: SwiftProtobuf.Message, SwiftProto if self.nullability != false { try visitor.visitSingularBoolField(value: self.nullability, fieldNumber: 2) } + if self.isShared != false { + try visitor.visitSingularBoolField(value: self.isShared, fieldNumber: 3) + } try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Fuzzilli_Protobuf_WasmReferenceType, rhs: Fuzzilli_Protobuf_WasmReferenceType) -> Bool { if lhs.kind != rhs.kind {return false} if lhs.nullability != rhs.nullability {return false} + if lhs.isShared != rhs.isShared {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -12852,10 +12859,15 @@ extension Fuzzilli_Protobuf_WasmGlobal: SwiftProtobuf.Message, SwiftProtobuf._Me } }() case 6: try { - var v: Fuzzilli_Protobuf_WasmReferenceTypeKind? - try decoder.decodeSingularEnumField(value: &v) + var v: Fuzzilli_Protobuf_WasmReferenceType? + var hadOneofValue = false + if let current = self.wasmGlobal { + hadOneofValue = true + if case .nullref(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) if let v = v { - if self.wasmGlobal != nil {try decoder.handleConflictingOneOf()} + if hadOneofValue {try decoder.handleConflictingOneOf()} self.wasmGlobal = .nullref(v) } }() @@ -12912,7 +12924,7 @@ extension Fuzzilli_Protobuf_WasmGlobal: SwiftProtobuf.Message, SwiftProtobuf._Me }() case .nullref?: try { guard case .nullref(let v)? = self.wasmGlobal else { preconditionFailure() } - try visitor.visitSingularEnumField(value: v, fieldNumber: 6) + try visitor.visitSingularMessageField(value: v, fieldNumber: 6) }() case .funcref?: try { guard case .funcref(let v)? = self.wasmGlobal else { preconditionFailure() } diff --git a/Sources/Fuzzilli/Protobuf/operations.proto b/Sources/Fuzzilli/Protobuf/operations.proto index 8ed578def..a4feaf8f3 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -920,6 +920,7 @@ enum WasmReferenceTypeKind { message WasmReferenceType { WasmReferenceTypeKind kind = 1; bool nullability = 2; + bool isShared = 3; } message WasmILType { @@ -1100,7 +1101,7 @@ message WasmGlobal { int64 valuei64 = 3; float valuef32 = 4; double valuef64 = 5; - WasmReferenceTypeKind nullref = 6; + WasmReferenceType nullref = 6; int64 funcref = 7; WasmILType imported = 8; } diff --git a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift index 967210b97..e8a5d66fa 100644 --- a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift @@ -554,7 +554,7 @@ public let WasmDeoptFuzzer = WasmProgramTemplate("WasmDeoptFuzzer") { b in } let table = wasmModule.addTable( - elementType: .wasmFuncRef, + elementType: .wasmFuncRef(), minSize: numCallees, definedEntries: (0.. functionSig.outputType let m = b.buildWasmModule { m in - let allWasmTypes: WeightedList = WeightedList([(.wasmi32, 1), (.wasmi64, 1), (.wasmf32, 1), (.wasmf64, 1), (.wasmExternRef, 1), (.wasmFuncRef, 1)]) + let allWasmTypes: WeightedList = WeightedList([(.wasmi32, 1), (.wasmi64, 1), (.wasmf32, 1), (.wasmf64, 1), (.wasmExternRef(), 1), (.wasmFuncRef(), 1)]) let wasmSignature = ProgramBuilder.convertJsSignatureToWasmSignature(wrappedSig, availableTypes: allWasmTypes) m.addWasmFunction(with: wasmSignature) {fbuilder, _, _ in let args = b.randomWasmArguments(forWasmSignature: wasmSignature) @@ -745,6 +745,7 @@ public func v8ProcessArgs(randomize: Bool, forSandbox: Bool) -> [String] { var args = [ "--expose-gc", "--expose-externalize-string", + "--experimental-wasm-shared", "--omit-quit", "--allow-natives-syntax", "--fuzzing", diff --git a/Tests/FuzzilliTests/JSTyperTests.swift b/Tests/FuzzilliTests/JSTyperTests.swift index 56ab52d11..da45fc8c3 100644 --- a/Tests/FuzzilliTests/JSTyperTests.swift +++ b/Tests/FuzzilliTests/JSTyperTests.swift @@ -1534,7 +1534,7 @@ class JSTyperTests: XCTestCase { } b.doReturn(obj) } - let wasmSignature = [] => [.wasmExternRef] + let wasmSignature = [] => [.wasmExternRef()] let typeDesc = b.type(of: typeGroup[0]).wasmTypeDefinition!.description! @@ -1574,7 +1574,7 @@ class JSTyperTests: XCTestCase { } // Function three - wasmModule.addWasmFunction(with: [.wasmExternRef] => [.wasmi32, .wasmi64]) { function, label, _ in + wasmModule.addWasmFunction(with: [.wasmExternRef()] => [.wasmi32, .wasmi64]) { function, label, _ in return [function.consti32(1), function.consti64(2)] } @@ -1585,7 +1585,7 @@ class JSTyperTests: XCTestCase { // Function five - wasmModule.addWasmFunction(with: [] => [.wasmExternRef]) { function, label, _ in + wasmModule.addWasmFunction(with: [] => [.wasmExternRef()]) { function, label, _ in // This forces an import and we should see a re-exported function on the module. return [function.wasmJsCall(function: plainFunction, withArgs: [], withWasmSignature: wasmSignature)!] } @@ -1795,7 +1795,7 @@ class JSTyperTests: XCTestCase { let wasmTableConstructor = b.getProperty("Table", of: wasm) let wasmTable = b.construct(wasmTableConstructor) // In theory this needs arguments. XCTAssertFalse(b.type(of: wasmTable).Is(.object(ofGroup: "WasmTable"))) - let realWasmTable = b.createWasmTable(elementType: .wasmAnyRef, limits: .init(min: 0), isTable64: false) + let realWasmTable = b.createWasmTable(elementType: .wasmAnyRef(), limits: .init(min: 0), isTable64: false) XCTAssert(b.type(of: realWasmTable).Is(.object(ofGroup: "WasmTable"))) XCTAssert(b.type(of: realWasmTable).Is(ObjectGroup.wasmTable.instanceType)) let tablePrototype = b.getProperty("prototype", of: wasmTableConstructor) diff --git a/Tests/FuzzilliTests/LifterTest.swift b/Tests/FuzzilliTests/LifterTest.swift index 7758461d1..39a00c095 100644 --- a/Tests/FuzzilliTests/LifterTest.swift +++ b/Tests/FuzzilliTests/LifterTest.swift @@ -3274,7 +3274,7 @@ class LifterTests: XCTestCase { let fuzzer = makeMockFuzzer() let b = fuzzer.makeBuilder() - let table = b.createWasmTable(elementType: .wasmFuncRef, limits: Limits(min: 1), isTable64: true) + let table = b.createWasmTable(elementType: .wasmFuncRef(), limits: Limits(min: 1), isTable64: true) XCTAssertTrue(b.type(of: table).Is(.object(ofGroup: "WasmTable"))) let f = b.buildPlainFunction(with: .parameters(n: 0)) {_ in diff --git a/Tests/FuzzilliTests/LiveTests.swift b/Tests/FuzzilliTests/LiveTests.swift index 4ffb8c5d9..8a6dce290 100644 --- a/Tests/FuzzilliTests/LiveTests.swift +++ b/Tests/FuzzilliTests/LiveTests.swift @@ -133,15 +133,16 @@ class LiveTests: XCTestCase { b.buildTryCatchFinally { // TODO(manoskouk): Once we support wasm-gc types in signatures, we'll need // something more sophisticated. + // TODO(pawkra): support shared refs. let args = wasmSignature.parameterTypes.map { switch $0 { case .wasmi64: return b.loadBigInt(123) - case .wasmFuncRef: + case ILType.wasmFuncRef(): return jsFunction - case .wasmNullExternRef, .wasmNullFuncRef, .wasmNullRef: + case ILType.wasmNullExternRef(), ILType.wasmNullFuncRef(), ILType.wasmNullRef(): return b.loadNull() - case .wasmExternRef, .wasmAnyRef: + case ILType.wasmExternRef(), ILType.wasmAnyRef(): return b.createObject(with: [:]) default: return b.loadInt(321) diff --git a/Tests/FuzzilliTests/ProgramBuilderTest.swift b/Tests/FuzzilliTests/ProgramBuilderTest.swift index 5a9c53b63..4478f3225 100644 --- a/Tests/FuzzilliTests/ProgramBuilderTest.swift +++ b/Tests/FuzzilliTests/ProgramBuilderTest.swift @@ -2863,21 +2863,21 @@ class ProgramBuilderTests: XCTestCase { let arrayI32Type = b.type(of: arrayI32) XCTAssert(arrayI32Type.Is(.wasmRef(.Index(), nullability: true))) XCTAssert(arrayI32Type.Is(.wasmRef(.Index(), nullability: false))) - XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmArray), nullability: true))) - XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmArray), nullability: false))) - XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmEq), nullability: true))) - XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmEq), nullability: false))) - XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmAny), nullability: true))) - XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmAny), nullability: false))) - XCTAssertFalse(arrayI32Type.Is(.wasmRef(.Abstract(.WasmStruct), nullability: true))) - XCTAssertFalse(arrayI32Type.Is(.wasmRef(.Abstract(.WasmStruct), nullability: false))) - XCTAssertFalse(arrayI32Type.Is(.wasmRef(.Abstract(.WasmExn), nullability: false))) + XCTAssert(arrayI32Type.Is(.wasmRef(.WasmArray, nullability: true))) + XCTAssert(arrayI32Type.Is(.wasmRef(.WasmArray, nullability: false))) + XCTAssert(arrayI32Type.Is(.wasmRef(.WasmEq, nullability: true))) + XCTAssert(arrayI32Type.Is(.wasmRef(.WasmEq, nullability: false))) + XCTAssert(arrayI32Type.Is(.wasmRef(.WasmAny, nullability: true))) + XCTAssert(arrayI32Type.Is(.wasmRef(.WasmAny, nullability: false))) + XCTAssertFalse(arrayI32Type.Is(.wasmRef(.WasmStruct, nullability: true))) + XCTAssertFalse(arrayI32Type.Is(.wasmRef(.WasmStruct, nullability: false))) + XCTAssertFalse(arrayI32Type.Is(.wasmRef(.WasmExn, nullability: false))) let arrayI32B = function.wasmArrayNewFixed(arrayType: arrayDefI32B, elements: []) let arrayI32BType = b.type(of: arrayI32B) XCTAssertFalse(arrayI32BType.Is(arrayI32Type)) XCTAssertFalse(arrayI32Type.Is(arrayI32BType)) - let refArrayType = ILType.wasmRef(.Abstract(.WasmArray), nullability: false) + let refArrayType = ILType.wasmRef(.WasmArray, nullability: false) XCTAssertEqual(arrayI32Type.union(with: arrayI32BType), refArrayType) XCTAssertEqual(arrayI32BType.union(with: arrayI32Type), refArrayType) XCTAssertEqual(arrayI32Type.intersection(with: arrayI32BType), .nothing) @@ -2887,14 +2887,14 @@ class ProgramBuilderTests: XCTestCase { let structType = b.type(of: structVar) XCTAssert(structType.Is(.wasmRef(.Index(), nullability: true))) XCTAssert(structType.Is(.wasmRef(.Index(), nullability: false))) - XCTAssert(structType.Is(.wasmRef(.Abstract(.WasmStruct), nullability: false))) - XCTAssert(structType.Is(.wasmRef(.Abstract(.WasmEq), nullability: false))) - XCTAssert(structType.Is(.wasmRef(.Abstract(.WasmAny), nullability: false))) - XCTAssertFalse(structType.Is(.wasmRef(.Abstract(.WasmArray), nullability: true))) - XCTAssertFalse(structType.Is(.wasmRef(.Abstract(.WasmArray), nullability: false))) - XCTAssertFalse(structType.Is(.wasmRef(.Abstract(.WasmExn), nullability: false))) - - let refEqType = ILType.wasmRef(.Abstract(.WasmEq), nullability: false) + XCTAssert(structType.Is(.wasmRef(.WasmStruct, nullability: false))) + XCTAssert(structType.Is(.wasmRef(.WasmEq, nullability: false))) + XCTAssert(structType.Is(.wasmRef(.WasmAny, nullability: false))) + XCTAssertFalse(structType.Is(.wasmRef(.WasmArray, nullability: true))) + XCTAssertFalse(structType.Is(.wasmRef(.WasmArray, nullability: false))) + XCTAssertFalse(structType.Is(.wasmRef(.WasmExn, nullability: false))) + + let refEqType = ILType.wasmRef(.WasmEq, nullability: false) XCTAssertEqual(structType.union(with: arrayI32Type), refEqType) XCTAssertEqual(arrayI32Type.union(with: structType), refEqType) XCTAssertEqual(structType.intersection(with: arrayI32Type), .nothing) @@ -2903,25 +2903,25 @@ class ProgramBuilderTests: XCTestCase { let i31 = function.wasmRefI31(function.consti32(42)) let i31Type = b.type(of: i31) XCTAssertFalse(i31Type.Is(.wasmRef(.Index(), nullability: true))) - XCTAssert(i31Type.Is(.wasmRef(.Abstract(.WasmEq), nullability: false))) - XCTAssert(i31Type.Is(.wasmRef(.Abstract(.WasmAny), nullability: false))) - XCTAssertFalse(i31Type.Is(.wasmRef(.Abstract(.WasmArray), nullability: false))) - XCTAssertFalse(i31Type.Is(.wasmRef(.Abstract(.WasmStruct), nullability: false))) - XCTAssertFalse(i31Type.Is(.wasmRef(.Abstract(.WasmExn), nullability: false))) + XCTAssert(i31Type.Is(.wasmRef(.WasmEq, nullability: false))) + XCTAssert(i31Type.Is(.wasmRef(.WasmAny, nullability: false))) + XCTAssertFalse(i31Type.Is(.wasmRef(.WasmArray, nullability: false))) + XCTAssertFalse(i31Type.Is(.wasmRef(.WasmStruct, nullability: false))) + XCTAssertFalse(i31Type.Is(.wasmRef(.WasmExn, nullability: false))) XCTAssertEqual(structType.union(with: i31Type), refEqType) XCTAssertEqual(arrayI32Type.union(with: i31Type), refEqType) XCTAssertEqual(i31Type.union(with: refEqType), refEqType) XCTAssertEqual(refArrayType.union(with: i31Type), refEqType) - let refStructType = ILType.wasmRef(.Abstract(.WasmStruct), nullability: false) + let refStructType = ILType.wasmRef(.WasmStruct, nullability: false) XCTAssertEqual(i31Type.union(with: refStructType), refEqType) XCTAssertEqual(i31Type.intersection(with: refEqType), i31Type) XCTAssertEqual(refEqType.intersection(with: i31Type), i31Type) - let refNone = ILType.wasmRef(.Abstract(.WasmNone), nullability: false) + let refNone = ILType.wasmRef(.WasmNone, nullability: false) XCTAssertEqual(i31Type.intersection(with: refArrayType), refNone) XCTAssertEqual(refStructType.intersection(with: i31Type), refNone) - XCTAssertEqual(i31Type.intersection(with: .wasmExnRef), .nothing) + XCTAssertEqual(i31Type.intersection(with: .wasmExnRef()), .nothing) return [] } diff --git a/Tests/FuzzilliTests/TypeSystemTest.swift b/Tests/FuzzilliTests/TypeSystemTest.swift index 5e6f0fd77..073388a11 100644 --- a/Tests/FuzzilliTests/TypeSystemTest.swift +++ b/Tests/FuzzilliTests/TypeSystemTest.swift @@ -1110,11 +1110,12 @@ class TypeSystemTests: XCTestCase { let strObjOrFuncObj = (ILType.string + ILType.object(withProperties: ["foo"])) | (ILType.function([.rest(.jsAnything)] => .float) + ILType.object(withProperties: ["foo"])) XCTAssertEqual(strObjOrFuncObj.description, ".string + .object(withProperties: [\"foo\"]) | .object(withProperties: [\"foo\"]) + .function()") - let nullExn = ILType.wasmRef(.Abstract(.WasmExn), nullability: true) - let nonNullAny = ILType.wasmRef(.Abstract(.WasmAny), nullability: false) - XCTAssertEqual(nullExn.description, ".wasmRef(.Abstract(null WasmExn))") + let nullExn = ILType.wasmRef(.WasmExn, shared: true, nullability: true) + let nonNullAny = ILType.wasmRef(.WasmAny, shared: false, nullability: false) + XCTAssertEqual(nullExn.description, ".wasmRef(.Abstract(null shared WasmExn))") XCTAssertEqual(nonNullAny.description, ".wasmRef(.Abstract(WasmAny))") + // TODO(pawkra): add shared variant. let arrayDesc = WasmArrayTypeDescription(elementType: .wasmi32, mutability: false, typeGroupIndex: 0) let arrayRef = ILType.wasmIndexRef(arrayDesc, nullability: true) XCTAssertEqual(arrayRef.description, ".wasmRef(null Index 0 Array[immutable .wasmi32])") @@ -1148,11 +1149,11 @@ class TypeSystemTests: XCTestCase { ".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) + signature: [.wasmi32, arrayRef] => [structRef, .wasmNullRef(shared: true)], 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))]])") + "[.wasmRef(Index 1 Struct), .wasmRef(.Abstract(null shared 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 @@ -1163,7 +1164,7 @@ class TypeSystemTests: XCTestCase { } func testWasmSubsumptionRules() { - let wasmTypes: [ILType] = [.wasmi32, .wasmi64, .wasmf32, .wasmf64, .wasmFuncRef, .wasmExternRef, .wasmI31Ref, .wasmExnRef] + let wasmTypes: [ILType] = [.wasmi32, .wasmi64, .wasmf32, .wasmf64] + ILType.allWasmRefTypes() // Make sure that no Wasm type is subsumed by (JS-)anything. for t in wasmTypes { XCTAssertEqual(t <= .jsAnything, false) @@ -1209,14 +1210,16 @@ class TypeSystemTests: XCTestCase { // Test nullability rules for abstract Wasm types. for heapType: WasmAbstractHeapType in WasmAbstractHeapType.allCases { - let nullable = ILType.wasmRef(.Abstract(heapType), nullability: true) - let nonNullable = ILType.wasmRef(.Abstract(heapType), nullability: false) - XCTAssert(nonNullable.Is(nullable)) - XCTAssertFalse(nullable.Is(nonNullable)) - XCTAssertEqual(nullable.union(with: nonNullable), nullable) - XCTAssertEqual(nonNullable.union(with: nullable), nullable) - XCTAssertEqual(nullable.intersection(with: nonNullable), nonNullable) - XCTAssertEqual(nonNullable.intersection(with: nullable), nonNullable) + for shared in [true, false] { + let nullable = ILType.wasmRef(heapType, shared: shared, nullability: true) + let nonNullable = ILType.wasmRef(heapType, shared: shared, nullability: false) + XCTAssert(nonNullable.Is(nullable)) + XCTAssertFalse(nullable.Is(nonNullable)) + XCTAssertEqual(nullable.union(with: nonNullable), nullable) + XCTAssertEqual(nonNullable.union(with: nullable), nullable) + XCTAssertEqual(nullable.intersection(with: nonNullable), nonNullable) + XCTAssertEqual(nonNullable.intersection(with: nullable), nonNullable) + } } } @@ -1276,7 +1279,6 @@ class TypeSystemTests: XCTestCase { XCTAssertEqual(type.intersection(type.getBottom()), type.getBottom()) } - // Testing a few combinations. XCTAssertEqual(WasmAbstractHeapType.WasmAny.union(.WasmEq), .WasmAny) XCTAssertEqual(WasmAbstractHeapType.WasmStruct.union(.WasmArray), .WasmEq) XCTAssertEqual(WasmAbstractHeapType.WasmI31.union(.WasmArray), .WasmEq) @@ -1287,35 +1289,48 @@ class TypeSystemTests: XCTestCase { XCTAssertEqual(WasmAbstractHeapType.WasmAny.intersection(.WasmArray), .WasmArray) // Tests on the whole ILType. - let ref = {t in ILType.wasmRef(.Abstract(t), nullability: false)} - let refNull = {t in ILType.wasmRef(.Abstract(t), nullability: true)} - for type in allTypes { - let refT = ref(type) - let refNullT = refNull(type) - XCTAssertEqual(refT.union(with: refNullT), refNullT) - XCTAssertEqual(refNullT.union(with: refT), refNullT) - XCTAssertEqual(refT.union(with: refT), refT) - XCTAssertEqual(refNullT.union(with: refNullT), refNullT) - XCTAssertEqual(refT.intersection(with: refT), refT) - XCTAssertEqual(refNullT.intersection(with: refNullT), refNullT) - XCTAssertEqual(refT.intersection(with: refNullT), refT) - XCTAssertEqual(refNullT.intersection(with: refT), refT) + for shared in [true, false] { + let ref: (WasmAbstractHeapType) -> ILType = {t in ILType.wasmRef(t, shared: shared, nullability: false,)} + let refNull = {t in ILType.wasmRef(t, shared: shared, nullability: true)} + + for type in allTypes { + let refT = ref(type) + let refNullT = refNull(type) + XCTAssertEqual(refT.union(with: refNullT), refNullT) + XCTAssertEqual(refNullT.union(with: refT), refNullT) + XCTAssertEqual(refT.union(with: refT), refT) + XCTAssertEqual(refNullT.union(with: refNullT), refNullT) + XCTAssertEqual(refT.intersection(with: refT), refT) + XCTAssertEqual(refNullT.intersection(with: refNullT), refNullT) + XCTAssertEqual(refT.intersection(with: refNullT), refT) + XCTAssertEqual(refNullT.intersection(with: refT), refT) + } + XCTAssertEqual(ref(.WasmAny).union(with: refNull(.WasmEq)), refNull(.WasmAny)) + XCTAssertEqual(ref(.WasmStruct).union(with: ref(.WasmArray)), ref(.WasmEq)) + // We should never do this for the type information of any Variable as .wasmGenericRef + // cannot be encoded in the Wasm module and any instruction that leads to such a static type + // is "broken". However, we will still need to allow this union type + // if we want to be able + // to request a .required(.wasmGenericRef) for operations like WasmRefIsNull. + XCTAssertEqual(ref(.WasmI31).union(with: refNull(.WasmExn)), .wasmGenericRef) + + XCTAssertEqual(ref(.WasmAny).intersection(with: refNull(.WasmEq)), ref(.WasmEq)) + XCTAssertEqual(refNull(.WasmI31).intersection(with: refNull(.WasmStruct)), refNull(.WasmNone)) + // Note that `ref none` is a perfectly valid type in Wasm but such a reference can never be + // constructed. + XCTAssertEqual(ref(.WasmArray).intersection(with: refNull(.WasmStruct)), ref(.WasmNone)) + XCTAssertEqual(refNull(.WasmArray).intersection(with: ref(.WasmAny)), ref(.WasmArray)) } - XCTAssertEqual(ref(.WasmAny).union(with: refNull(.WasmEq)), refNull(.WasmAny)) - XCTAssertEqual(ref(.WasmStruct).union(with: ref(.WasmArray)), ref(.WasmEq)) - // We should never do this for the type information of any Variable as .wasmGenericRef - // cannot be encoded in the Wasm module and any instruction that leads to such a static type - // is "broken". However, we will still need to allow this union type if we want to be able - // to request a .required(.wasmGenericRef) for operations like WasmRefIsNull. - XCTAssertEqual(ref(.WasmI31).union(with: refNull(.WasmExn)), .wasmGenericRef) - - XCTAssertEqual(ref(.WasmAny).intersection(with: refNull(.WasmEq)), ref(.WasmEq)) - XCTAssertEqual(refNull(.WasmI31).intersection(with: refNull(.WasmStruct)), refNull(.WasmNone)) - // Note that `ref none` is a perfectly valid type in Wasm but such a reference can never be - // constructed. - XCTAssertEqual(ref(.WasmArray).intersection(with: refNull(.WasmStruct)), ref(.WasmNone)) - XCTAssertEqual(refNull(.WasmArray).intersection(with: ref(.WasmAny)), ref(.WasmArray)) + let ref = {t, shared in ILType.wasmRef(t, shared: shared, nullability: false,)} + let refNull = {t, shared in ILType.wasmRef(t, shared: shared, nullability: true)} + // Shared and unshared ref hierarchies are disjoint. + for (lhsShared, rhsShared) in [(true, false), (false, true)] { + for type in allTypes { + XCTAssertEqual(ref(type, lhsShared).union(with: ref(type, rhsShared)), .wasmGenericRef) + XCTAssertEqual(refNull(type, lhsShared).union(with: refNull(type, rhsShared)), .wasmGenericRef) + } + } } func testUnboundFunctionSubsumptionRules() { @@ -1433,15 +1448,10 @@ class TypeSystemTests: XCTestCase { .wasmf32, .wasmi64, .wasmf64, - .wasmFuncRef, - .wasmExternRef, - .wasmExnRef, - .wasmI31Ref, - .wasmRefI31, .wasmFunctionDef([.wasmi32] => [.wasmi64]), .wasmFunctionDef([.wasmf32] => [.wasmi32]), - .wasmFunctionDef([.wasmExternRef] => [.wasmExternRef]), + .wasmFunctionDef([.wasmExternRef()] => [.wasmExternRef()]), .wasmMemory(limits: Limits(min: 10)), .wasmMemory(limits: Limits(min: 10, max: 20)), - ] + ] + ILType.allWasmRefTypes() } diff --git a/Tests/FuzzilliTests/WasmTableTests.swift b/Tests/FuzzilliTests/WasmTableTests.swift index c3e59c9b7..2e4f9335f 100644 --- a/Tests/FuzzilliTests/WasmTableTests.swift +++ b/Tests/FuzzilliTests/WasmTableTests.swift @@ -22,7 +22,7 @@ class WasmTableTests: XCTestCase { let js = buildAndLiftProgram { b in let module = b.buildWasmModule { wasmModule in - let table = wasmModule.addTable(elementType: .wasmFuncRef, minSize: 10, maxSize: 20, isTable64: false) + let table = wasmModule.addTable(elementType: .wasmFuncRef(), minSize: 10, maxSize: 20, isTable64: false) wasmModule.addWasmFunction(with: [] => [.wasmi32]) { f, _, _ in let size = f.wasmTableSize(table: table) @@ -31,7 +31,7 @@ class WasmTableTests: XCTestCase { expectedOutput += "10\n" wasmModule.addWasmFunction(with: [] => [.wasmi32, .wasmi32]) { f, _, _ in - let initialValue = f.wasmRefNull(type: .wasmFuncRef) + let initialValue = f.wasmRefNull(type: .wasmFuncRef()) let growBy = f.consti32(5) let oldSize = f.wasmTableGrow(table: table, with: initialValue, by: growBy) let newSize = f.wasmTableSize(table: table) diff --git a/Tests/FuzzilliTests/WasmTests.swift b/Tests/FuzzilliTests/WasmTests.swift index f7955e212..8a78971bb 100644 --- a/Tests/FuzzilliTests/WasmTests.swift +++ b/Tests/FuzzilliTests/WasmTests.swift @@ -44,16 +44,16 @@ func testForErrorOutput(program: String, runner: JavaScriptExecutor, errorMessag class WasmSignatureConversionTests: XCTestCase { func testJsSignatureConversion() { - XCTAssertEqual(ProgramBuilder.convertJsSignatureToWasmSignature([.number] => .integer, availableTypes: WeightedList([(.wasmi32, 1), (.wasmFuncRef, 1), (.wasmExternRef, 1)])), [.wasmi32] => [.wasmi32]) - XCTAssertEqual(ProgramBuilder.convertJsSignatureToWasmSignature([.number] => .integer, availableTypes: WeightedList([(.wasmf32, 1), (.wasmFuncRef, 1), (.wasmExternRef, 1)])), [.wasmf32] => [.wasmi32]) + XCTAssertEqual(ProgramBuilder.convertJsSignatureToWasmSignature([.number] => .integer, availableTypes: WeightedList([(.wasmi32, 1), (.wasmFuncRef(), 1), (.wasmExternRef(), 1)])), [.wasmi32] => [.wasmi32]) + XCTAssertEqual(ProgramBuilder.convertJsSignatureToWasmSignature([.number] => .integer, availableTypes: WeightedList([(.wasmf32, 1), (.wasmFuncRef(), 1), (.wasmExternRef(), 1)])), [.wasmf32] => [.wasmi32]) } func testWasmSignatureConversion() { XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmi32, .wasmi64] => [.wasmf32]), [.integer, .bigint] => .float) - XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmi32, .wasmExnRef] => [.wasmf64]), [.integer, .jsAnything] => .float) - XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmExternRef, .wasmFuncRef] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) - XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmRef(.Index(), nullability: false), .wasmFuncRef] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) - XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmRef(.Abstract(.WasmExtern), nullability: false), .wasmFuncRef] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) + XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmi32, .wasmExnRef()] => [.wasmf64]), [.integer, .jsAnything] => .float) + XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmExternRef(), .wasmFuncRef()] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) + XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmRef(.Index(), nullability: false), .wasmFuncRef()] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) + XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmRef(.WasmExtern, nullability: false), .wasmFuncRef()] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) // TODO(cffsmith): Change this once we know how we want to represent .wasmSimd128 types in JS. XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmSimd128] => [.wasmSimd128]), [.jsAnything] => .jsAnything) } @@ -440,7 +440,7 @@ class WasmFoundationTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "1338\n4242\n6.61e-321\n") } - func testGlobalExnRef() throws { + func globalExnRef(sharedRef: Bool) throws { let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--experimental-wasm-exnref"]) let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) @@ -450,8 +450,8 @@ class WasmFoundationTests: XCTestCase { let tagi32 = b.createWasmTag(parameterTypes: [.wasmi32]) let module = b.buildWasmModule { wasmModule in // Note that globals of exnref can only be defined in wasm, not in JS. - let global = wasmModule.addGlobal(wasmGlobal: .exnref, isMutable: true) - XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmExnRef, isMutable: true))) + let global = wasmModule.addGlobal(wasmGlobal: .exnref(shared: sharedRef), isMutable: true) + XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmExnRef(shared: sharedRef), isMutable: true))) wasmModule.addWasmFunction(with: [] => [.wasmi32]) { function, label, args in let value = function.wasmLoadGlobal(globalVariable: global) @@ -460,12 +460,12 @@ class WasmFoundationTests: XCTestCase { // Throw an exception, catch it and store it in the global. wasmModule.addWasmFunction(with: [] => []) { function, label, args in - let exnref = function.wasmBuildBlockWithResults(with: [] => [.wasmExnRef], args: []) { catchLabel, _ in + let exnref = function.wasmBuildBlockWithResults(with: [] => [.wasmExnRef(shared: sharedRef)], args: []) { catchLabel, _ in function.wasmBuildTryTable(with: [] => [], args: [catchLabel], catches: [.AllRef]) { _, _ in function.WasmBuildThrow(tag: tagi32, inputs: [function.consti32(42)]) return [] } - return [function.wasmRefNull(type: .wasmExnRef)] + return [function.wasmRefNull(type: .wasmExnRef(shared: sharedRef))] }[0] function.wasmStoreGlobal(globalVariable: global, to: exnref) return [] @@ -473,12 +473,12 @@ class WasmFoundationTests: XCTestCase { // Rethrow the exception stored in the global, catch it and extract the integer. wasmModule.addWasmFunction(with: [] => [.wasmi32]) { function, label, args in - let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef], args: []) { catchLabel, _ in + let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef(shared: sharedRef)], args: []) { catchLabel, _ in function.wasmBuildTryTable(with: [] => [], args: [tagi32, catchLabel], catches: [.Ref]) { _, _ in - function.wasmBuildThrowRef(exception: function.wasmLoadGlobal(globalVariable: global)) + function.wasmBuildThrowRef(exception: function.wasmLoadGlobal(globalVariable: global), sharedRef: sharedRef) return [] } - return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef)] + return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef(shared: sharedRef))] } return [caughtValues[0]] } @@ -509,12 +509,12 @@ class WasmFoundationTests: XCTestCase { let otherModule = b.buildWasmModule { wasmModule in // Rethrow the exception stored in the global, catch it and extract the integer. wasmModule.addWasmFunction(with: [] => [.wasmi32]) { function, label, args in - let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef], args: []) { catchLabel, _ in + let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef(shared: sharedRef)], args: []) { catchLabel, _ in function.wasmBuildTryTable(with: [] => [], args: [tagi32, catchLabel], catches: [.Ref]) { _, _ in - function.wasmBuildThrowRef(exception: function.wasmLoadGlobal(globalVariable: global)) + function.wasmBuildThrowRef(exception: function.wasmLoadGlobal(globalVariable: global), sharedRef: sharedRef) return [] } - return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef)] + return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef(shared: sharedRef))] } return [caughtValues[0]] } @@ -529,7 +529,16 @@ class WasmFoundationTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "1\n0\n42\nexception\n42\n") } - func testGlobalExternRef() throws { + func testGlobalExnRefShared() throws { + // TODO(pawkra) + throw XCTSkip("Enable the test once we are emit & support shared refs in ProgramBuilder.") + } + + func testGlobalExnRefUnshared() throws { + try globalExnRef(sharedRef: false) + } + + func globalExternRef(sharedRef: Bool) throws { let runner = try GetJavaScriptExecutorOrSkipTest() let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) @@ -537,14 +546,14 @@ class WasmFoundationTests: XCTestCase { let b = fuzzer.makeBuilder() let module = b.buildWasmModule { wasmModule in - let global = wasmModule.addGlobal(wasmGlobal: .externref, isMutable: true) - XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmExternRef, isMutable: true))) + let global = wasmModule.addGlobal(wasmGlobal: .externref(shared: sharedRef), isMutable: true) + XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmExternRef(shared: sharedRef), isMutable: true))) - wasmModule.addWasmFunction(with: [] => [.wasmExternRef]) { function, label, args in + wasmModule.addWasmFunction(with: [] => [.wasmExternRef(shared: sharedRef)]) { function, label, args in [function.wasmLoadGlobal(globalVariable: global)] } - wasmModule.addWasmFunction(with: [.wasmExternRef] => []) { function, label, args in + wasmModule.addWasmFunction(with: [.wasmExternRef(shared: sharedRef)] => []) { function, label, args in function.wasmStoreGlobal(globalVariable: global, to: args[0]) return [] } @@ -570,6 +579,15 @@ class WasmFoundationTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "null\nHello!\nHello!\n") } + func testglobalExternRefShared() throws { + // TODO(pawkra) + throw XCTSkip("Enable the test once we are emit & support shared refs in ProgramBuilder.") + } + + func testglobalExternRefUnshared() throws { + try globalExternRef(sharedRef: false) + } + func testGlobalExternRefFromJS() throws { let runner = try GetJavaScriptExecutorOrSkipTest() let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) @@ -577,8 +595,9 @@ class WasmFoundationTests: XCTestCase { let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() - let global: Variable = b.createWasmGlobal(value: .externref, isMutable: true) - XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmExternRef, isMutable: true))) + // TODO(pawkra): add shared ref variant. + let global: Variable = b.createWasmGlobal(value: .externref(shared: false), isMutable: true) + XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmExternRef(), isMutable: true))) let outputFunc = b.createNamedVariable(forBuiltin: "output") // The initial value is "undefined" (because we didn't provide an explicit initialization). @@ -594,22 +613,22 @@ class WasmFoundationTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "undefined\nHello!\n") } - func testGlobalI31Ref() throws { - let runner = try GetJavaScriptExecutorOrSkipTest() + func globalI31Ref(sharedRef: Bool) throws { + let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--experimental-wasm-shared"]) let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() let module = b.buildWasmModule { wasmModule in - let global = wasmModule.addGlobal(wasmGlobal: .i31ref, isMutable: true) - XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmI31Ref, isMutable: true))) + let global = wasmModule.addGlobal(wasmGlobal: .i31ref(shared: sharedRef), isMutable: true) + XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmI31Ref(shared: sharedRef), isMutable: true))) - wasmModule.addWasmFunction(with: [] => [.wasmI31Ref]) { function, label, args in + wasmModule.addWasmFunction(with: [] => [.wasmI31Ref(shared: sharedRef)]) { function, label, args in [function.wasmLoadGlobal(globalVariable: global)] } - wasmModule.addWasmFunction(with: [.wasmI31Ref] => []) { function, label, args in + wasmModule.addWasmFunction(with: [.wasmI31Ref(shared: sharedRef)] => []) { function, label, args in function.wasmStoreGlobal(globalVariable: global, to: args[0]) return [] } @@ -634,6 +653,14 @@ class WasmFoundationTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "null\n-42\n-42\n") } + func testGlobalI31RefShared() throws { + try globalI31Ref(sharedRef: true) + } + + func testGlobalI31RefUnshared() throws { + try globalI31Ref(sharedRef: false) + } + func testGlobalI31RefFromJS() throws { let runner = try GetJavaScriptExecutorOrSkipTest() let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) @@ -641,8 +668,9 @@ class WasmFoundationTests: XCTestCase { let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() - let global: Variable = b.createWasmGlobal(value: .i31ref, isMutable: true) - XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmI31Ref, isMutable: true))) + // TODO(pawkra): add shared ref variant. + let global: Variable = b.createWasmGlobal(value: .i31ref(shared: false), isMutable: true) + XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmI31Ref(), isMutable: true))) let outputFunc = b.createNamedVariable(forBuiltin: "output") // The initial value is "null" (because we didn't provide an explicit initialization). @@ -666,8 +694,8 @@ class WasmFoundationTests: XCTestCase { let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() - let javaScriptTable = b.createWasmTable(elementType: .wasmExternRef, limits: Limits(min: 5, max: 25), isTable64: isTable64) - XCTAssertEqual(b.type(of: javaScriptTable), .wasmTable(wasmTableType: WasmTableType(elementType: .wasmExternRef, limits: Limits(min: 5, max: 25), isTable64: isTable64, knownEntries: []))) + let javaScriptTable = b.createWasmTable(elementType: .wasmExternRef(), limits: Limits(min: 5, max: 25), isTable64: isTable64) + XCTAssertEqual(b.type(of: javaScriptTable), .wasmTable(wasmTableType: WasmTableType(elementType: .wasmExternRef(), limits: Limits(min: 5, max: 25), isTable64: isTable64, knownEntries: []))) let object = b.createObject(with: ["a": b.loadInt(41), "b": b.loadInt(42)]) @@ -675,9 +703,9 @@ class WasmFoundationTests: XCTestCase { b.callMethod("set", on: javaScriptTable, withArgs: [isTable64 ? b.loadBigInt(1) : b.loadInt(1), object]) let module = b.buildWasmModule { wasmModule in - let tableRef = wasmModule.addTable(elementType: .wasmExternRef, minSize: 2, isTable64: isTable64) + let tableRef = wasmModule.addTable(elementType: .wasmExternRef(), minSize: 2, isTable64: isTable64) - wasmModule.addWasmFunction(with: [] => [.wasmExternRef]) { function, _, _ in + wasmModule.addWasmFunction(with: [] => [.wasmExternRef()]) { function, _, _ in let offset = isTable64 ? function.consti64(0) : function.consti32(0) var ref = function.wasmTableGet(tableRef: tableRef, idx: offset) let offset1 = isTable64 ? function.consti64(1) : function.consti32(1) @@ -727,7 +755,7 @@ class WasmFoundationTests: XCTestCase { let wasmFunction = wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmi32]) { function, label, params in [function.wasmi32BinOp(params[0], function.consti32(1), binOpKind: .Add)] } - wasmModule.addTable(elementType: .wasmFuncRef, + wasmModule.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: [.init(indexInTable: 0, signature: [.wasmi32] => [.wasmi32]), .init(indexInTable: 1, signature: [] => [.wasmi64])], definedEntryValues: [wasmFunction, jsFunction], @@ -745,7 +773,7 @@ class WasmFoundationTests: XCTestCase { XCTAssertEqual(b.type(of: importedFunction), .function([] => .bigint)) // This is the table type that we expect to see on the exports based on the dynamic object group typing. - let tableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef, limits: Limits(min: 10), isTable64: isTable64, knownEntries: [ + let tableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef(), limits: Limits(min: 10), isTable64: isTable64, knownEntries: [ .init(indexInTable: 0, signature: [.wasmi32] => [.wasmi32]), .init(indexInTable: 1, signature: [] => [.wasmi64]) @@ -793,7 +821,7 @@ class WasmFoundationTests: XCTestCase { let wasmFunction = wasmModule.addWasmFunction(with: [.wasmi64] => [.wasmi64, .wasmi64]) { function, label, params in return [params[0], function.consti64(1)] } - let table = wasmModule.addTable(elementType: .wasmFuncRef, + let table = wasmModule.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: [.init(indexInTable: 0, signature: [.wasmi64] => [.wasmi64, .wasmi64]), .init(indexInTable: 1, signature: [.wasmi64] => [.wasmi64])], definedEntryValues: [wasmFunction, jsFunction], @@ -843,7 +871,7 @@ class WasmFoundationTests: XCTestCase { let wasmFunction = wasmModule.addWasmFunction(with: [.wasmi64] => [.wasmi64, .wasmi64]) { function, label, params in return [params[0], function.consti64(1)] } - wasmModule.addTable(elementType: .wasmFuncRef, + wasmModule.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: [.init(indexInTable: 0, signature: [.wasmi64] => [.wasmi64, .wasmi64]), .init(indexInTable: 1, signature: [.wasmi64] => [.wasmi64])], definedEntryValues: [wasmFunction, jsFunction], @@ -851,7 +879,7 @@ class WasmFoundationTests: XCTestCase { } let table = b.getProperty("wt0", of: module.loadExports()) - let tableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef, limits: Limits(min: 10), isTable64: false, knownEntries: [ + let tableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef(), limits: Limits(min: 10), isTable64: false, knownEntries: [ .init(indexInTable: 0, signature: [.wasmi64] => [.wasmi64, .wasmi64]), .init(indexInTable: 1, signature: [.wasmi64] => [.wasmi64]) ])) @@ -866,7 +894,7 @@ class WasmFoundationTests: XCTestCase { fn.wasmCallIndirect(signature: [.wasmi64] => [.wasmi64], table: table, functionArgs: [params[1]], tableIndex: params[0]) } - wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmFuncRef]) { function, label, params in + wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmFuncRef()]) { function, label, params in [function.wasmTableGet(tableRef: table, idx: params[0])] } @@ -881,7 +909,7 @@ class WasmFoundationTests: XCTestCase { let reexportedTable = b.getProperty("iwt0", of: exports) // This is the table type that we expect to see on the exports based on the dynamic object group typing. - let reexportedTableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef, limits: Limits(min: 10), isTable64: false, knownEntries: [ + let reexportedTableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef(), limits: Limits(min: 10), isTable64: false, knownEntries: [ .init(indexInTable: 0, signature: [.wasmi64] => [.wasmi64, .wasmi64]), .init(indexInTable: 1, signature: [.wasmi64] => [.wasmi64]) @@ -997,7 +1025,7 @@ class WasmFoundationTests: XCTestCase { let wasmFunction = wasmModule.addWasmFunction(with: [.wasmi64] => [.wasmi64, .wasmi64]) { function, label, params in return [params[0], function.consti64(1)] } - let table = wasmModule.addTable(elementType: .wasmFuncRef, + let table = wasmModule.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: [.init(indexInTable: 0, signature: [.wasmi64] => [.wasmi64, .wasmi64]), .init(indexInTable: 1, signature: [.wasmi64] => [.wasmi64])], definedEntryValues: [wasmFunction, jsFunction], @@ -3760,10 +3788,11 @@ class WasmFoundationTests: XCTestCase { let outputFunc = b.createNamedVariable(forBuiltin: "output") let tagVoid = b.createWasmTag(parameterTypes: []) let tagi32 = b.createWasmTag(parameterTypes: [.wasmi32]) + // TODO(pawkra): add shared ref variant. let module = b.buildWasmModule { wasmModule in wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmi32]) { function, label, args in - function.wasmBuildBlockWithResults(with: [] => [.wasmExnRef], args: []) { catchAllRefLabel, _ in - let catchRefI32 = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef], args: []) { catchRefLabel, _ in + function.wasmBuildBlockWithResults(with: [] => [.wasmExnRef()], args: []) { catchAllRefLabel, _ in + let catchRefI32 = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef()], args: []) { catchRefLabel, _ in function.wasmBuildTryTable(with: [] => [], args: [tagi32, catchRefLabel, catchAllRefLabel], catches: [.Ref, .AllRef]) { _, _ in function.wasmBuildIfElse(function.wasmi32EqualZero(args[0]), hint: .None) { function.WasmBuildThrow(tag: tagVoid, inputs: []) @@ -3772,10 +3801,10 @@ class WasmFoundationTests: XCTestCase { } return [] } - return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef)] + return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef())] } function.wasmReturn(catchRefI32[0]) - return [function.wasmRefNull(type: .wasmExnRef)] + return [function.wasmRefNull(type: .wasmExnRef())] } return [function.consti32(100)] } @@ -3865,10 +3894,8 @@ class WasmFoundationTests: XCTestCase { let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() - // Assumption: All types but the bottom (null) types are supported in the JS API. - let supportedTypes = WasmAbstractHeapType.allCases.filter {!$0.isBottom()}.map { heapType in - ILType.wasmRef(.Abstract(heapType), nullability:true) - } + // Assumption: All types apart from bottom (null) & shared types are supported in the JS API. + let supportedTypes = WasmAbstractHeapType.allNonBottomTypes().map {ILType.wasmRef($0, nullability: true)} b.createWasmTag(parameterTypes: supportedTypes) let prog = b.finalize() let jsProg = fuzzer.lifter.lift(prog, withOptions: [.includeComments]) @@ -3879,7 +3906,7 @@ class WasmFoundationTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "") } - func testThrowRef() throws { + func throwRef(sharedRef: Bool) throws { let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--experimental-wasm-exnref"]) let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) @@ -3894,17 +3921,17 @@ class WasmFoundationTests: XCTestCase { let module = b.buildWasmModule { wasmModule in // Inner function that throws, catches and then rethrows the value. let callee = wasmModule.addWasmFunction(with: [.wasmi32] => []) { function, label, args in - let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef], args: []) { catchRefLabel, _ in + let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef(shared: sharedRef)], args: []) { catchRefLabel, _ in function.wasmBuildTryTable(with: [] => [], args: [tagi32, catchRefLabel], catches: [.Ref]) { _, _ in function.WasmBuildThrow(tag: tagi32, inputs: [args[0]]) return [] } - return [function.consti32(0), function.wasmRefNull(type: .wasmExnRef)] + return [function.consti32(0), function.wasmRefNull(type: .wasmExnRef(shared: sharedRef))] } // Print the caught i32 value. function.wasmJsCall(function: printInteger, withArgs: [caughtValues[0]], withWasmSignature: [.wasmi32] => []) // To rethrow the exception, perform a throw_ref with the exnref. - function.wasmBuildThrowRef(exception: caughtValues[1]) + function.wasmBuildThrowRef(exception: caughtValues[1], sharedRef: sharedRef) return [] } @@ -3929,6 +3956,15 @@ class WasmFoundationTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "42\n42\n") } + func testThrowRefShared() throws { + // TODO(pawkra) + throw XCTSkip("Enable the test once we are emit & support shared refs in ProgramBuilder.") + } + + func testThrowRefUnshared() throws { + try throwRef(sharedRef: false) + } + func testUnreachable() throws { let runner = try GetJavaScriptExecutorOrSkipTest() let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) @@ -3972,7 +4008,7 @@ class WasmFoundationTests: XCTestCase { trueValue: function.consti64(123), falseValue: function.consti64(321))] } - wasmModule.addWasmFunction(with: [.wasmi32, .wasmExternRef, .wasmExternRef] => [.wasmExternRef]) { function, label, args in + wasmModule.addWasmFunction(with: [.wasmi32, .wasmExternRef(), .wasmExternRef()] => [.wasmExternRef()]) { function, label, args in [function.wasmSelect(on: args[0], trueValue: args[1], falseValue: args[2])] } } @@ -4148,13 +4184,13 @@ class WasmFoundationTests: XCTestCase { let f1 = module.addWasmFunction(with: [] => [.wasmi64]) { f, _, _ in return [f.consti64(1)]} let f2 = module.addWasmFunction(with: [] => [.wasmi64]) { f, _, _ in return [f.consti64(2)]} let f3 = module.addWasmFunction(with: [] => [.wasmi64]) { f, _, _ in return [f.consti64(3)]} - - module.addTable(elementType: .wasmFuncRef, + // TODO(pawkra): add shared ref variant. + module.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: [], definedEntryValues: [], isTable64: isTable64) - let table2 = module.addTable(elementType: .wasmFuncRef, + let table2 = module.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: [], definedEntryValues: [], @@ -4196,12 +4232,13 @@ class WasmFoundationTests: XCTestCase { let f2 = module.addWasmFunction(with: [] => [.wasmi64]) { f, _, _ in return [f.consti64(2)]} let f3 = module.addWasmFunction(with: [] => [.wasmi64]) { f, _, _ in return [f.consti64(3)]} - let table1 = module.addTable(elementType: .wasmFuncRef, + // TODO(pawkra): add shared ref variant. + let table1 = module.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: [], definedEntryValues: [], isTable64: isTable64) - let table2 = module.addTable(elementType: .wasmFuncRef, + let table2 = module.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: (0..<4).map { WasmTableType.IndexInTableAndWasmSignature.init(indexInTable: $0, signature: [] => [.wasmi64]) }, definedEntryValues: [f3, f3, f1, f2], @@ -4468,8 +4505,9 @@ class WasmGCTests: XCTestCase { return [arrayi32, signature] } + // TODO(pawkra): add shared ref variant. let module = b.buildWasmModule { wasmModule in - wasmModule.addWasmFunction(with: [] => [.wasmFuncRef]) { function, label, args 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. @@ -4494,7 +4532,7 @@ class WasmGCTests: XCTestCase { let jsProg = buildAndLiftProgram { b in let module = b.buildWasmModule { wasmModule in - wasmModule.addWasmFunction(with: [] => [.wasmFuncRef]) { function, label, args 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. @@ -4708,15 +4746,19 @@ class WasmGCTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "1\n0\n") } - func testRefNullAbstractTypes() throws { - let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--experimental-wasm-exnref"]) + func refNullAbstractTypes(sharedRef: Bool) throws { + let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--experimental-wasm-exnref", "--experimental-wasm-shared"]) let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() + // TODO(pawkra): simplify once V8 adds support for shared references of type: func, exn + let unsupportedHeapType: Set = sharedRef ? [.WasmFunc, .WasmNoFunc, .WasmExn, .WasmNoExn] : [] + let supportedHeapTypes = Array(Set(WasmAbstractHeapType.allCases).subtracting(unsupportedHeapType)) + let module = b.buildWasmModule { wasmModule in - for heapType in WasmAbstractHeapType.allCases { - let valueType = ILType.wasmRef(.Abstract(heapType), nullability: true) + for heapType in supportedHeapTypes { + let valueType = ILType.wasmRef(heapType, shared: sharedRef, nullability: true) if heapType.isUsableInJS() { // ref.null wasmModule.addWasmFunction(with: [] => [valueType]) { function, label, args in @@ -4732,8 +4774,8 @@ class WasmGCTests: XCTestCase { let exports = module.loadExports() let outputFunc = b.createNamedVariable(forBuiltin: "output") - let exportedFctCount = WasmAbstractHeapType.allCases.count - + WasmAbstractHeapType.allCases.count {$0.isUsableInJS()} + let exportedFctCount = supportedHeapTypes.count + + supportedHeapTypes.count {$0.isUsableInJS()} for i in 0.. [.wasmI31Ref]) { function, label, args in + wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmI31Ref()]) { function, label, args in [function.wasmRefI31(args[0])] } - wasmModule.addWasmFunction(with: [.wasmI31Ref] => [.wasmi32, .wasmi32]) { function, label, args in - [function.wasmI31Get(args[0], isSigned: true), - function.wasmI31Get(args[0], isSigned: false)] + wasmModule.addWasmFunction(with: [.wasmI31Ref()] => [.wasmi32, .wasmi32]) { function, label, args in + [function.wasmI31Get(args[0], isSigned: true, shared: false), + function.wasmI31Get(args[0], isSigned: false, shared: false)] } } @@ -4780,36 +4830,75 @@ class WasmGCTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "42\n-42\n42,42\n-42,2147483606\n") } - func testExternAnyConversions() throws { + func i31Ref(sharedRef: Bool) throws { + let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--experimental-wasm-shared"]) + let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) + let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) + let b = fuzzer.makeBuilder() + + let module = b.buildWasmModule { wasmModule in + let f1 = wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmI31Ref(shared: sharedRef)]) { function, label, args in + [function.wasmRefI31(args[0])] + } + let f2 = wasmModule.addWasmFunction(with: [.wasmI31Ref(shared: sharedRef)] => [.wasmi32, .wasmi32]) { function, label, args in + [function.wasmI31Get(args[0], isSigned: true, shared: sharedRef), + function.wasmI31Get(args[0], isSigned: false, shared: sharedRef)] + } + wasmModule.addWasmFunction(with: [] => [.wasmi32, .wasmi32]) { function, label, args in + let ref = function.wasmCallDirect(signature: [.wasmi32] => [.wasmI31Ref(shared: sharedRef)], function: f1, functionArgs: [function.consti32(-42)]) + return function.wasmCallDirect(signature: [.wasmI31Ref(shared: sharedRef)] => [.wasmi32, .wasmi32], function: f2, functionArgs: ref) + } + } + + let exports = module.loadExports() + let outputFunc = b.createNamedVariable(forBuiltin: "output") + let result = b.callMethod(module.getExportedMethod(at: 2), on: exports, withArgs: [b.loadInt(42)]) + b.callFunction(outputFunc, withArgs: [result]) + + let prog = b.finalize() + let jsProg = fuzzer.lifter.lift(prog) + testForOutput(program: jsProg, runner: runner, outputString: "-42,2147483606\n") + } + + func testi31RefSharedRef() throws { + // TODO(pawkra) + throw XCTSkip("Enable the test once we are emit & support shared refs in ProgramBuilder.") + } + + func testi31RefUnsharedRef() throws { + try i31Ref(sharedRef: false) + } + + func externAnyConversions(sharedRef: Bool) throws { let runner = try GetJavaScriptExecutorOrSkipTest() let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() let module = b.buildWasmModule { wasmModule in - wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmRefExtern]) { function, label, args in + wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmRefExtern(shared: sharedRef)]) { function, label, args in // As ref.i31 produces a non null `ref i31`, the result of extern.convert_any is a // non-nullable `ref extern`. - let result = function.wasmExternConvertAny(function.wasmRefI31(args[0])) - XCTAssertEqual(b.type(of: result), .wasmRefExtern) + let result = function.wasmExternConvertAny(function.wasmRefI31(args[0]), shared: sharedRef) + XCTAssertEqual(b.type(of: result), .wasmRefExtern(shared: sharedRef)) return [result] } - wasmModule.addWasmFunction(with: [.wasmRefExtern] => [.wasmRefAny]) { function, label, args in - let result = function.wasmAnyConvertExtern(args[0]) - XCTAssertEqual(b.type(of: result), .wasmRefAny) + wasmModule.addWasmFunction(with: [.wasmRefExtern(shared: sharedRef)] => [.wasmRefAny(shared: sharedRef)]) { function, label, args in + let result = function.wasmAnyConvertExtern(args[0], shared: sharedRef) + XCTAssertEqual(b.type(of: result), .wasmRefAny(shared: sharedRef)) return [result] } - wasmModule.addWasmFunction(with: [] => [.wasmExternRef]) { function, label, args in - let result = function.wasmExternConvertAny(function.wasmRefNull(type: .wasmNullRef)) - XCTAssertEqual(b.type(of: result), .wasmExternRef) + wasmModule.addWasmFunction(with: [] => [.wasmExternRef(shared: sharedRef)]) { function, label, args in + let result = function.wasmExternConvertAny(function.wasmRefNull(type: .wasmNullRef(shared: sharedRef)), shared: sharedRef) + XCTAssertEqual(b.type(of: result), .wasmExternRef(shared: sharedRef)) return [result] } - wasmModule.addWasmFunction(with: [] => [.wasmAnyRef]) { function, label, args in - let result = function.wasmAnyConvertExtern(function.wasmRefNull(type: .wasmNullExternRef)) - XCTAssertEqual(b.type(of: result), .wasmAnyRef) + wasmModule.addWasmFunction(with: [] => [.wasmAnyRef(shared: sharedRef)]) { function, label, args in + let result = function.wasmAnyConvertExtern(function.wasmRefNull(type: .wasmNullExternRef(shared: sharedRef)), shared: sharedRef) + XCTAssertEqual(b.type(of: result), .wasmAnyRef(shared: sharedRef)) return [result] } } @@ -4825,6 +4914,15 @@ class WasmGCTests: XCTestCase { let jsProg = fuzzer.lifter.lift(prog) testForOutput(program: jsProg, runner: runner, outputString: "42\n42\nnull\nnull\n") } + + func testExternAnyConversionsSharedRefs() throws { + // TODO(pawkra): double check that shared references can be converted to extern ref. + throw XCTSkip("Fix once we are able to emit shared i31, extern, null, and nullextern refs.") + } + + func testExternAnyConversionsUnsharedRefs() throws { + try externAnyConversions(sharedRef: false) + } } class WasmNumericalTests: XCTestCase { @@ -6359,9 +6457,10 @@ class WasmJSPITests: XCTestCase { XCTAssertEqual(b.type(of: importFunction), .object(ofGroup: "WasmSuspendingObject")) // Now lets build the module + // TODO(pawkra): add shared ref variant. let module = b.buildWasmModule { m in - m.addWasmFunction(with: [.wasmExternRef] => [.wasmi32]) { f, label, args in - [f.wasmJsCall(function: importFunction, withArgs: args, withWasmSignature: [.wasmExternRef] => [.wasmi32])!] + m.addWasmFunction(with: [.wasmExternRef()] => [.wasmi32]) { f, label, args in + [f.wasmJsCall(function: importFunction, withArgs: args, withWasmSignature: [.wasmExternRef()] => [.wasmi32])!] } } From 57064c378bb5e96913095f6d1f708f88e621084e Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Tue, 2 Dec 2025 06:15:38 -0800 Subject: [PATCH 24/89] Revert "Add support for shared references." This reverts commit e35cbb5b7b784cae910776e4edeefc8553316d56. Reason for revert: Crashes and not reviewed yet. Original change's description: > Add support for shared references. > > Generating shared ref variables to be done in following CLs. > > See https://github.com/WebAssembly/shared-everything-threads/blob/main/proposals/shared-everything-threads/Overview.md. > > Bug: 448349112 > Change-Id: I3358ce9cdd528147b66f1954ef1a008b048e06df > Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8734256 > Commit-Queue: Matthias Liedtke > Reviewed-by: Dominik Klemba > Commit-Queue: Pawel Krawczyk Bug: 448349112 No-Presubmit: true No-Tree-Checks: true No-Try: true Change-Id: I8bc73bef53d053078db9318de6408d4dbf2f4cda Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8810396 Bot-Commit: Rubber Stamper Auto-Submit: Matthias Liedtke Commit-Queue: Rubber Stamper --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 99 +++--- Sources/Fuzzilli/CodeGen/CodeGenerator.swift | 6 +- .../Fuzzilli/CodeGen/ProgramTemplates.swift | 11 +- .../Fuzzilli/CodeGen/WasmCodeGenerators.swift | 39 +-- Sources/Fuzzilli/Configuration.swift | 1 - .../Environment/JavaScriptEnvironment.swift | 2 +- Sources/Fuzzilli/FuzzIL/Instruction.swift | 69 ++--- Sources/Fuzzilli/FuzzIL/JSTyper.swift | 11 +- Sources/Fuzzilli/FuzzIL/TypeSystem.swift | 127 ++------ Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 45 ++- .../Fuzzilli/Lifting/JavaScriptLifter.swift | 23 +- Sources/Fuzzilli/Lifting/WasmLifter.swift | 81 ++--- .../Fuzzilli/Mutators/OperationMutator.swift | 32 +- Sources/Fuzzilli/Protobuf/operations.pb.swift | 28 +- Sources/Fuzzilli/Protobuf/operations.proto | 3 +- .../Profiles/V8CommonProfile.swift | 5 +- Tests/FuzzilliTests/JSTyperTests.swift | 8 +- Tests/FuzzilliTests/LifterTest.swift | 2 +- Tests/FuzzilliTests/LiveTests.swift | 7 +- Tests/FuzzilliTests/ProgramBuilderTest.swift | 52 ++-- Tests/FuzzilliTests/TypeSystemTest.swift | 108 +++---- Tests/FuzzilliTests/WasmTableTests.swift | 4 +- Tests/FuzzilliTests/WasmTests.swift | 283 ++++++------------ 23 files changed, 403 insertions(+), 643 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 63ef596ed..6bc1dbd6b 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -1217,6 +1217,7 @@ public class ProgramBuilder { numberOfHiddenVariables -= 1 } + /// Hides a variable containing a function from the function's body. /// /// For example, in @@ -1249,20 +1250,19 @@ public class ProgramBuilder { unhide(variableToHide) } - // TODO(pawkra): enable shared types. private static func matchingWasmTypes(jsType: ILType) -> [ILType] { if jsType.Is(.integer) { return [.wasmi32, .wasmf64, .wasmf32] } else if jsType.Is(.number) { - return [.wasmf32, .wasmf64, .wasmi32, .wasmRefI31(), .wasmI31Ref()] + return [.wasmf32, .wasmf64, .wasmi32, .wasmRefI31, .wasmI31Ref] } else if jsType.Is(.bigint) { return [.wasmi64] } else if jsType.Is(.function()) { // TODO(gc): Add support for specific signatures. - return [.wasmFuncRef()] + return [.wasmFuncRef] } else { // TODO(gc): Add support for types of the anyref hierarchy. - return [.wasmExternRef()] + return [.wasmExternRef] } } @@ -1272,7 +1272,6 @@ public class ProgramBuilder { } // Helper that converts a Wasm type to its deterministic known JS counterparts. - // TODO(pawkra): enable shared types. private static func mapWasmToJsType(_ type: ILType) -> ILType { if type.Is(.wasmi32) { return .integer @@ -1289,12 +1288,12 @@ public class ProgramBuilder { return .jsAnything } else if type.Is(.nothing) { return .undefined - } else if type.Is(.wasmFuncRef()) { + } else if type.Is(.wasmFuncRef) { // TODO(cffsmith): refine this type with the signature if we can. return .function() - } else if type.Is(.wasmI31Ref()) { + } else if type.Is(.wasmI31Ref) { return .integer - } else if type.Is(.wasmNullRef()) || type.Is(.wasmNullExternRef()) || type.Is(.wasmNullFuncRef()) { + } else if type.Is(.wasmNullRef) || type.Is(.wasmNullExternRef) || type.Is(.wasmNullFuncRef) { // This is slightly imprecise: The null types only accept null, not undefined but // Fuzzilli doesn't differentiate between null and undefined in its type system. return .nullish @@ -3882,9 +3881,8 @@ public class ProgramBuilder { b.emit(WasmDropElementSegment(), withInputs: [elementSegment], types: [.wasmElementSegment()]) } - // TODO(pawkra): support shared tables and element segments. public func wasmTableInit(elementSegment: Variable, table: Variable, tableOffset: Variable, elementSegmentOffset: Variable, nrOfElementsToUpdate: Variable) { - let elementSegmentType = ILType.wasmFuncRef() + let elementSegmentType = ILType.wasmFuncRef let tableElemType = b.type(of: table).wasmTableType!.elementType assert(elementSegmentType.Is(tableElemType)) @@ -4014,22 +4012,18 @@ public class ProgramBuilder { return Array(b.emit(WasmEndLoop(outputTypes: signature.outputTypes), withInputs: fallthroughResults, types: signature.outputTypes).outputs) } - // TODO(pawkra): enable shared types. @discardableResult func wasmBuildTryTable(with signature: WasmSignature, args: [Variable], catches: [WasmBeginTryTable.CatchKind], body: (Variable, [Variable]) -> [Variable]) -> [Variable] { assert(zip(signature.parameterTypes, args).allSatisfy {b.type(of: $1).Is($0)}) #if DEBUG var argIndex = signature.parameterTypes.count - let assertLabelTypeData: (ILType) -> () = { labelType in - assert(labelType.Is(.anyLabel)) - assert(labelType.wasmLabelType!.parameters.last!.Is(.wasmExnRef())) - } for catchKind in catches { switch catchKind { case .Ref: assert(b.type(of: args[argIndex]).Is(.object(ofGroup: "WasmTag"))) let labelType = b.type(of: args[argIndex + 1]) - assertLabelTypeData(labelType) + assert(labelType.Is(.anyLabel)) + assert(labelType.wasmLabelType!.parameters.last!.Is(.wasmExnRef)) argIndex += 2 case .NoRef: assert(b.type(of: args[argIndex]).Is(.object(ofGroup: "WasmTag"))) @@ -4037,7 +4031,8 @@ public class ProgramBuilder { argIndex += 2 case .AllRef: let labelType = b.type(of: args[argIndex]) - assertLabelTypeData(labelType) + assert(labelType.Is(.anyLabel)) + assert(labelType.wasmLabelType!.parameters.last!.Is(.wasmExnRef)) argIndex += 1 case .AllNoRef: assert(b.type(of: args[argIndex]).Is(.anyLabel)) @@ -4100,8 +4095,8 @@ public class ProgramBuilder { b.emit(WasmThrow(parameterTypes: tagType.parameters), withInputs: [tag] + inputs, types: [.object(ofGroup: "WasmTag")] + tagType.parameters) } - public func wasmBuildThrowRef(exception: Variable, sharedRef: Bool) { - b.emit(WasmThrowRef(), withInputs: [exception], types: [.wasmExnRef(shared: sharedRef)]) + public func wasmBuildThrowRef(exception: Variable) { + b.emit(WasmThrowRef(), withInputs: [exception], types: [.wasmExnRef]) } public func wasmBuildLegacyRethrow(_ exceptionLabel: Variable) { @@ -4142,23 +4137,15 @@ public class ProgramBuilder { // TODO(cffsmith): Can we improve this once we have better support for ad hoc // code generation in other contexts? switch type.wasmReferenceType?.kind { - case .Abstract(let heapTypeInfo): - // TODO(pawkra): add support for shared refs. - assert(!heapTypeInfo.shared) - if probability(0.2) && type.wasmReferenceType!.nullability { - return self.wasmRefNull(type: type) - } - // Prefer generating a non-null value. - if heapTypeInfo.heapType == .WasmI31 { - return self.wasmRefI31(self.consti32(Int32(truncatingIfNeeded: b.randomInt()))) - } - // TODO(pawkra): support different types. For now - // fallback to refNull if possible. - if (type.wasmReferenceType!.nullability) { - return self.wasmRefNull(type: type) - } else { - return nil + case .Abstract(let heapType): + if heapType == .WasmI31 { + // Prefer generating a non-null value. + return probability(0.2) && type.wasmReferenceType!.nullability + ? self.wasmRefNull(type: type) + : self.wasmRefI31(self.consti32(Int32(truncatingIfNeeded: b.randomInt()))) } + assert(type.wasmReferenceType!.nullability) + return self.wasmRefNull(type: type) case .Index(_), .none: break // Unimplemented @@ -4356,18 +4343,18 @@ public class ProgramBuilder { } @discardableResult - public func wasmI31Get(_ refI31: Variable, isSigned: Bool, shared: Bool) -> Variable { - return b.emit(WasmI31Get(isSigned: isSigned), withInputs: [refI31], types: [.wasmI31Ref(shared: shared)]).output + public func wasmI31Get(_ refI31: Variable, isSigned: Bool) -> Variable { + return b.emit(WasmI31Get(isSigned: isSigned), withInputs: [refI31], types: [.wasmI31Ref]).output } @discardableResult - public func wasmAnyConvertExtern(_ ref: Variable, shared: Bool) -> Variable { - b.emit(WasmAnyConvertExtern(), withInputs: [ref], types: [.wasmExternRef(shared: shared)]).output + public func wasmAnyConvertExtern(_ ref: Variable) -> Variable { + b.emit(WasmAnyConvertExtern(), withInputs: [ref], types: [.wasmExternRef]).output } @discardableResult - public func wasmExternConvertAny(_ ref: Variable, shared: Bool) -> Variable { - b.emit(WasmExternConvertAny(), withInputs: [ref], types: [.wasmAnyRef(shared: shared)]).output + public func wasmExternConvertAny(_ ref: Variable) -> Variable { + b.emit(WasmExternConvertAny(), withInputs: [ref], types: [.wasmAnyRef]).output } } @@ -4436,14 +4423,13 @@ public class ProgramBuilder { @discardableResult public func addElementSegment(elements: [Variable]) -> Variable { - let inputTypes = Array(repeating: getEntryTypeForTable(elementType: ILType.wasmFuncRef()), count: elements.count) + let inputTypes = Array(repeating: getEntryTypeForTable(elementType: ILType.wasmFuncRef), count: elements.count) return b.emit(WasmDefineElementSegment(size: UInt32(elements.count)), withInputs: elements, types: inputTypes).output } - // TODO(pawkra): enable shared tables and element segments (and functions?). public func getEntryTypeForTable(elementType: ILType) -> ILType { switch elementType { - case .wasmFuncRef(): + case .wasmFuncRef: return .wasmFunctionDef() | .function() default: return .object() @@ -4519,7 +4505,6 @@ public class ProgramBuilder { /// Produces a WasmGlobal that is valid to create in the given Context. public func randomWasmGlobal(forContext context: Context) -> WasmGlobal { - // TODO(pawkra): enable shared types. switch context { case .javascript: // These are valid in JS according to: https://webassembly.github.io/spec/js-api/#globals. @@ -4529,7 +4514,7 @@ public class ProgramBuilder { {.wasmf64(self.randomFloat())}, {.wasmi32(Int32(truncatingIfNeeded: self.randomInt()))}, {.wasmi64(self.randomInt())}, - {.externref(shared: false)}) + {.externref}) case .wasm: // TODO: Add simd128 and nullrefs. return withEqualProbability( @@ -4537,9 +4522,9 @@ public class ProgramBuilder { {.wasmf64(self.randomFloat())}, {.wasmi32(Int32(truncatingIfNeeded: self.randomInt()))}, {.wasmi64(self.randomInt())}, - {.externref(shared: false)}, - {.exnref(shared: false)}, - {.i31ref(shared: false)}) + {.externref}, + {.exnref}, + {.i31ref}) default: fatalError("Unsupported context \(context) for a WasmGlobal.") } @@ -4549,23 +4534,21 @@ public class ProgramBuilder { // TODO(mliedtke): The list of types should be shared with function signature generation // etc. We should also support non-nullable references but that requires being able // to generate valid ones which currently isn't the case for most of them. - // TODO(pawkra): enable shared types. return (0.. WasmSignature { // TODO: generalize this to support more types. Also add support for simd128 and // (null)exnref, note however that these types raise exceptions when used from JS. - // TODO(pawkra): enable shared types. let valueTypes: [ILType] = [.wasmi32, .wasmi64, .wasmf32, .wasmf64] - let abstractRefTypes: [ILType] = [.wasmExternRef(), .wasmAnyRef(), .wasmI31Ref()] - let nullTypes: [ILType] = [.wasmNullRef(), .wasmNullExternRef(), .wasmNullFuncRef()] + let abstractRefTypes: [ILType] = [.wasmExternRef, .wasmAnyRef, .wasmI31Ref] + let nullTypes: [ILType] = [.wasmNullRef, .wasmNullExternRef, .wasmNullFuncRef] let randomType = { chooseUniform( from: chooseBiased(from: [nullTypes, abstractRefTypes, valueTypes], factor: 1.5)) @@ -4580,10 +4563,9 @@ public class ProgramBuilder { // abstract heap types. To be able to emit them, generateRandomWasmVar() needs to be able // to generate a sequence that produces such a non-nullable value which might be difficult // for some types as of now. - // TODO(pawkra): enable shared types. (0.. [Variable] { @@ -5337,4 +5319,3 @@ public class ProgramBuilder { } } - diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerator.swift b/Sources/Fuzzilli/CodeGen/CodeGenerator.swift index 61d9e181e..174e72924 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerator.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerator.swift @@ -105,7 +105,7 @@ public class GeneratorStub: Contributor { if type.Is(.wasmTypeDef()) { type.wasmTypeDefinition?.description is WasmArrayTypeDescription } else if type.Is(.anyNonNullableIndexRef) { - type.Is(.wasmArrayRef()) + type.Is(.wasmArrayRef) } else { false } @@ -113,7 +113,7 @@ public class GeneratorStub: Contributor { if type.Is(.wasmTypeDef()) { type.wasmTypeDefinition?.description is WasmStructTypeDescription } else if type.Is(.anyNonNullableIndexRef) { - type.Is(.wasmStructRef()) + type.Is(.wasmStructRef) } else { false } @@ -121,7 +121,7 @@ public class GeneratorStub: Contributor { if type.Is(.wasmTypeDef()) { type.wasmTypeDefinition?.description is WasmSignatureTypeDescription } else if type.Is(.anyNonNullableIndexRef) { - type.Is(.wasmFuncRef()) + type.Is(.wasmFuncRef) } else { false } diff --git a/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift b/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift index f356a4cc5..126519941 100644 --- a/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift +++ b/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift @@ -99,7 +99,7 @@ public let ProgramTemplates = [ let signature = b.type(of: f!).signature ?? Signature.forUnknownFunction // As we do not yet know what types we have in the Wasm module when we try to call this, let Fuzzilli know that it could potentially use all Wasm types here. - let allWasmTypes: WeightedList = WeightedList([(.wasmi32, 1), (.wasmi64, 1), (.wasmf32, 1), (.wasmf64, 1), (.wasmExternRef(), 1), (.wasmFuncRef(), 1)]) + let allWasmTypes: WeightedList = WeightedList([(.wasmi32, 1), (.wasmi64, 1), (.wasmf32, 1), (.wasmf64, 1), (.wasmExternRef, 1), (.wasmFuncRef, 1)]) var wasmSignature = ProgramBuilder.convertJsSignatureToWasmSignature(signature, availableTypes: allWasmTypes) let wrapped = b.wrapSuspending(function: f!) @@ -152,10 +152,7 @@ public let ProgramTemplates = [ let tagToThrow = chooseUniform(from: wasmTags) let throwParamTypes = b.type(of: tagToThrow).wasmTagType!.parameters let tagToCatchForRethrow = chooseUniform(from: tags) - // TODO(pawkra): support shared variant - let sharedRef = false - let wasmExnRefType = ILType.wasmExnRef(shared: sharedRef) - let catchBlockOutputTypes = b.type(of: tagToCatchForRethrow).wasmTagType!.parameters + [wasmExnRefType] + let catchBlockOutputTypes = b.type(of: tagToCatchForRethrow).wasmTagType!.parameters + [.wasmExnRef] let module = b.buildWasmModule { wasmModule in // Wasm function that throws a tag, catches a tag (the same or a different one) to @@ -176,7 +173,7 @@ public let ProgramTemplates = [ return catchBlockOutputTypes.map(function.findOrGenerateWasmVar) } b.build(n: 10) - function.wasmBuildThrowRef(exception: b.randomVariable(ofType: wasmExnRefType)!, sharedRef: sharedRef) + function.wasmBuildThrowRef(exception: b.randomVariable(ofType: .wasmExnRef)!) return [] } } @@ -209,7 +206,7 @@ public let ProgramTemplates = [ return calleeSig.outputTypes.map(function.findOrGenerateWasmVar) }} - let table = wasmModule.addTable(elementType: .wasmFuncRef(), + let table = wasmModule.addTable(elementType: .wasmFuncRef, minSize: 10, definedEntries: callees.enumerated().map { (index, callee) in .init(indexInTable: index, signature: calleeSig) diff --git a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift index 855bd34c9..697f84d4a 100644 --- a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift @@ -270,7 +270,6 @@ public let WasmCodeGenerators: [CodeGenerator] = [ value: newValue) }, - // TODO(pawkra): add shared variant. CodeGenerator("WasmRefNullGenerator", inContext: .single(.wasmFunction)) { b in let function = b.currentWasmModule.currentWasmFunction if let typeDef = (b.findVariable { b.type(of: $0).Is(.wasmTypeDef()) }), @@ -281,7 +280,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ function.wasmRefNull( type: .wasmRef( .Abstract( - HeapTypeInfo(chooseUniform(from: WasmAbstractHeapType.allCases), shared: false)), + chooseUniform(from: WasmAbstractHeapType.allCases)), nullability: true)) } }, @@ -297,19 +296,16 @@ public let WasmCodeGenerators: [CodeGenerator] = [ b.currentWasmModule.currentWasmFunction.wasmRefI31(value) }, - // TODO(pawkra): add shared variant. What about non-null case? - CodeGenerator("WasmI31GetGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmI31Ref())) { b, ref in - b.currentWasmModule.currentWasmFunction.wasmI31Get(ref, isSigned: Bool.random(), shared: false) + CodeGenerator("WasmI31GetGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmI31Ref)) { b, ref in + b.currentWasmModule.currentWasmFunction.wasmI31Get(ref, isSigned: Bool.random()) }, - // TODO(pawkra): add shared variant. What about non-null case? - CodeGenerator("WasmAnyConvertExternGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmExternRef())) { b, ref in - b.currentWasmModule.currentWasmFunction.wasmAnyConvertExtern(ref, shared: false) + CodeGenerator("WasmAnyConvertExternGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmExternRef)) { b, ref in + b.currentWasmModule.currentWasmFunction.wasmAnyConvertExtern(ref) }, - // TODO(pawkra): add shared variant. What about non-null case? - CodeGenerator("WasmExternConvertAnyGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmAnyRef())) { b, ref in - b.currentWasmModule.currentWasmFunction.wasmExternConvertAny(ref, shared: false) + CodeGenerator("WasmExternConvertAnyGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmAnyRef)) { b, ref in + b.currentWasmModule.currentWasmFunction.wasmExternConvertAny(ref) }, // Primitive Value Generators @@ -600,8 +596,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ // TODO(manoskouk): Generalize these. let minSize = 10 let maxSize: Int? = nil - // TODO(pawkra): support shared variant. - let elementType = ILType.wasmFuncRef() + let elementType = ILType.wasmFuncRef let definedEntryIndices: [Int] var definedEntries: [WasmTableType.IndexInTableAndWasmSignature] = [] @@ -614,7 +609,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ // Currently, only generate entries for funcref tables. // TODO(manoskouk): Generalize this. - if elementType == .wasmFuncRef() { + if elementType == .wasmFuncRef { if b.randomVariable(ofType: expectedEntryType) != nil { // There is at least one function in scope. Add some initial entries to the table. // TODO(manoskouk): Generalize this. @@ -643,7 +638,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ }, CodeGenerator("WasmDefineElementSegmentGenerator", inContext: .single(.wasm)) { b in - let expectedEntryType = b.currentWasmModule.getEntryTypeForTable(elementType: .wasmFuncRef()) + let expectedEntryType = b.currentWasmModule.getEntryTypeForTable(elementType: ILType.wasmFuncRef) if b.randomVariable(ofType: expectedEntryType) == nil { return } @@ -674,8 +669,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ inputs: .required(.object(ofGroup: "WasmTable")) ) { b, table in let tableType = b.type(of: table).wasmTableType! - // TODO(pawkra): support shared variant. - if !tableType.elementType.Is(.wasmFuncRef()) { return } + if !tableType.elementType.Is(.wasmFuncRef) { return } guard let indexedSignature = tableType.knownEntries.randomElement() else { return } @@ -735,8 +729,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ ) { b, table in let function = b.currentWasmModule.currentWasmFunction let tableType = b.type(of: table).wasmTableType! - // TODO(pawkra): support shared variant. - if !tableType.elementType.Is(.wasmFuncRef()) { return } + if !tableType.elementType.Is(.wasmFuncRef) { return } guard let indexedSignature = (tableType.knownEntries.filter { @@ -1535,11 +1528,10 @@ public let WasmCodeGenerators: [CodeGenerator] = [ CodeGenerator( "WasmThrowRefGenerator", inContext: .single(.wasmFunction), - // TODO(pawkra): support shared variant. - inputs: .required(.wasmExnRef()) + inputs: .required(.wasmExnRef) ) { b, exception in let function = b.currentWasmModule.currentWasmFunction - function.wasmBuildThrowRef(exception: exception, sharedRef: false) + function.wasmBuildThrowRef(exception: exception) }, CodeGenerator( @@ -1632,8 +1624,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ [] } if withExnRef { - // TODO(pawkra): support shared variant. - outputTypes.append(.wasmExnRef()) + outputTypes.append(.wasmExnRef) } function.wasmBeginBlock(with: [] => outputTypes, args: []) return outputTypes diff --git a/Sources/Fuzzilli/Configuration.swift b/Sources/Fuzzilli/Configuration.swift index 9179ae663..121e9cbb8 100644 --- a/Sources/Fuzzilli/Configuration.swift +++ b/Sources/Fuzzilli/Configuration.swift @@ -14,7 +14,6 @@ public struct Configuration { /// The commandline arguments used by this instance. - /// TODO(pawkra): use these args or remove them (for now the only usage is printing them). public let arguments: [String] /// Timeout in milliseconds after which child processes will be killed. diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index 6e8e212f8..9ef54f216 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -2215,7 +2215,7 @@ public extension ObjectGroup { name: "WebAssembly", instanceType: nil, properties: [ - "JSTag": .object(ofGroup: "WasmTag", withWasmType: WasmTagType([.wasmExternRef()], isJSTag: true)), + "JSTag": .object(ofGroup: "WasmTag", withWasmType: WasmTagType([.wasmExternRef], isJSTag: true)), "Module": .jsWebAssemblyModuleConstructor, "Global": .jsWebAssemblyGlobalConstructor, "Instance": .jsWebAssemblyInstanceConstructor, diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index 6bf1de3e7..6486e4603 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -388,8 +388,8 @@ extension Instruction: ProtobufConvertible { $0.nullability = underlyingWasmType.wasmReferenceType!.nullability } } - case .Abstract(let heapTypeInfo): - let kind = switch heapTypeInfo.heapType { + case .Abstract(let heapType): + let kind = switch heapType { case .WasmExn: Fuzzilli_Protobuf_WasmReferenceTypeKind.exnref case .WasmI31: @@ -420,7 +420,6 @@ extension Instruction: ProtobufConvertible { $0.refType = Fuzzilli_Protobuf_WasmReferenceType.with { $0.kind = kind $0.nullability = underlyingWasmType.wasmReferenceType!.nullability - $0.isShared = heapTypeInfo.shared } } } @@ -499,26 +498,17 @@ extension Instruction: ProtobufConvertible { return Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal.valuef64(val) case .refFunc(let val): return Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal.funcref(Int64(val)) - case .externref(let shared): - return nullrefGlobal(.externref, shared: shared) - case .exnref(let shared): - return nullrefGlobal(.exnref, shared: shared) - case .i31ref(let shared): - return nullrefGlobal(.i31Ref, shared: shared) + case .externref: + return Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal.nullref(Fuzzilli_Protobuf_WasmReferenceTypeKind.externref) + case .exnref: + return Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal.nullref(Fuzzilli_Protobuf_WasmReferenceTypeKind.exnref) + case .i31ref: + return Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal.nullref(Fuzzilli_Protobuf_WasmReferenceTypeKind.i31Ref) case .imported(let ilType): return Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal.imported(ILTypeToWasmTypeEnum(ilType)) } } - func nullrefGlobal(_ kind: Fuzzilli_Protobuf_WasmReferenceTypeKind, shared: Bool) -> Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal { - return Fuzzilli_Protobuf_WasmGlobal.OneOf_WasmGlobal.nullref(Fuzzilli_Protobuf_WasmReferenceType.with { - $0.kind = kind - // TODO(gc): set nullability - $0.nullability = false - $0.isShared = shared - }) - } - func convertWasmCatch(catchKind: WasmBeginTryTable.CatchKind) -> Fuzzilli_Protobuf_WasmCatchKind { switch catchKind { case .NoRef: @@ -1694,40 +1684,37 @@ extension Instruction: ProtobufConvertible { fatalError("Unrecognized wasm value type \(value)") } case .refType(_): - if wasmType.refType.kind == .index { - return .wasmRef(.Index(), nullability: wasmType.refType.nullability) - } - let heapType: WasmAbstractHeapType = switch wasmType.refType.kind { + let refKind: WasmReferenceType.Kind = switch wasmType.refType.kind { + case .index: + .Index() case .externref: - .WasmExtern + .Abstract(.WasmExtern) case .funcref: - .WasmFunc + .Abstract(.WasmFunc) case .exnref: - .WasmExn + .Abstract(.WasmExn) case .i31Ref: - .WasmI31 + .Abstract(.WasmI31) case .anyref: - .WasmAny + .Abstract(.WasmAny) case .eqref: - .WasmEq + .Abstract(.WasmEq) case .structref: - .WasmStruct + .Abstract(.WasmStruct) case .arrayref: - .WasmArray + .Abstract(.WasmArray) case .noneref: - .WasmNone + .Abstract(.WasmNone) case .noexternref: - .WasmNoExtern + .Abstract(.WasmNoExtern) case .nofuncref: - .WasmNoFunc + .Abstract(.WasmNoFunc) case .noexnref: - .WasmNoExn - case .index: - fatalError("Unexpected index type.") + .Abstract(.WasmNoExn) case .UNRECOGNIZED(let value): fatalError("Unrecognized wasm reference type \(value)") } - return .wasmRef(heapType, shared: wasmType.refType.isShared, nullability: wasmType.refType.nullability) + return .wasmRef(refKind, nullability: wasmType.refType.nullability) case .none: fatalError("Absent wasm type") } @@ -1798,13 +1785,13 @@ extension Instruction: ProtobufConvertible { func convertWasmGlobal(_ proto: Fuzzilli_Protobuf_WasmGlobal) -> WasmGlobal { switch proto.wasmGlobal { case .nullref(let val): - switch val.kind { + switch val { case .externref: - return .externref(shared: val.isShared) + return .externref case .exnref: - return .exnref(shared: val.isShared) + return .exnref case .i31Ref: - return .i31ref(shared: val.isShared) + return .i31ref default: fatalError("Unrecognized global wasm reference type \(val)") } diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index 65f9689ac..054f242a2 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -888,18 +888,17 @@ public struct JSTyper: Analyzer { case .wasmRefIsNull(_): setType(of: instr.output, to: .wasmi32) case .wasmRefI31(_): - // TODO(pawkra): support shared variant. - setType(of: instr.output, to: .wasmRefI31()) + setType(of: instr.output, to: .wasmRefI31) case .wasmI31Get(_): setType(of: instr.output, to: .wasmi32) case .wasmAnyConvertExtern(_): // any.convert_extern forwards the nullability bit from the input. let null = type(of: instr.input(0)).wasmReferenceType!.nullability - setType(of: instr.output, to: .wasmRef(.WasmAny, shared: false, nullability: null)) + setType(of: instr.output, to: .wasmRef(.Abstract(.WasmAny), nullability: null)) case .wasmExternConvertAny(_): - // extern.convert_any forwards the nullability from the input. + // extern.convert_any forwards the nullability bit from the input. let null = type(of: instr.input(0)).wasmReferenceType!.nullability - setType(of: instr.output, to: .wasmRef(.WasmExtern, shared: false, nullability: null)) + setType(of: instr.output, to: .wasmRef(.Abstract(.WasmExtern), nullability: null)) case .wasmDefineAdHocSignatureType(let op): startTypeGroup() addSignatureType(def: instr.output, signature: op.signature, inputs: instr.inputs) @@ -1835,7 +1834,7 @@ public struct JSTyper: Analyzer { set(instr.output, .wasmTable(wasmTableType: WasmTableType(elementType: op.tableType.elementType, limits: op.tableType.limits, isTable64: op.tableType.isTable64, knownEntries: []))) case .createWasmJSTag(_): - set(instr.output, .object(ofGroup: "WasmTag", withWasmType: WasmTagType([.wasmExternRef()], isJSTag: true))) + set(instr.output, .object(ofGroup: "WasmTag", withWasmType: WasmTagType([.wasmExternRef], isJSTag: true))) case .createWasmTag(let op): set(instr.output, .object(ofGroup: "WasmTag", withWasmType: WasmTagType(op.parameterTypes))) diff --git a/Sources/Fuzzilli/FuzzIL/TypeSystem.swift b/Sources/Fuzzilli/FuzzIL/TypeSystem.swift index 5fe3d7269..729550e9c 100644 --- a/Sources/Fuzzilli/FuzzIL/TypeSystem.swift +++ b/Sources/Fuzzilli/FuzzIL/TypeSystem.swift @@ -247,33 +247,23 @@ public struct ILType: Hashable { public static let wasmi64 = ILType(definiteType: .wasmi64) public static let wasmf32 = ILType(definiteType: .wasmf32) public static let wasmf64 = ILType(definiteType: .wasmf64) - public static func wasmExternRef(shared: Bool = false) -> ILType { wasmRef(.WasmExtern, shared: shared, nullability: true) } - public static func wasmRefExtern(shared: Bool = false) -> ILType { wasmRef(.WasmExtern, shared: shared, nullability: false) } - public static func wasmFuncRef(shared: Bool = false) -> ILType { wasmRef(.WasmFunc, shared: shared, nullability: true) } - public static func wasmExnRef(shared: Bool = false) -> ILType { wasmRef(.WasmExn, shared: shared, nullability: true) } - public static func wasmI31Ref(shared: Bool = false) -> ILType { wasmRef(.WasmI31, shared: shared, nullability: true) } - public static func wasmRefI31(shared: Bool = false) -> ILType { wasmRef(.WasmI31, shared: shared, nullability: false) } - public static func wasmAnyRef(shared: Bool = false) -> ILType { wasmRef(.WasmAny, shared: shared, nullability: true) } - public static func wasmRefAny(shared: Bool = false) -> ILType { wasmRef(.WasmAny, shared: shared, nullability: false) } - public static func wasmNullRef(shared: Bool = false) -> ILType { wasmRef(.WasmNone, shared: shared, nullability: true) } - public static func wasmNullExternRef(shared: Bool = false) -> ILType { wasmRef(.WasmNoExtern, shared: shared, nullability: true) } - public static func wasmNullFuncRef(shared: Bool = false) -> ILType { wasmRef(.WasmNoFunc, shared: shared, nullability: true) } - public static func wasmEqRef(shared: Bool = false) -> ILType { wasmRef(.WasmEq, shared: shared, nullability: true) } - public static func wasmStructRef(shared: Bool = false) -> ILType { wasmRef(.WasmStruct, shared: shared, nullability: true) } - public static func wasmArrayRef(shared: Bool = false) -> ILType { wasmRef(.WasmArray, shared: shared, nullability: true) } + public static let wasmExternRef = ILType.wasmRef(.Abstract(.WasmExtern), nullability: true) + public static let wasmRefExtern = ILType.wasmRef(.Abstract(.WasmExtern), nullability: false) + public static let wasmFuncRef = ILType.wasmRef(.Abstract(.WasmFunc), nullability: true) + public static let wasmExnRef = ILType.wasmRef(.Abstract(.WasmExn), nullability: true) + public static let wasmI31Ref = ILType.wasmRef(.Abstract(.WasmI31), nullability: true) + public static let wasmRefI31 = ILType.wasmRef(.Abstract(.WasmI31), nullability: false) + public static let wasmAnyRef = ILType.wasmRef(.Abstract(.WasmAny), nullability: true) + public static let wasmRefAny = ILType.wasmRef(.Abstract(.WasmAny), nullability: false) + public static let wasmNullRef = ILType.wasmRef(.Abstract(.WasmNone), nullability: true) + public static let wasmNullExternRef = ILType.wasmRef(.Abstract(.WasmNoExtern), nullability: true) + public static let wasmNullFuncRef = ILType.wasmRef(.Abstract(.WasmNoFunc), nullability: true) + public static let wasmEqRef = ILType.wasmRef(.Abstract(.WasmEq), nullability: true) + public static let wasmStructRef = ILType.wasmRef(.Abstract(.WasmStruct), nullability: true) + public static let wasmArrayRef = ILType.wasmRef(.Abstract(.WasmArray), nullability: true) public static let wasmSimd128 = ILType(definiteType: .wasmSimd128) public static let wasmGenericRef = ILType(definiteType: .wasmRef) - public static func allWasmRefTypes() -> [ILType] { - var refTypes: [ILType] = [] - for sharedRef in [true, false] { - for heapType in WasmAbstractHeapType.allCases { - refTypes.append(wasmRef(heapType, shared: sharedRef, nullability: true)) - } - } - return refTypes - } - static func wasmTypeDef(description: WasmTypeDescription? = nil) -> ILType { let typeDef = WasmTypeDefinition() typeDef.description = description @@ -285,10 +275,6 @@ public struct ILType: Hashable { wasmTypeDef(description: .selfReference) } - static func wasmRef(_ heapType: WasmAbstractHeapType, shared: Bool = false, nullability: Bool = true) -> ILType { - ILType.wasmRef(.Abstract(.init(heapType, shared: shared)), nullability: nullability) - } - static func wasmRef(_ kind: WasmReferenceType.Kind, nullability: Bool) -> ILType { return ILType(definiteType: .wasmRef, ext: TypeExtension( properties: [], methods: [], signature: nil, @@ -300,7 +286,7 @@ public struct ILType: Hashable { } // The union of all primitive wasm types - public static let wasmPrimitive = .wasmi32 | .wasmi64 | .wasmf32 | .wasmf64 | .wasmSimd128 | .wasmGenericRef + public static let wasmPrimitive = .wasmi32 | .wasmi64 | .wasmf32 | .wasmf64 | .wasmExternRef | .wasmFuncRef | .wasmI31Ref | .wasmSimd128 | .wasmGenericRef public static let wasmNumericalPrimitive = .wasmi32 | .wasmi64 | .wasmf32 | .wasmf64 @@ -1103,13 +1089,11 @@ extension ILType: CustomStringConvertible { } let nullPrefix = refType.nullability ? "null " : "" switch refType.kind { - case .Abstract(let heapTypeInfo): - let sharedPrefix = heapTypeInfo.shared ? "shared " : "" - return ".wasmRef(.Abstract(\(nullPrefix)\(sharedPrefix)\(heapTypeInfo.heapType)))" + case .Abstract(let heapType): + return ".wasmRef(.Abstract(\(nullPrefix)\(heapType)))" case .Index(let indexRef): if let desc = indexRef.get() { - let sharedPrefix = if desc.abstractHeapSupertype?.shared ?? false { "shared " } else { "" } - return ".wasmRef(\(nullPrefix)Index \(sharedPrefix)\(desc.format(abbreviate: abbreviate)))" + return ".wasmRef(\(nullPrefix)Index \(desc.format(abbreviate: abbreviate)))" } return ".wasmRef(\(nullPrefix)Index)" } @@ -1407,9 +1391,9 @@ public class WasmTypeDefinition: WasmTypeExtension { } // TODO: Add continuation types for core stack switching. +// TODO: Add shared bit for shared-everything-threads. // TODO: Add internal string type for JS string builtins. -// TODO(pawkra): rename to HeapType -public enum WasmAbstractHeapType: CaseIterable, Comparable { +enum WasmAbstractHeapType: CaseIterable, Comparable { // Note: The union, intersection, ... implementations are inspired by Binaryen's implementation, // so when extending the type system, feel free to use that implemenation as an orientation. // https://github.com/WebAssembly/binaryen/blob/main/src/wasm/wasm-type.cpp @@ -1491,8 +1475,8 @@ public enum WasmAbstractHeapType: CaseIterable, Comparable { if self == other { return self } - if !self.inSameHierarchy(other) { - return nil // Incompatible heap types. + if self.getBottom() != other.getBottom() { + return nil } if self.subsumes(other) { return other @@ -1506,57 +1490,6 @@ public enum WasmAbstractHeapType: CaseIterable, Comparable { func subsumes(_ other: Self) -> Bool { union(other) == self } - - public static func allNonBottomTypes() -> [WasmAbstractHeapType] { - return WasmAbstractHeapType.allCases.filter { !$0.isBottom() } - } -} - -public class HeapTypeInfo : Hashable { - public let heapType: WasmAbstractHeapType - public let shared: Bool - - init(_ heapType: WasmAbstractHeapType, shared: Bool) { - self.heapType = heapType - self.shared = shared - } - - public static func ==(lhs: HeapTypeInfo, rhs: HeapTypeInfo) -> Bool { - return lhs.heapType == rhs.heapType && lhs.shared == rhs.shared - } - - func union(_ other: HeapTypeInfo) -> HeapTypeInfo? { - if (shared != other.shared) { - return nil; - } - if let unionHeapType = heapType.union(other.heapType) { - return HeapTypeInfo(unionHeapType, shared: shared) - } - return nil - } - - func intersection(_ other: HeapTypeInfo) -> HeapTypeInfo? { - if (shared != other.shared) { - return nil; - } - if let intersectionHeapType = heapType.intersection(other.heapType) { - return HeapTypeInfo(intersectionHeapType, shared: shared) - } - return nil - } - - func subsumes(_ other: HeapTypeInfo) -> Bool { - if (shared != other.shared) { - return false; - } - return heapType.subsumes(other.heapType) - } - - - public func hash(into hasher: inout Hasher) { - hasher.combine(heapType) - hasher.combine(shared) - } } // A wrapper around a WasmTypeDescription without owning the WasmTypeDescription. @@ -1581,7 +1514,7 @@ public class WasmReferenceType: WasmTypeExtension { // corresponding WasmTypeDefinition extension attached to the type of the operation // defining the wasm-gc type (and is kept alive by the JSTyper). case Index(UnownedWasmTypeDescription = UnownedWasmTypeDescription()) - case Abstract(HeapTypeInfo) + case Abstract(WasmAbstractHeapType) func union(_ other: Self) -> Self? { switch self { @@ -2135,11 +2068,10 @@ class WasmTypeDescription: Hashable, CustomStringConvertible { // The "closest" super type that is an abstract type (.WasmArray for arrays, .WasmStruct for // structs). It is nil for unresolved forward/self references for which the concrete abstract // super type is still undecided. - // TODO(pawkra): rename to heapSupertype - public let abstractHeapSupertype: HeapTypeInfo? + public let abstractHeapSupertype: WasmAbstractHeapType? // TODO(gc): We will also need to support subtyping of struct and array types at some point. - init(typeGroupIndex: Int, superType: HeapTypeInfo? = nil) { + init(typeGroupIndex: Int, superType: WasmAbstractHeapType? = nil) { self.typeGroupIndex = typeGroupIndex self.abstractHeapSupertype = superType } @@ -2169,8 +2101,7 @@ class WasmSignatureTypeDescription: WasmTypeDescription { init(signature: WasmSignature, typeGroupIndex: Int) { self.signature = signature - // TODO(pawkra): support shared variant. - super.init(typeGroupIndex: typeGroupIndex, superType: HeapTypeInfo.init(.WasmFunc, shared: false)) + super.init(typeGroupIndex: typeGroupIndex, superType: .WasmFunc) } override func format(abbreviate: Bool) -> String { @@ -2191,8 +2122,7 @@ class WasmArrayTypeDescription: WasmTypeDescription { init(elementType: ILType, mutability: Bool, typeGroupIndex: Int) { self.elementType = elementType self.mutability = mutability - // TODO(pawkra): support shared variant. - super.init(typeGroupIndex: typeGroupIndex, superType: HeapTypeInfo.init(.WasmArray, shared: false)) + super.init(typeGroupIndex: typeGroupIndex, superType: .WasmArray) } override func format(abbreviate: Bool) -> String { @@ -2223,8 +2153,7 @@ class WasmStructTypeDescription: WasmTypeDescription { init(fields: [Field], typeGroupIndex: Int) { self.fields = fields - // TODO(pawkra): support shared variant. - super.init(typeGroupIndex: typeGroupIndex, superType: HeapTypeInfo.init(.WasmStruct, shared: false)) + super.init(typeGroupIndex: typeGroupIndex, superType: .WasmStruct) } override func format(abbreviate: Bool) -> String { diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index 8a441966f..452dd9cc5 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -695,9 +695,9 @@ public enum WasmGlobal { case wasmf64(Float64) // Empty reference // TODO(gc): Add support for globals with non-nullable references. - case externref(shared: Bool) - case exnref(shared: Bool) - case i31ref(shared: Bool) + case externref + case exnref + case i31ref // function reference case refFunc(Int) @@ -715,12 +715,12 @@ public enum WasmGlobal { return .wasmf32 case .wasmf64: return .wasmf64 - case let .externref(shared): - return ILType.wasmExternRef(shared: shared) - case let .exnref(shared): - return ILType.wasmExnRef(shared: shared) - case let .i31ref(shared): - return ILType.wasmI31Ref(shared: shared) + case .externref: + return .wasmExternRef + case .exnref: + return .wasmExnRef + case .i31ref: + return .wasmI31Ref case .imported(let type): assert(type.wasmGlobalType != nil) return type.wasmGlobalType!.valueType @@ -729,7 +729,6 @@ public enum WasmGlobal { } } - //TODO(pawkra): rename to jsTypeName func typeString() -> String { switch self { case .wasmi64(_): @@ -740,26 +739,18 @@ public enum WasmGlobal { return "f32" case .wasmf64(_): return "f64" - case let .externref(shared): - assertNotShared(shared) + case .externref: return "externref" - case let .exnref(shared): - assertNotShared(shared) + case .exnref: return "exnref" - case let .i31ref(shared): - assertNotShared(shared) + case .i31ref: return "i31ref" default: fatalError("Unimplemented / unhandled") } } - private func assertNotShared(_ shared: Bool) { - assert(!shared, "Shared references not supported in JS > WASM API") - } - // Returns a JS string representing the initial value. - // TODO(pawkra): rename to valueToJsString func valueToString() -> String { switch self { case .wasmi64(let val): @@ -770,10 +761,10 @@ public enum WasmGlobal { return "\(val)" case .wasmf64(let val): return "\(val)" - case .externref(_): + case .externref: return "" - case .exnref(_), - .i31ref(_): + case .exnref, + .i31ref: return "null" default: fatalError("Unimplemented / unhandled") @@ -810,11 +801,9 @@ final class WasmDefineTable: WasmOperation { self.definedEntries = definedEntries // TODO(manoskouk): Find a way to define non-function tables with initializers. - // TODO(pawkra): support shared refs. - let isWasmFuncRef = elementType == .wasmFuncRef() - assert(isWasmFuncRef || definedEntries.isEmpty) + assert(elementType == .wasmFuncRef || definedEntries.isEmpty) - super.init(numInputs: isWasmFuncRef ? definedEntries.count : 0, + super.init(numInputs: elementType == .wasmFuncRef ? definedEntries.count : 0, numOutputs: 1, attributes: [.isMutable], requiredContext: [.wasm]) diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift index bcd7057b7..49cfb5a04 100644 --- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift +++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift @@ -1540,12 +1540,11 @@ public class JavaScriptLifter: Lifter { let LET = w.varKeyword let type: String switch op.tableType.elementType { - case .wasmExternRef(): + case .wasmExternRef: type = "externref" - case .wasmFuncRef(): + case .wasmFuncRef: type = "anyfunc" // TODO(mliedtke): add tables for i31ref. - // TODO(pawkra): add shared ref variants. default: fatalError("Unknown table type") } @@ -1580,21 +1579,21 @@ public class JavaScriptLifter: Lifter { return "\"i64\"" case .wasmSimd128: return "\"v128\"" - case ILType.wasmExternRef(): - return "\"externref\"" - case ILType.wasmFuncRef(): + case .wasmExternRef: + return "\"externref\"" + case .wasmFuncRef: return "\"anyfunc\"" - case ILType.wasmAnyRef(): + case .wasmAnyRef: return "\"anyref\"" - case ILType.wasmEqRef(): + case .wasmEqRef: return "\"eqref\"" - case ILType.wasmI31Ref(): + case .wasmI31Ref: return "\"i31ref\"" - case ILType.wasmStructRef(): + case .wasmStructRef: return "\"structref\"" - case ILType.wasmArrayRef(): + case .wasmArrayRef: return "\"arrayref\"" - case ILType.wasmExnRef(): + case .wasmExnRef: return "\"exnref\"" default: diff --git a/Sources/Fuzzilli/Lifting/WasmLifter.swift b/Sources/Fuzzilli/Lifting/WasmLifter.swift index a11ed0dbd..4944ac161 100644 --- a/Sources/Fuzzilli/Lifting/WasmLifter.swift +++ b/Sources/Fuzzilli/Lifting/WasmLifter.swift @@ -527,33 +527,39 @@ public class WasmLifter { self.bytecode += [0x1, 0x0, 0x0, 0x0] } - private func encodeAbstractHeapType(_ heapTypeInfo: HeapTypeInfo) -> Data { - // Base of v8 implementation. See - // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/wasm-constants.h?q=symbol:ValueTypeCode - let sharedFlagPrefix: [UInt8] = heapTypeInfo.shared ? [0x65] : [] - let opCode: UInt8 = - switch (heapTypeInfo.heapType) { - case .WasmExtern: 0x6F - case .WasmFunc: 0x70 - case .WasmAny: 0x6E - case .WasmEq: 0x6D - case .WasmI31: 0x6C - case .WasmStruct: 0x6B - case .WasmArray: 0x6A - case .WasmExn: 0x69 - case .WasmNone: 0x71 - case .WasmNoExtern: 0x72 - case .WasmNoFunc: 0x73 - case .WasmNoExn: 0x74 - } - return Data(sharedFlagPrefix + [opCode]) + private func encodeAbstractHeapType(_ heapType: WasmAbstractHeapType) -> Data { + switch (heapType) { + case .WasmExtern: + return Data([0x6F]) + case .WasmFunc: + return Data([0x70]) + case .WasmAny: + return Data([0x6E]) + case .WasmEq: + return Data([0x6D]) + case .WasmI31: + return Data([0x6C]) + case .WasmStruct: + return Data([0x6B]) + case .WasmArray: + return Data([0x6A]) + case .WasmExn: + return Data([0x69]) + case .WasmNone: + return Data([0x71]) + case .WasmNoExtern: + return Data([0x72]) + case .WasmNoFunc: + return Data([0x73]) + case .WasmNoExn: + return Data([0x74]) + } } private func encodeWasmGCType(_ description: WasmTypeDescription?) throws -> Data { guard let description else { throw WasmLifter.CompileError.missingTypeInformation } - // TODO(pawkra): encode shared bit return Leb128.unsignedEncode(typeDescToIndex[description]!) } @@ -562,22 +568,28 @@ public class WasmLifter { let isNullable = refType.nullability let nullabilityByte: UInt8 = isNullable ? 0x63 : 0x64 - return try Data([nullabilityByte]) + encodeHeapType(type) + switch refType.kind { + case .Index(let description): + return try Data([nullabilityByte]) + encodeWasmGCType(description.get()) + case .Abstract(let heapType): + return Data([nullabilityByte]) + encodeAbstractHeapType(heapType) + } } // HINT: If you crash here, you might not have specified an encoding for your new type in `ILTypeMapping`. return ILTypeMapping[type] ?? ILTypeMapping[defaultType!]! } - private func encodeHeapType(_ type: ILType) throws -> Data { + private func encodeHeapType(_ type: ILType, defaultType: ILType? = nil) throws -> Data { if let refType = type.wasmReferenceType { switch refType.kind { case .Index(let description): return try encodeWasmGCType(description.get()) - case .Abstract(let heapTypeInfo): - return encodeAbstractHeapType(heapTypeInfo) + case .Abstract(let heapType): + return encodeAbstractHeapType(heapType) } } - fatalError("This function supports only wasmReferenceType.") + // HINT: If you crash here, you might not have specified an encoding for your new type in `ILTypeMapping`. + return ILTypeMapping[type] ?? ILTypeMapping[defaultType!]! } private func buildTypeEntry(for desc: WasmTypeDescription, data: inout Data) throws { @@ -651,7 +663,7 @@ public class WasmLifter { } temp += Leb128.unsignedEncode(signature.outputTypes.count) for outputType in signature.outputTypes { - temp += try encodeType(outputType, defaultType: .wasmExternRef()) + temp += try encodeType(outputType, defaultType: .wasmExternRef) } } @@ -1064,14 +1076,14 @@ public class WasmLifter { temporaryInstruction = Instruction(Consti32(value: val), output: Variable()) case .wasmi64(let val): temporaryInstruction = Instruction(Consti64(value: val), output: Variable()) - case .externref(let shared): - temp += try! Data([0xD0]) + encodeHeapType(.wasmExternRef(shared: shared)) + Data([0x0B]) + case .externref: + temp += try! Data([0xD0]) + encodeHeapType(.wasmExternRef) + Data([0x0B]) continue - case .exnref(let shared): - temp += try! Data([0xD0]) + encodeHeapType(.wasmExnRef(shared: shared)) + Data([0x0B]) + case .exnref: + temp += try! Data([0xD0]) + encodeHeapType(.wasmExnRef) + Data([0x0B]) continue - case .i31ref(let shared): - temp += try! Data([0xD0]) + encodeHeapType(.wasmI31Ref(shared: shared)) + Data([0x0B]) + case .i31ref: + temp += try! Data([0xD0]) + encodeHeapType(.wasmI31Ref) + Data([0x0B]) continue case .refFunc(_), .imported(_): @@ -1494,8 +1506,7 @@ public class WasmLifter { self.exports.append(.global(instr)) case .wasmDefineTable(let tableDef): self.exports.append(.table(instr)) - // TODO(pawkra): support shared refs. - if tableDef.elementType == .wasmFuncRef() { + if tableDef.elementType == .wasmFuncRef { for (value, definedEntry) in zip(instr.inputs, tableDef.definedEntries) { if !typer.type(of: value).Is(.wasmFunctionDef()) { // Check if we need to import the inputs. diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift index 4f8b7b843..94e7e297c 100644 --- a/Sources/Fuzzilli/Mutators/OperationMutator.swift +++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift @@ -369,21 +369,23 @@ public class OperationMutator: BaseInstructionMutator { case .wasmDefineGlobal(let op): // We never change the type of the global, only the value as changing the type will break the following code pretty much instantly. - let wasmGlobal:WasmGlobal = - switch op.wasmGlobal.toType() { - case .wasmf32: - .wasmf32(Float32(b.randomFloat())) - case .wasmf64: - .wasmf64(b.randomFloat()) - case .wasmi32: - .wasmi32(Int32(truncatingIfNeeded: b.randomInt())) - case .wasmi64: - .wasmi64(b.randomInt()) - case ILType.wasmExternRef(), ILType.wasmExnRef(), ILType.wasmI31Ref(): - op.wasmGlobal - default: - fatalError("unexpected/unimplemented Value Type!") - } + let wasmGlobal: WasmGlobal + switch op.wasmGlobal.toType() { + case .wasmf32: + wasmGlobal = .wasmf32(Float32(b.randomFloat())) + case .wasmf64: + wasmGlobal = .wasmf64(b.randomFloat()) + case .wasmi32: + wasmGlobal = .wasmi32(Int32(truncatingIfNeeded: b.randomInt())) + case .wasmi64: + wasmGlobal = .wasmi64(b.randomInt()) + case .wasmExternRef, + .wasmExnRef, + .wasmI31Ref: + wasmGlobal = op.wasmGlobal + default: + fatalError("unexpected/unimplemented Value Type!") + } newOp = WasmDefineGlobal(wasmGlobal: wasmGlobal, isMutable: probability(0.5)) case .wasmDefineTable(let op): // TODO: change table size? diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index cdca77467..2b2248232 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -4138,8 +4138,6 @@ public struct Fuzzilli_Protobuf_WasmReferenceType: Sendable { public var nullability: Bool = false - public var isShared: Bool = false - public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} @@ -4714,10 +4712,10 @@ public struct Fuzzilli_Protobuf_WasmGlobal: Sendable { set {wasmGlobal = .valuef64(newValue)} } - public var nullref: Fuzzilli_Protobuf_WasmReferenceType { + public var nullref: Fuzzilli_Protobuf_WasmReferenceTypeKind { get { if case .nullref(let v)? = wasmGlobal {return v} - return Fuzzilli_Protobuf_WasmReferenceType() + return .index } set {wasmGlobal = .nullref(newValue)} } @@ -4745,7 +4743,7 @@ public struct Fuzzilli_Protobuf_WasmGlobal: Sendable { case valuei64(Int64) case valuef32(Float) case valuef64(Double) - case nullref(Fuzzilli_Protobuf_WasmReferenceType) + case nullref(Fuzzilli_Protobuf_WasmReferenceTypeKind) case funcref(Int64) case imported(Fuzzilli_Protobuf_WasmILType) @@ -11564,7 +11562,7 @@ extension Fuzzilli_Protobuf_WasmReturn: SwiftProtobuf.Message, SwiftProtobuf._Me extension Fuzzilli_Protobuf_WasmReferenceType: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmReferenceType" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}kind\0\u{1}nullability\0\u{1}isShared\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}kind\0\u{1}nullability\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -11574,7 +11572,6 @@ extension Fuzzilli_Protobuf_WasmReferenceType: SwiftProtobuf.Message, SwiftProto switch fieldNumber { case 1: try { try decoder.decodeSingularEnumField(value: &self.kind) }() case 2: try { try decoder.decodeSingularBoolField(value: &self.nullability) }() - case 3: try { try decoder.decodeSingularBoolField(value: &self.isShared) }() default: break } } @@ -11587,16 +11584,12 @@ extension Fuzzilli_Protobuf_WasmReferenceType: SwiftProtobuf.Message, SwiftProto if self.nullability != false { try visitor.visitSingularBoolField(value: self.nullability, fieldNumber: 2) } - if self.isShared != false { - try visitor.visitSingularBoolField(value: self.isShared, fieldNumber: 3) - } try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Fuzzilli_Protobuf_WasmReferenceType, rhs: Fuzzilli_Protobuf_WasmReferenceType) -> Bool { if lhs.kind != rhs.kind {return false} if lhs.nullability != rhs.nullability {return false} - if lhs.isShared != rhs.isShared {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -12859,15 +12852,10 @@ extension Fuzzilli_Protobuf_WasmGlobal: SwiftProtobuf.Message, SwiftProtobuf._Me } }() case 6: try { - var v: Fuzzilli_Protobuf_WasmReferenceType? - var hadOneofValue = false - if let current = self.wasmGlobal { - hadOneofValue = true - if case .nullref(let m) = current {v = m} - } - try decoder.decodeSingularMessageField(value: &v) + var v: Fuzzilli_Protobuf_WasmReferenceTypeKind? + try decoder.decodeSingularEnumField(value: &v) if let v = v { - if hadOneofValue {try decoder.handleConflictingOneOf()} + if self.wasmGlobal != nil {try decoder.handleConflictingOneOf()} self.wasmGlobal = .nullref(v) } }() @@ -12924,7 +12912,7 @@ extension Fuzzilli_Protobuf_WasmGlobal: SwiftProtobuf.Message, SwiftProtobuf._Me }() case .nullref?: try { guard case .nullref(let v)? = self.wasmGlobal else { preconditionFailure() } - try visitor.visitSingularMessageField(value: v, fieldNumber: 6) + try visitor.visitSingularEnumField(value: v, fieldNumber: 6) }() case .funcref?: try { guard case .funcref(let v)? = self.wasmGlobal else { preconditionFailure() } diff --git a/Sources/Fuzzilli/Protobuf/operations.proto b/Sources/Fuzzilli/Protobuf/operations.proto index a4feaf8f3..8ed578def 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -920,7 +920,6 @@ enum WasmReferenceTypeKind { message WasmReferenceType { WasmReferenceTypeKind kind = 1; bool nullability = 2; - bool isShared = 3; } message WasmILType { @@ -1101,7 +1100,7 @@ message WasmGlobal { int64 valuei64 = 3; float valuef32 = 4; double valuef64 = 5; - WasmReferenceType nullref = 6; + WasmReferenceTypeKind nullref = 6; int64 funcref = 7; WasmILType imported = 8; } diff --git a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift index e8a5d66fa..967210b97 100644 --- a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift @@ -554,7 +554,7 @@ public let WasmDeoptFuzzer = WasmProgramTemplate("WasmDeoptFuzzer") { b in } let table = wasmModule.addTable( - elementType: .wasmFuncRef(), + elementType: .wasmFuncRef, minSize: numCallees, definedEntries: (0.. functionSig.outputType let m = b.buildWasmModule { m in - let allWasmTypes: WeightedList = WeightedList([(.wasmi32, 1), (.wasmi64, 1), (.wasmf32, 1), (.wasmf64, 1), (.wasmExternRef(), 1), (.wasmFuncRef(), 1)]) + let allWasmTypes: WeightedList = WeightedList([(.wasmi32, 1), (.wasmi64, 1), (.wasmf32, 1), (.wasmf64, 1), (.wasmExternRef, 1), (.wasmFuncRef, 1)]) let wasmSignature = ProgramBuilder.convertJsSignatureToWasmSignature(wrappedSig, availableTypes: allWasmTypes) m.addWasmFunction(with: wasmSignature) {fbuilder, _, _ in let args = b.randomWasmArguments(forWasmSignature: wasmSignature) @@ -745,7 +745,6 @@ public func v8ProcessArgs(randomize: Bool, forSandbox: Bool) -> [String] { var args = [ "--expose-gc", "--expose-externalize-string", - "--experimental-wasm-shared", "--omit-quit", "--allow-natives-syntax", "--fuzzing", diff --git a/Tests/FuzzilliTests/JSTyperTests.swift b/Tests/FuzzilliTests/JSTyperTests.swift index da45fc8c3..56ab52d11 100644 --- a/Tests/FuzzilliTests/JSTyperTests.swift +++ b/Tests/FuzzilliTests/JSTyperTests.swift @@ -1534,7 +1534,7 @@ class JSTyperTests: XCTestCase { } b.doReturn(obj) } - let wasmSignature = [] => [.wasmExternRef()] + let wasmSignature = [] => [.wasmExternRef] let typeDesc = b.type(of: typeGroup[0]).wasmTypeDefinition!.description! @@ -1574,7 +1574,7 @@ class JSTyperTests: XCTestCase { } // Function three - wasmModule.addWasmFunction(with: [.wasmExternRef()] => [.wasmi32, .wasmi64]) { function, label, _ in + wasmModule.addWasmFunction(with: [.wasmExternRef] => [.wasmi32, .wasmi64]) { function, label, _ in return [function.consti32(1), function.consti64(2)] } @@ -1585,7 +1585,7 @@ class JSTyperTests: XCTestCase { // Function five - wasmModule.addWasmFunction(with: [] => [.wasmExternRef()]) { function, label, _ in + wasmModule.addWasmFunction(with: [] => [.wasmExternRef]) { function, label, _ in // This forces an import and we should see a re-exported function on the module. return [function.wasmJsCall(function: plainFunction, withArgs: [], withWasmSignature: wasmSignature)!] } @@ -1795,7 +1795,7 @@ class JSTyperTests: XCTestCase { let wasmTableConstructor = b.getProperty("Table", of: wasm) let wasmTable = b.construct(wasmTableConstructor) // In theory this needs arguments. XCTAssertFalse(b.type(of: wasmTable).Is(.object(ofGroup: "WasmTable"))) - let realWasmTable = b.createWasmTable(elementType: .wasmAnyRef(), limits: .init(min: 0), isTable64: false) + let realWasmTable = b.createWasmTable(elementType: .wasmAnyRef, limits: .init(min: 0), isTable64: false) XCTAssert(b.type(of: realWasmTable).Is(.object(ofGroup: "WasmTable"))) XCTAssert(b.type(of: realWasmTable).Is(ObjectGroup.wasmTable.instanceType)) let tablePrototype = b.getProperty("prototype", of: wasmTableConstructor) diff --git a/Tests/FuzzilliTests/LifterTest.swift b/Tests/FuzzilliTests/LifterTest.swift index 39a00c095..7758461d1 100644 --- a/Tests/FuzzilliTests/LifterTest.swift +++ b/Tests/FuzzilliTests/LifterTest.swift @@ -3274,7 +3274,7 @@ class LifterTests: XCTestCase { let fuzzer = makeMockFuzzer() let b = fuzzer.makeBuilder() - let table = b.createWasmTable(elementType: .wasmFuncRef(), limits: Limits(min: 1), isTable64: true) + let table = b.createWasmTable(elementType: .wasmFuncRef, limits: Limits(min: 1), isTable64: true) XCTAssertTrue(b.type(of: table).Is(.object(ofGroup: "WasmTable"))) let f = b.buildPlainFunction(with: .parameters(n: 0)) {_ in diff --git a/Tests/FuzzilliTests/LiveTests.swift b/Tests/FuzzilliTests/LiveTests.swift index 8a6dce290..4ffb8c5d9 100644 --- a/Tests/FuzzilliTests/LiveTests.swift +++ b/Tests/FuzzilliTests/LiveTests.swift @@ -133,16 +133,15 @@ class LiveTests: XCTestCase { b.buildTryCatchFinally { // TODO(manoskouk): Once we support wasm-gc types in signatures, we'll need // something more sophisticated. - // TODO(pawkra): support shared refs. let args = wasmSignature.parameterTypes.map { switch $0 { case .wasmi64: return b.loadBigInt(123) - case ILType.wasmFuncRef(): + case .wasmFuncRef: return jsFunction - case ILType.wasmNullExternRef(), ILType.wasmNullFuncRef(), ILType.wasmNullRef(): + case .wasmNullExternRef, .wasmNullFuncRef, .wasmNullRef: return b.loadNull() - case ILType.wasmExternRef(), ILType.wasmAnyRef(): + case .wasmExternRef, .wasmAnyRef: return b.createObject(with: [:]) default: return b.loadInt(321) diff --git a/Tests/FuzzilliTests/ProgramBuilderTest.swift b/Tests/FuzzilliTests/ProgramBuilderTest.swift index 4478f3225..5a9c53b63 100644 --- a/Tests/FuzzilliTests/ProgramBuilderTest.swift +++ b/Tests/FuzzilliTests/ProgramBuilderTest.swift @@ -2863,21 +2863,21 @@ class ProgramBuilderTests: XCTestCase { let arrayI32Type = b.type(of: arrayI32) XCTAssert(arrayI32Type.Is(.wasmRef(.Index(), nullability: true))) XCTAssert(arrayI32Type.Is(.wasmRef(.Index(), nullability: false))) - XCTAssert(arrayI32Type.Is(.wasmRef(.WasmArray, nullability: true))) - XCTAssert(arrayI32Type.Is(.wasmRef(.WasmArray, nullability: false))) - XCTAssert(arrayI32Type.Is(.wasmRef(.WasmEq, nullability: true))) - XCTAssert(arrayI32Type.Is(.wasmRef(.WasmEq, nullability: false))) - XCTAssert(arrayI32Type.Is(.wasmRef(.WasmAny, nullability: true))) - XCTAssert(arrayI32Type.Is(.wasmRef(.WasmAny, nullability: false))) - XCTAssertFalse(arrayI32Type.Is(.wasmRef(.WasmStruct, nullability: true))) - XCTAssertFalse(arrayI32Type.Is(.wasmRef(.WasmStruct, nullability: false))) - XCTAssertFalse(arrayI32Type.Is(.wasmRef(.WasmExn, nullability: false))) + XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmArray), nullability: true))) + XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmArray), nullability: false))) + XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmEq), nullability: true))) + XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmEq), nullability: false))) + XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmAny), nullability: true))) + XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmAny), nullability: false))) + XCTAssertFalse(arrayI32Type.Is(.wasmRef(.Abstract(.WasmStruct), nullability: true))) + XCTAssertFalse(arrayI32Type.Is(.wasmRef(.Abstract(.WasmStruct), nullability: false))) + XCTAssertFalse(arrayI32Type.Is(.wasmRef(.Abstract(.WasmExn), nullability: false))) let arrayI32B = function.wasmArrayNewFixed(arrayType: arrayDefI32B, elements: []) let arrayI32BType = b.type(of: arrayI32B) XCTAssertFalse(arrayI32BType.Is(arrayI32Type)) XCTAssertFalse(arrayI32Type.Is(arrayI32BType)) - let refArrayType = ILType.wasmRef(.WasmArray, nullability: false) + let refArrayType = ILType.wasmRef(.Abstract(.WasmArray), nullability: false) XCTAssertEqual(arrayI32Type.union(with: arrayI32BType), refArrayType) XCTAssertEqual(arrayI32BType.union(with: arrayI32Type), refArrayType) XCTAssertEqual(arrayI32Type.intersection(with: arrayI32BType), .nothing) @@ -2887,14 +2887,14 @@ class ProgramBuilderTests: XCTestCase { let structType = b.type(of: structVar) XCTAssert(structType.Is(.wasmRef(.Index(), nullability: true))) XCTAssert(structType.Is(.wasmRef(.Index(), nullability: false))) - XCTAssert(structType.Is(.wasmRef(.WasmStruct, nullability: false))) - XCTAssert(structType.Is(.wasmRef(.WasmEq, nullability: false))) - XCTAssert(structType.Is(.wasmRef(.WasmAny, nullability: false))) - XCTAssertFalse(structType.Is(.wasmRef(.WasmArray, nullability: true))) - XCTAssertFalse(structType.Is(.wasmRef(.WasmArray, nullability: false))) - XCTAssertFalse(structType.Is(.wasmRef(.WasmExn, nullability: false))) - - let refEqType = ILType.wasmRef(.WasmEq, nullability: false) + XCTAssert(structType.Is(.wasmRef(.Abstract(.WasmStruct), nullability: false))) + XCTAssert(structType.Is(.wasmRef(.Abstract(.WasmEq), nullability: false))) + XCTAssert(structType.Is(.wasmRef(.Abstract(.WasmAny), nullability: false))) + XCTAssertFalse(structType.Is(.wasmRef(.Abstract(.WasmArray), nullability: true))) + XCTAssertFalse(structType.Is(.wasmRef(.Abstract(.WasmArray), nullability: false))) + XCTAssertFalse(structType.Is(.wasmRef(.Abstract(.WasmExn), nullability: false))) + + let refEqType = ILType.wasmRef(.Abstract(.WasmEq), nullability: false) XCTAssertEqual(structType.union(with: arrayI32Type), refEqType) XCTAssertEqual(arrayI32Type.union(with: structType), refEqType) XCTAssertEqual(structType.intersection(with: arrayI32Type), .nothing) @@ -2903,25 +2903,25 @@ class ProgramBuilderTests: XCTestCase { let i31 = function.wasmRefI31(function.consti32(42)) let i31Type = b.type(of: i31) XCTAssertFalse(i31Type.Is(.wasmRef(.Index(), nullability: true))) - XCTAssert(i31Type.Is(.wasmRef(.WasmEq, nullability: false))) - XCTAssert(i31Type.Is(.wasmRef(.WasmAny, nullability: false))) - XCTAssertFalse(i31Type.Is(.wasmRef(.WasmArray, nullability: false))) - XCTAssertFalse(i31Type.Is(.wasmRef(.WasmStruct, nullability: false))) - XCTAssertFalse(i31Type.Is(.wasmRef(.WasmExn, nullability: false))) + XCTAssert(i31Type.Is(.wasmRef(.Abstract(.WasmEq), nullability: false))) + XCTAssert(i31Type.Is(.wasmRef(.Abstract(.WasmAny), nullability: false))) + XCTAssertFalse(i31Type.Is(.wasmRef(.Abstract(.WasmArray), nullability: false))) + XCTAssertFalse(i31Type.Is(.wasmRef(.Abstract(.WasmStruct), nullability: false))) + XCTAssertFalse(i31Type.Is(.wasmRef(.Abstract(.WasmExn), nullability: false))) XCTAssertEqual(structType.union(with: i31Type), refEqType) XCTAssertEqual(arrayI32Type.union(with: i31Type), refEqType) XCTAssertEqual(i31Type.union(with: refEqType), refEqType) XCTAssertEqual(refArrayType.union(with: i31Type), refEqType) - let refStructType = ILType.wasmRef(.WasmStruct, nullability: false) + let refStructType = ILType.wasmRef(.Abstract(.WasmStruct), nullability: false) XCTAssertEqual(i31Type.union(with: refStructType), refEqType) XCTAssertEqual(i31Type.intersection(with: refEqType), i31Type) XCTAssertEqual(refEqType.intersection(with: i31Type), i31Type) - let refNone = ILType.wasmRef(.WasmNone, nullability: false) + let refNone = ILType.wasmRef(.Abstract(.WasmNone), nullability: false) XCTAssertEqual(i31Type.intersection(with: refArrayType), refNone) XCTAssertEqual(refStructType.intersection(with: i31Type), refNone) - XCTAssertEqual(i31Type.intersection(with: .wasmExnRef()), .nothing) + XCTAssertEqual(i31Type.intersection(with: .wasmExnRef), .nothing) return [] } diff --git a/Tests/FuzzilliTests/TypeSystemTest.swift b/Tests/FuzzilliTests/TypeSystemTest.swift index 073388a11..5e6f0fd77 100644 --- a/Tests/FuzzilliTests/TypeSystemTest.swift +++ b/Tests/FuzzilliTests/TypeSystemTest.swift @@ -1110,12 +1110,11 @@ class TypeSystemTests: XCTestCase { let strObjOrFuncObj = (ILType.string + ILType.object(withProperties: ["foo"])) | (ILType.function([.rest(.jsAnything)] => .float) + ILType.object(withProperties: ["foo"])) XCTAssertEqual(strObjOrFuncObj.description, ".string + .object(withProperties: [\"foo\"]) | .object(withProperties: [\"foo\"]) + .function()") - let nullExn = ILType.wasmRef(.WasmExn, shared: true, nullability: true) - let nonNullAny = ILType.wasmRef(.WasmAny, shared: false, nullability: false) - XCTAssertEqual(nullExn.description, ".wasmRef(.Abstract(null shared WasmExn))") + let nullExn = ILType.wasmRef(.Abstract(.WasmExn), nullability: true) + let nonNullAny = ILType.wasmRef(.Abstract(.WasmAny), nullability: false) + XCTAssertEqual(nullExn.description, ".wasmRef(.Abstract(null WasmExn))") XCTAssertEqual(nonNullAny.description, ".wasmRef(.Abstract(WasmAny))") - // TODO(pawkra): add shared variant. let arrayDesc = WasmArrayTypeDescription(elementType: .wasmi32, mutability: false, typeGroupIndex: 0) let arrayRef = ILType.wasmIndexRef(arrayDesc, nullability: true) XCTAssertEqual(arrayRef.description, ".wasmRef(null Index 0 Array[immutable .wasmi32])") @@ -1149,11 +1148,11 @@ class TypeSystemTests: XCTestCase { ".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(shared: true)], typeGroupIndex: 0) + 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 shared WasmNone))]])") + "[.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 @@ -1164,7 +1163,7 @@ class TypeSystemTests: XCTestCase { } func testWasmSubsumptionRules() { - let wasmTypes: [ILType] = [.wasmi32, .wasmi64, .wasmf32, .wasmf64] + ILType.allWasmRefTypes() + let wasmTypes: [ILType] = [.wasmi32, .wasmi64, .wasmf32, .wasmf64, .wasmFuncRef, .wasmExternRef, .wasmI31Ref, .wasmExnRef] // Make sure that no Wasm type is subsumed by (JS-)anything. for t in wasmTypes { XCTAssertEqual(t <= .jsAnything, false) @@ -1210,16 +1209,14 @@ class TypeSystemTests: XCTestCase { // Test nullability rules for abstract Wasm types. for heapType: WasmAbstractHeapType in WasmAbstractHeapType.allCases { - for shared in [true, false] { - let nullable = ILType.wasmRef(heapType, shared: shared, nullability: true) - let nonNullable = ILType.wasmRef(heapType, shared: shared, nullability: false) - XCTAssert(nonNullable.Is(nullable)) - XCTAssertFalse(nullable.Is(nonNullable)) - XCTAssertEqual(nullable.union(with: nonNullable), nullable) - XCTAssertEqual(nonNullable.union(with: nullable), nullable) - XCTAssertEqual(nullable.intersection(with: nonNullable), nonNullable) - XCTAssertEqual(nonNullable.intersection(with: nullable), nonNullable) - } + let nullable = ILType.wasmRef(.Abstract(heapType), nullability: true) + let nonNullable = ILType.wasmRef(.Abstract(heapType), nullability: false) + XCTAssert(nonNullable.Is(nullable)) + XCTAssertFalse(nullable.Is(nonNullable)) + XCTAssertEqual(nullable.union(with: nonNullable), nullable) + XCTAssertEqual(nonNullable.union(with: nullable), nullable) + XCTAssertEqual(nullable.intersection(with: nonNullable), nonNullable) + XCTAssertEqual(nonNullable.intersection(with: nullable), nonNullable) } } @@ -1279,6 +1276,7 @@ class TypeSystemTests: XCTestCase { XCTAssertEqual(type.intersection(type.getBottom()), type.getBottom()) } + // Testing a few combinations. XCTAssertEqual(WasmAbstractHeapType.WasmAny.union(.WasmEq), .WasmAny) XCTAssertEqual(WasmAbstractHeapType.WasmStruct.union(.WasmArray), .WasmEq) XCTAssertEqual(WasmAbstractHeapType.WasmI31.union(.WasmArray), .WasmEq) @@ -1289,48 +1287,35 @@ class TypeSystemTests: XCTestCase { XCTAssertEqual(WasmAbstractHeapType.WasmAny.intersection(.WasmArray), .WasmArray) // Tests on the whole ILType. - for shared in [true, false] { - let ref: (WasmAbstractHeapType) -> ILType = {t in ILType.wasmRef(t, shared: shared, nullability: false,)} - let refNull = {t in ILType.wasmRef(t, shared: shared, nullability: true)} - - for type in allTypes { - let refT = ref(type) - let refNullT = refNull(type) - XCTAssertEqual(refT.union(with: refNullT), refNullT) - XCTAssertEqual(refNullT.union(with: refT), refNullT) - XCTAssertEqual(refT.union(with: refT), refT) - XCTAssertEqual(refNullT.union(with: refNullT), refNullT) - XCTAssertEqual(refT.intersection(with: refT), refT) - XCTAssertEqual(refNullT.intersection(with: refNullT), refNullT) - XCTAssertEqual(refT.intersection(with: refNullT), refT) - XCTAssertEqual(refNullT.intersection(with: refT), refT) - } - XCTAssertEqual(ref(.WasmAny).union(with: refNull(.WasmEq)), refNull(.WasmAny)) - XCTAssertEqual(ref(.WasmStruct).union(with: ref(.WasmArray)), ref(.WasmEq)) - // We should never do this for the type information of any Variable as .wasmGenericRef - // cannot be encoded in the Wasm module and any instruction that leads to such a static type - // is "broken". However, we will still need to allow this union type - // if we want to be able - // to request a .required(.wasmGenericRef) for operations like WasmRefIsNull. - XCTAssertEqual(ref(.WasmI31).union(with: refNull(.WasmExn)), .wasmGenericRef) - - XCTAssertEqual(ref(.WasmAny).intersection(with: refNull(.WasmEq)), ref(.WasmEq)) - XCTAssertEqual(refNull(.WasmI31).intersection(with: refNull(.WasmStruct)), refNull(.WasmNone)) - // Note that `ref none` is a perfectly valid type in Wasm but such a reference can never be - // constructed. - XCTAssertEqual(ref(.WasmArray).intersection(with: refNull(.WasmStruct)), ref(.WasmNone)) - XCTAssertEqual(refNull(.WasmArray).intersection(with: ref(.WasmAny)), ref(.WasmArray)) + let ref = {t in ILType.wasmRef(.Abstract(t), nullability: false)} + let refNull = {t in ILType.wasmRef(.Abstract(t), nullability: true)} + for type in allTypes { + let refT = ref(type) + let refNullT = refNull(type) + XCTAssertEqual(refT.union(with: refNullT), refNullT) + XCTAssertEqual(refNullT.union(with: refT), refNullT) + XCTAssertEqual(refT.union(with: refT), refT) + XCTAssertEqual(refNullT.union(with: refNullT), refNullT) + XCTAssertEqual(refT.intersection(with: refT), refT) + XCTAssertEqual(refNullT.intersection(with: refNullT), refNullT) + XCTAssertEqual(refT.intersection(with: refNullT), refT) + XCTAssertEqual(refNullT.intersection(with: refT), refT) } - let ref = {t, shared in ILType.wasmRef(t, shared: shared, nullability: false,)} - let refNull = {t, shared in ILType.wasmRef(t, shared: shared, nullability: true)} - // Shared and unshared ref hierarchies are disjoint. - for (lhsShared, rhsShared) in [(true, false), (false, true)] { - for type in allTypes { - XCTAssertEqual(ref(type, lhsShared).union(with: ref(type, rhsShared)), .wasmGenericRef) - XCTAssertEqual(refNull(type, lhsShared).union(with: refNull(type, rhsShared)), .wasmGenericRef) - } - } + XCTAssertEqual(ref(.WasmAny).union(with: refNull(.WasmEq)), refNull(.WasmAny)) + XCTAssertEqual(ref(.WasmStruct).union(with: ref(.WasmArray)), ref(.WasmEq)) + // We should never do this for the type information of any Variable as .wasmGenericRef + // cannot be encoded in the Wasm module and any instruction that leads to such a static type + // is "broken". However, we will still need to allow this union type if we want to be able + // to request a .required(.wasmGenericRef) for operations like WasmRefIsNull. + XCTAssertEqual(ref(.WasmI31).union(with: refNull(.WasmExn)), .wasmGenericRef) + + XCTAssertEqual(ref(.WasmAny).intersection(with: refNull(.WasmEq)), ref(.WasmEq)) + XCTAssertEqual(refNull(.WasmI31).intersection(with: refNull(.WasmStruct)), refNull(.WasmNone)) + // Note that `ref none` is a perfectly valid type in Wasm but such a reference can never be + // constructed. + XCTAssertEqual(ref(.WasmArray).intersection(with: refNull(.WasmStruct)), ref(.WasmNone)) + XCTAssertEqual(refNull(.WasmArray).intersection(with: ref(.WasmAny)), ref(.WasmArray)) } func testUnboundFunctionSubsumptionRules() { @@ -1448,10 +1433,15 @@ class TypeSystemTests: XCTestCase { .wasmf32, .wasmi64, .wasmf64, + .wasmFuncRef, + .wasmExternRef, + .wasmExnRef, + .wasmI31Ref, + .wasmRefI31, .wasmFunctionDef([.wasmi32] => [.wasmi64]), .wasmFunctionDef([.wasmf32] => [.wasmi32]), - .wasmFunctionDef([.wasmExternRef()] => [.wasmExternRef()]), + .wasmFunctionDef([.wasmExternRef] => [.wasmExternRef]), .wasmMemory(limits: Limits(min: 10)), .wasmMemory(limits: Limits(min: 10, max: 20)), - ] + ILType.allWasmRefTypes() + ] } diff --git a/Tests/FuzzilliTests/WasmTableTests.swift b/Tests/FuzzilliTests/WasmTableTests.swift index 2e4f9335f..c3e59c9b7 100644 --- a/Tests/FuzzilliTests/WasmTableTests.swift +++ b/Tests/FuzzilliTests/WasmTableTests.swift @@ -22,7 +22,7 @@ class WasmTableTests: XCTestCase { let js = buildAndLiftProgram { b in let module = b.buildWasmModule { wasmModule in - let table = wasmModule.addTable(elementType: .wasmFuncRef(), minSize: 10, maxSize: 20, isTable64: false) + let table = wasmModule.addTable(elementType: .wasmFuncRef, minSize: 10, maxSize: 20, isTable64: false) wasmModule.addWasmFunction(with: [] => [.wasmi32]) { f, _, _ in let size = f.wasmTableSize(table: table) @@ -31,7 +31,7 @@ class WasmTableTests: XCTestCase { expectedOutput += "10\n" wasmModule.addWasmFunction(with: [] => [.wasmi32, .wasmi32]) { f, _, _ in - let initialValue = f.wasmRefNull(type: .wasmFuncRef()) + let initialValue = f.wasmRefNull(type: .wasmFuncRef) let growBy = f.consti32(5) let oldSize = f.wasmTableGrow(table: table, with: initialValue, by: growBy) let newSize = f.wasmTableSize(table: table) diff --git a/Tests/FuzzilliTests/WasmTests.swift b/Tests/FuzzilliTests/WasmTests.swift index 8a78971bb..f7955e212 100644 --- a/Tests/FuzzilliTests/WasmTests.swift +++ b/Tests/FuzzilliTests/WasmTests.swift @@ -44,16 +44,16 @@ func testForErrorOutput(program: String, runner: JavaScriptExecutor, errorMessag class WasmSignatureConversionTests: XCTestCase { func testJsSignatureConversion() { - XCTAssertEqual(ProgramBuilder.convertJsSignatureToWasmSignature([.number] => .integer, availableTypes: WeightedList([(.wasmi32, 1), (.wasmFuncRef(), 1), (.wasmExternRef(), 1)])), [.wasmi32] => [.wasmi32]) - XCTAssertEqual(ProgramBuilder.convertJsSignatureToWasmSignature([.number] => .integer, availableTypes: WeightedList([(.wasmf32, 1), (.wasmFuncRef(), 1), (.wasmExternRef(), 1)])), [.wasmf32] => [.wasmi32]) + XCTAssertEqual(ProgramBuilder.convertJsSignatureToWasmSignature([.number] => .integer, availableTypes: WeightedList([(.wasmi32, 1), (.wasmFuncRef, 1), (.wasmExternRef, 1)])), [.wasmi32] => [.wasmi32]) + XCTAssertEqual(ProgramBuilder.convertJsSignatureToWasmSignature([.number] => .integer, availableTypes: WeightedList([(.wasmf32, 1), (.wasmFuncRef, 1), (.wasmExternRef, 1)])), [.wasmf32] => [.wasmi32]) } func testWasmSignatureConversion() { XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmi32, .wasmi64] => [.wasmf32]), [.integer, .bigint] => .float) - XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmi32, .wasmExnRef()] => [.wasmf64]), [.integer, .jsAnything] => .float) - XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmExternRef(), .wasmFuncRef()] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) - XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmRef(.Index(), nullability: false), .wasmFuncRef()] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) - XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmRef(.WasmExtern, nullability: false), .wasmFuncRef()] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) + XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmi32, .wasmExnRef] => [.wasmf64]), [.integer, .jsAnything] => .float) + XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmExternRef, .wasmFuncRef] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) + XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmRef(.Index(), nullability: false), .wasmFuncRef] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) + XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmRef(.Abstract(.WasmExtern), nullability: false), .wasmFuncRef] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) // TODO(cffsmith): Change this once we know how we want to represent .wasmSimd128 types in JS. XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmSimd128] => [.wasmSimd128]), [.jsAnything] => .jsAnything) } @@ -440,7 +440,7 @@ class WasmFoundationTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "1338\n4242\n6.61e-321\n") } - func globalExnRef(sharedRef: Bool) throws { + func testGlobalExnRef() throws { let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--experimental-wasm-exnref"]) let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) @@ -450,8 +450,8 @@ class WasmFoundationTests: XCTestCase { let tagi32 = b.createWasmTag(parameterTypes: [.wasmi32]) let module = b.buildWasmModule { wasmModule in // Note that globals of exnref can only be defined in wasm, not in JS. - let global = wasmModule.addGlobal(wasmGlobal: .exnref(shared: sharedRef), isMutable: true) - XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmExnRef(shared: sharedRef), isMutable: true))) + let global = wasmModule.addGlobal(wasmGlobal: .exnref, isMutable: true) + XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmExnRef, isMutable: true))) wasmModule.addWasmFunction(with: [] => [.wasmi32]) { function, label, args in let value = function.wasmLoadGlobal(globalVariable: global) @@ -460,12 +460,12 @@ class WasmFoundationTests: XCTestCase { // Throw an exception, catch it and store it in the global. wasmModule.addWasmFunction(with: [] => []) { function, label, args in - let exnref = function.wasmBuildBlockWithResults(with: [] => [.wasmExnRef(shared: sharedRef)], args: []) { catchLabel, _ in + let exnref = function.wasmBuildBlockWithResults(with: [] => [.wasmExnRef], args: []) { catchLabel, _ in function.wasmBuildTryTable(with: [] => [], args: [catchLabel], catches: [.AllRef]) { _, _ in function.WasmBuildThrow(tag: tagi32, inputs: [function.consti32(42)]) return [] } - return [function.wasmRefNull(type: .wasmExnRef(shared: sharedRef))] + return [function.wasmRefNull(type: .wasmExnRef)] }[0] function.wasmStoreGlobal(globalVariable: global, to: exnref) return [] @@ -473,12 +473,12 @@ class WasmFoundationTests: XCTestCase { // Rethrow the exception stored in the global, catch it and extract the integer. wasmModule.addWasmFunction(with: [] => [.wasmi32]) { function, label, args in - let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef(shared: sharedRef)], args: []) { catchLabel, _ in + let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef], args: []) { catchLabel, _ in function.wasmBuildTryTable(with: [] => [], args: [tagi32, catchLabel], catches: [.Ref]) { _, _ in - function.wasmBuildThrowRef(exception: function.wasmLoadGlobal(globalVariable: global), sharedRef: sharedRef) + function.wasmBuildThrowRef(exception: function.wasmLoadGlobal(globalVariable: global)) return [] } - return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef(shared: sharedRef))] + return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef)] } return [caughtValues[0]] } @@ -509,12 +509,12 @@ class WasmFoundationTests: XCTestCase { let otherModule = b.buildWasmModule { wasmModule in // Rethrow the exception stored in the global, catch it and extract the integer. wasmModule.addWasmFunction(with: [] => [.wasmi32]) { function, label, args in - let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef(shared: sharedRef)], args: []) { catchLabel, _ in + let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef], args: []) { catchLabel, _ in function.wasmBuildTryTable(with: [] => [], args: [tagi32, catchLabel], catches: [.Ref]) { _, _ in - function.wasmBuildThrowRef(exception: function.wasmLoadGlobal(globalVariable: global), sharedRef: sharedRef) + function.wasmBuildThrowRef(exception: function.wasmLoadGlobal(globalVariable: global)) return [] } - return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef(shared: sharedRef))] + return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef)] } return [caughtValues[0]] } @@ -529,16 +529,7 @@ class WasmFoundationTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "1\n0\n42\nexception\n42\n") } - func testGlobalExnRefShared() throws { - // TODO(pawkra) - throw XCTSkip("Enable the test once we are emit & support shared refs in ProgramBuilder.") - } - - func testGlobalExnRefUnshared() throws { - try globalExnRef(sharedRef: false) - } - - func globalExternRef(sharedRef: Bool) throws { + func testGlobalExternRef() throws { let runner = try GetJavaScriptExecutorOrSkipTest() let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) @@ -546,14 +537,14 @@ class WasmFoundationTests: XCTestCase { let b = fuzzer.makeBuilder() let module = b.buildWasmModule { wasmModule in - let global = wasmModule.addGlobal(wasmGlobal: .externref(shared: sharedRef), isMutable: true) - XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmExternRef(shared: sharedRef), isMutable: true))) + let global = wasmModule.addGlobal(wasmGlobal: .externref, isMutable: true) + XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmExternRef, isMutable: true))) - wasmModule.addWasmFunction(with: [] => [.wasmExternRef(shared: sharedRef)]) { function, label, args in + wasmModule.addWasmFunction(with: [] => [.wasmExternRef]) { function, label, args in [function.wasmLoadGlobal(globalVariable: global)] } - wasmModule.addWasmFunction(with: [.wasmExternRef(shared: sharedRef)] => []) { function, label, args in + wasmModule.addWasmFunction(with: [.wasmExternRef] => []) { function, label, args in function.wasmStoreGlobal(globalVariable: global, to: args[0]) return [] } @@ -579,15 +570,6 @@ class WasmFoundationTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "null\nHello!\nHello!\n") } - func testglobalExternRefShared() throws { - // TODO(pawkra) - throw XCTSkip("Enable the test once we are emit & support shared refs in ProgramBuilder.") - } - - func testglobalExternRefUnshared() throws { - try globalExternRef(sharedRef: false) - } - func testGlobalExternRefFromJS() throws { let runner = try GetJavaScriptExecutorOrSkipTest() let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) @@ -595,9 +577,8 @@ class WasmFoundationTests: XCTestCase { let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() - // TODO(pawkra): add shared ref variant. - let global: Variable = b.createWasmGlobal(value: .externref(shared: false), isMutable: true) - XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmExternRef(), isMutable: true))) + let global: Variable = b.createWasmGlobal(value: .externref, isMutable: true) + XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmExternRef, isMutable: true))) let outputFunc = b.createNamedVariable(forBuiltin: "output") // The initial value is "undefined" (because we didn't provide an explicit initialization). @@ -613,22 +594,22 @@ class WasmFoundationTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "undefined\nHello!\n") } - func globalI31Ref(sharedRef: Bool) throws { - let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--experimental-wasm-shared"]) + func testGlobalI31Ref() throws { + let runner = try GetJavaScriptExecutorOrSkipTest() let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() let module = b.buildWasmModule { wasmModule in - let global = wasmModule.addGlobal(wasmGlobal: .i31ref(shared: sharedRef), isMutable: true) - XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmI31Ref(shared: sharedRef), isMutable: true))) + let global = wasmModule.addGlobal(wasmGlobal: .i31ref, isMutable: true) + XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmI31Ref, isMutable: true))) - wasmModule.addWasmFunction(with: [] => [.wasmI31Ref(shared: sharedRef)]) { function, label, args in + wasmModule.addWasmFunction(with: [] => [.wasmI31Ref]) { function, label, args in [function.wasmLoadGlobal(globalVariable: global)] } - wasmModule.addWasmFunction(with: [.wasmI31Ref(shared: sharedRef)] => []) { function, label, args in + wasmModule.addWasmFunction(with: [.wasmI31Ref] => []) { function, label, args in function.wasmStoreGlobal(globalVariable: global, to: args[0]) return [] } @@ -653,14 +634,6 @@ class WasmFoundationTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "null\n-42\n-42\n") } - func testGlobalI31RefShared() throws { - try globalI31Ref(sharedRef: true) - } - - func testGlobalI31RefUnshared() throws { - try globalI31Ref(sharedRef: false) - } - func testGlobalI31RefFromJS() throws { let runner = try GetJavaScriptExecutorOrSkipTest() let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) @@ -668,9 +641,8 @@ class WasmFoundationTests: XCTestCase { let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() - // TODO(pawkra): add shared ref variant. - let global: Variable = b.createWasmGlobal(value: .i31ref(shared: false), isMutable: true) - XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmI31Ref(), isMutable: true))) + let global: Variable = b.createWasmGlobal(value: .i31ref, isMutable: true) + XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmI31Ref, isMutable: true))) let outputFunc = b.createNamedVariable(forBuiltin: "output") // The initial value is "null" (because we didn't provide an explicit initialization). @@ -694,8 +666,8 @@ class WasmFoundationTests: XCTestCase { let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() - let javaScriptTable = b.createWasmTable(elementType: .wasmExternRef(), limits: Limits(min: 5, max: 25), isTable64: isTable64) - XCTAssertEqual(b.type(of: javaScriptTable), .wasmTable(wasmTableType: WasmTableType(elementType: .wasmExternRef(), limits: Limits(min: 5, max: 25), isTable64: isTable64, knownEntries: []))) + let javaScriptTable = b.createWasmTable(elementType: .wasmExternRef, limits: Limits(min: 5, max: 25), isTable64: isTable64) + XCTAssertEqual(b.type(of: javaScriptTable), .wasmTable(wasmTableType: WasmTableType(elementType: .wasmExternRef, limits: Limits(min: 5, max: 25), isTable64: isTable64, knownEntries: []))) let object = b.createObject(with: ["a": b.loadInt(41), "b": b.loadInt(42)]) @@ -703,9 +675,9 @@ class WasmFoundationTests: XCTestCase { b.callMethod("set", on: javaScriptTable, withArgs: [isTable64 ? b.loadBigInt(1) : b.loadInt(1), object]) let module = b.buildWasmModule { wasmModule in - let tableRef = wasmModule.addTable(elementType: .wasmExternRef(), minSize: 2, isTable64: isTable64) + let tableRef = wasmModule.addTable(elementType: .wasmExternRef, minSize: 2, isTable64: isTable64) - wasmModule.addWasmFunction(with: [] => [.wasmExternRef()]) { function, _, _ in + wasmModule.addWasmFunction(with: [] => [.wasmExternRef]) { function, _, _ in let offset = isTable64 ? function.consti64(0) : function.consti32(0) var ref = function.wasmTableGet(tableRef: tableRef, idx: offset) let offset1 = isTable64 ? function.consti64(1) : function.consti32(1) @@ -755,7 +727,7 @@ class WasmFoundationTests: XCTestCase { let wasmFunction = wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmi32]) { function, label, params in [function.wasmi32BinOp(params[0], function.consti32(1), binOpKind: .Add)] } - wasmModule.addTable(elementType: .wasmFuncRef(), + wasmModule.addTable(elementType: .wasmFuncRef, minSize: 10, definedEntries: [.init(indexInTable: 0, signature: [.wasmi32] => [.wasmi32]), .init(indexInTable: 1, signature: [] => [.wasmi64])], definedEntryValues: [wasmFunction, jsFunction], @@ -773,7 +745,7 @@ class WasmFoundationTests: XCTestCase { XCTAssertEqual(b.type(of: importedFunction), .function([] => .bigint)) // This is the table type that we expect to see on the exports based on the dynamic object group typing. - let tableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef(), limits: Limits(min: 10), isTable64: isTable64, knownEntries: [ + let tableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef, limits: Limits(min: 10), isTable64: isTable64, knownEntries: [ .init(indexInTable: 0, signature: [.wasmi32] => [.wasmi32]), .init(indexInTable: 1, signature: [] => [.wasmi64]) @@ -821,7 +793,7 @@ class WasmFoundationTests: XCTestCase { let wasmFunction = wasmModule.addWasmFunction(with: [.wasmi64] => [.wasmi64, .wasmi64]) { function, label, params in return [params[0], function.consti64(1)] } - let table = wasmModule.addTable(elementType: .wasmFuncRef(), + let table = wasmModule.addTable(elementType: .wasmFuncRef, minSize: 10, definedEntries: [.init(indexInTable: 0, signature: [.wasmi64] => [.wasmi64, .wasmi64]), .init(indexInTable: 1, signature: [.wasmi64] => [.wasmi64])], definedEntryValues: [wasmFunction, jsFunction], @@ -871,7 +843,7 @@ class WasmFoundationTests: XCTestCase { let wasmFunction = wasmModule.addWasmFunction(with: [.wasmi64] => [.wasmi64, .wasmi64]) { function, label, params in return [params[0], function.consti64(1)] } - wasmModule.addTable(elementType: .wasmFuncRef(), + wasmModule.addTable(elementType: .wasmFuncRef, minSize: 10, definedEntries: [.init(indexInTable: 0, signature: [.wasmi64] => [.wasmi64, .wasmi64]), .init(indexInTable: 1, signature: [.wasmi64] => [.wasmi64])], definedEntryValues: [wasmFunction, jsFunction], @@ -879,7 +851,7 @@ class WasmFoundationTests: XCTestCase { } let table = b.getProperty("wt0", of: module.loadExports()) - let tableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef(), limits: Limits(min: 10), isTable64: false, knownEntries: [ + let tableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef, limits: Limits(min: 10), isTable64: false, knownEntries: [ .init(indexInTable: 0, signature: [.wasmi64] => [.wasmi64, .wasmi64]), .init(indexInTable: 1, signature: [.wasmi64] => [.wasmi64]) ])) @@ -894,7 +866,7 @@ class WasmFoundationTests: XCTestCase { fn.wasmCallIndirect(signature: [.wasmi64] => [.wasmi64], table: table, functionArgs: [params[1]], tableIndex: params[0]) } - wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmFuncRef()]) { function, label, params in + wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmFuncRef]) { function, label, params in [function.wasmTableGet(tableRef: table, idx: params[0])] } @@ -909,7 +881,7 @@ class WasmFoundationTests: XCTestCase { let reexportedTable = b.getProperty("iwt0", of: exports) // This is the table type that we expect to see on the exports based on the dynamic object group typing. - let reexportedTableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef(), limits: Limits(min: 10), isTable64: false, knownEntries: [ + let reexportedTableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef, limits: Limits(min: 10), isTable64: false, knownEntries: [ .init(indexInTable: 0, signature: [.wasmi64] => [.wasmi64, .wasmi64]), .init(indexInTable: 1, signature: [.wasmi64] => [.wasmi64]) @@ -1025,7 +997,7 @@ class WasmFoundationTests: XCTestCase { let wasmFunction = wasmModule.addWasmFunction(with: [.wasmi64] => [.wasmi64, .wasmi64]) { function, label, params in return [params[0], function.consti64(1)] } - let table = wasmModule.addTable(elementType: .wasmFuncRef(), + let table = wasmModule.addTable(elementType: .wasmFuncRef, minSize: 10, definedEntries: [.init(indexInTable: 0, signature: [.wasmi64] => [.wasmi64, .wasmi64]), .init(indexInTable: 1, signature: [.wasmi64] => [.wasmi64])], definedEntryValues: [wasmFunction, jsFunction], @@ -3788,11 +3760,10 @@ class WasmFoundationTests: XCTestCase { let outputFunc = b.createNamedVariable(forBuiltin: "output") let tagVoid = b.createWasmTag(parameterTypes: []) let tagi32 = b.createWasmTag(parameterTypes: [.wasmi32]) - // TODO(pawkra): add shared ref variant. let module = b.buildWasmModule { wasmModule in wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmi32]) { function, label, args in - function.wasmBuildBlockWithResults(with: [] => [.wasmExnRef()], args: []) { catchAllRefLabel, _ in - let catchRefI32 = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef()], args: []) { catchRefLabel, _ in + function.wasmBuildBlockWithResults(with: [] => [.wasmExnRef], args: []) { catchAllRefLabel, _ in + let catchRefI32 = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef], args: []) { catchRefLabel, _ in function.wasmBuildTryTable(with: [] => [], args: [tagi32, catchRefLabel, catchAllRefLabel], catches: [.Ref, .AllRef]) { _, _ in function.wasmBuildIfElse(function.wasmi32EqualZero(args[0]), hint: .None) { function.WasmBuildThrow(tag: tagVoid, inputs: []) @@ -3801,10 +3772,10 @@ class WasmFoundationTests: XCTestCase { } return [] } - return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef())] + return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef)] } function.wasmReturn(catchRefI32[0]) - return [function.wasmRefNull(type: .wasmExnRef())] + return [function.wasmRefNull(type: .wasmExnRef)] } return [function.consti32(100)] } @@ -3894,8 +3865,10 @@ class WasmFoundationTests: XCTestCase { let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() - // Assumption: All types apart from bottom (null) & shared types are supported in the JS API. - let supportedTypes = WasmAbstractHeapType.allNonBottomTypes().map {ILType.wasmRef($0, nullability: true)} + // Assumption: All types but the bottom (null) types are supported in the JS API. + let supportedTypes = WasmAbstractHeapType.allCases.filter {!$0.isBottom()}.map { heapType in + ILType.wasmRef(.Abstract(heapType), nullability:true) + } b.createWasmTag(parameterTypes: supportedTypes) let prog = b.finalize() let jsProg = fuzzer.lifter.lift(prog, withOptions: [.includeComments]) @@ -3906,7 +3879,7 @@ class WasmFoundationTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "") } - func throwRef(sharedRef: Bool) throws { + func testThrowRef() throws { let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--experimental-wasm-exnref"]) let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) @@ -3921,17 +3894,17 @@ class WasmFoundationTests: XCTestCase { let module = b.buildWasmModule { wasmModule in // Inner function that throws, catches and then rethrows the value. let callee = wasmModule.addWasmFunction(with: [.wasmi32] => []) { function, label, args in - let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef(shared: sharedRef)], args: []) { catchRefLabel, _ in + let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef], args: []) { catchRefLabel, _ in function.wasmBuildTryTable(with: [] => [], args: [tagi32, catchRefLabel], catches: [.Ref]) { _, _ in function.WasmBuildThrow(tag: tagi32, inputs: [args[0]]) return [] } - return [function.consti32(0), function.wasmRefNull(type: .wasmExnRef(shared: sharedRef))] + return [function.consti32(0), function.wasmRefNull(type: .wasmExnRef)] } // Print the caught i32 value. function.wasmJsCall(function: printInteger, withArgs: [caughtValues[0]], withWasmSignature: [.wasmi32] => []) // To rethrow the exception, perform a throw_ref with the exnref. - function.wasmBuildThrowRef(exception: caughtValues[1], sharedRef: sharedRef) + function.wasmBuildThrowRef(exception: caughtValues[1]) return [] } @@ -3956,15 +3929,6 @@ class WasmFoundationTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "42\n42\n") } - func testThrowRefShared() throws { - // TODO(pawkra) - throw XCTSkip("Enable the test once we are emit & support shared refs in ProgramBuilder.") - } - - func testThrowRefUnshared() throws { - try throwRef(sharedRef: false) - } - func testUnreachable() throws { let runner = try GetJavaScriptExecutorOrSkipTest() let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) @@ -4008,7 +3972,7 @@ class WasmFoundationTests: XCTestCase { trueValue: function.consti64(123), falseValue: function.consti64(321))] } - wasmModule.addWasmFunction(with: [.wasmi32, .wasmExternRef(), .wasmExternRef()] => [.wasmExternRef()]) { function, label, args in + wasmModule.addWasmFunction(with: [.wasmi32, .wasmExternRef, .wasmExternRef] => [.wasmExternRef]) { function, label, args in [function.wasmSelect(on: args[0], trueValue: args[1], falseValue: args[2])] } } @@ -4184,13 +4148,13 @@ class WasmFoundationTests: XCTestCase { let f1 = module.addWasmFunction(with: [] => [.wasmi64]) { f, _, _ in return [f.consti64(1)]} let f2 = module.addWasmFunction(with: [] => [.wasmi64]) { f, _, _ in return [f.consti64(2)]} let f3 = module.addWasmFunction(with: [] => [.wasmi64]) { f, _, _ in return [f.consti64(3)]} - // TODO(pawkra): add shared ref variant. - module.addTable(elementType: .wasmFuncRef(), + + module.addTable(elementType: .wasmFuncRef, minSize: 10, definedEntries: [], definedEntryValues: [], isTable64: isTable64) - let table2 = module.addTable(elementType: .wasmFuncRef(), + let table2 = module.addTable(elementType: .wasmFuncRef, minSize: 10, definedEntries: [], definedEntryValues: [], @@ -4232,13 +4196,12 @@ class WasmFoundationTests: XCTestCase { let f2 = module.addWasmFunction(with: [] => [.wasmi64]) { f, _, _ in return [f.consti64(2)]} let f3 = module.addWasmFunction(with: [] => [.wasmi64]) { f, _, _ in return [f.consti64(3)]} - // TODO(pawkra): add shared ref variant. - let table1 = module.addTable(elementType: .wasmFuncRef(), + let table1 = module.addTable(elementType: .wasmFuncRef, minSize: 10, definedEntries: [], definedEntryValues: [], isTable64: isTable64) - let table2 = module.addTable(elementType: .wasmFuncRef(), + let table2 = module.addTable(elementType: .wasmFuncRef, minSize: 10, definedEntries: (0..<4).map { WasmTableType.IndexInTableAndWasmSignature.init(indexInTable: $0, signature: [] => [.wasmi64]) }, definedEntryValues: [f3, f3, f1, f2], @@ -4505,9 +4468,8 @@ class WasmGCTests: XCTestCase { return [arrayi32, signature] } - // TODO(pawkra): add shared ref variant. let module = b.buildWasmModule { wasmModule in - wasmModule.addWasmFunction(with: [] => [.wasmFuncRef()]) { function, label, args 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. @@ -4532,7 +4494,7 @@ class WasmGCTests: XCTestCase { let jsProg = buildAndLiftProgram { b in let module = b.buildWasmModule { wasmModule in - wasmModule.addWasmFunction(with: [] => [.wasmFuncRef()]) { function, label, args 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. @@ -4746,19 +4708,15 @@ class WasmGCTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "1\n0\n") } - func refNullAbstractTypes(sharedRef: Bool) throws { - let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--experimental-wasm-exnref", "--experimental-wasm-shared"]) + func testRefNullAbstractTypes() throws { + let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--experimental-wasm-exnref"]) let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() - // TODO(pawkra): simplify once V8 adds support for shared references of type: func, exn - let unsupportedHeapType: Set = sharedRef ? [.WasmFunc, .WasmNoFunc, .WasmExn, .WasmNoExn] : [] - let supportedHeapTypes = Array(Set(WasmAbstractHeapType.allCases).subtracting(unsupportedHeapType)) - let module = b.buildWasmModule { wasmModule in - for heapType in supportedHeapTypes { - let valueType = ILType.wasmRef(heapType, shared: sharedRef, nullability: true) + for heapType in WasmAbstractHeapType.allCases { + let valueType = ILType.wasmRef(.Abstract(heapType), nullability: true) if heapType.isUsableInJS() { // ref.null wasmModule.addWasmFunction(with: [] => [valueType]) { function, label, args in @@ -4774,8 +4732,8 @@ class WasmGCTests: XCTestCase { let exports = module.loadExports() let outputFunc = b.createNamedVariable(forBuiltin: "output") - let exportedFctCount = supportedHeapTypes.count - + supportedHeapTypes.count {$0.isUsableInJS()} + let exportedFctCount = WasmAbstractHeapType.allCases.count + + WasmAbstractHeapType.allCases.count {$0.isUsableInJS()} for i in 0.. [.wasmI31Ref()]) { function, label, args in + wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmI31Ref]) { function, label, args in [function.wasmRefI31(args[0])] } - wasmModule.addWasmFunction(with: [.wasmI31Ref()] => [.wasmi32, .wasmi32]) { function, label, args in - [function.wasmI31Get(args[0], isSigned: true, shared: false), - function.wasmI31Get(args[0], isSigned: false, shared: false)] + wasmModule.addWasmFunction(with: [.wasmI31Ref] => [.wasmi32, .wasmi32]) { function, label, args in + [function.wasmI31Get(args[0], isSigned: true), + function.wasmI31Get(args[0], isSigned: false)] } } @@ -4830,75 +4780,36 @@ class WasmGCTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "42\n-42\n42,42\n-42,2147483606\n") } - func i31Ref(sharedRef: Bool) throws { - let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--experimental-wasm-shared"]) - let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) - let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) - let b = fuzzer.makeBuilder() - - let module = b.buildWasmModule { wasmModule in - let f1 = wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmI31Ref(shared: sharedRef)]) { function, label, args in - [function.wasmRefI31(args[0])] - } - let f2 = wasmModule.addWasmFunction(with: [.wasmI31Ref(shared: sharedRef)] => [.wasmi32, .wasmi32]) { function, label, args in - [function.wasmI31Get(args[0], isSigned: true, shared: sharedRef), - function.wasmI31Get(args[0], isSigned: false, shared: sharedRef)] - } - wasmModule.addWasmFunction(with: [] => [.wasmi32, .wasmi32]) { function, label, args in - let ref = function.wasmCallDirect(signature: [.wasmi32] => [.wasmI31Ref(shared: sharedRef)], function: f1, functionArgs: [function.consti32(-42)]) - return function.wasmCallDirect(signature: [.wasmI31Ref(shared: sharedRef)] => [.wasmi32, .wasmi32], function: f2, functionArgs: ref) - } - } - - let exports = module.loadExports() - let outputFunc = b.createNamedVariable(forBuiltin: "output") - let result = b.callMethod(module.getExportedMethod(at: 2), on: exports, withArgs: [b.loadInt(42)]) - b.callFunction(outputFunc, withArgs: [result]) - - let prog = b.finalize() - let jsProg = fuzzer.lifter.lift(prog) - testForOutput(program: jsProg, runner: runner, outputString: "-42,2147483606\n") - } - - func testi31RefSharedRef() throws { - // TODO(pawkra) - throw XCTSkip("Enable the test once we are emit & support shared refs in ProgramBuilder.") - } - - func testi31RefUnsharedRef() throws { - try i31Ref(sharedRef: false) - } - - func externAnyConversions(sharedRef: Bool) throws { + func testExternAnyConversions() throws { let runner = try GetJavaScriptExecutorOrSkipTest() let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() let module = b.buildWasmModule { wasmModule in - wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmRefExtern(shared: sharedRef)]) { function, label, args in + wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmRefExtern]) { function, label, args in // As ref.i31 produces a non null `ref i31`, the result of extern.convert_any is a // non-nullable `ref extern`. - let result = function.wasmExternConvertAny(function.wasmRefI31(args[0]), shared: sharedRef) - XCTAssertEqual(b.type(of: result), .wasmRefExtern(shared: sharedRef)) + let result = function.wasmExternConvertAny(function.wasmRefI31(args[0])) + XCTAssertEqual(b.type(of: result), .wasmRefExtern) return [result] } - wasmModule.addWasmFunction(with: [.wasmRefExtern(shared: sharedRef)] => [.wasmRefAny(shared: sharedRef)]) { function, label, args in - let result = function.wasmAnyConvertExtern(args[0], shared: sharedRef) - XCTAssertEqual(b.type(of: result), .wasmRefAny(shared: sharedRef)) + wasmModule.addWasmFunction(with: [.wasmRefExtern] => [.wasmRefAny]) { function, label, args in + let result = function.wasmAnyConvertExtern(args[0]) + XCTAssertEqual(b.type(of: result), .wasmRefAny) return [result] } - wasmModule.addWasmFunction(with: [] => [.wasmExternRef(shared: sharedRef)]) { function, label, args in - let result = function.wasmExternConvertAny(function.wasmRefNull(type: .wasmNullRef(shared: sharedRef)), shared: sharedRef) - XCTAssertEqual(b.type(of: result), .wasmExternRef(shared: sharedRef)) + wasmModule.addWasmFunction(with: [] => [.wasmExternRef]) { function, label, args in + let result = function.wasmExternConvertAny(function.wasmRefNull(type: .wasmNullRef)) + XCTAssertEqual(b.type(of: result), .wasmExternRef) return [result] } - wasmModule.addWasmFunction(with: [] => [.wasmAnyRef(shared: sharedRef)]) { function, label, args in - let result = function.wasmAnyConvertExtern(function.wasmRefNull(type: .wasmNullExternRef(shared: sharedRef)), shared: sharedRef) - XCTAssertEqual(b.type(of: result), .wasmAnyRef(shared: sharedRef)) + wasmModule.addWasmFunction(with: [] => [.wasmAnyRef]) { function, label, args in + let result = function.wasmAnyConvertExtern(function.wasmRefNull(type: .wasmNullExternRef)) + XCTAssertEqual(b.type(of: result), .wasmAnyRef) return [result] } } @@ -4914,15 +4825,6 @@ class WasmGCTests: XCTestCase { let jsProg = fuzzer.lifter.lift(prog) testForOutput(program: jsProg, runner: runner, outputString: "42\n42\nnull\nnull\n") } - - func testExternAnyConversionsSharedRefs() throws { - // TODO(pawkra): double check that shared references can be converted to extern ref. - throw XCTSkip("Fix once we are able to emit shared i31, extern, null, and nullextern refs.") - } - - func testExternAnyConversionsUnsharedRefs() throws { - try externAnyConversions(sharedRef: false) - } } class WasmNumericalTests: XCTestCase { @@ -6457,10 +6359,9 @@ class WasmJSPITests: XCTestCase { XCTAssertEqual(b.type(of: importFunction), .object(ofGroup: "WasmSuspendingObject")) // Now lets build the module - // TODO(pawkra): add shared ref variant. let module = b.buildWasmModule { m in - m.addWasmFunction(with: [.wasmExternRef()] => [.wasmi32]) { f, label, args in - [f.wasmJsCall(function: importFunction, withArgs: args, withWasmSignature: [.wasmExternRef()] => [.wasmi32])!] + m.addWasmFunction(with: [.wasmExternRef] => [.wasmi32]) { f, label, args in + [f.wasmJsCall(function: importFunction, withArgs: args, withWasmSignature: [.wasmExternRef] => [.wasmi32])!] } } From d50f02e2e5dbc779ac75a5b2dbf3582860aa60df Mon Sep 17 00:00:00 2001 From: Dominik Klemba Date: Tue, 2 Dec 2025 23:46:56 +0000 Subject: [PATCH 25/89] Enable inlining of Arrow Functions in JavaScript lifting This change allows the JavaScriptLifter to inline arrow functions (e.g., 'foo(() => 42)') by treating them as expressions. - Adds ArrowFunctionExpression to JSExpressions. - Updates JavaScriptLifter to detect recursive arrow functions and block boundaries. - Non-recursive arrow functions are buffered and assigned as expressions. - Recursive arrow functions retain the original variable declaration strategy. - Implements concise body syntax ('() => expr') for single-line returns without comments. - Updates JavaScriptWriter to use emitBlock for multi-line inlined expressions. Bug: 464228572, 456164925 Change-Id: Ic4618c2ba92ad96d95303e83f8551c13beef508c Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8808456 Reviewed-by: Matthias Liedtke Commit-Queue: Dominik Klemba Auto-Submit: Dominik Klemba --- Sources/Fuzzilli/Lifting/JSExpressions.swift | 43 +-- .../Fuzzilli/Lifting/JavaScriptLifter.swift | 131 ++++++++-- Tests/FuzzilliTests/LifterTest.swift | 245 +++++++++++++++++- 3 files changed, 374 insertions(+), 45 deletions(-) diff --git a/Sources/Fuzzilli/Lifting/JSExpressions.swift b/Sources/Fuzzilli/Lifting/JSExpressions.swift index 40a298a3d..e06d4f7fd 100644 --- a/Sources/Fuzzilli/Lifting/JSExpressions.swift +++ b/Sources/Fuzzilli/Lifting/JSExpressions.swift @@ -13,9 +13,9 @@ // limitations under the License. // JavaScript expressions. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence -public let Identifier = ExpressionType(precedence: 20, characteristic: .pure) -public let Literal = ExpressionType(precedence: 20, characteristic: .pure) -public let Keyword = ExpressionType(precedence: 20, characteristic: .pure) +public let Identifier = ExpressionType(precedence: 20, characteristic: .pure) +public let Literal = ExpressionType(precedence: 20, characteristic: .pure) +public let Keyword = ExpressionType(precedence: 20, characteristic: .pure) // RegExp are objects, and so for example for the FuzzIL program // v1 <- CreateRegExp // Compare v1, v1 @@ -25,24 +25,25 @@ public let Keyword = ExpressionType(precedence: 20, // and // /a/ === /a/; // (false) // the former being the correct JavaScript equivalent. -public let RegExpLiteral = ExpressionType(precedence: 20, characteristic: .effectful) -public let CallExpression = ExpressionType(precedence: 19, associativity: .left, characteristic: .effectful) -public let MemberExpression = ExpressionType(precedence: 19, associativity: .left, characteristic: .effectful) -public let NewExpression = ExpressionType(precedence: 19, characteristic: .effectful) +public let RegExpLiteral = ExpressionType(precedence: 20, characteristic: .effectful) +public let CallExpression = ExpressionType(precedence: 19, associativity: .left, characteristic: .effectful) +public let MemberExpression = ExpressionType(precedence: 19, associativity: .left, characteristic: .effectful) +public let NewExpression = ExpressionType(precedence: 19, characteristic: .effectful) // Artificial, need brackets around some literals for syntactic reasons -public let NumberLiteral = ExpressionType(precedence: 17, characteristic: .pure) +public let NumberLiteral = ExpressionType(precedence: 17, characteristic: .pure) // A helper expression type since negative numbers are technically unary expressions, but then they wouldn't // be inlined since unary expressions aren't generally pure. -public let NegativeNumberLiteral = ExpressionType(precedence: 17, characteristic: .pure) -public let StringLiteral = ExpressionType(precedence: 17, characteristic: .pure) -public let TemplateLiteral = ExpressionType(precedence: 17, characteristic: .effectful) -public let ObjectLiteral = ExpressionType(precedence: 17, characteristic: .effectful) -public let ArrayLiteral = ExpressionType(precedence: 17, characteristic: .effectful) -public let PostfixExpression = ExpressionType(precedence: 16, characteristic: .effectful) -public let UnaryExpression = ExpressionType(precedence: 15, associativity: .right, characteristic: .effectful) -public let BinaryExpression = ExpressionType(precedence: 14, associativity: .none, characteristic: .effectful) -public let TernaryExpression = ExpressionType(precedence: 4, associativity: .none, characteristic: .effectful) -public let AssignmentExpression = ExpressionType(precedence: 3, characteristic: .effectful) -public let YieldExpression = ExpressionType(precedence: 2, associativity: .right, characteristic: .effectful) -public let SpreadExpression = ExpressionType(precedence: 2, characteristic: .effectful) -public let CommaExpression = ExpressionType(precedence: 1, associativity: .left, characteristic: .effectful) +public let NegativeNumberLiteral = ExpressionType(precedence: 17, characteristic: .pure) +public let StringLiteral = ExpressionType(precedence: 17, characteristic: .pure) +public let TemplateLiteral = ExpressionType(precedence: 17, characteristic: .effectful) +public let ObjectLiteral = ExpressionType(precedence: 17, characteristic: .effectful) +public let ArrayLiteral = ExpressionType(precedence: 17, characteristic: .effectful) +public let PostfixExpression = ExpressionType(precedence: 16, characteristic: .effectful) +public let UnaryExpression = ExpressionType(precedence: 15, associativity: .right, characteristic: .effectful) +public let BinaryExpression = ExpressionType(precedence: 14, associativity: .none, characteristic: .effectful) +public let TernaryExpression = ExpressionType(precedence: 4, associativity: .none, characteristic: .effectful) +public let AssignmentExpression = ExpressionType(precedence: 3, associativity: .right, characteristic: .effectful) +public let ArrowFunctionExpression = ExpressionType(precedence: 3, associativity: .right, characteristic: .effectful) +public let YieldExpression = ExpressionType(precedence: 2, associativity: .right, characteristic: .effectful) +public let SpreadExpression = ExpressionType(precedence: 2, characteristic: .effectful) +public let CommaExpression = ExpressionType(precedence: 1, associativity: .left, characteristic: .effectful) diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift index 49cfb5a04..fd96d0caf 100644 --- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift +++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift @@ -64,6 +64,17 @@ public class JavaScriptLifter: Lifter { } } + struct FunctionLiftingState { + let outputVariable: Variable + // If true, the function refers to itself (e.g. recursion), preventing inlining. + let isSelfReferencing: Bool + // For inlineable functions: + var parameters: String = "" + var isAsync: Bool = false + let startInstructionIndex: Int + } + private var functionLiftingStack = Stack() + public init(prefix: String = "", suffix: String = "", ecmaVersion: ECMAScriptVersion, @@ -77,6 +88,13 @@ public class JavaScriptLifter: Lifter { self.alwaysEmitVariables = alwaysEmitVariables } + func isUsedInBlock(variable: Variable, blockStart: Int, blockEnd: Int, analyzer: DefUseAnalyzer) -> Bool { + // Check if any use of 'variable' is inside [blockStart, blockEnd] + // Note: The variable is defined at blockStart, so we only care about uses strictly after that. + // And strictly before or at blockEnd (the EndInstruction itself can use the variable). + return analyzer.uses(of: variable).contains { $0.index > blockStart && $0.index <= blockEnd } + } + public func lift(_ program: Program, withOptions options: LiftingOptions) -> String { liftedSamples += 1 // Perform some analysis on the program, for example to determine variable uses @@ -90,6 +108,18 @@ public class JavaScriptLifter: Lifter { var typer: JSTyper? = nil // The currently active WasmLifter, we can only have one of them. var wasmInstructions = Code() + + // Map block start index to end index. + let blockEndIndices = program.code.reduce(into: (indices: [Int: Int](), stack: Stack())) { context, instr in + if instr.isBlockEnd { + let start = context.stack.pop() + context.indices[start] = instr.index + } + if instr.isBlockStart { + context.stack.push(instr.index) + } + }.indices + for instr in program.code { analyzer.analyze(instr) if instr.op is Explore { needToSupportExploration = true } @@ -820,12 +850,8 @@ public class JavaScriptLifter: Lifter { liftFunctionDefinitionBegin(instr, keyword: "function", using: &w) case .beginArrowFunction(let op): - let LET = w.declarationKeyword(for: instr.output) - let V = w.declare(instr.output) - let vars = w.declareAll(instr.innerOutputs, usePrefix: "a") - let PARAMS = liftParameters(op.parameters, as: vars) - w.emit("\(LET) \(V) = (\(PARAMS)) => {") - w.enterNewBlock() + guard let endIndex = blockEndIndices[instr.index] else { fatalError("Block analysis failed") } + liftArrowFunctionDefinitionBegin(instr, parameters: op.parameters, isAsync: false, using: &w, functionEndIndex: endIndex, analyzer: analyzer) case .beginGeneratorFunction: liftFunctionDefinitionBegin(instr, keyword: "function*", using: &w) @@ -834,20 +860,43 @@ public class JavaScriptLifter: Lifter { liftFunctionDefinitionBegin(instr, keyword: "async function", using: &w) case .beginAsyncArrowFunction(let op): - let LET = w.declarationKeyword(for: instr.output) - let V = w.declare(instr.output) - let vars = w.declareAll(instr.innerOutputs, usePrefix: "a") - let PARAMS = liftParameters(op.parameters, as: vars) - w.emit("\(LET) \(V) = async (\(PARAMS)) => {") - w.enterNewBlock() + guard let endIndex = blockEndIndices[instr.index] else { fatalError("Block analysis failed") } + liftArrowFunctionDefinitionBegin(instr, parameters: op.parameters, isAsync: true, using: &w, functionEndIndex: endIndex, analyzer: analyzer) case .beginAsyncGeneratorFunction: liftFunctionDefinitionBegin(instr, keyword: "async function*", using: &w) case .endArrowFunction(_), .endAsyncArrowFunction: - w.leaveCurrentBlock() - w.emit("};") + let state = functionLiftingStack.pop() + if state.isSelfReferencing { + w.leaveCurrentBlock() + w.emit("};") + } else { + w.emitPendingExpressions() + let body = w.popTemporaryOutputBuffer() + + let trimmedBody = body.trimmingCharacters(in: .whitespacesAndNewlines) + var conciseBody: String? + + let lastInstr = program.code[instr.index - 1] + if lastInstr.index > state.startInstructionIndex, + let returnOp = lastInstr.op as? Return, + returnOp.hasReturnValue, + !trimmedBody.contains("\n"), + !trimmedBody.contains("//"), + trimmedBody.hasPrefix("return ") { + let content = trimmedBody.dropFirst("return ".count).trimmingCharacters(in: .whitespaces.union(.init(charactersIn: ";"))) + conciseBody = content.hasPrefix("{") ? "(\(content))" : content + } + + let implementation = conciseBody ?? "{\n\(body)}" + let asyncPrefix = state.isAsync ? "async " : "" + let arrowFuncCode = "\(asyncPrefix)(\(state.parameters)) => \(implementation)" + + let expr = ArrowFunctionExpression.new(arrowFuncCode) + w.assign(expr, to: state.outputVariable) + } case .endPlainFunction(_), .endGeneratorFunction(_), @@ -1851,6 +1900,45 @@ public class JavaScriptLifter: Lifter { w.enterNewBlock() } + private func liftArrowFunctionDefinitionBegin( + _ instr: Instruction, + parameters: Parameters, + isAsync: Bool, + using w: inout JavaScriptWriter, + functionEndIndex: Int, + analyzer: DefUseAnalyzer + ) { + let isSelfReferencing = isUsedInBlock( + variable: instr.output, + blockStart: instr.index, + blockEnd: functionEndIndex, + analyzer: analyzer + ) + + let vars = w.declareAll(instr.innerOutputs, usePrefix: "a") + let params = liftParameters(parameters, as: vars) + + functionLiftingStack.push(FunctionLiftingState( + outputVariable: instr.output, + isSelfReferencing: isSelfReferencing, + parameters: params, + isAsync: isAsync, + startInstructionIndex: instr.index + )) + + if isSelfReferencing { + let keyword = w.declarationKeyword(for: instr.output) + let varName = w.declare(instr.output) + + w.emit("\(keyword) \(varName) = \(isAsync ? "async " : "")(\(params)) => {") + w.enterNewBlock() + } else { + w.emitPendingExpressions() + w.pushTemporaryOutputBuffer(initialIndentionLevel: 1) + } + } + + private func liftCallArguments(_ args: Arguments, spreading spreads: [Bool] = []) -> String where Arguments.Element == Expression { var arguments = [String]() for (i, a) in args.enumerated() { @@ -1991,6 +2079,7 @@ public class JavaScriptLifter: Lifter { // List of effectful expressions that are still waiting to be inlined. In the order that they need to be executed at runtime. // The expressions are identified by the FuzzIL output variable that they generate. The actual expression is stored in the expressions dictionary. private var pendingExpressions = [Variable]() + private var pendingExpressionsStack = Stack<[Variable]>() // We also try to inline reassignments once, into the next use of the reassigned FuzzIL variable. However, for all subsequent uses we have to use the // identifier of the JavaScript variable again (the lhs of the reassignment). This map is used to remember these identifiers. @@ -2237,7 +2326,7 @@ public class JavaScriptLifter: Lifter { mutating func emit(_ line: String) { emitPendingExpressions() - writer.emit(line) + writer.emitBlock(line) } /// Emit a (potentially multi-line) comment. @@ -2262,6 +2351,8 @@ public class JavaScriptLifter: Lifter { mutating func pushTemporaryOutputBuffer(initialIndentionLevel: Int) { temporaryOutputBufferStack.push(writer) + pendingExpressionsStack.push(pendingExpressions) + pendingExpressions = [] writer = ScriptWriter(stripComments: writer.stripComments, includeLineNumbers: false, indent: writer.indent.count, initialIndentionLevel: initialIndentionLevel) } @@ -2269,6 +2360,7 @@ public class JavaScriptLifter: Lifter { assert(pendingExpressions.isEmpty) let code = writer.code writer = temporaryOutputBufferStack.pop() + pendingExpressions = pendingExpressionsStack.pop() return code } @@ -2314,12 +2406,11 @@ public class JavaScriptLifter: Lifter { if EXPR.type === AssignmentExpression { // Reassignments require special handling: there is already a variable declared for the lhs, // so we only need to emit the AssignmentExpression as an expression statement. - writer.emit("\(EXPR);") + writer.emitBlock("\(EXPR);") } else if analyzer.numUses(of: v) > 0 { let LET = declarationKeyword(for: v) let V = declare(v) - // Need to use writer.emit instead of emit here as the latter will emit all pending expressions. - writer.emit("\(LET) \(V) = \(EXPR);") + writer.emitBlock("\(LET) \(V) = \(EXPR);") } else { // Pending expressions with no uses are allowed and are for example necessary to be able to // combine multiple expressions into a single comma-expression for e.g. a loop header. @@ -2329,9 +2420,9 @@ public class JavaScriptLifter: Lifter { // not be distinguishable from block statements. So create a dummy variable. let LET = constKeyword let V = declare(v) - writer.emit("\(LET) \(V) = \(EXPR);") + writer.emitBlock("\(LET) \(V) = \(EXPR);") } else { - writer.emit("\(EXPR);") + writer.emitBlock("\(EXPR);") } } } diff --git a/Tests/FuzzilliTests/LifterTest.swift b/Tests/FuzzilliTests/LifterTest.swift index 7758461d1..258ed4a02 100644 --- a/Tests/FuzzilliTests/LifterTest.swift +++ b/Tests/FuzzilliTests/LifterTest.swift @@ -1038,10 +1038,7 @@ class LifterTests: XCTestCase { return a1; } f0(13.37); - const v4 = () => { - return "foobar"; - }; - f0 = v4; + f0 = () => "foobar"; f0(); """ @@ -3398,4 +3395,244 @@ class LifterTests: XCTestCase { """ XCTAssertEqual(actual, expected) } + + func testArrowFunctionInliningWithSideEffects() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + let f = b.createNamedVariable(forBuiltin: "sideEffect") + b.callFunction(f) + + let arrow = b.buildArrowFunction(with: .parameters(n: 0)) { _ in + b.doReturn(b.loadInt(42)) + } + + b.callFunction(arrow) + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + // The side-effect must be emitted before the arrow function is defined. + // The arrow function is assigned to a temporary because it's used as a callee. + let expected = """ + sideEffect(); + const t1 = () => 42; + t1(); + + """ + XCTAssertEqual(actual, expected) + } + + func testArrowFunctionRecursiveLifting() { + let fuzzer = makeMockFuzzer() + // Manually construct a recursive arrow function, as the helper does not support it. + let b2 = fuzzer.makeBuilder() + let i = b2.loadInt(10) + let params = Parameters(count: 1) + let instr = b2.emit(BeginArrowFunction(parameters: params)) + let fRec = instr.output + let arg0 = instr.innerOutput(0) + + let cond = b2.compare(arg0, with: b2.loadInt(0), using: .equal) + b2.buildIf(cond) { + b2.doReturn(b2.loadInt(0)) + } + let sub = b2.binary(arg0, b2.loadInt(1), with: .Sub) + let recCall = b2.callFunction(fRec, withArgs: [sub]) // Recursion! + b2.doReturn(recCall) + b2.emit(EndArrowFunction()) + + b2.callFunction(fRec, withArgs: [i]) + + let program2 = b2.finalize() + let actual = fuzzer.lifter.lift(program2) + + // Recursive arrow functions are not inlined, so they are assigned to a constant. + let expected = """ + const v1 = (a2) => { + if (a2 == 0) { + return 0; + } + return v1(a2 - 1); + }; + v1(10); + + """ + XCTAssertEqual(actual, expected) + } + + func testArrowFunctionAsArgument() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + let f = b.createNamedVariable(forBuiltin: "f") + let arrow = b.buildArrowFunction(with: .parameters(n: 1)) { args in + b.doReturn(args[0]) + } + b.callFunction(f, withArgs: [arrow]) + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + f((a2) => a2); + + """ + XCTAssertEqual(actual, expected) + } + + func testNestedArrowFunctions() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + let arrowOuter = b.buildArrowFunction(with: .parameters(n: 1)) { argsOuter in + let arrowInner = b.buildArrowFunction(with: .parameters(n: 1)) { argsInner in + b.doReturn(b.binary(argsOuter[0], argsInner[0], with: .Add)) + } + b.doReturn(arrowInner) + } + b.callFunction(arrowOuter, withArgs: [b.loadInt(1)]) + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + const t0 = (a1) => (a3) => a1 + a3; + t0(1); + + """ + XCTAssertEqual(actual, expected) + } + + func testAsyncArrowFunctionInlining() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + let arrow = b.buildAsyncArrowFunction(with: .parameters(n: 0)) { _ in + b.doReturn(b.loadInt(42)) + } + b.callFunction(arrow) + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + const t0 = async () => 42; + t0(); + + """ + XCTAssertEqual(actual, expected) + } + + func testArrowFunctionMultipleUses() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + let arrow = b.buildArrowFunction(with: .parameters(n: 0)) { _ in } + let f = b.createNamedVariable(forBuiltin: "f") + b.callFunction(f, withArgs: [arrow, arrow]) + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + const v0 = () => { + }; + f(v0, v0); + + """ + XCTAssertEqual(actual, expected) + } + + func testArrowFunctionPropertyAssignment() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + let obj = b.createNamedVariable(forBuiltin: "obj") + let arrow = b.buildArrowFunction(with: .parameters(n: 0)) { _ in + b.doReturn(b.loadInt(42)) + } + b.setProperty("x", of: obj, to: arrow) + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + obj.x = () => 42; + + """ + XCTAssertEqual(actual, expected) + } + + func testArrowFunctionObjectLiteral() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + let o = b.buildObjectLiteral { _ in } + let f = b.buildArrowFunction(with: .parameters(n: 0)) { _ in + b.doReturn(b.buildObjectLiteral { _ in }) + } + b.setProperty("x", of: o, to: f) + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + const v0 = {}; + v0.x = () => ({}); + + """ + XCTAssertEqual(actual, expected) + } + + func testNestedArrowFunctionIndentation() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + b.buildIf(b.loadBool(true)) { + let o = b.buildObjectLiteral { _ in } + let f = b.buildArrowFunction(with: .parameters(n: 0)) { _ in + let v = b.createNamedVariable("a", declarationMode: .const, initialValue: b.loadInt(1)) + b.doReturn(b.binary(v, v, with: .Add)) + } + b.setProperty("x", of: o, to: f) + } + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + if (true) { + const v1 = {}; + v1.x = () => { + const a = 1; + return a + a; + }; + } + + """ + XCTAssertEqual(actual, expected) + } + + func testConciseArrowFunctionStartingWithObjectLiteral() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + + let f = b.buildArrowFunction(with: .parameters(n: 0)) { _ in + let o = b.buildObjectLiteral { _ in } + let i = b.loadInt(1) + b.doReturn(b.binary(o, i, with: .Add)) + } + b.callFunction(f) + + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + const t0 = () => ({} + 1); + t0(); + + """ + XCTAssertEqual(actual, expected) + } } From c88fbb3914796aedab816acf1475435883749e4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Gro=C3=9F?= Date: Wed, 3 Dec 2025 13:33:54 +0100 Subject: [PATCH 26/89] Add BytecodeFuzzer ProgramTemplate for the V8Sandbox profile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a mini-fuzzer for the new BytecodeVerifier in V8. It uses %GetBytecode to obtain a JS representation of the BytecodeArray of an existing function, mutates it, then installs it back on the function using %InstallBytecode and finally executes the function. As the verifier only ensures that the bytecode does not cause a sandbox breakout (not general memory corruption), the mini-fuzzer is also specific to the V8Sandbox fuzzing profile. Bug: 461681036 Change-Id: Iac64f3c9532f47455c57cf4251197771b0663612 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8814316 Commit-Queue: Samuel Groß Reviewed-by: Matthias Liedtke --- .../Profiles/V8SandboxProfile.swift | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift b/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift index 3177c273e..1df53793d 100644 --- a/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift @@ -51,6 +51,50 @@ fileprivate struct SandboxFuzzingPostProcessor: FuzzingPostProcessor { } } +let BytecodeFuzzer = ProgramTemplate("BytecodeFuzzer") { b in + b.buildPrefix() + + // Generate some random code to produce some values + b.build(n: 10) + + // Create a random function + let f = b.buildPlainFunction(with: b.randomParameters()) { args in + b.build(n: 25) + } + + // Invoke the function once to trigger bytecode compilation + b.callFunction(f, withArgs: b.randomArguments(forCalling: f)) + + // Get the Bytecode object + let bytecodeObj = b.eval("%GetBytecode(%@)", with: [f], hasOutput: true)! + + // Wrap the bytecode in a Uint8Array + let bytecode = b.getProperty("bytecode", of: bytecodeObj) + let Uint8Array = b.createNamedVariable(forBuiltin: "Uint8Array") + let u8 = b.construct(Uint8Array, withArgs: [bytecode]) + + // Mutate the bytecode + let numMutations = Int.random(in: 1...3) + for _ in 0..([ + (BytecodeFuzzer, 2) ]), disabledCodeGenerators: [], From b716b2f70f559cfa1ea5b7121d79919828d61998 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Thu, 4 Dec 2025 15:20:59 +0100 Subject: [PATCH 27/89] Enable a shard mode for transpile_tests.py This enables calling the script with the arguments --num-shards and --shard-index. The former defines on how many shards (bots) the overall task gets distributed, the latter the index n to deterministically determined the sub-task for the n'th shard. The test order is deterministic and we assume that this script is called from different shards with the same test archive. The sub task is then evenly divided with a simple modulo algorithm. Bug: 442444727 Change-Id: I32803d2bae14f9387e445b627363f4de7ac7efe4 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8817538 Reviewed-by: Matthias Liedtke Commit-Queue: Michael Achenbach --- Tools/transpile_tests/test_transpile_tests.py | 104 +++++++++++++----- Tools/transpile_tests/transpile_tests.py | 26 ++++- 2 files changed, 101 insertions(+), 29 deletions(-) diff --git a/Tools/transpile_tests/test_transpile_tests.py b/Tools/transpile_tests/test_transpile_tests.py index cc776ce27..a772cd9b6 100644 --- a/Tools/transpile_tests/test_transpile_tests.py +++ b/Tools/transpile_tests/test_transpile_tests.py @@ -29,39 +29,41 @@ TEST_DATA = Path(__file__).parent / 'testdata' -class TestTranspileTests(fake_filesystem_unittest.TestCase): - @fake_filesystem_unittest.patchfs(allow_root_user=True) - def test_full_run(self, fs): - base_dir = TEST_DATA / 'transpile_full_run' / 'v8' - fs.create_dir('/output') - fs.add_real_directory(base_dir) +# Mock out the FuzzIL tool, but mimick writing the desired output file. +# Simulate one compilation failure for the test with "fail" in its name. +Process = namedtuple('Process', 'returncode stdout') +def fake_transpile(input_file, output_file): + if 'fail' in str(output_file): + return Process(1, 'Failed!'.encode('utf-8')) + with open(output_file, 'w') as f: + f.write('') + return Process(0, b'') - Process = namedtuple('Process', 'returncode stdout') - # Mock out the FuzzIL tool, but mimick writing the desired output file. - # Simulate one compilation failure for the test with "fail" in its name. - def fake_transpile(input_file, output_file): - if 'fail' in str(output_file): - return Process(1, 'Failed!'.encode('utf-8')) - with open(output_file, 'w') as f: - f.write('') - return Process(0, b'') +# Replace the multiprocessing Pool with a fake that doesn't mess with the +# fake file system. +class FakePool: + def __init__(self, _): + pass - # Replace the multiprocessing Pool with a fake that doesn't mess with the - # fake file system. - class FakePool: - def __init__(self, _): - pass + def __enter__(self): + return self - def __enter__(self): - return self + def __exit__(self, *_): + pass - def __exit__(self, *_): - pass + def imap_unordered(self, *args): + return map(*args) - def imap_unordered(self, *args): - return map(*args) + +class TestTranspileTests(fake_filesystem_unittest.TestCase): + + @fake_filesystem_unittest.patchfs(allow_root_user=True) + def test_full_run(self, fs): + base_dir = TEST_DATA / 'transpile_full_run' / 'v8' + fs.create_dir('/output') + fs.add_real_directory(base_dir) f = io.StringIO() with contextlib.redirect_stdout(f): @@ -107,5 +109,55 @@ def imap_unordered(self, *args): } self.assertEqual(expected_results, actual_results) + @fake_filesystem_unittest.patchfs(allow_root_user=True) + def test_shard_run(self, fs): + base_dir = TEST_DATA / 'transpile_full_run' / 'v8' + fs.create_dir('/output') + fs.add_real_directory(base_dir) + + f = io.StringIO() + with contextlib.redirect_stdout(f): + with patch( + 'transpile_tests.run_transpile_tool', fake_transpile): + with patch( + 'multiprocessing.Pool', FakePool): + transpile_tests.main([ + '--config', 'test262', + '--base-dir', str(base_dir), + '--output-dir', '/output', + '--json-output', '/output.json', + '--num-shards', '2', + '--shard-index', '1', + ]) + + # Verify the output. + self.assertEqual( + 'Successfully compiled 50.00% (1 of 2) test cases.', + f.getvalue().strip()) + + # Verify the written output files. + expected_files = [ + '/output/test/test262/data/test/folder1/subfolder1/Test2.js', + ] + self.assertEqual( + expected_files, glob.glob('/output/**/*.*', recursive=True)) + + # Verify the results written to the json output file. + with open('/output.json') as f: + actual_results = json.load(f) + + expected_results = { + 'num_tests': 2, + 'num_successes': 1, + 'percent_successes': 50.0, + 'failures': [ + { + 'output': 'Failed!', + 'path': 'test/test262/data/test/folder2/Test4_fail.js', + }, + ], + } + self.assertEqual(expected_results, actual_results) + if __name__ == '__main__': unittest.main() diff --git a/Tools/transpile_tests/transpile_tests.py b/Tools/transpile_tests/transpile_tests.py index 8deccb1a9..e7fee5f9e 100644 --- a/Tools/transpile_tests/transpile_tests.py +++ b/Tools/transpile_tests/transpile_tests.py @@ -122,6 +122,17 @@ def verbose_print(options, text): if options.verbose: print(text) +def supports_index_on_shard(options, index): + """If the task is distributed over multiple shards (bots), this returns if + a particular deterministic test index is part of the current run. + + The test list must be equal and have a deterministic order across shards. + + With the default options, this function returns always True, e.g.: + index % 1 == 0. + """ + return index % options.num_shards == options.shard_index + def transpile_suite(options, base_dir, output_dir): """Transpile all tests from one suite configuration in parallel.""" @@ -132,9 +143,10 @@ def transpile_suite(options, base_dir, output_dir): # Prepare inputs as a generator over tuples of input/output path. verbose_print(options, f'Listing tests in {test_input_dir}') def test_input_gen(): - for abspath in list_test_filenames( - test_input_dir, metadata_parser.is_supported): - yield (abspath, output_dir / abspath.relative_to(base_dir)) + for index, abspath in enumerate(list_test_filenames( + test_input_dir, metadata_parser.is_supported)): + if supports_index_on_shard(options, index): + yield (abspath, output_dir / abspath.relative_to(base_dir)) # Iterate over all tests in parallel and collect stats. num_tests = 0 @@ -185,6 +197,13 @@ def parse_args(args): '--json-output', help='Optional absolute path to a json file, ' 'where this script will write its stats to.') + parser.add_argument( + '--num-shards', type=int, default=1, choices=range(1, 9), + help='Overall number of shards to split this task into.') + parser.add_argument( + '--shard-index', type=int, default=0, choices=range(0, 8), + help='Index of the current shard for doing a part of the ' + 'overall task.') parser.add_argument( '-v', '--verbose', default=False, action='store_true', help='Print more verbose output.') @@ -193,6 +212,7 @@ def parse_args(args): def main(args): options = parse_args(args) + assert options.shard_index < options.num_shards base_dir = Path(options.base_dir) output_dir = Path(options.output_dir) results = transpile_suite(options, base_dir, output_dir) From e1bb49257a26a50b7015f42112bf45c7303b01d1 Mon Sep 17 00:00:00 2001 From: Dominik Klemba Date: Fri, 5 Dec 2025 02:44:27 -0800 Subject: [PATCH 28/89] Revert "Throw exception in TryCatchFinally blocks (with certain probability)." This reverts commit 8a542afbbf2c6b6dd5f62fdcf8361ec7f6979110. Reason for revert: V8/d8 is not seeded, therefore crashes are not reproducible (and the code is unstable). Original change's description: > Throw exception in TryCatchFinally blocks (with certain probability). > > Bug: 455512155,455513417 > Change-Id: I52dc1b9d27d02ee1e5d905eca3705d9a9c4a6661 > Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8796096 > Commit-Queue: Pawel Krawczyk > Reviewed-by: Dominik Klemba Bug: 455512155,455513417 Change-Id: I17514fcc50b60232faccd0a7b418fad0b187174d Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8821316 Bot-Commit: Rubber Stamper Commit-Queue: Dominik Klemba Reviewed-by: Michael Achenbach Auto-Submit: Dominik Klemba Commit-Queue: Michael Achenbach --- .../CodeGen/CodeGeneratorWeights.swift | 1 - Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 32 ++----------------- 2 files changed, 2 insertions(+), 31 deletions(-) diff --git a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift index c60c853ce..1dff70aed 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift @@ -186,7 +186,6 @@ public let codeGeneratorWeights = [ "TryCatchGenerator": 5, "TryFinallyGenerator": 5, "ThrowGenerator": 1, - "NondeterministiclyThrowGenerator": 1, "BlockStatementGenerator": 1, // Special generators diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 002b46b01..0c5442f4a 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -2492,8 +2492,6 @@ public let CodeGenerators: [CodeGenerator] = [ inContext: .single(.javascript), provides: [.javascript] ) { b in - if (probability(0.1)) { throwRandomJSVariable(b) } - else if (probability(0.3)) { nondeterministiclyThrowRandomJSVariable(b, withProbability: 0.5)} b.emit(BeginCatch()) }, GeneratorStub( @@ -2526,8 +2524,6 @@ public let CodeGenerators: [CodeGenerator] = [ inContext: .single(.javascript), provides: [.javascript] ) { b in - if (probability(0.1)) { throwRandomJSVariable(b) } - else if (probability(0.3)) { nondeterministiclyThrowRandomJSVariable(b, withProbability: 0.5)} b.emit(BeginCatch()) }, GeneratorStub( @@ -2564,11 +2560,8 @@ public let CodeGenerators: [CodeGenerator] = [ ]), CodeGenerator("ThrowGenerator") { b in - throwRandomJSVariable(b) - }, - - CodeGenerator("NondeterministiclyThrowGenerator") { b in - nondeterministiclyThrowRandomJSVariable(b, withProbability: 0.3) + let v = b.randomJsVariable() + b.throwException(v) }, // @@ -3025,24 +3018,3 @@ public let CodeGenerators: [CodeGenerator] = [ }, catchBody: { _ in }) }, ] - -private func nondeterministiclyThrowRandomJSVariable(_ b: ProgramBuilder, withProbability probability: Double) { - assert(probability >= 0 && probability <= 1) - - let threshold = b.loadFloat(probability) - let Math = b.createNamedVariable(forBuiltin: "Math") - let randomNumber = b.callMethod("random", on: Math, withArgs: []) - // Let's not influence other generators with Math & randomly generated number. - b.hide(Math) - b.hide(randomNumber) - - let condition = b.compare(threshold, with: randomNumber, using: Comparator.greaterThan) - b.buildIf(condition) { - throwRandomJSVariable(b) - } -} - -private func throwRandomJSVariable(_ b: ProgramBuilder) { - let v = b.randomJsVariable() - b.throwException(v) -} From 9c370c32b942af893a51ed0e06dcf1bf6d41a7d4 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Fri, 5 Dec 2025 14:47:37 +0100 Subject: [PATCH 29/89] Find the FuzzILTool relative to the transpiler script This makes it possible to call the script from some nested work dir. Bug: 442444727 Change-Id: I5f6f4313b652cb09e4d168785e78a2334495ccd9 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8821322 Auto-Submit: Michael Achenbach Commit-Queue: Michael Achenbach Reviewed-by: Matthias Liedtke --- Tools/transpile_tests/transpile_tests.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tools/transpile_tests/transpile_tests.py b/Tools/transpile_tests/transpile_tests.py index e7fee5f9e..ef82f11f2 100644 --- a/Tools/transpile_tests/transpile_tests.py +++ b/Tools/transpile_tests/transpile_tests.py @@ -30,6 +30,8 @@ from pathlib import Path +BASE_DIR = Path(__file__).parent.parent.parent + class DefaultMetaDataParser: """Class instantiated once per test configuration/suite, providing a @@ -96,7 +98,7 @@ def list_test_filenames(test_root, is_supported_fun): def run_transpile_tool(input_file, output_file): cmd = [ - '.build/debug/FuzzILTool', + BASE_DIR / '.build/debug/FuzzILTool', '--compile', input_file, f'--outputPathJS={output_file}', From 3b241b0b0ea992ce6e99433a44b18bdfdaa92ba1 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Mon, 24 Nov 2025 14:53:56 +0100 Subject: [PATCH 30/89] [wasm] Change loops to use wasm-gc signatures This allows using parameter types which are indexed types (things like `(ref null 1)`). Implementation: - Each WasmLoop instruction now takes its signature as the first input. - The static signature types are removed from the begin and endLoop. - The loop code generator emits an "ad hoc" signature in order to emit signatures for which we already have corresponding inputs available. Bug: 445356784 Change-Id: Ic58ab7d6a092a39de77c974142dd7f976786e8e1 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8792956 Reviewed-by: Pawel Krawczyk Commit-Queue: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 44 ++++++++--- .../Fuzzilli/CodeGen/WasmCodeGenerators.swift | 71 +++++++++++------ Sources/Fuzzilli/FuzzIL/Instruction.swift | 11 +-- Sources/Fuzzilli/FuzzIL/JSTyper.swift | 35 +++++++-- Sources/Fuzzilli/FuzzIL/TypeSystem.swift | 9 +++ Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 21 +++-- Sources/Fuzzilli/Lifting/FuzzILLifter.swift | 4 +- Sources/Fuzzilli/Lifting/WasmLifter.swift | 10 +-- .../Fuzzilli/Minimization/BlockReducer.swift | 19 +++-- Sources/Fuzzilli/Mutators/InputMutator.swift | 5 +- Sources/Fuzzilli/Protobuf/operations.pb.swift | 31 +++----- Sources/Fuzzilli/Protobuf/operations.proto | 5 +- Tests/FuzzilliTests/WasmTests.swift | 78 ++++++++++++++++--- 13 files changed, 241 insertions(+), 102 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 6bc1dbd6b..c8de47c92 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -1345,6 +1345,10 @@ public class ProgramBuilder { jsTyper.setType(of: variable, to: variableType) } + public func getWasmTypeDef(for type: ILType) -> Variable { + jsTyper.getWasmTypeDef(for: type) + } + /// This helper function converts parameter types into argument types, for example by "unrolling" rest parameters and handling optional parameters. private static func prepareArgumentTypes(forParameters params: ParameterList) -> [ILType] { var argumentTypes = [ILType]() @@ -3998,18 +4002,25 @@ public class ProgramBuilder { return Array(b.emit(WasmEndIf(outputTypes: signature.outputTypes), withInputs: falseResults, types: signature.outputTypes).outputs) } - // The first output of this block is a label variable, which is just there to explicitly mark control-flow and allow branches. - public func wasmBuildLoop(with signature: WasmSignature, body: (Variable, [Variable]) -> Void) { - let instr = b.emit(WasmBeginLoop(with: signature)) - body(instr.innerOutput(0), Array(instr.innerOutputs(1...))) - b.emit(WasmEndLoop()) + @discardableResult + public func wasmBuildLoop(with signature: WasmSignature, args: [Variable], body: (Variable, [Variable]) -> [Variable]) -> [Variable] { + let signatureDef = b.wasmDefineAdHocSignatureType(signature: signature) + return wasmBuildLoopImpl(signature: signature, signatureDef: signatureDef, args: args, body: body) } @discardableResult - public func wasmBuildLoop(with signature: WasmSignature, args: [Variable], body: (Variable, [Variable]) -> [Variable]) -> [Variable] { - let instr = b.emit(WasmBeginLoop(with: signature), withInputs: args, types: signature.parameterTypes) + public func wasmBuildLoop(with signatureDef: Variable, args: [Variable], body: (Variable, [Variable]) -> [Variable]) -> [Variable] { + let signature = b.type(of: signatureDef).wasmFunctionSignatureDefSignature + return wasmBuildLoopImpl(signature: signature, signatureDef: signatureDef, args: args, body: body) + } + + private func wasmBuildLoopImpl(signature: WasmSignature, signatureDef: Variable, args: [Variable], body: (Variable, [Variable]) -> [Variable]) -> [Variable] { + let instr = b.emit(WasmBeginLoop(with: signature), withInputs: [signatureDef] + args, + types: [.wasmTypeDef()] + signature.parameterTypes) let fallthroughResults = body(instr.innerOutput(0), Array(instr.innerOutputs(1...))) - return Array(b.emit(WasmEndLoop(outputTypes: signature.outputTypes), withInputs: fallthroughResults, types: signature.outputTypes).outputs) + return Array(b.emit(WasmEndLoop(outputCount: signature.outputTypes.count), + withInputs: [signatureDef] + fallthroughResults, + types: [.wasmTypeDef()] + signature.outputTypes).outputs) } @discardableResult @@ -4637,8 +4648,20 @@ public class ProgramBuilder { /// Like wasmDefineSignatureType but instead of within a type group this defines a signature /// type directly inside a wasm function. + /// This takes a signature with resolved index types for ease-of-use (meaning it accepts full + /// index reference types directly inside the signature). @discardableResult - func wasmDefineAdHocSignatureType(signature: WasmSignature, indexTypes: [Variable]) -> Variable { + func wasmDefineAdHocSignatureType(signature: WasmSignature) -> Variable { + let indexTypes = (signature.parameterTypes + signature.outputTypes) + .filter {$0.Is(.anyIndexRef)} + .map(getWasmTypeDef) + let cleanIndexTypes = {(type: ILType) -> ILType in + type.Is(.anyIndexRef) + ? .wasmRef(.Index(), nullability: type.wasmReferenceType!.nullability) + : type + } + let signature = signature.parameterTypes.map(cleanIndexTypes) + => signature.outputTypes.map(cleanIndexTypes) return emit(WasmDefineAdHocSignatureType(signature: signature), withInputs: indexTypes).output } @@ -4854,8 +4877,6 @@ public class ProgramBuilder { activeWasmModule!.blockSignatures.push(op.signature) case .wasmBeginBlock(let op): activeWasmModule!.blockSignatures.push(op.signature) - case .wasmBeginLoop(let op): - activeWasmModule!.blockSignatures.push(op.signature) case .wasmBeginTry(let op): activeWasmModule!.blockSignatures.push(op.signature) case .wasmBeginTryDelegate(let op): @@ -4863,7 +4884,6 @@ public class ProgramBuilder { case .wasmBeginTryTable(let op): activeWasmModule!.blockSignatures.push(op.signature) case .wasmEndIf(_), - .wasmEndLoop(_), .wasmEndTry(_), .wasmEndTryDelegate(_), .wasmEndTryTable(_), diff --git a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift index 697f84d4a..6a37e6717 100644 --- a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift @@ -1261,7 +1261,9 @@ public let WasmCodeGenerators: [CodeGenerator] = [ let function = b.currentWasmModule.currentWasmFunction let loopCtr = function.consti32(10) b.runtimeData.push("loopCounter", loopCtr) - b.emit(WasmBeginLoop(with: [] => [])) + let signature = b.wasmDefineAdHocSignatureType(signature: [] => []) + b.runtimeData.push("loopSignature", signature) + b.emit(WasmBeginLoop(with: [] => []), withInputs: [signature]) // Increase loop counter. let result = function.wasmi32BinOp( loopCtr, function.consti32(1), binOpKind: .Sub) @@ -1275,38 +1277,59 @@ public let WasmCodeGenerators: [CodeGenerator] = [ let loopCtr = b.runtimeData.pop("loopCounter") // Backedge of loop, we continue if it is not equal to zero. let isNotZero = function.wasmi32CompareOp(loopCtr, function.consti32(0), using: .Ne) - b.emit(WasmEndLoop(outputTypes: [])) + b.emit(WasmEndLoop(outputCount: 0), withInputs: [b.runtimeData.pop("loopSignature")]) }, ]), - 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 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) + CodeGenerator("WasmLoopWithSignatureGenerator", [ + GeneratorStub("WasmBeginLoopWithSignatureGenerator", inContext: .single(.wasmFunction), + provides: [.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 args = [function.consti32(0)] + randomArgs + let parameters = args.map(b.type) + // TODO(mliedtke): Also allow index types in the output types. + let outputTypes = b.randomWasmBlockOutputTypes(upTo: 5) + // Calculate index types for the dynamically created signature. + let indexTypes = (parameters + outputTypes) + .filter {$0.Is(.anyIndexRef)} + .map(b.getWasmTypeDef) + + // TODO(mliedtke): We need to cleanup the index types here! + let signature = b.wasmDefineAdHocSignatureType(signature: parameters => outputTypes) + let loopBegin = b.emit(WasmBeginLoop(parameterCount: parameters.count), + withInputs: [signature] + args) + let loopCounter = loopBegin.innerOutput(1) + assert(b.type(of: loopCounter).Is(.wasmi32)) + b.runtimeData.push("loopCounter", loopCounter) + b.runtimeData.push("loopSignature", signature) + b.runtimeData.push("loopLabel", loopBegin.innerOutput(0)) + }, + GeneratorStub("WasmEndLoopWithSignatureGenerator", inContext: .single(.wasmFunction), + provides: [.wasmFunction]) { b in + let signature = b.runtimeData.pop("loopSignature") + let function = b.currentWasmModule.currentWasmFunction - function.wasmBuildLoop(with: parameters => outputTypes, args: args) { - label, loopArgs in - b.buildRecursive(n: defaultCodeGenerationAmount) + // Note that due to the do-while style implementation, the actual iteration count is at + // least 1. + let iterationCount = Int32.random(in: 0...16) let loopCtr = function.wasmi32BinOp( - args[0], function.consti32(1), binOpKind: .Add) + b.runtimeData.pop("loopCounter"), 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)! } + let wasmSignature = b.type(of: signature).wasmFunctionSignatureDefSignature + let backedgeArgs = [loopCtr] + wasmSignature.parameterTypes.dropFirst() + .map {b.randomVariable(ofType: $0)!} function.wasmBranchIf( - condition, to: label, args: backedgeArgs, + condition, to: b.runtimeData.pop("loopLabel"), args: backedgeArgs, hint: b.randomWasmBranchHint()) - return outputTypes.map(function.findOrGenerateWasmVar) - } - }, + let outputs = wasmSignature.outputTypes.map(function.findOrGenerateWasmVar) + b.emit(WasmEndLoop(outputCount: outputs.count), withInputs: [signature] + outputs) + }, + ]), // TODO Turn this into a multi-part Generator CodeGenerator("WasmLegacyTryCatchGenerator", inContext: .single(.wasmFunction)) { diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index 6486e4603..86f98e4a7 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -1395,12 +1395,11 @@ extension Instruction: ProtobufConvertible { } case .wasmBeginLoop(let op): $0.wasmBeginLoop = Fuzzilli_Protobuf_WasmBeginLoop.with { - $0.parameterTypes = op.signature.parameterTypes.map(ILTypeToWasmTypeEnum) - $0.outputTypes = op.signature.outputTypes.map(ILTypeToWasmTypeEnum) + $0.parameterCount = Int32(op.parameterCount) } case .wasmEndLoop(let op): $0.wasmEndLoop = Fuzzilli_Protobuf_WasmEndLoop.with { - $0.outputTypes = op.outputTypes.map(ILTypeToWasmTypeEnum) + $0.outputCount = Int32(op.numOutputs) } case .wasmBeginTryTable(let op): $0.wasmBeginTryTable = Fuzzilli_Protobuf_WasmBeginTryTable.with { @@ -2456,11 +2455,9 @@ extension Instruction: ProtobufConvertible { case .wasmEndBlock(let p): op = WasmEndBlock(outputTypes: p.outputTypes.map(WasmTypeEnumToILType)) case .wasmBeginLoop(let p): - let parameters = p.parameterTypes.map(WasmTypeEnumToILType) - let outputs = p.outputTypes.map(WasmTypeEnumToILType) - op = WasmBeginLoop(with: parameters => outputs) + op = WasmBeginLoop(parameterCount: Int(p.parameterCount)) case .wasmEndLoop(let p): - op = WasmEndLoop(outputTypes: p.outputTypes.map(WasmTypeEnumToILType)) + op = WasmEndLoop(outputCount: Int(p.outputCount)) case .wasmBeginTryTable(let p): let parameters = p.parameterTypes.map(WasmTypeEnumToILType) let outputs = p.outputTypes.map(WasmTypeEnumToILType) diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index 054f242a2..ae3e96826 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -38,6 +38,8 @@ public struct JSTyper: Analyzer { private var typeGroupDependencies: [Int:Set] = [:] private var selfReferences: [Variable: [(inout JSTyper, Variable?) -> ()]] = [:] private var isWithinTypeGroup = false + // Tracks the type definition variable for each Wasm index type. + private var wasmTypeDefMap = [WasmTypeDescription:Variable]() // Tracks the active function definitions and contains the instruction that started the function. private var activeFunctionDefinitions = Stack() @@ -401,6 +403,7 @@ public struct JSTyper: Analyzer { state.reset() signatures.removeAll() typeGroups.removeAll() + wasmTypeDefMap.removeAll() defUseAnalyzer = DefUseAnalyzer() isWithinTypeGroup = false dynamicObjectGroupManager = ObjectGroupManager() @@ -432,6 +435,20 @@ public struct JSTyper: Analyzer { } } + mutating func registerWasmTypeDef(_ def: Variable) { + let desc = type(of: def).wasmTypeDefinition!.description! + assert(wasmTypeDefMap[desc] == nil, "duplicate type description") + wasmTypeDefMap[desc] = def + } + + func getWasmTypeDef(for type: ILType) -> Variable { + assert(type.isWasmReferenceType) + guard case .Index(let desc) = type.wasmReferenceType!.kind else { + fatalError("\(type) is not an index type") + } + return wasmTypeDefMap[desc.get()!]! + } + mutating func addSignatureType(def: Variable, signature: WasmSignature, inputs: ArraySlice) { assert(isWithinTypeGroup) var inputs = inputs.makeIterator() @@ -445,7 +462,7 @@ public struct JSTyper: Analyzer { if paramType.requiredInputCount() == 0 { return paramType } - assert(paramType.Is(.wasmRef(.Index(), nullability: true))) + assert(paramType.Is(.anyIndexRef)) let typeDef = inputs.next()! let elementDesc = type(of: typeDef).wasmTypeDefinition!.description! if elementDesc == .selfReference { @@ -809,16 +826,18 @@ public struct JSTyper: Analyzer { wasmTypeBeginBlock(instr, op.signature) case .wasmEndIf(let op): wasmTypeEndBlock(instr, op.outputTypes) - case .wasmBeginLoop(let op): + case .wasmBeginLoop(_): // Note that different to all other blocks the loop's label parameters are the input types // of the block, not the result types (because a branch to a loop label jumps to the // beginning of the loop block instead of the end.) - setType(of: instr.innerOutputs.first!, to: .label(op.signature.parameterTypes)) - for (innerOutput, paramType) in zip(instr.innerOutputs.dropFirst(), op.signature.parameterTypes) { + let signature = type(of: instr.input(0)).wasmFunctionSignatureDefSignature + setType(of: instr.innerOutputs.first!, to: .label(signature.parameterTypes)) + for (innerOutput, paramType) in zip(instr.innerOutputs.dropFirst(), signature.parameterTypes) { setType(of: innerOutput, to: paramType) } - case .wasmEndLoop(let op): - wasmTypeEndBlock(instr, op.outputTypes) + case .wasmEndLoop(_): + let signature = type(of: instr.input(0)).wasmFunctionSignatureDefSignature + wasmTypeEndBlock(instr, signature.outputTypes) case .wasmBeginTryTable(let op): wasmTypeBeginBlock(instr, op.signature) instr.inputs.forEach { input in @@ -903,6 +922,7 @@ public struct JSTyper: Analyzer { startTypeGroup() addSignatureType(def: instr.output, signature: op.signature, inputs: instr.inputs) finishTypeGroup() + registerWasmTypeDef(instr.output) default: if instr.numInnerOutputs + instr.numOutputs != 0 { fatalError("Missing typing of outputs for \(instr.op.opcode)") @@ -1886,6 +1906,9 @@ public struct JSTyper: Analyzer { set(output, state.type(of: input)) } finishTypeGroup() + for output in instr.outputs { + registerWasmTypeDef(output) + } case .wasmDefineSignatureType(let op): addSignatureType(def: instr.output, signature: op.signature, inputs: instr.inputs) diff --git a/Sources/Fuzzilli/FuzzIL/TypeSystem.swift b/Sources/Fuzzilli/FuzzIL/TypeSystem.swift index 729550e9c..a0aa62eed 100644 --- a/Sources/Fuzzilli/FuzzIL/TypeSystem.swift +++ b/Sources/Fuzzilli/FuzzIL/TypeSystem.swift @@ -291,6 +291,7 @@ public struct ILType: Hashable { public static let wasmNumericalPrimitive = .wasmi32 | .wasmi64 | .wasmf32 | .wasmf64 public static let anyNonNullableIndexRef = wasmRef(.Index(), nullability: false) + public static let anyIndexRef = wasmRef(.Index(), nullability: true) // // Type testing @@ -596,6 +597,14 @@ public struct ILType: Hashable { return (wasmType as! WasmFunctionDefinition).signature } + public var wasmFunctionSignatureDefSignature: WasmSignature { + let desc = (wasmType as? WasmTypeDefinition)?.description + guard let desc = desc as? WasmSignatureTypeDescription else { + fatalError("\(self) is not a Wasm signature type defintion") + } + return desc.signature + } + public var isWasmDefaultable: Bool { let isPacked = Is(.wasmPackedI8) || Is(.wasmPackedI16) return isPacked diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index 452dd9cc5..307ff0346 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -1315,21 +1315,26 @@ final class WasmEndIf: WasmOperation { final class WasmBeginLoop: WasmOperation { override var opcode: Opcode { .wasmBeginLoop(self) } - let signature: WasmSignature - init(with signature: WasmSignature) { - self.signature = signature - super.init(numInputs: signature.parameterTypes.count, numInnerOutputs: 1 + signature.parameterTypes.count, attributes: [.isBlockStart, .propagatesSurroundingContext], requiredContext: [.wasmFunction]) + init(parameterCount: Int) { + // Inputs: the signature + the inputs to the loop. + // inner outputs: The loop label + the arguments of the loop. + super.init(numInputs: 1 + parameterCount, numInnerOutputs: 1 + parameterCount, attributes: [.isBlockStart, .propagatesSurroundingContext], requiredContext: [.wasmFunction]) + } + + convenience init(with signature: WasmSignature) { + self.init(parameterCount: signature.parameterTypes.count) } + + var parameterCount: Int {numInputs - 1} } final class WasmEndLoop: WasmOperation { override var opcode: Opcode { .wasmEndLoop(self) } - let outputTypes: [ILType] - init(outputTypes: [ILType] = []) { - self.outputTypes = outputTypes - super.init(numInputs: outputTypes.count, numOutputs: outputTypes.count, attributes: [.isBlockEnd, .resumesSurroundingContext], requiredContext: [.wasmFunction]) + init(outputCount: Int) { + // Inputs: the signature + the outputs of the loop. + super.init(numInputs: 1 + outputCount, numOutputs: outputCount, attributes: [.isBlockEnd, .resumesSurroundingContext], requiredContext: [.wasmFunction]) } } diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift index 33d47fa16..f7985fe99 100644 --- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift +++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift @@ -1097,9 +1097,9 @@ public class FuzzILLifter: Lifter { w.emit("WasmEndBlock \(inputs)") } - case .wasmBeginLoop(let op): + case .wasmBeginLoop(_): let inputs = instr.inputs.map(lift).joined(separator: ", ") - w.emit("WasmBeginLoop (\(op.signature)) [\(inputs)] -> L:\(instr.innerOutput(0)) [\(liftCallArguments(instr.innerOutputs(1...)))]") + w.emit("WasmBeginLoop [\(inputs)] -> L:\(instr.innerOutput(0)) [\(liftCallArguments(instr.innerOutputs(1...)))]") w.increaseIndentionLevel() case .wasmEndLoop(let op): diff --git a/Sources/Fuzzilli/Lifting/WasmLifter.swift b/Sources/Fuzzilli/Lifting/WasmLifter.swift index 4944ac161..bcfb03a07 100644 --- a/Sources/Fuzzilli/Lifting/WasmLifter.swift +++ b/Sources/Fuzzilli/Lifting/WasmLifter.swift @@ -1268,8 +1268,7 @@ public class WasmLifter { self.currentFunction!.labelBranchDepthMapping[instr.innerOutput(0)] = self.currentFunction!.variableAnalyzer.wasmBranchDepth // Needs typer analysis return true - case .wasmBeginLoop(let op): - registerSignature(op.signature) + case .wasmBeginLoop(_): self.currentFunction!.labelBranchDepthMapping[instr.innerOutput(0)] = self.currentFunction!.variableAnalyzer.wasmBranchDepth // Needs typer analysis return true @@ -1455,7 +1454,7 @@ public class WasmLifter { for (idx, input) in instr.inputs.enumerated() { let inputType = typer.type(of: input) - if inputType.Is(.wasmTypeDef()) || inputType.Is(.wasmRef(.Index(), nullability: true)) { + if inputType.Is(.wasmTypeDef()) || inputType.Is(.anyIndexRef) { let typeDesc = typer.getTypeDescription(of: input) guard typeDesc.typeGroupIndex != -1 else { throw CompileError.fatalError("Missing type group index for \(input)") @@ -1912,8 +1911,9 @@ public class WasmLifter { // A Block can "produce" (push) an item on the value stack, just like a function. Similarly, a block can also have parameters. // Ref: https://webassembly.github.io/spec/core/binary/instructions.html#binary-blocktype return Data([0x02] + Leb128.unsignedEncode(getSignatureIndexStrict(op.signature))) - case .wasmBeginLoop(let op): - return Data([0x03] + Leb128.unsignedEncode(getSignatureIndexStrict(op.signature))) + case .wasmBeginLoop(_): + let signatureDesc = typer.getTypeDescription(of: wasmInstruction.input(0)) + return Data([0x03] + Leb128.unsignedEncode(typeDescToIndex[signatureDesc]!)) case .wasmBeginTryTable(let op): var inputIndex = op.signature.parameterTypes.count let catchTable: Data = try op.catches.map { diff --git a/Sources/Fuzzilli/Minimization/BlockReducer.swift b/Sources/Fuzzilli/Minimization/BlockReducer.swift index 5a1efac08..3fd0a0ad0 100644 --- a/Sources/Fuzzilli/Minimization/BlockReducer.swift +++ b/Sources/Fuzzilli/Minimization/BlockReducer.swift @@ -95,13 +95,20 @@ struct BlockReducer: Reducer { .wasmBeginTryTable: reduceGenericBlockGroup(group, with: helper) - case .wasmBeginBlock, - .wasmBeginLoop: + case .wasmBeginBlock: + // TODO(mliedtke): Deduplicate with .wasmBeginLoop once blocks also use wasm-gc + // signatures. let rewroteProgram = reduceGenericWasmBlockGroup(group, with: helper) if rewroteProgram { return } + case .wasmBeginLoop: + let rewroteProgram = reduceGenericWasmBlockGroup(group, with: helper, usesSignature: true) + if rewroteProgram { + return + } + case .wasmBeginCatchAll, .wasmBeginCatch: // These instructions are handled in the reduceWasmTryCatch. @@ -311,7 +318,7 @@ struct BlockReducer: Reducer { // Reduce a wasm block. In some cases this reduction fully rewrites the program // invalidating pre-computed BlockGroups. If that happens, the function returns true indicating // that following reductions need to rerun the Blockgroups analysis. - private func reduceGenericWasmBlockGroup(_ group: BlockGroup, with helper: MinimizationHelper) -> Bool { + private func reduceGenericWasmBlockGroup(_ group: BlockGroup, with helper: MinimizationHelper, usesSignature: Bool = false) -> Bool { // Try to remove just the block. var candidates = group.blockInstructionIndices if helper.tryNopping(candidates) { @@ -334,9 +341,11 @@ struct BlockReducer: Reducer { let beginInstr = helper.code[group.head] let endInstr = helper.code[group.tail] + let blockInputs = usesSignature ? beginInstr.inputs.dropFirst() : beginInstr.inputs + let endInstrInputs = usesSignature ? endInstr.inputs.dropFirst() : endInstr.inputs var varReplacements = Dictionary( - uniqueKeysWithValues: zip(beginInstr.innerOutputs.dropFirst(), beginInstr.inputs)) - varReplacements.merge(zip(endInstr.outputs, endInstr.inputs.map {varReplacements[$0] ?? $0}), + uniqueKeysWithValues: zip(beginInstr.innerOutputs.dropFirst(), blockInputs)) + varReplacements.merge(zip(endInstr.outputs, endInstrInputs.map {varReplacements[$0] ?? $0}), uniquingKeysWith: {_, _ in fatalError("duplicate variables")}) var newCode = Code() for (i, instr) in helper.code.enumerated() { diff --git a/Sources/Fuzzilli/Mutators/InputMutator.swift b/Sources/Fuzzilli/Mutators/InputMutator.swift index 01398b3ac..7f03a9ffc 100644 --- a/Sources/Fuzzilli/Mutators/InputMutator.swift +++ b/Sources/Fuzzilli/Mutators/InputMutator.swift @@ -66,7 +66,10 @@ public class InputMutator: BaseInstructionMutator { b.context.contains(.wasmFunction) || b.context.contains(.wasmTypeGroup) { let type = b.type(of: inouts[selectedInput]) - replacement = b.randomVariable(ofType: type) + // TODO(mliedtke): For type definitions we need a lot of consistency. E.g. the signature + // flowing into the block begin operation and the block end operation need to be in + // sync. + replacement = type.Is(.wasmTypeDef()) ? inouts[selectedInput] : b.randomVariable(ofType: type) } else { switch self.typeAwareness { case .loose: diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index 2b2248232..883ab8d90 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -5173,9 +5173,7 @@ public struct Fuzzilli_Protobuf_WasmBeginLoop: Sendable { // `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 parameterCount: Int32 = 0 public var unknownFields = SwiftProtobuf.UnknownStorage() @@ -5187,7 +5185,7 @@ public struct Fuzzilli_Protobuf_WasmEndLoop: Sendable { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var outputTypes: [Fuzzilli_Protobuf_WasmILType] = [] + public var outputCount: Int32 = 0 public var unknownFields = SwiftProtobuf.UnknownStorage() @@ -13718,7 +13716,7 @@ extension Fuzzilli_Protobuf_WasmEndBlock: SwiftProtobuf.Message, SwiftProtobuf._ extension Fuzzilli_Protobuf_WasmBeginLoop: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmBeginLoop" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameterTypes\0\u{1}outputTypes\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameterCount\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -13726,26 +13724,21 @@ extension Fuzzilli_Protobuf_WasmBeginLoop: SwiftProtobuf.Message, SwiftProtobuf. // 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) }() + case 1: try { try decoder.decodeSingularInt32Field(value: &self.parameterCount) }() 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) + if self.parameterCount != 0 { + try visitor.visitSingularInt32Field(value: self.parameterCount, fieldNumber: 1) } try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Fuzzilli_Protobuf_WasmBeginLoop, rhs: Fuzzilli_Protobuf_WasmBeginLoop) -> Bool { - if lhs.parameterTypes != rhs.parameterTypes {return false} - if lhs.outputTypes != rhs.outputTypes {return false} + if lhs.parameterCount != rhs.parameterCount {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -13753,7 +13746,7 @@ extension Fuzzilli_Protobuf_WasmBeginLoop: SwiftProtobuf.Message, SwiftProtobuf. extension Fuzzilli_Protobuf_WasmEndLoop: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmEndLoop" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}outputTypes\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}outputCount\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -13761,21 +13754,21 @@ extension Fuzzilli_Protobuf_WasmEndLoop: 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.decodeRepeatedMessageField(value: &self.outputTypes) }() + case 1: try { try decoder.decodeSingularInt32Field(value: &self.outputCount) }() default: break } } } public func traverse(visitor: inout V) throws { - if !self.outputTypes.isEmpty { - try visitor.visitRepeatedMessageField(value: self.outputTypes, fieldNumber: 1) + if self.outputCount != 0 { + try visitor.visitSingularInt32Field(value: self.outputCount, fieldNumber: 1) } try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Fuzzilli_Protobuf_WasmEndLoop, rhs: Fuzzilli_Protobuf_WasmEndLoop) -> Bool { - if lhs.outputTypes != rhs.outputTypes {return false} + if lhs.outputCount != rhs.outputCount {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 8ed578def..d26a6d56b 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -1247,12 +1247,11 @@ message WasmEndBlock { } message WasmBeginLoop { - repeated WasmILType parameterTypes = 1; - repeated WasmILType outputTypes = 2; + int32 parameterCount = 1; } message WasmEndLoop { - repeated WasmILType outputTypes = 1; + int32 outputCount = 1; } enum WasmCatchKind { diff --git a/Tests/FuzzilliTests/WasmTests.swift b/Tests/FuzzilliTests/WasmTests.swift index f7955e212..987599fb4 100644 --- a/Tests/FuzzilliTests/WasmTests.swift +++ b/Tests/FuzzilliTests/WasmTests.swift @@ -324,13 +324,14 @@ class WasmFoundationTests: XCTestCase { wasmModule.addWasmFunction(with: [] => [.wasmi32]) { function, _, _ in let ctr = function.consti32(10) - function.wasmBuildLoop(with: [] => []) { label, args in + function.wasmBuildLoop(with: [] => [], args: []) { label, args in XCTAssert(b.type(of: label).Is(.anyLabel)) let result = function.wasmi32BinOp(ctr, function.consti32(1), binOpKind: .Sub) function.wasmReassign(variable: ctr, to: result) // The backedge, loop if we are not at zero yet. let isNotZero = function.wasmi32CompareOp(ctr, function.consti32(0), using: .Ne) function.wasmBranchIf(isNotZero, to: label) + return [] } return [ctr] } @@ -2684,7 +2685,7 @@ class WasmFoundationTests: XCTestCase { let max = function.consti32(10) let one = function.consti32(1) - function.wasmBuildLoop(with: [] => []) { label, args in + function.wasmBuildLoop(with: [] => [], args: []) { label, args in XCTAssert(b.type(of: label).Is(.anyLabel)) let result = function.wasmi32BinOp(ctr, one, binOpKind: .Add) let varUpdate = function.wasmi64BinOp(variable, function.consti64(2), binOpKind: .Add) @@ -2692,6 +2693,7 @@ class WasmFoundationTests: XCTestCase { function.wasmReassign(variable: variable, to: varUpdate) let comp = function.wasmi32CompareOp(ctr, max, using: .Lt_s) function.wasmBranchIf(comp, to: label) + return [] } // Now combine the result of the break and the loop into one and return it. @@ -4489,17 +4491,35 @@ class WasmGCTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "null\n") } - func testAdHocSignature() throws { + func testLoopWithWasmGCSignature() throws { let runner = try GetJavaScriptExecutorOrSkipTest() let jsProg = buildAndLiftProgram { b in + let typeGroup = b.wasmDefineTypeGroup { + let arrayType = b.wasmDefineArrayType(elementType: .wasmi32, mutability: true) + let signature = b.wasmDefineSignatureType( + signature: [.wasmRef(.Index(), nullability: true)] + => [.wasmRef(.Index(), nullability: true)], + indexTypes: [arrayType, arrayType]) + return [arrayType, signature] + } + let arrayType = typeGroup[0] + let loopSignature = typeGroup[1] + 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. - let signatureType = b.wasmDefineAdHocSignatureType(signature: [.wasmi32] => [.wasmi32], indexTypes: []) - return [function.wasmRefNull(typeDef: signatureType)] + wasmModule.addWasmFunction(with: [] => [.wasmi32]) { function, label, args in + let index = function.consti32(0) + let array = function.wasmArrayNewFixed(arrayType: arrayType, + elements: [function.consti32(1)]) + let arrayResult = function.wasmBuildLoop(with: loopSignature, args: [array]) { loopLabel, args in + let val = function.wasmArrayGet(array: args[0], index: index) + let newVal = function.wasmi32BinOp(val, val, binOpKind: .Add) + function.wasmArraySet(array: args[0], index: index, element: newVal) + let cond = function.wasmi32CompareOp(newVal, function.consti32(50), using: .Lt_s) + function.wasmBranchIf(cond, to: loopLabel, args: [args[0]]) + return args + }[0] + return [function.wasmArrayGet(array: arrayResult, index: index)] } } @@ -4509,7 +4529,45 @@ class WasmGCTests: XCTestCase { b.callFunction(outputFunc, withArgs: [wasmOut]) } - testForOutput(program: jsProg, runner: runner, outputString: "null\n") + testForOutput(program: jsProg, runner: runner, outputString: "64\n") + } + + func testLoopWithAdHocWasmGCSignature() throws { + let runner = try GetJavaScriptExecutorOrSkipTest() + let jsProg = buildAndLiftProgram { b in + + let structType = b.wasmDefineTypeGroup {[ + b.wasmDefineStructType( + fields: [WasmStructTypeDescription.Field(type: .wasmi32, mutability: true)], + indexTypes: []) + ]}[0] + + let module = b.buildWasmModule { wasmModule in + wasmModule.addWasmFunction(with: [] => [.wasmi32]) { function, label, args in + let structVal = function.wasmStructNewDefault(structType: structType) + function.wasmStructSet(theStruct: structVal, fieldIndex: 0, value: function.consti32(1)) + let signature = [b.type(of: structVal)] => [.wasmi32] + // This loop creates a new struct on each iteration just for having different + // values on the loop entry and the loop backedge (`br_if`). + return function.wasmBuildLoop(with: signature, args: [structVal]) { loopLabel, args in + let val = function.wasmStructGet(theStruct: args[0], fieldIndex: 0) + let newVal = function.wasmi32BinOp(val, val, binOpKind: .Add) + let newStruct = function.wasmStructNewDefault(structType: structType) + function.wasmStructSet(theStruct: newStruct, fieldIndex: 0, value: newVal) + let cond = function.wasmi32CompareOp(newVal, function.consti32(50), using: .Lt_s) + function.wasmBranchIf(cond, to: loopLabel, args: [newStruct]) + return [newVal] + } + } + } + + 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: "64\n") } func testSelfReferenceType() throws { From ea68e6435ba035d11b619fcdd07af72bbf65e45e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Fl=C3=BCckiger?= Date: Mon, 8 Dec 2025 15:18:38 +0100 Subject: [PATCH 31/89] Implement support for a number of recent JS language features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * JSON.parse source text access & RawJSON * Iterator helpers * upsert Change-Id: I1dad9b38c1a42ba8cfdb055651db06e0947dd184 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8814317 Reviewed-by: Matthias Liedtke Commit-Queue: Olivier Flückiger Auto-Submit: Olivier Flückiger --- .../Environment/JavaScriptEnvironment.swift | 172 ++++++++++++------ 1 file changed, 116 insertions(+), 56 deletions(-) diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index 9ef54f216..83c2509bb 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -322,6 +322,9 @@ public class JavaScriptEnvironment: ComponentBase { registerObjectGroup(.jsStrings) registerObjectGroup(.jsArrays) registerObjectGroup(.jsArguments) + registerObjectGroup(.jsIterator) + registerObjectGroup(.jsIteratorPrototype) + registerObjectGroup(.jsIteratorConstructor) registerObjectGroup(.jsGenerators) registerObjectGroup(.jsPromises) registerObjectGroup(.jsRegExps) @@ -552,6 +555,7 @@ public class JavaScriptEnvironment: ComponentBase { registerBuiltin("Boolean", ofType: .jsBooleanConstructor) registerBuiltin("Number", ofType: .jsNumberConstructor) registerBuiltin("Symbol", ofType: .jsSymbolConstructor) + registerBuiltin("Iterator", ofType: .jsIteratorConstructor) registerBuiltin("BigInt", ofType: .jsBigIntConstructor) registerBuiltin("RegExp", ofType: .jsRegExpConstructor) for variant in ["Error", "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", "URIError", "SuppressedError"] { @@ -1009,9 +1013,15 @@ public extension ILType { /// Type of a JavaScript array. static let jsArray = ILType.iterable + ILType.object(ofGroup: "Array", withProperties: ["length"], withMethods: ["at", "concat", "copyWithin", "fill", "find", "findIndex", "findLast", "findLastIndex", "pop", "push", "reverse", "shift", "unshift", "slice", "sort", "splice", "includes", "indexOf", "keys", "entries", "forEach", "filter", "map", "every", "some", "reduce", "reduceRight", "toString", "toLocaleString", "toReversed", "toSorted", "toSpliced", "with", "join", "lastIndexOf", "values", "flat", "flatMap"]) - /// Type of a function's arguments object. + /// Type of a JavaScript function's arguments object. static let jsArguments = ILType.iterable + ILType.object(ofGroup: "Arguments", withProperties: ["length", "callee"]) + /// Type of a JavaScript Iterator object. + static let jsIterator = ILType.iterable + ILType.object(ofGroup: "Iterator", withProperties: ["value", "done"], withMethods: ["next", "return", "throw", "map", "filter", "take", "drop", "flatMap", "reduce", "toArray", "forEach", "some", "every", "find"]) + + /// Type of the JavaScript Iterator constructor builtin. + static let jsIteratorConstructor = ILType.object(ofGroup: "IteratorConstructor", withProperties: ["prototype"], withMethods: ["from"]) + /// Type of a JavaScript generator object. static let jsGenerator = ILType.iterable + ILType.object(ofGroup: "Generator", withMethods: ["next", "return", "throw"]) @@ -1019,10 +1029,10 @@ public extension ILType { static let jsPromise = ILType.object(ofGroup: "Promise", withMethods: ["catch", "finally", "then"]) /// Type of a JavaScript Map object. - static let jsMap = ILType.iterable + ILType.object(ofGroup: "Map", withProperties: ["size"], withMethods: ["clear", "delete", "entries", "forEach", "get", "has", "keys", "set", "values"]) + static let jsMap = ILType.iterable + ILType.object(ofGroup: "Map", withProperties: ["size"], withMethods: ["clear", "delete", "entries", "forEach", "get", "has", "keys", "set", "values", "getOrInsert", "getOrInsertComputed"]) /// Type of a JavaScript WeakMap object. - static let jsWeakMap = ILType.object(ofGroup: "WeakMap", withMethods: ["delete", "get", "has", "set"]) + static let jsWeakMap = ILType.object(ofGroup: "WeakMap", withMethods: ["delete", "get", "has", "set", "getOrInsert", "getOrInsertComputed"]) /// Type of a JavaScript Set object. static let jsSet = ILType.iterable + ILType.object(ofGroup: "Set", withProperties: ["size"], withMethods: ["add", "clear", "delete", "entries", "forEach", "has", "keys", "values"]) @@ -1149,7 +1159,7 @@ public extension ILType { static let jsDateConstructor = ILType.functionAndConstructor([.opt(.string | .number)] => .jsDate) + .object(ofGroup: "DateConstructor", withProperties: ["prototype"], withMethods: ["UTC", "now", "parse"]) /// Type of the JavaScript JSON object builtin. - static let jsJSONObject = ILType.object(ofGroup: "JSON", withMethods: ["parse", "stringify"]) + static let jsJSONObject = ILType.object(ofGroup: "JSON", withMethods: ["parse", "stringify", "rawJSON", "isRawJSON"]) /// Type of the JavaScript Reflect object builtin. static let jsReflectObject = ILType.object(ofGroup: "Reflect", withMethods: ["apply", "construct", "defineProperty", "deleteProperty", "get", "getOwnPropertyDescriptor", "getPrototypeOf", "has", "isExtensible", "ownKeys", "preventExtensions", "set", "setPrototypeOf"]) @@ -1306,10 +1316,16 @@ public extension ObjectGroup { // 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 + // TODO(mliedtke): Find a nicer interface than manually excluding some + // magic combinations here. if receiver.name == "Intl.DateTimeFormat" || receiver.name == "Intl.NumberFormat" { properties.removeValue(forKey: "format") } else if receiver.name == "Intl.Collator" { properties.removeValue(forKey: "compare") + } else if receiver.name == "Iterator" { + properties.removeValue(forKey: "next") + properties.removeValue(forKey: "return") + properties.removeValue(forKey: "throw") } return ObjectGroup( name: name, @@ -1342,7 +1358,7 @@ public extension ObjectGroup { "indexOf" : [.jsAnything, .opt(.integer)] => .integer, "lastIndexOf" : [.jsAnything, .opt(.integer)] => .integer, "match" : [.regexp] => .jsString, - "matchAll" : [.regexp] => .jsString, + "matchAll" : [.regexp] => .jsIterator, "normalize" : [] => .jsString, // the first parameter must be a specific string value, so we have a CodeGenerator for that instead "padEnd" : [.integer, .opt(.string)] => .jsString, "padStart" : [.integer, .opt(.string)] => .jsString, @@ -1399,7 +1415,7 @@ public extension ObjectGroup { methods: [ "at" : [.integer] => .jsAnything, "copyWithin" : [.integer, .integer, .opt(.integer)] => .jsArray, - "entries" : [] => .jsArray, + "entries" : [] => .jsIterator, "every" : [.function(), .opt(.object())] => .boolean, "fill" : [.jsAnything, .opt(.integer), .opt(.integer)] => .undefined, "find" : [.function(), .opt(.object())] => .jsAnything, @@ -1410,14 +1426,14 @@ public extension ObjectGroup { "includes" : [.jsAnything, .opt(.integer)] => .boolean, "indexOf" : [.jsAnything, .opt(.integer)] => .integer, "join" : [.string] => .jsString, - "keys" : [] => .object(), // returns an array iterator + "keys" : [] => .jsIterator, "lastIndexOf" : [.jsAnything, .opt(.integer)] => .integer, "reduce" : [.function(), .opt(.jsAnything)] => .jsAnything, "reduceRight" : [.function(), .opt(.jsAnything)] => .jsAnything, "reverse" : [] => .undefined, "some" : [.function(), .opt(.jsAnything)] => .boolean, "sort" : [.function()] => .undefined, - "values" : [] => .object(), + "values" : [] => .jsIterator, "pop" : [] => .jsAnything, "push" : [.jsAnything...] => .integer, "shift" : [] => .jsAnything, @@ -1477,14 +1493,52 @@ public extension ObjectGroup { methods: [:] ) + static let jsIterator = ObjectGroup( + name: "Iterator", + instanceType: .jsIterator, + properties: [ + "done": .boolean, + "value": .jsAnything + ], + methods: [ + "next" : [.opt(.jsAnything)] => .object(withProperties: ["done", "value"]), + "return" : [.jsAnything] => .object(withProperties: ["done", "value"]), + "throw" : [.jsAnything] => .object(withProperties: ["done", "value"]), + "map" : [.function()] => .jsIterator, + "filter" : [.function()] => .jsIterator, + "take" : [.integer] => .jsIterator, + "drop" : [.integer] => .jsIterator, + "flatMap" : [.function()] => .jsIterator, + "reduce" : [.function(), .opt(.jsAnything)] => .jsAnything, + "toArray" : [] => .jsArray, + "forEach" : [.function()] => .undefined, + "some" : [.function()] => .boolean, + "every" : [.function()] => .boolean, + "find" : [.function()] => .jsAnything, + ] + ) + + static let jsIteratorPrototype = createPrototypeObjectGroup(jsIterator) + + static let jsIteratorConstructor = ObjectGroup( + name: "IteratorConstructor", + instanceType: .jsIteratorConstructor, + properties: [ + "prototype" : jsIteratorPrototype.instanceType + ], + methods: [ + "from" : [.jsAnything] => .jsIterator, + ] + ) + static let jsGenerators = ObjectGroup( name: "Generator", instanceType: .jsGenerator, properties: [:], methods: [ - "next" : [.opt(.jsAnything)] => .object(withProperties: ["done", "value"]), - "return" : [.opt(.jsAnything)] => .object(withProperties: ["done", "value"]), - "throw" : [.opt(.jsAnything)] => .object(withProperties: ["done", "value"]) + "next" : [.opt(.jsAnything)] => .object(withProperties: ["done", "value"]), + "return" : [.opt(.jsAnything)] => .object(withProperties: ["done", "value"]), + "throw" : [.opt(.jsAnything)] => .object(withProperties: ["done", "value"]), ] ) @@ -1508,15 +1562,17 @@ public extension ObjectGroup { "size" : .integer ], methods: [ - "clear" : [] => .undefined, - "delete" : [.jsAnything] => .boolean, - "entries" : [] => .object(), - "forEach" : [.function(), .opt(.object())] => .undefined, - "get" : [.jsAnything] => .jsAnything, - "has" : [.jsAnything] => .boolean, - "keys" : [] => .object(), - "set" : [.jsAnything, .jsAnything] => .jsMap, - "values" : [] => .object(), + "clear" : [] => .undefined, + "delete" : [.jsAnything] => .boolean, + "entries" : [] => .jsIterator, + "forEach" : [.function(), .opt(.object())] => .undefined, + "get" : [.jsAnything] => .jsAnything, + "has" : [.jsAnything] => .boolean, + "keys" : [] => .jsIterator, + "set" : [.jsAnything, .jsAnything] => .jsMap, + "values" : [] => .jsIterator, + "getOrInsert" : [.jsAnything, .jsAnything] => .jsAnything, + "getOrInsertComputed": [.jsAnything, .function()] => .jsAnything, ] ) @@ -1526,10 +1582,12 @@ public extension ObjectGroup { instanceType: .jsWeakMap, properties: [:], methods: [ - "delete" : [.jsAnything] => .boolean, - "get" : [.jsAnything] => .jsAnything, - "has" : [.jsAnything] => .boolean, - "set" : [.jsAnything, .jsAnything] => .jsWeakMap, + "delete" : [.jsAnything] => .boolean, + "get" : [.jsAnything] => .jsAnything, + "has" : [.jsAnything] => .boolean, + "set" : [.jsAnything, .jsAnything] => .jsWeakMap, + "getOrInsert" : [.jsAnything, .jsAnything] => .jsAnything, + "getOrInsertComputed": [.jsAnything, .function()] => .jsAnything, ] ) @@ -1544,11 +1602,11 @@ public extension ObjectGroup { "add" : [.jsAnything] => .jsSet, "clear" : [] => .undefined, "delete" : [.jsAnything] => .boolean, - "entries" : [] => .object(), + "entries" : [] => .jsIterator, "forEach" : [.function(), .opt(.object())] => .undefined, "has" : [.jsAnything] => .boolean, - "keys" : [] => .object(), - "values" : [] => .object(), + "keys" : [] => .jsIterator, + "values" : [] => .jsIterator, ] ) @@ -1592,12 +1650,12 @@ public extension ObjectGroup { properties: [ "byteLength" : .integer, "maxByteLength" : .integer, - "resizable" : .boolean + "resizable" : .boolean, ], methods: [ - "resize" : [.integer] => .undefined, - "slice" : [.integer, .opt(.integer)] => .jsArrayBuffer, - "transfer" : [] => .jsArrayBuffer, + "resize" : [.integer] => .undefined, + "slice" : [.integer, .opt(.integer)] => .jsArrayBuffer, + "transfer" : [.opt(.integer)] => .jsArrayBuffer, ] ) @@ -1636,32 +1694,32 @@ public extension ObjectGroup { "length" : .integer ], methods: [ - "at" : [.integer] => .jsAnything, - "copyWithin" : [.integer, .integer, .opt(.integer)] => .undefined, - "entries" : [] => .jsArray, - "every" : [.function(), .opt(.object())] => .boolean, - "fill" : [.jsAnything, .opt(.integer), .opt(.integer)] => .undefined, - "find" : [.function(), .opt(.object())] => .jsAnything, - "findIndex" : [.function(), .opt(.object())] => .integer, - "findLast" : [.function(), .opt(.object())] => .jsAnything, + "at" : [.integer] => .jsAnything, + "copyWithin" : [.integer, .integer, .opt(.integer)] => .undefined, + "entries" : [] => .jsIterator, + "every" : [.function(), .opt(.object())] => .boolean, + "fill" : [.jsAnything, .opt(.integer), .opt(.integer)] => .undefined, + "find" : [.function(), .opt(.object())] => .jsAnything, + "findIndex" : [.function(), .opt(.object())] => .integer, + "findLast" : [.function(), .opt(.object())] => .jsAnything, "findLastIndex" : [.function(), .opt(.object())] => .integer, - "forEach" : [.function(), .opt(.object())] => .undefined, - "includes" : [.jsAnything, .opt(.integer)] => .boolean, - "indexOf" : [.jsAnything, .opt(.integer)] => .integer, - "join" : [.string] => .jsString, - "keys" : [] => .object(), // returns an array iterator - "lastIndexOf" : [.jsAnything, .opt(.integer)] => .integer, - "reduce" : [.function(), .opt(.jsAnything)] => .jsAnything, - "reduceRight" : [.function(), .opt(.jsAnything)] => .jsAnything, - "reverse" : [] => .undefined, - "set" : [.object(), .opt(.integer)] => .undefined, - "some" : [.function(), .opt(.jsAnything)] => .boolean, - "sort" : [.function()] => .undefined, - "values" : [] => .object(), - "filter" : [.function(), .opt(.object())] => .jsTypedArray(variant), - "map" : [.function(), .opt(.object())] => .jsTypedArray(variant), - "slice" : [.opt(.integer), .opt(.integer)] => .jsTypedArray(variant), - "subarray" : [.opt(.integer), .opt(.integer)] => .jsTypedArray(variant), + "forEach" : [.function(), .opt(.object())] => .undefined, + "includes" : [.jsAnything, .opt(.integer)] => .boolean, + "indexOf" : [.jsAnything, .opt(.integer)] => .integer, + "join" : [.string] => .jsString, + "keys" : [] => .jsIterator, + "lastIndexOf" : [.jsAnything, .opt(.integer)] => .integer, + "reduce" : [.function(), .opt(.jsAnything)] => .jsAnything, + "reduceRight" : [.function(), .opt(.jsAnything)] => .jsAnything, + "reverse" : [] => .undefined, + "set" : [.object(), .opt(.integer)] => .undefined, + "some" : [.function(), .opt(.jsAnything)] => .boolean, + "sort" : [.function()] => .undefined, + "values" : [] => .jsIterator, + "filter" : [.function(), .opt(.object())] => .jsTypedArray(variant), + "map" : [.function(), .opt(.object())] => .jsTypedArray(variant), + "slice" : [.opt(.integer), .opt(.integer)] => .jsTypedArray(variant), + "subarray" : [.opt(.integer), .opt(.integer)] => .jsTypedArray(variant), "toString" : [] => .jsString, "toLocaleString" : [.opt(.string), .opt(.object())] => .jsString, "toReversed" : [] => .jsTypedArray(variant), @@ -2032,6 +2090,8 @@ public extension ObjectGroup { methods: [ "parse" : [.string, .opt(.function())] => .jsAnything, "stringify" : [.jsAnything, .opt(.function()), .opt(.number | .string)] => .jsString, + "rawJSON" : [.plain(.string | .number | .boolean)] => .jsAnything, + "isRawJSON" : [.jsAnything] => .boolean, ] ) From 525e9ddc6e318015f0beb349f042d203d2f3a8cc Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Tue, 9 Dec 2025 10:22:09 +0100 Subject: [PATCH 32/89] Add merge script for transpile-tests results This adds a simple script to merge data from multiple sharded calls to transpile_tests.py. We keep the merge script side-by-side with the main script to ease changing details in the data later, e.g. adding additional keys. This also drops two redundant entries from the current format. Bug: 442444727 Change-Id: I774c078455028a01eb97276b90120a0f03c14f7a Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8832116 Reviewed-by: Matthias Liedtke Commit-Queue: Michael Achenbach --- Tools/transpile_tests/merge_json_results.py | 65 ++++++++++++++++ Tools/transpile_tests/test_merge_results.py | 75 +++++++++++++++++++ Tools/transpile_tests/test_transpile_tests.py | 4 - Tools/transpile_tests/transpile_tests.py | 4 +- 4 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 Tools/transpile_tests/merge_json_results.py create mode 100644 Tools/transpile_tests/test_merge_results.py diff --git a/Tools/transpile_tests/merge_json_results.py b/Tools/transpile_tests/merge_json_results.py new file mode 100644 index 000000000..8aa66bb2d --- /dev/null +++ b/Tools/transpile_tests/merge_json_results.py @@ -0,0 +1,65 @@ +# 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. + +""" +Script to merge multiple output json files from transpile_tests.py into one. +""" + +import argparse +import json +import sys + + +def merge_test_results(inputs): + num_tests = 0 + failures = [] + for result in inputs: + num_tests += result["num_tests"] + failures += result["failures"] + + return { + 'num_tests': num_tests, + 'failures': failures, + } + + +def parse_args(args): + parser = argparse.ArgumentParser() + parser.add_argument( + '--json-input', action='append', required=True, + help='Path to a json results file from transpile_tests.py.') + parser.add_argument( + '--json-output', required=True, + help='Path to the merged json results file.') + return parser.parse_args(args) + + +def main(args): + options = parse_args(args) + + inputs = [] + for input_path in options.json_input: + with open(input_path) as f: + inputs.append(json.load(f)) + + result = merge_test_results(inputs) + with open(options.json_output, 'w') as f: + json.dump(result, f, sort_keys=True, indent=2) + + print(f'Merged results for {result["num_tests"]} tests ' + f'and {len(result["failures"])} failures.') + + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/Tools/transpile_tests/test_merge_results.py b/Tools/transpile_tests/test_merge_results.py new file mode 100644 index 000000000..cdc446fb2 --- /dev/null +++ b/Tools/transpile_tests/test_merge_results.py @@ -0,0 +1,75 @@ +# 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 contextlib +import io +import json +import unittest + +from pyfakefs import fake_filesystem_unittest + +import merge_json_results + + +class TestMergeResults(fake_filesystem_unittest.TestCase): + + @fake_filesystem_unittest.patchfs(allow_root_user=True) + def test_full_run(self, fs): + with open('/in1.json', 'w') as f: + json.dump({ + 'num_tests': 2, + 'failures': [ + {'path': 'path/to/failure1', 'output': 'foo'}, + ] + }, f) + + with open('/in2.json', 'w') as f: + json.dump({ + 'num_tests': 3, + 'failures': [ + {'path': 'path/to/failure2', 'output': 'bar 42\nbar 43'}, + {'path': 'path/to/failure3', 'output': 'baz'}, + ] + }, f) + + f = io.StringIO() + with contextlib.redirect_stdout(f): + merge_json_results.main([ + '--json-input', '/in1.json', + '--json-input', '/in2.json', + '--json-output', '/output.json', + ]) + + # Verify the output. + self.assertEqual( + 'Merged results for 5 tests and 3 failures.', + f.getvalue().strip()) + + # Verify the results written to the json output file. + with open('/output.json') as f: + actual_results = json.load(f) + + expected_results = { + 'num_tests': 5, + 'failures': [ + {'path': 'path/to/failure1', 'output': 'foo'}, + {'path': 'path/to/failure2', 'output': 'bar 42\nbar 43'}, + {'path': 'path/to/failure3', 'output': 'baz'}, + ], + } + self.assertEqual(expected_results, actual_results) + + +if __name__ == '__main__': + unittest.main() diff --git a/Tools/transpile_tests/test_transpile_tests.py b/Tools/transpile_tests/test_transpile_tests.py index a772cd9b6..ef1c7b95e 100644 --- a/Tools/transpile_tests/test_transpile_tests.py +++ b/Tools/transpile_tests/test_transpile_tests.py @@ -98,8 +98,6 @@ def test_full_run(self, fs): expected_results = { 'num_tests': 4, - 'num_successes': 3, - 'percent_successes': 75.0, 'failures': [ { 'output': 'Failed!', @@ -148,8 +146,6 @@ def test_shard_run(self, fs): expected_results = { 'num_tests': 2, - 'num_successes': 1, - 'percent_successes': 50.0, 'failures': [ { 'output': 'Failed!', diff --git a/Tools/transpile_tests/transpile_tests.py b/Tools/transpile_tests/transpile_tests.py index ef82f11f2..b9ba6ca95 100644 --- a/Tools/transpile_tests/transpile_tests.py +++ b/Tools/transpile_tests/transpile_tests.py @@ -172,15 +172,13 @@ def test_input_gen(): f'({num_successes} of {num_tests}) test cases.') return { 'num_tests': num_tests, - 'num_successes': num_successes, - 'percent_successes': ratio, 'failures': failures, } def write_json_output(path, results): with open(path, 'w') as f: - json.dump(results, f) + json.dump(results, f, sort_keys=True, indent=2) def parse_args(args): From e0c17b64984290afdf5bcfea3140d6184515aeb3 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Tue, 9 Dec 2025 12:50:40 +0100 Subject: [PATCH 33/89] Rename merge script test Bug: 442444727 Change-Id: I6bc7c8744a7237e6d7c7c4bb204aeb530ed272e5 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8832418 Auto-Submit: Michael Achenbach Commit-Queue: Pawel Krawczyk Reviewed-by: Pawel Krawczyk --- .../{test_merge_results.py => test_merge_json_results.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Tools/transpile_tests/{test_merge_results.py => test_merge_json_results.py} (100%) diff --git a/Tools/transpile_tests/test_merge_results.py b/Tools/transpile_tests/test_merge_json_results.py similarity index 100% rename from Tools/transpile_tests/test_merge_results.py rename to Tools/transpile_tests/test_merge_json_results.py From cb649fa38a977cd559c08d95af161082ccd23c87 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Tue, 9 Dec 2025 19:07:48 +0100 Subject: [PATCH 34/89] [v8] Add %AllocationTimeout generator Besides the existing --gc-interval=n flag, this can help finding bugs for a GC happening at a specific point in a builtin or runtime function. Bug: 467294029 Change-Id: I9d78d7d01d229ecd3e0c631f9d1e2f54a456b4ba Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8832419 Reviewed-by: Michael Achenbach Commit-Queue: Matthias Liedtke --- Sources/FuzzilliCli/Profiles/V8CommonProfile.swift | 7 +++++++ Sources/FuzzilliCli/Profiles/V8Profile.swift | 1 + 2 files changed, 8 insertions(+) diff --git a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift index 967210b97..3c65c893c 100644 --- a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift @@ -51,6 +51,13 @@ public let V8GcGenerator = CodeGenerator("GcGenerator") { b in b.callFunction(gc, withArgs: b.findOrGenerateArguments(forSignature: b.fuzzer.environment.type(ofBuiltin: "gc").signature!)) } +public let V8AllocationTimeoutGenerator = CodeGenerator("AllocationTimeoutGenerator") { b in + // Repeated GCs are expensive, so only rarely use an interval. + let interval = probability(0.1) ? Int64.random(in: 100...10000) : -1 + let timeout = Int64.random(in: 0...(Bool.random() ? 10 : 100)) // prefer small values + b.eval("%SetAllocationTimeout(%@, %@)", with: [b.loadInt(interval), b.loadInt(timeout)]); +} + public let V8MajorGcGenerator = CodeGenerator("MajorGcGenerator") { b in // Differently to `gc()`, this intrinsic is registered with less effects, preventing fewer // optimizations in V8's optimizing compilers. diff --git a/Sources/FuzzilliCli/Profiles/V8Profile.swift b/Sources/FuzzilliCli/Profiles/V8Profile.swift index ade2d87ac..3d3e60bf2 100644 --- a/Sources/FuzzilliCli/Profiles/V8Profile.swift +++ b/Sources/FuzzilliCli/Profiles/V8Profile.swift @@ -63,6 +63,7 @@ let v8Profile = Profile( (WorkerGenerator, 10), (V8GcGenerator, 5), + (V8AllocationTimeoutGenerator, 5), (V8MajorGcGenerator, 5), (WasmStructGenerator, 15), From 36d62582f60991d3194bd2e820440d503a7f3d07 Mon Sep 17 00:00:00 2001 From: Hendrik Wuethrich Date: Thu, 11 Dec 2025 09:54:18 +0000 Subject: [PATCH 35/89] Add ManyArgumentsCall CodeGenerator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Calling apply() with an array like this generator does will create a function call with as many arguments as the size of the array. It is meant to cover the discrepencies in max argument counts between turboshaft and maglev. Bug: b/455503442 Change-Id: Ia605368687970369e168796273486d75de4cc811 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8839116 Reviewed-by: Matthias Liedtke Commit-Queue: Hendrik Wüthrich --- .../CodeGen/CodeGeneratorWeights.swift | 1 + Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift index 1dff70aed..f44c008ac 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift @@ -129,6 +129,7 @@ public let codeGeneratorWeights = [ "ComputedPropertyConfigurationGenerator": 10, "FunctionCallGenerator": 30, "FunctionCallWithSpreadGenerator": 3, + "ManyArgumentsCall": 3, "ConstructorCallGenerator": 20, "ConstructorCallWithSpreadGenerator": 3, "MethodCallGenerator": 30, diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 0c5442f4a..f649e17d8 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -1846,6 +1846,25 @@ public let CodeGenerators: [CodeGenerator] = [ f, withArgs: arguments, spreading: spreads, guard: needGuard) }, + + CodeGenerator("ManyArgumentsCall", inputs: .preferred(.function())) { b, f in + // These sizes are around the max arguments for maglev (2^16 - 10) + // and turboshaft (2^16). + let sizes: [Int64] = [65524, 65525, 65526, 65534, 65535, 65536] + let size = b.loadInt(chooseUniform(from: sizes)) + let constructor = b.createNamedVariable(forBuiltin: "Array") + let largeArray = b.construct(constructor, withArgs: [size]) + + let needGuard = b.type(of: f).MayNotBe(.function()) + + if probability(0.5) { + let receiver = probability(0.5) ? b.loadNull() : b.randomVariable(forUseAs: .object()) + b.callMethod("apply", on: f, withArgs: [receiver, largeArray], guard: needGuard) + } else { + b.callFunction(f, withArgs: [largeArray], spreading: [true], guard: needGuard) + } + }, + CodeGenerator( "ConstructorCallWithSpreadGenerator", inputs: .preferred(.constructor()) ) { b, c in From 60b6d27a24c64e6ba3ad92c64cc078a7f221453d Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Fri, 12 Dec 2025 12:19:53 +0100 Subject: [PATCH 36/89] [v8] Drop SSE3 code gen flag for Wasm This flag is incomplete. Before fuzzing it, all V8 tests should pass. Recent fuzzer reports show that this is not the case. Bug: 468167782 Change-Id: I80d2cba60f1d553dd47cff338dee40a3b7c1ffbd Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8843276 Commit-Queue: Matthias Liedtke Auto-Submit: Matthias Liedtke Reviewed-by: Thibaud Michaud Commit-Queue: Thibaud Michaud --- Sources/FuzzilliCli/Profiles/V8CommonProfile.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift index 3c65c893c..d53a07dac 100644 --- a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift @@ -1061,7 +1061,6 @@ public func v8ProcessArgs(randomize: Bool, forSandbox: Bool) -> [String] { chooseBooleanFlag("wasm-bulkmem-inlining") chooseBooleanFlag("wasm-lazy-compilation") chooseBooleanFlag("wasm-lazy-validation") - chooseBooleanFlag("wasm-simd-ssse3-codegen") } return args From 62e4d2fda66f659c8d95abc17b5da7293475f7e2 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Thu, 11 Dec 2025 21:49:35 +0100 Subject: [PATCH 37/89] Make transpiler results format more fine-grained. This enhances the results format after test transpilation. Before, we had only one level of: {num_tests: int, failures: [{path: string, output: string}]} Now we'll key the two lowest directory levels in Test262, e.g. for a typical path like: language/literals/boolean/S7.8.2_A1_T1.js, the key would be language/literals. All results under this directory will be listed as a dict value, with numbers and failures as previously, further directories accordingly: { language/literals: {num_tests: ..., failures: ...}, language/identifiers: ... ... } We will now transpile all Test262 tests in one run and won't need to exclude any subdirectories, like staging, as we can now report separate numbers anyways. This also updates the merge script to the new format and adds additional unit tests for some helper functions. Bug: 442444727 Change-Id: Idf23c650c646bc970d81fc8a318d4a8c76797a4d Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8841396 Commit-Queue: Michael Achenbach Reviewed-by: Liviu Rau --- Tools/transpile_tests/merge_json_results.py | 23 ++-- .../test_merge_json_results.py | 61 ++++++--- Tools/transpile_tests/test_transpile_tests.py | 119 +++++++++++++++--- Tools/transpile_tests/transpile_tests.py | 105 ++++++++++++---- 4 files changed, 242 insertions(+), 66 deletions(-) diff --git a/Tools/transpile_tests/merge_json_results.py b/Tools/transpile_tests/merge_json_results.py index 8aa66bb2d..0b0a38fab 100644 --- a/Tools/transpile_tests/merge_json_results.py +++ b/Tools/transpile_tests/merge_json_results.py @@ -20,18 +20,22 @@ import json import sys +from collections import defaultdict, Counter + def merge_test_results(inputs): - num_tests = 0 - failures = [] + num_tests = Counter() + failures = defaultdict(list) + for result in inputs: - num_tests += result["num_tests"] - failures += result["failures"] + for key, value in result.items(): + num_tests[key] += value["num_tests"] + failures[key] += value["failures"] - return { - 'num_tests': num_tests, - 'failures': failures, - } + return dict( + (key, {'num_tests': num_tests[key], 'failures': failures[key]}) + for key in sorted(num_tests.keys()) + ) def parse_args(args): @@ -57,8 +61,7 @@ def main(args): with open(options.json_output, 'w') as f: json.dump(result, f, sort_keys=True, indent=2) - print(f'Merged results for {result["num_tests"]} tests ' - f'and {len(result["failures"])} failures.') + print(f'Successfully merged results.') if __name__ == '__main__': diff --git a/Tools/transpile_tests/test_merge_json_results.py b/Tools/transpile_tests/test_merge_json_results.py index cdc446fb2..a297172f2 100644 --- a/Tools/transpile_tests/test_merge_json_results.py +++ b/Tools/transpile_tests/test_merge_json_results.py @@ -28,19 +28,37 @@ class TestMergeResults(fake_filesystem_unittest.TestCase): def test_full_run(self, fs): with open('/in1.json', 'w') as f: json.dump({ - 'num_tests': 2, - 'failures': [ - {'path': 'path/to/failure1', 'output': 'foo'}, - ] + 'a/a': { + 'num_tests': 2, + 'failures': [], + }, + 'a/b': { + 'num_tests': 2, + 'failures': [ + {'path': 'a/b/c/failure1', 'output': 'foo'}, + ], + }, }, f) with open('/in2.json', 'w') as f: json.dump({ - 'num_tests': 3, - 'failures': [ - {'path': 'path/to/failure2', 'output': 'bar 42\nbar 43'}, - {'path': 'path/to/failure3', 'output': 'baz'}, - ] + 'a': { + 'num_tests': 1, + 'failures': [ + {'path': 'a/failure4', 'output': 'foo'}, + ], + }, + 'a/a': { + 'num_tests': 1, + 'failures': [], + }, + 'a/b': { + 'num_tests': 3, + 'failures': [ + {'path': 'a/b/c/failure2', 'output': 'bar 42\nbar 43'}, + {'path': 'a/b/d/failure3', 'output': 'baz'}, + ] + }, }, f) f = io.StringIO() @@ -53,7 +71,7 @@ def test_full_run(self, fs): # Verify the output. self.assertEqual( - 'Merged results for 5 tests and 3 failures.', + 'Successfully merged results.', f.getvalue().strip()) # Verify the results written to the json output file. @@ -61,13 +79,24 @@ def test_full_run(self, fs): actual_results = json.load(f) expected_results = { - 'num_tests': 5, - 'failures': [ - {'path': 'path/to/failure1', 'output': 'foo'}, - {'path': 'path/to/failure2', 'output': 'bar 42\nbar 43'}, - {'path': 'path/to/failure3', 'output': 'baz'}, - ], + 'a': { + 'failures': [{'output': 'foo', 'path': 'a/failure4'}], + 'num_tests': 1, + }, + 'a/a': { + 'failures': [], + 'num_tests': 3, + }, + 'a/b': { + 'failures': [ + {'output': 'foo', 'path': 'a/b/c/failure1'}, + {'output': 'bar 42\nbar 43', 'path': 'a/b/c/failure2'}, + {'output': 'baz', 'path': 'a/b/d/failure3'}, + ], + 'num_tests': 5, + }, } + self.assertEqual(expected_results, actual_results) diff --git a/Tools/transpile_tests/test_transpile_tests.py b/Tools/transpile_tests/test_transpile_tests.py index ef1c7b95e..3e6277428 100644 --- a/Tools/transpile_tests/test_transpile_tests.py +++ b/Tools/transpile_tests/test_transpile_tests.py @@ -97,13 +97,14 @@ def test_full_run(self, fs): actual_results = json.load(f) expected_results = { - 'num_tests': 4, - 'failures': [ - { - 'output': 'Failed!', - 'path': 'test/test262/data/test/folder2/Test4_fail.js', - }, - ], + 'folder1/subfolder1': { + 'failures': [], + 'num_tests': 2, + }, + 'folder2': { + 'failures': [{'output': 'Failed!', 'path': 'folder2/Test4_fail.js'}], + 'num_tests': 2, + }, } self.assertEqual(expected_results, actual_results) @@ -145,15 +146,105 @@ def test_shard_run(self, fs): actual_results = json.load(f) expected_results = { - 'num_tests': 2, - 'failures': [ - { - 'output': 'Failed!', - 'path': 'test/test262/data/test/folder2/Test4_fail.js', - }, - ], + 'folder1/subfolder1': { + 'failures': [], + 'num_tests': 1, + }, + 'folder2': { + 'failures': [{'output': 'Failed!', 'path': 'folder2/Test4_fail.js'}], + 'num_tests': 1, + }, } self.assertEqual(expected_results, actual_results) + +Options = namedtuple('Options', 'verbose') + +class UnitTests(unittest.TestCase): + def test_level_key_0(self): + counter = transpile_tests.TestCounter(0, Options(False)) + self.assertEqual('', counter.level_key(Path('test.js'))) + self.assertEqual('', counter.level_key(Path('level1/test.js'))) + + def test_level_key_2(self): + counter = transpile_tests.TestCounter(2, Options(False)) + self.assertEqual( + '', counter.level_key(Path('test.js'))) + self.assertEqual( + 'level1', counter.level_key(Path('level1/test.js'))) + self.assertEqual( + 'level1/level2', + counter.level_key(Path('level1/level2/test.js'))) + self.assertEqual( + 'level1/level2', + counter.level_key(Path('level1/level2/level3/test.js'))) + + def test_simple_counts(self): + counter = transpile_tests.TestCounter(1, Options(False)) + self.assertEqual({}, counter.results()) + + counter.count(0, Path('pass1.js'), 'good') + self.assertEqual( + { + '': {'failures': [], 'num_tests': 1}, + }, + counter.results()) + + counter.count(0, Path('a/b/pass1.js'), 'good') + self.assertEqual( + { + '': {'failures': [], 'num_tests': 1}, + 'a': {'failures': [], 'num_tests': 1}, + }, + counter.results()) + + counter.count(1, Path('a/fail1.js'), 'bad') + self.assertEqual( + { + '': {'failures': [], 'num_tests': 1}, + 'a': { + 'failures': [{'output': 'bad', 'path': 'a/fail1.js'}], + 'num_tests': 2, + }, + }, + counter.results()) + + def test_complex_count(self): + counter = transpile_tests.TestCounter(2, Options(False)) + counter.count(0, Path('A1/A2/3/pass1.js'), 'good') + counter.count(0, Path('A1/B2/3/pass1.js'), 'good') + counter.count(1, Path('A1/B2/3/fail1.js'), 'bad') + counter.count(0, Path('A1/A2/3/pass2.js'), 'good') + counter.count(0, Path('A1/A2/3/pass3.js'), 'good') + counter.count(1, Path('fail4.js'), 'bad') + counter.count(0, Path('B1/A2/3/pass1.js'), 'good') + counter.count(1, Path('B1/A2/fail2.js'), 'bad') + counter.count(1, Path('B1/fail3.js'), 'bad') + counter.count(1, Path('fail5.js'), 'bad') + counter.count(1, Path('fail6.js'), 'bad') + counter.count(0, Path('A1/B2/3/pass4.js'), 'good') + + self.assertEqual( + { + '': {'num_tests': 3, 'failures':[ + {'output': 'bad', 'path': 'fail4.js'}, + {'output': 'bad', 'path': 'fail5.js'}, + {'output': 'bad', 'path': 'fail6.js'}, + ]}, + 'B1': {'num_tests': 1, 'failures':[ + {'output': 'bad', 'path': 'B1/fail3.js'}, + ]}, + 'A1/A2': {'num_tests': 3, 'failures':[]}, + 'A1/B2': {'num_tests': 3, 'failures':[ + {'output': 'bad', 'path': 'A1/B2/3/fail1.js'}, + ]}, + 'B1/A2': {'num_tests': 2, 'failures':[ + {'output': 'bad', 'path': 'B1/A2/fail2.js'}, + ]}, + }, + counter.results() + ) + + if __name__ == '__main__': unittest.main() diff --git a/Tools/transpile_tests/transpile_tests.py b/Tools/transpile_tests/transpile_tests.py index b9ba6ca95..106895e2e 100644 --- a/Tools/transpile_tests/transpile_tests.py +++ b/Tools/transpile_tests/transpile_tests.py @@ -28,6 +28,7 @@ import subprocess import sys +from collections import defaultdict, Counter from pathlib import Path BASE_DIR = Path(__file__).parent.parent.parent @@ -49,7 +50,6 @@ def __init__(self, base_dir): 'parseTestRecord', f'{tools_abs_path}/parseTestRecord.py') self.parse = loader.load_module().parseTestRecord self.excluded_suffixes = ['_FIXTURE.js'] - self.excluded_dirs = ['staging'] def is_supported(self, abspath, relpath): if not super().is_supported(abspath, relpath): @@ -59,10 +59,6 @@ def is_supported(self, abspath, relpath): for suffix in self.excluded_suffixes): return False - if any(str(relpath).startswith(directory) - for directory in self.excluded_dirs): - return False - with open(abspath, encoding='utf-8') as f: content = f.read() record = self.parse(content, relpath) @@ -74,15 +70,82 @@ def is_supported(self, abspath, relpath): 'test262': { 'path': 'test/test262/data/test', 'excluded_suffixes': ['_FIXTURE.js'], - # TODO(https://crbug.com/442444727): We might want to track the staging - # tests separately. Those typically address in-progress JS features with - # a high import-failure rate. - 'excluded_dirs': ['staging'], + 'levels': 2, 'metadata_parser': Test262MetaDataParser, } } +class TestCounter: + """Class to count test results, keyed by a number of directories of each + test's relative path. + + The number of levels per key is passed to the constructor. Then when + counting tests, each tests goes into a results bucket for its key, e.g.: + With level 2, test 'a/b/c/d/test1.js' will have key 'a/b'. + + The final results structure is a dict key -> results, where results + is a dict: + { 'num_tests': , 'failures': } + + Example for 2 tests with 1 failure under the a/b directory and 1 tests under + a/c: + { + 'a/b': { + 'num_tests': 2, + 'failures': [{'path': 'a/b/c/d/test1.js', 'output': 'stdout'}], + }, + 'a/c': { + 'num_tests': 1, + 'failures': [], + }, + } + """ + def __init__(self, levels, options): + self.levels = levels + self.options = options + self.num_tests = Counter() + self.failures = defaultdict(list) + self.total_tests = 0 + self.num_failures = 0 + + @property + def num_successes(self): + return self.total_tests - self.num_failures + + def level_key(self, relpath): + parts = list(relpath.parts) + assert parts + assert parts[0] != '/' + + parts = parts[:-1] + parts = parts[:self.levels] + return '/'.join(parts) + + def count(self, exit_code, relpath, stdout): + self.total_tests += 1 + key = self.level_key(relpath) + self.num_tests[key] += 1 + if exit_code != 0: + self.num_failures += 1 + verbose_print(self.options, f'Failed to compile {relpath}') + self.failures[key].append({'path': str(relpath), 'output': stdout}) + if (self.total_tests + 1) % 500 == 0: + print(f'Processed {self.total_tests + 1} test cases.') + + def results(self): + return dict( + ( + key, + { + 'num_tests': self.num_tests[key], + 'failures': self.failures[key], + }, + ) + for key in sorted(self.num_tests.keys()) + ) + + def list_test_filenames(test_root, is_supported_fun): """Walk directories and return all absolute test filenames for supported tests. @@ -151,29 +214,19 @@ def test_input_gen(): yield (abspath, output_dir / abspath.relative_to(base_dir)) # Iterate over all tests in parallel and collect stats. - num_tests = 0 - failures = [] + counter = TestCounter(test_config['levels'], options) with multiprocessing.Pool(multiprocessing.cpu_count()) as pool: for exit_code, abspath, stdout in pool.imap_unordered( transpile_test, test_input_gen()): - num_tests += 1 - if exit_code != 0: - relpath = abspath.relative_to(base_dir) - failures.append({'path': str(relpath), 'output': stdout}) - verbose_print(options, f'Failed to compile {relpath}') - if (num_tests + 1) % 500 == 0: - print(f'Processed {num_tests + 1} test cases.') + relpath = abspath.relative_to(test_input_dir) + counter.count(exit_code, relpath, stdout) # Render and return results. - assert num_tests, 'Failed to find any tests.' - num_successes = num_tests - len(failures) - ratio = float(num_successes) / num_tests * 100 + assert counter.total_tests, 'Failed to find any tests.' + ratio = float(counter.num_successes) / counter.total_tests * 100 print(f'Successfully compiled {ratio:.2f}% ' - f'({num_successes} of {num_tests}) test cases.') - return { - 'num_tests': num_tests, - 'failures': failures, - } + f'({counter.num_successes} of {counter.total_tests}) test cases.') + return counter.results() def write_json_output(path, results): From d9aa0f61142d1a4ea62f81b93a9eb0e845a916e5 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Fri, 12 Dec 2025 14:39:11 +0100 Subject: [PATCH 38/89] Preserve leading comments when transpiling JS JS files often contain meta data in leading comments. We require these comments to be able to execute JS code again after transpiling it with the FuzzILTool. This preserves such comments, whenever the `--outputPathJS` option is used. The comments are extracted using information from the Babel AST in the parser. Bug: 442444727 Change-Id: Ibc9fda5f99a69123672b75970f9b5801c2695074 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8839676 Reviewed-by: Matthias Liedtke Commit-Queue: Michael Achenbach --- Sources/FuzzILTool/main.swift | 2 +- Sources/Fuzzilli/Compiler/Parser/parser.js | 15 +++++++++++++-- Sources/Fuzzilli/Protobuf/ast.pb.swift | 13 ++++++++++--- Sources/Fuzzilli/Protobuf/ast.proto | 3 ++- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Sources/FuzzILTool/main.swift b/Sources/FuzzILTool/main.swift index a8a3b6009..bff607265 100644 --- a/Sources/FuzzILTool/main.swift +++ b/Sources/FuzzILTool/main.swift @@ -188,7 +188,7 @@ else if args.has("--compile") { } if let js_path = args["--outputPathJS"] { - let content = jsLifter.lift(program) + let content = ast.leadingComments + jsLifter.lift(program) do { try content.write(to: URL(fileURLWithPath: js_path), atomically: false, encoding: String.Encoding.utf8) } catch { diff --git a/Sources/Fuzzilli/Compiler/Parser/parser.js b/Sources/Fuzzilli/Compiler/Parser/parser.js index 081449c34..9d31b61a1 100644 --- a/Sources/Fuzzilli/Compiler/Parser/parser.js +++ b/Sources/Fuzzilli/Compiler/Parser/parser.js @@ -36,6 +36,17 @@ function tryReadFile(path) { function parse(script, proto) { let ast = Parser.parse(script, { plugins: ["explicitResourceManagement", "v8intrinsic"] }); + // We assume leading comments (and whitespace) until the starting + // character of the first node of the program. This way + // is easier than rebuilding the comments from Babel's + // `leadingComments` AST nodes, which don't include whitespace and + // newlines. + const firstNode = ast.program.body[0]; + let leadingComments = ''; + if (firstNode) { + leadingComments = script.substring(0, firstNode.start); + } + function assertNoError(err) { if (err) throw err; } @@ -46,7 +57,7 @@ function parse(script, proto) { function visitProgram(node) { const AST = proto.lookupType('compiler.protobuf.AST'); - let program = {statements: []}; + let program = {leadingComments: leadingComments, statements: []}; for (let child of node.body) { program.statements.push(visitStatement(child)); } @@ -667,7 +678,7 @@ protobuf.load(astProtobufDefinitionPath, function(err, root) { if (err) throw err; - let ast = parse(script, root); + const ast = parse(script, root); // Uncomment this to print the AST to stdout (will be very verbose). //console.log(JSON.stringify(ast, null, 2)); diff --git a/Sources/Fuzzilli/Protobuf/ast.pb.swift b/Sources/Fuzzilli/Protobuf/ast.pb.swift index 61f091cb1..da0503c8f 100644 --- a/Sources/Fuzzilli/Protobuf/ast.pb.swift +++ b/Sources/Fuzzilli/Protobuf/ast.pb.swift @@ -153,6 +153,8 @@ public struct Compiler_Protobuf_AST: Sendable { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. + public var leadingComments: String = String() + public var statements: [Compiler_Protobuf_Statement] = [] public var unknownFields = SwiftProtobuf.UnknownStorage() @@ -2455,7 +2457,7 @@ extension Compiler_Protobuf_FunctionType: SwiftProtobuf._ProtoNameProviding { extension Compiler_Protobuf_AST: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".AST" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}statements\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}leadingComments\0\u{1}statements\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -2463,20 +2465,25 @@ extension Compiler_Protobuf_AST: SwiftProtobuf.Message, SwiftProtobuf._MessageIm // 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.statements) }() + case 1: try { try decoder.decodeSingularStringField(value: &self.leadingComments) }() + case 2: try { try decoder.decodeRepeatedMessageField(value: &self.statements) }() default: break } } } public func traverse(visitor: inout V) throws { + if !self.leadingComments.isEmpty { + try visitor.visitSingularStringField(value: self.leadingComments, fieldNumber: 1) + } if !self.statements.isEmpty { - try visitor.visitRepeatedMessageField(value: self.statements, fieldNumber: 1) + try visitor.visitRepeatedMessageField(value: self.statements, fieldNumber: 2) } try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Compiler_Protobuf_AST, rhs: Compiler_Protobuf_AST) -> Bool { + if lhs.leadingComments != rhs.leadingComments {return false} if lhs.statements != rhs.statements {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true diff --git a/Sources/Fuzzilli/Protobuf/ast.proto b/Sources/Fuzzilli/Protobuf/ast.proto index 56fd5fbdc..3a4545cde 100644 --- a/Sources/Fuzzilli/Protobuf/ast.proto +++ b/Sources/Fuzzilli/Protobuf/ast.proto @@ -16,7 +16,8 @@ syntax = "proto3"; package compiler.protobuf; message AST { - repeated Statement statements = 1; + string leadingComments = 1; + repeated Statement statements = 2; } // A parameter in a function declaration. Not an expression on its own. From f065d0ba226269bf13fae85e71708efeff06fc23 Mon Sep 17 00:00:00 2001 From: Dominik Klemba Date: Mon, 15 Dec 2025 06:59:52 +0000 Subject: [PATCH 39/89] RAB and GSAB Fuzzing Added generators for: - ResizableArrayBuffer (RAB) and GrowableSharedArrayBuffer (GSAB) operations (resize, grow). - Creating TypedArrays and DataViews from buffers (including fixed-length views). - Converting Wasm memory to RAB/GSAB. - Increasing chance of accessing last element Improved existing generators: - ResizableArrayBufferGenerator, GrowableSharedArrayBufferGenerator Added a program template testing the interaction between Wasm memory growth and JS buffer resizing. Change-Id: I2127a84796470efff4304402f8fd7a9cc3b8f008 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8836397 Commit-Queue: Dominik Klemba Reviewed-by: Matthias Liedtke --- .../CodeGen/CodeGeneratorWeights.swift | 12 +- Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 71 ++++++++++- .../CodeGen/ProgramTemplateWeights.swift | 2 + .../Fuzzilli/CodeGen/ProgramTemplates.swift | 118 ++++++++++++++++++ .../Fuzzilli/CodeGen/WasmCodeGenerators.swift | 26 ++++ .../Environment/JavaScriptEnvironment.swift | 7 ++ Tests/FuzzilliTests/JSTyperTests.swift | 21 ++++ Tests/FuzzilliTests/ProgramBuilderTest.swift | 27 ++++ 8 files changed, 280 insertions(+), 4 deletions(-) diff --git a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift index f44c008ac..18a364cd4 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift @@ -203,8 +203,13 @@ public let codeGeneratorWeights = [ "EvalGenerator": 3, "NumberComputationGenerator": 40, "ImitationGenerator": 30, - "ResizableArrayBufferGenerator": 5, - "GrowableSharedArrayBufferGenerator": 5, + "ResizableArrayBufferGenerator": 5*6, // TODO(tacet): Revert increased fuzzing probability factors by 02/2026 or later. + "ResizableBufferResizeGenerator": 5*3, + "GrowableSharedArrayBufferGenerator": 5*6, + "GrowableSharedBufferGrowGenerator": 5*3, + "TypedArrayFromBufferGenerator": 10*3, + "DataViewFromBufferGenerator": 5*3, + "TypedArrayLastIndexGenerator": 5*3, "FastToSlowPropertiesGenerator": 10, "IteratorGenerator": 5, "ConstructWithDifferentNewTargetGenerator": 5, @@ -217,6 +222,9 @@ public let codeGeneratorWeights = [ // JS generators for wasm features (e.g. APIs on the WebAssembly global object). "WasmGlobalGenerator": 4, "WasmMemoryGenerator": 4, + "WasmMemoryToResizableBufferGenerator": 5*3, // TODO(tacet): Revert increased fuzzing probability factors by 02/2026 or later. + "WasmMemoryToFixedLengthBufferGenerator": 5*3, + "WasmMemoryJSGrowGenerator": 5*3, "WasmTagGenerator": 4, "WasmLegacyTryCatchComplexGenerator": 5, diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index f649e17d8..40d6f737f 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -276,6 +276,39 @@ public let CodeGenerators: [CodeGenerator] = [ b.construct(constructor, withArgs: [size]) }, + CodeGenerator("TypedArrayFromBufferGenerator", + inContext: .single(.javascript), + inputs: .required(.jsArrayBuffer | .jsSharedArrayBuffer) + ) { b, buffer in + let constructor = b.createNamedVariable( + forBuiltin: chooseUniform( + from: JavaScriptEnvironment.typedArrayConstructors + ) + ) + b.construct(constructor, withArgs: [buffer]) + // TODO(tacet): add Fixed length view. withArgs: [buffer, offset, length] + }, + + CodeGenerator("DataViewFromBufferGenerator", + inContext: .single(.javascript), + inputs: .required(.jsArrayBuffer | .jsSharedArrayBuffer), + produces: [.jsDataView] + ) { b, buffer in + let constructor = b.createNamedVariable(forBuiltin: "DataView") + b.construct(constructor, withArgs: [buffer]) + // TODO(tacet): add Fixed length view. withArgs: [buffer, offset, length] + }, + + CodeGenerator("TypedArrayLastIndexGenerator", + inContext: .single(.javascript), + inputs: .required(.object(withProperties: ["buffer", "length"])), + produces: [.integer, .jsAnything] + ) { b, view in + let len = b.getProperty("length", of: view) + let index = b.binary(len, b.loadInt(1), with: .Sub) + b.getComputedProperty(index, of: view) + }, + CodeGenerator("BuiltinIntlGenerator") { b in let _ = chooseUniform(from: [b.constructIntlDateTimeFormat, b.constructIntlCollator, b.constructIntlListFormat, b.constructIntlNumberFormat, b.constructIntlPluralRules, b.constructIntlRelativeTimeFormat, b.constructIntlSegmenter])() }, @@ -2869,7 +2902,7 @@ public let CodeGenerators: [CodeGenerator] = [ // assert(b.type(of: imitation) == b.type(of: orig)) }, - CodeGenerator("ResizableArrayBufferGenerator", inputs: .one) { b, v in + CodeGenerator("ResizableArrayBufferGenerator", produces: [.jsArrayBuffer]) { b in let size = b.randomSize(upTo: 0x1000) var maxSize = b.randomSize() if maxSize < size { @@ -2894,7 +2927,7 @@ public let CodeGenerators: [CodeGenerator] = [ b.construct(View, withArgs: [ab]) }, - CodeGenerator("GrowableSharedArrayBufferGenerator", inputs: .one) { b, v in + CodeGenerator("GrowableSharedArrayBufferGenerator", produces: [.jsSharedArrayBuffer]) { b in let size = b.randomSize(upTo: 0x1000) var maxSize = b.randomSize() if maxSize < size { @@ -3036,4 +3069,38 @@ public let CodeGenerators: [CodeGenerator] = [ b.callFunction(f, withArgs: args) }, catchBody: { _ in }) }, + + CodeGenerator( + "ResizableBufferResizeGenerator", + inContext: .single(.javascript), + inputs: .required(.jsArrayBuffer) + ) { b, buffer in + let numPages = Int64.random(in: 0...256) + let newSize: Variable + // WebAssembly memories cannot shrink, and must be a multiple of the page size. + if probability(0.5) { + newSize = b.loadInt(numPages * Int64(WasmConstants.specWasmMemPageSize)) + } else { + newSize = b.loadInt(Int64.random(in: 0...0x1000000)) + } + b.callMethod("resize", on: buffer, withArgs: [newSize], guard: true) + }, + + CodeGenerator( + "GrowableSharedBufferGrowGenerator", + inContext: .single(.javascript), + inputs: .required(.jsSharedArrayBuffer) + ) { b, buffer in + let currentByteLength = b.getProperty("byteLength", of: buffer) + let numPages = Int64.random(in: 0...16) + // WebAssembly memories must be a multiple of the page size. + let delta: Variable + if probability(0.5) { + delta = b.loadInt(numPages * Int64(WasmConstants.specWasmMemPageSize)) + } else { + delta = b.loadInt(Int64.random(in: 0...0x100000)) + } + let newSize = b.binary(currentByteLength, delta, with: .Add) + b.callMethod("grow", on: buffer, withArgs: [newSize], guard: true) + }, ] diff --git a/Sources/Fuzzilli/CodeGen/ProgramTemplateWeights.swift b/Sources/Fuzzilli/CodeGen/ProgramTemplateWeights.swift index c57cf76b4..f642ceab8 100644 --- a/Sources/Fuzzilli/CodeGen/ProgramTemplateWeights.swift +++ b/Sources/Fuzzilli/CodeGen/ProgramTemplateWeights.swift @@ -27,4 +27,6 @@ public let programTemplateWeights = [ "JITTrickyFunction": 2, "JSONFuzzer": 1, "WasmReturnCalls": 2, + "WasmResizableMemory": 2*6, // TODO(tacet): Revert increased fuzzing probability factors by 02/2026 or later. + "WasmMemoryBufferReattachment": 2*6, ] diff --git a/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift b/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift index 126519941..4f859ba4d 100644 --- a/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift +++ b/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift @@ -500,4 +500,122 @@ public let ProgramTemplates = [ // Generate some more random code to (hopefully) use the parsed JSON in some interesting way. b.build(n: 25) }, + + WasmProgramTemplate("WasmResizableMemory") { b in + b.buildPrefix() + b.build(n: 50) + + // 1. Define a Wasm module with a randomly configured memory and a single exported function. + let isMemory64 = probability(0.5) + let isShared = probability(0.5) + let signature = b.randomWasmSignature() + + let jsMemory = b.createWasmMemory(minPages: 1, maxPages: Int.random(in: 50...200), isShared: isShared, isMemory64: isMemory64) + + let module = b.buildWasmModule { m in + let internalMem = m.addMemory(minPages: 1, maxPages: Int.random(in: 50...200), isShared: isShared, isMemory64: isMemory64) + + m.addWasmFunction(with: signature) { f, _, args in + b.build(n: 30) + if probability(0.5), let memory = b.randomVariable(ofType: .object(ofGroup: "WasmMemory")) { + let numPagesToGrow = f.memoryArgument(Int64.random(in: 0..<5), b.type(of: memory).wasmMemoryType!) + f.wasmMemoryGrow(memory: memory, growByPages: numPagesToGrow) + b.build(n: 5) + } + return signature.outputTypes.map { f.findOrGenerateWasmVar(ofType: $0) } + } + } + + // 2. In JavaScript, obtain references to the exported Wasm function and memory. + let exports = module.loadExports() + let wasmFunc = b.getProperty("w0", of: exports) + let exportedMem = b.getProperty("wm0", of: exports) + + // 3. Create a ResizableArrayBuffer (RAB) and a random TypedArray view on the Wasm memory. + let rab = b.callMethod("toResizableBuffer", on: exportedMem) + let randomViewConstructor = b.createNamedVariable(forBuiltin: chooseUniform(from: JavaScriptEnvironment.typedArrayConstructors)) + let view = b.construct(randomViewConstructor, withArgs: [rab]) + + b.build(n: 10) + + // 4. Build a loop of mixed Wasm and JS operations. + b.buildRepeatLoop(n: 15) { i in + b.build(n: 5) + + // Call the Wasm function, which may modify the memory. + b.callFunction(wasmFunc, withArgs: b.randomArguments(forCalling: wasmFunc)) + b.build(n: 15) + + // Explicitly grow the Wasm memory from the JavaScript side. + if probability(0.5), let memory = b.randomVariable(ofType: .object(ofGroup: "WasmMemory")) { + b.callMethod("grow", on: memory, withArgs: [b.loadInt(Int64.random(in: 0...5))], guard: true) + b.build(n: 5) + } + } + }, + + WasmProgramTemplate("WasmMemoryBufferReattachment") { b in + b.buildPrefix() + b.build(n: 15) + + let jsMemory = b.createWasmMemory(minPages: 1, maxPages: 256, isShared: false, isMemory64: probability(0.5)) + + // 1. Setup: Wasm Module with Memory and Grow export + let module = b.buildWasmModule { m in + let mem = m.addMemory(minPages: 1, maxPages: 256, isShared: false, isMemory64: probability(0.5)) + m.addWasmFunction(with: [.wasmi32] => [.wasmi32]) { f, _, args in + // Interleave random Wasm instructions before growing + b.build(n: 15) + + let isMem64 = b.type(of: mem).wasmMemoryType!.isMemory64 + let pagesToGrow = isMem64 ? f.extendi32Toi64(args[0], isSigned: false) : args[0] + let oldSize = f.wasmMemoryGrow(memory: mem, growByPages: pagesToGrow) + + return [isMem64 ? f.wrapi64Toi32(oldSize) : oldSize] + } + } + let exports = module.loadExports() + let memory = b.getProperty("wm0", of: exports) + let wasmGrow = b.getProperty("w0", of: exports) + + // 2. JS Loop + b.buildRepeatLoop(n: 20) { _ in + + // 3. Swift Loop: Unroll a long sequence of mixed operations + let steps = Int.random(in: 2...3) + for _ in 0.. Resizable) + if probability(0.5) { + b.callMethod("toResizableBuffer", on: memory) + } else { + b.callMethod("toFixedLengthBuffer", on: memory) + } + + b.build(n: 3) + + // B. Create View (that will likely detach soon) + let buffer = b.getProperty("buffer", of: memory) + let TypedArray = b.createNamedVariable(forBuiltin: chooseUniform(from: ["Int8Array", "Uint32Array", "Float64Array", "DataView"])) + let view = b.construct(TypedArray, withArgs: [buffer]) + b.build(n: 3) + + // C. Growth/Resize Event (Mix of JS, Wasm, and RAB APIs) + withEqualProbability({ + _ = b.callMethod("grow", on: memory, withArgs: [b.loadInt(Int64.random(in: 0...4))], guard: true) + }, { + _ = b.callFunction(wasmGrow, withArgs: [b.loadInt(Int64.random(in: 0...4))]) + }, { + let isResizable = b.getProperty("resizable", of: buffer) + b.buildIf(isResizable) { + let currentLen = b.getProperty("byteLength", of: buffer) + let newLen = b.binary(currentLen, b.loadInt(Int64(WasmConstants.specWasmMemPageSize)), with: .Add) + b.callMethod("resize", on: buffer, withArgs: [newLen]) + } + }) + } + b.build(n: 15) + } + }, ] diff --git a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift index 6a37e6717..9ee6a3375 100644 --- a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift @@ -1875,6 +1875,32 @@ public let WasmCodeGenerators: [CodeGenerator] = [ // Then we need a WasmJsCallSuspendingFunctionGenerator that takes such a WasmSuspenderObject function, unpacks the signature and emits a WasmJsCall // Then we also need a WrapPromisingGenerator that requires a WebAssembly module object, gets the exports field and its methods and then wraps one of those. // For all of this to work we need to add a WasmTypeExtension and ideally the dynamic object group inference. + + CodeGenerator( + "WasmMemoryToResizableBufferGenerator", + inContext: .single(.javascript), + inputs: .required(.object(ofGroup: "WasmMemory")), + produces: [.jsArrayBuffer | .jsSharedArrayBuffer] + ) { b, memory in + b.callMethod("toResizableBuffer", on: memory) + }, + + CodeGenerator( + "WasmMemoryToFixedLengthBufferGenerator", + inContext: .single(.javascript), + inputs: .required(.object(ofGroup: "WasmMemory")), + produces: [.jsArrayBuffer | .jsSharedArrayBuffer] + ) { b, memory in + b.callMethod("toFixedLengthBuffer", on: memory) + }, + + CodeGenerator( + "WasmMemoryJSGrowGenerator", + inContext: .single(.javascript), + inputs: .required(.object(ofGroup: "WasmMemory")) + ) { b, memory in + b.callMethod("grow", on: memory, withArgs: [b.loadInt(Int64.random(in: 0...10))]) + }, ] fileprivate let wasmArrayTypeGenerator = GeneratorStub( diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index 83c2509bb..ede66726a 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -18,6 +18,13 @@ public class JavaScriptEnvironment: ComponentBase { // Possible return values of the 'typeof' operator. public static let jsTypeNames = ["undefined", "boolean", "number", "string", "symbol", "function", "object", "bigint"] + // TODO: use it in all places where it can be used. + public static let typedArrayConstructors = [ + "Uint8Array", "Int8Array", "Uint16Array", "Int16Array", + "Uint32Array", "Int32Array", "Float32Array", "Float64Array", + "Uint8ClampedArray", "BigInt64Array", "BigUint64Array", + ] + // Integer values that are more likely to trigger edge-cases. public static let InterestingIntegers: [Int64] = [ -9223372036854775808, -9223372036854775807, // Int64 min, mostly for BigInts diff --git a/Tests/FuzzilliTests/JSTyperTests.swift b/Tests/FuzzilliTests/JSTyperTests.swift index 56ab52d11..181f128f3 100644 --- a/Tests/FuzzilliTests/JSTyperTests.swift +++ b/Tests/FuzzilliTests/JSTyperTests.swift @@ -1904,4 +1904,25 @@ class JSTyperTests: XCTestCase { XCTAssert(b.type(of: result).Is(requestedCtor)) } } + + func testBufferUnionType() { + // Explicitly verify the properties of the ArrayBuffer | SharedArrayBuffer union type. + let unionType = ILType.jsArrayBuffer | .jsSharedArrayBuffer + + XCTAssert(unionType.Is(.jsArrayBuffer | .jsSharedArrayBuffer)) + XCTAssert(unionType.MayBe(.jsArrayBuffer)) + XCTAssert(unionType.MayBe(.jsSharedArrayBuffer)) + XCTAssertFalse(unionType.Is(.jsArrayBuffer)) // It's not *definitely* an ArrayBuffer + XCTAssertFalse(unionType.Is(.jsSharedArrayBuffer)) + + // Common properties should be preserved + XCTAssert(unionType.properties.contains("byteLength")) + XCTAssert(unionType.methods.contains("slice")) + + // Disjoint properties/methods should be removed in the union + XCTAssertFalse(unionType.methods.contains("resize")) // Only on ArrayBuffer + XCTAssertFalse(unionType.methods.contains("grow")) // Only on SharedArrayBuffer + + XCTAssertNil(unionType.group) + } } diff --git a/Tests/FuzzilliTests/ProgramBuilderTest.swift b/Tests/FuzzilliTests/ProgramBuilderTest.swift index 5a9c53b63..938bde343 100644 --- a/Tests/FuzzilliTests/ProgramBuilderTest.swift +++ b/Tests/FuzzilliTests/ProgramBuilderTest.swift @@ -3013,6 +3013,33 @@ class ProgramBuilderTests: XCTestCase { } } + func testTypedArrayFromBufferGenerator() { + let fuzzer = makeMockFuzzer() + let numPrograms = 50 + + for _ in 0...numPrograms { + let b = fuzzer.makeBuilder() + b.buildPrefix() + + let generator = fuzzer.codeGenerators.filter { + $0.name == "TypedArrayFromBufferGenerator" + }[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 _ = b.finalize() + + // XCTAssertGreaterThan(numGeneratedInstructions, 0) + // TODO(tacet): Fails in around 5% of times, we should figure out how to fix it. + } + } + func testArrayGetSchedulingTest() { let fuzzer = makeMockFuzzer() let numPrograms = 30 From d334575f8afc42adad7e364b919b42f0dc59a966 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Mon, 15 Dec 2025 14:12:25 +0100 Subject: [PATCH 40/89] Increase depth of configured test-transpilation results In the overall results directory tree, certain directories contain large and interesting subtrees (e.g. large directories with low import coverage). This change enables us listing such additional directories to expand their subdirectories in the results mapping. E.g. if now a directory is listed in this config, the directory itself and one level of subdirectories below are now also listed as single result entities. Bug: 442444727 Change-Id: Iba585221622c054985f2307389fccf35d3b10fec Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8845316 Commit-Queue: Michael Achenbach Reviewed-by: Pawel Krawczyk Reviewed-by: Liviu Rau --- Tools/transpile_tests/test_transpile_tests.py | 74 ++++++++++++++++++- Tools/transpile_tests/transpile_tests.py | 36 ++++++++- 2 files changed, 103 insertions(+), 7 deletions(-) diff --git a/Tools/transpile_tests/test_transpile_tests.py b/Tools/transpile_tests/test_transpile_tests.py index 3e6277428..a388763ec 100644 --- a/Tools/transpile_tests/test_transpile_tests.py +++ b/Tools/transpile_tests/test_transpile_tests.py @@ -162,12 +162,12 @@ def test_shard_run(self, fs): class UnitTests(unittest.TestCase): def test_level_key_0(self): - counter = transpile_tests.TestCounter(0, Options(False)) + counter = transpile_tests.TestCounter(0, [], Options(False)) self.assertEqual('', counter.level_key(Path('test.js'))) self.assertEqual('', counter.level_key(Path('level1/test.js'))) def test_level_key_2(self): - counter = transpile_tests.TestCounter(2, Options(False)) + counter = transpile_tests.TestCounter(2, [], Options(False)) self.assertEqual( '', counter.level_key(Path('test.js'))) self.assertEqual( @@ -179,8 +179,69 @@ def test_level_key_2(self): 'level1/level2', counter.level_key(Path('level1/level2/level3/test.js'))) + def test_level_key_expansion(self): + expand_level_paths = ['a'] + counter = transpile_tests.TestCounter( + 1, expand_level_paths, Options(False)) + self.assertEqual( + '', counter.level_key(Path('test.js'))) + self.assertEqual( + 'a', counter.level_key(Path('a/test.js'))) + self.assertEqual( + 'a/b', + counter.level_key(Path('a/b/test.js'))) + self.assertEqual( + 'a/b', + counter.level_key(Path('a/b/c/test.js'))) + self.assertEqual( + 'b', + counter.level_key(Path('b/c/test.js'))) + + def test_level_key_deep_expansion(self): + expand_level_paths = ['a/b/c', 'c/d/e'] + counter = transpile_tests.TestCounter( + 1, expand_level_paths, Options(False)) + self.assertEqual( + 'a', counter.level_key(Path('a/c/test.js'))) + self.assertEqual( + 'a', counter.level_key(Path('a/b/x/test.js'))) + self.assertEqual( + 'c', counter.level_key(Path('c/a/test.js'))) + self.assertEqual( + 'a/b/c', + counter.level_key(Path('a/b/c/test.js'))) + self.assertEqual( + 'a/b/c/d', + counter.level_key(Path('a/b/c/d/test.js'))) + self.assertEqual( + 'a/b/c/d', + counter.level_key(Path('a/b/c/d/e/test.js'))) + self.assertEqual( + 'c/d/e/f', + counter.level_key(Path('c/d/e/f/g/test.js'))) + + def test_level_key_expansion_with_prefix(self): + expand_level_paths = ['a/b', 'a/b/c'] + counter = transpile_tests.TestCounter( + 0, expand_level_paths, Options(False)) + self.assertEqual( + '', + counter.level_key(Path('a/test.js'))) + self.assertEqual( + 'a/b', + counter.level_key(Path('a/b/test.js'))) + self.assertEqual( + 'a/b/c', + counter.level_key(Path('a/b/c/test.js'))) + self.assertEqual( + 'a/b/c/d', + counter.level_key(Path('a/b/c/d/test.js'))) + self.assertEqual( + 'a/b/c/d', + counter.level_key(Path('a/b/c/d/e/test.js'))) + def test_simple_counts(self): - counter = transpile_tests.TestCounter(1, Options(False)) + counter = transpile_tests.TestCounter(1, [], Options(False)) self.assertEqual({}, counter.results()) counter.count(0, Path('pass1.js'), 'good') @@ -210,7 +271,10 @@ def test_simple_counts(self): counter.results()) def test_complex_count(self): - counter = transpile_tests.TestCounter(2, Options(False)) + # Path 'A1/B2/C3' will be expanded beyond the configured level 2. + expand_level_paths = ['A1/B2/C3'] + counter = transpile_tests.TestCounter( + 2, expand_level_paths, Options(False)) counter.count(0, Path('A1/A2/3/pass1.js'), 'good') counter.count(0, Path('A1/B2/3/pass1.js'), 'good') counter.count(1, Path('A1/B2/3/fail1.js'), 'bad') @@ -223,6 +287,7 @@ def test_complex_count(self): counter.count(1, Path('fail5.js'), 'bad') counter.count(1, Path('fail6.js'), 'bad') counter.count(0, Path('A1/B2/3/pass4.js'), 'good') + counter.count(0, Path('A1/B2/C3/D4/pass5.js'), 'good') self.assertEqual( { @@ -238,6 +303,7 @@ def test_complex_count(self): 'A1/B2': {'num_tests': 3, 'failures':[ {'output': 'bad', 'path': 'A1/B2/3/fail1.js'}, ]}, + 'A1/B2/C3/D4': {'num_tests': 1, 'failures': []}, 'B1/A2': {'num_tests': 2, 'failures':[ {'output': 'bad', 'path': 'B1/A2/fail2.js'}, ]}, diff --git a/Tools/transpile_tests/transpile_tests.py b/Tools/transpile_tests/transpile_tests.py index 106895e2e..02b913a18 100644 --- a/Tools/transpile_tests/transpile_tests.py +++ b/Tools/transpile_tests/transpile_tests.py @@ -71,6 +71,17 @@ def is_supported(self, abspath, relpath): 'path': 'test/test262/data/test', 'excluded_suffixes': ['_FIXTURE.js'], 'levels': 2, + 'expand_level_paths': [ + 'annexB/language', + 'built-ins/Object', + 'built-ins/RegExp', + 'built-ins/Temporal', + 'intl402/Temporal', + 'language/expressions', + 'language/expressions/class', + 'language/statements', + 'language/statements/class', + ], 'metadata_parser': Test262MetaDataParser, } } @@ -84,6 +95,10 @@ class TestCounter: counting tests, each tests goes into a results bucket for its key, e.g.: With level 2, test 'a/b/c/d/test1.js' will have key 'a/b'. + With `expand_level_paths` we can configure those paths under which we + should expand results beyond the configured level. Each listed path + will create result keys with one level deeper than that path. + The final results structure is a dict key -> results, where results is a dict: { 'num_tests': , 'failures': } @@ -101,8 +116,11 @@ class TestCounter: }, } """ - def __init__(self, levels, options): + def __init__(self, levels, expand_level_paths, options): self.levels = levels + # Cache the depth level of each expansion path + self.expand_level_paths = [ + (p, p.count('/') + 1) for p in expand_level_paths] self.options = options self.num_tests = Counter() self.failures = defaultdict(list) @@ -113,13 +131,24 @@ def __init__(self, levels, options): def num_successes(self): return self.total_tests - self.num_failures + def largest_expansion_level(self, relpath): + """Returns the level until which relpath should be expanded, based on + the configured expansion paths. + """ + path_str = str(relpath) + candidates = [-1] + for path, count in self.expand_level_paths: + if path_str.startswith(path): + candidates.append(count) + return max(candidates) + 1 + def level_key(self, relpath): parts = list(relpath.parts) assert parts assert parts[0] != '/' parts = parts[:-1] - parts = parts[:self.levels] + parts = parts[:max(self.levels, self.largest_expansion_level(relpath))] return '/'.join(parts) def count(self, exit_code, relpath, stdout): @@ -214,7 +243,8 @@ def test_input_gen(): yield (abspath, output_dir / abspath.relative_to(base_dir)) # Iterate over all tests in parallel and collect stats. - counter = TestCounter(test_config['levels'], options) + counter = TestCounter( + test_config['levels'], test_config['expand_level_paths'], options) with multiprocessing.Pool(multiprocessing.cpu_count()) as pool: for exit_code, abspath, stdout in pool.imap_unordered( transpile_test, test_input_gen()): From d3450fa8ea46f385e3a547c9db4cfcee72f5b180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Gro=C3=9F?= Date: Mon, 15 Dec 2025 11:30:58 +0100 Subject: [PATCH 41/89] Abort runtime-assisted mutations on FuzzIL translation errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, if we caught an error during translation of a RuntimeAssistedMutator's Actions back to FuzzIL instructions, we simply logged an error but otherwise continued the mutation. This, however, can lead to invalid programs as we're essentially deleting the current instruction (which may e.g. have produced an output needed later on). This might then lead to various other crashes later on. Instead, we should simply abort the mutation when we see such an error. Bug: 468928010 Change-Id: Iefd1010d9c7bd72444d5be1258f81b3063f7b39b Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8849276 Reviewed-by: Michael Achenbach Commit-Queue: Samuel Groß --- Sources/Fuzzilli/Mutators/FixupMutator.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Sources/Fuzzilli/Mutators/FixupMutator.swift b/Sources/Fuzzilli/Mutators/FixupMutator.swift index 957a59f14..fc9865bed 100644 --- a/Sources/Fuzzilli/Mutators/FixupMutator.swift +++ b/Sources/Fuzzilli/Mutators/FixupMutator.swift @@ -287,9 +287,18 @@ public class FixupMutator: RuntimeAssistedMutator { assert(!op.hasOutput || b.visibleVariables.last == instr.output) assert(op.originalOperation == b.lastInstruction().op.name) // We expect the old and new operations to be the same (but potentially performed on different inputs) } catch ActionError.actionTranslationError(let msg) { + // In case of an error we won't have emitted an instruction. As such, we need + // to abort the mutation here (unfortunately), as we might now have an + // inconsistent program (we essentially dropped a random instruction that e.g. + // may have produced an output variable used later on). + // This is mostly unexpected (so we log an error), but can legitimately happen + // during V8 sandbox fuzzing as we will randomly corrupt heap memory there. logger.error("Failed to process action: \(msg)") + return (nil, .unexpectedError) } catch { logger.error("Unexpected error during action processing \(error)") + // Same as above, we need to abort the mutation here. + return (nil, .unexpectedError) } b.trace("Fixup done") if verbose && modifiedActions.contains(action.id) { From 1b58e0c3ce5504b755a9b73f72e1fbd4235ed739 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Tue, 16 Dec 2025 10:21:10 +0100 Subject: [PATCH 42/89] Unwrap JS parser errors on import for better readability Without this, the wrapped error's text is later shown with encoded linebreaks ("\n"). Bug: 442444727 Change-Id: I2b0aa87d7582d83a8339b105f03ac87df59da873 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8852616 Reviewed-by: Pawel Krawczyk Commit-Queue: Michael Achenbach --- Sources/FuzzILTool/main.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/FuzzILTool/main.swift b/Sources/FuzzILTool/main.swift index bff607265..3843dbc1f 100644 --- a/Sources/FuzzILTool/main.swift +++ b/Sources/FuzzILTool/main.swift @@ -173,8 +173,12 @@ else if args.has("--compile") { let ast: JavaScriptParser.AST do { ast = try parser.parse(path) + } catch JavaScriptParser.ParserError.parsingFailed(let error) { + // Unwrap the JS parse error here to format its linebreaks. + print("Failed to parse \(path):\n\(error)") + exit(-1) } catch { - print("Failed to parse \(path): \(error)") + print("Failed to parse \(path):\n\(error)") exit(-1) } From 11e6d6a9a6840337d9cf759dadf10b17cfd89b14 Mon Sep 17 00:00:00 2001 From: Pawel Krawczyk Date: Mon, 15 Dec 2025 17:01:43 +0000 Subject: [PATCH 43/89] Shared reference - adjust type system. See https://github.com/WebAssembly/shared-everything-threads/blob/main/proposals/shared-everything-threads/Overview.md. Bug: 448349112 Change-Id: Ifcc6666c0f3c282078954902853dff23b72e43f9 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8832117 Reviewed-by: Danylo Mocherniuk Commit-Queue: Pawel Krawczyk --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 82 +++++---- Sources/Fuzzilli/CodeGen/CodeGenerator.swift | 6 +- .../Fuzzilli/CodeGen/ProgramTemplates.swift | 8 +- .../Fuzzilli/CodeGen/WasmCodeGenerators.swift | 26 +-- .../Environment/JavaScriptEnvironment.swift | 2 +- Sources/Fuzzilli/FuzzIL/Instruction.swift | 39 +++-- Sources/Fuzzilli/FuzzIL/JSTyper.swift | 13 +- Sources/Fuzzilli/FuzzIL/TypeSystem.swift | 122 +++++++++++--- Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 17 +- .../Fuzzilli/Lifting/JavaScriptLifter.swift | 23 +-- Sources/Fuzzilli/Lifting/WasmLifter.swift | 87 +++++----- .../Fuzzilli/Mutators/OperationMutator.swift | 32 ++-- .../Profiles/V8CommonProfile.swift | 4 +- Tests/FuzzilliTests/JSTyperTests.swift | 8 +- Tests/FuzzilliTests/LifterTest.swift | 2 +- Tests/FuzzilliTests/LiveTests.swift | 7 +- Tests/FuzzilliTests/ProgramBuilderTest.swift | 52 +++--- Tests/FuzzilliTests/TypeSystemTest.swift | 107 ++++++------ Tests/FuzzilliTests/WasmTableTests.swift | 4 +- Tests/FuzzilliTests/WasmTests.swift | 159 ++++++++++-------- 20 files changed, 457 insertions(+), 343 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index c8de47c92..3a5080ca9 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -1217,7 +1217,6 @@ public class ProgramBuilder { numberOfHiddenVariables -= 1 } - /// Hides a variable containing a function from the function's body. /// /// For example, in @@ -1250,19 +1249,20 @@ public class ProgramBuilder { unhide(variableToHide) } + // TODO(pawkra): enable shared types. private static func matchingWasmTypes(jsType: ILType) -> [ILType] { if jsType.Is(.integer) { return [.wasmi32, .wasmf64, .wasmf32] } else if jsType.Is(.number) { - return [.wasmf32, .wasmf64, .wasmi32, .wasmRefI31, .wasmI31Ref] + return [.wasmf32, .wasmf64, .wasmi32, .wasmRefI31(), .wasmI31Ref()] } else if jsType.Is(.bigint) { return [.wasmi64] } else if jsType.Is(.function()) { // TODO(gc): Add support for specific signatures. - return [.wasmFuncRef] + return [.wasmFuncRef()] } else { // TODO(gc): Add support for types of the anyref hierarchy. - return [.wasmExternRef] + return [.wasmExternRef()] } } @@ -1272,6 +1272,7 @@ public class ProgramBuilder { } // Helper that converts a Wasm type to its deterministic known JS counterparts. + // TODO(pawkra): enable shared types. private static func mapWasmToJsType(_ type: ILType) -> ILType { if type.Is(.wasmi32) { return .integer @@ -1288,12 +1289,12 @@ public class ProgramBuilder { return .jsAnything } else if type.Is(.nothing) { return .undefined - } else if type.Is(.wasmFuncRef) { + } else if type.Is(.wasmFuncRef()) { // TODO(cffsmith): refine this type with the signature if we can. return .function() - } else if type.Is(.wasmI31Ref) { + } else if type.Is(.wasmI31Ref()) { return .integer - } else if type.Is(.wasmNullRef) || type.Is(.wasmNullExternRef) || type.Is(.wasmNullFuncRef) { + } else if type.Is(.wasmNullRef()) || type.Is(.wasmNullExternRef()) || type.Is(.wasmNullFuncRef()) { // This is slightly imprecise: The null types only accept null, not undefined but // Fuzzilli doesn't differentiate between null and undefined in its type system. return .nullish @@ -3885,8 +3886,9 @@ public class ProgramBuilder { b.emit(WasmDropElementSegment(), withInputs: [elementSegment], types: [.wasmElementSegment()]) } + // TODO(pawkra): support shared tables and element segments. public func wasmTableInit(elementSegment: Variable, table: Variable, tableOffset: Variable, elementSegmentOffset: Variable, nrOfElementsToUpdate: Variable) { - let elementSegmentType = ILType.wasmFuncRef + let elementSegmentType = ILType.wasmFuncRef() let tableElemType = b.type(of: table).wasmTableType!.elementType assert(elementSegmentType.Is(tableElemType)) @@ -4023,18 +4025,22 @@ public class ProgramBuilder { types: [.wasmTypeDef()] + signature.outputTypes).outputs) } + // TODO(pawkra): enable shared types. @discardableResult func wasmBuildTryTable(with signature: WasmSignature, args: [Variable], catches: [WasmBeginTryTable.CatchKind], body: (Variable, [Variable]) -> [Variable]) -> [Variable] { assert(zip(signature.parameterTypes, args).allSatisfy {b.type(of: $1).Is($0)}) #if DEBUG var argIndex = signature.parameterTypes.count + let assertLabelTypeData: (ILType) -> () = { labelType in + assert(labelType.Is(.anyLabel)) + assert(labelType.wasmLabelType!.parameters.last!.Is(.wasmExnRef())) + } for catchKind in catches { switch catchKind { case .Ref: assert(b.type(of: args[argIndex]).Is(.object(ofGroup: "WasmTag"))) let labelType = b.type(of: args[argIndex + 1]) - assert(labelType.Is(.anyLabel)) - assert(labelType.wasmLabelType!.parameters.last!.Is(.wasmExnRef)) + assertLabelTypeData(labelType) argIndex += 2 case .NoRef: assert(b.type(of: args[argIndex]).Is(.object(ofGroup: "WasmTag"))) @@ -4042,8 +4048,7 @@ public class ProgramBuilder { argIndex += 2 case .AllRef: let labelType = b.type(of: args[argIndex]) - assert(labelType.Is(.anyLabel)) - assert(labelType.wasmLabelType!.parameters.last!.Is(.wasmExnRef)) + assertLabelTypeData(labelType) argIndex += 1 case .AllNoRef: assert(b.type(of: args[argIndex]).Is(.anyLabel)) @@ -4107,7 +4112,7 @@ public class ProgramBuilder { } public func wasmBuildThrowRef(exception: Variable) { - b.emit(WasmThrowRef(), withInputs: [exception], types: [.wasmExnRef]) + b.emit(WasmThrowRef(), withInputs: [exception], types: [.wasmExnRef()]) } public func wasmBuildLegacyRethrow(_ exceptionLabel: Variable) { @@ -4148,15 +4153,22 @@ public class ProgramBuilder { // TODO(cffsmith): Can we improve this once we have better support for ad hoc // code generation in other contexts? switch type.wasmReferenceType?.kind { - case .Abstract(let heapType): - if heapType == .WasmI31 { - // Prefer generating a non-null value. - return probability(0.2) && type.wasmReferenceType!.nullability - ? self.wasmRefNull(type: type) - : self.wasmRefI31(self.consti32(Int32(truncatingIfNeeded: b.randomInt()))) + case .Abstract(let heapTypeInfo): + // TODO(pawkra): add support for shared refs. + assert(!heapTypeInfo.shared) + if probability(0.2) && type.wasmReferenceType!.nullability { + return self.wasmRefNull(type: type) + } + // Prefer generating a non-null value. + if heapTypeInfo.heapType == .WasmI31 { + return self.wasmRefI31(self.consti32(Int32(truncatingIfNeeded: b.randomInt()))) + } + // TODO(pawkra): support other non-nullable types. + if (type.wasmReferenceType!.nullability) { + return self.wasmRefNull(type: type) + } else { + return nil } - assert(type.wasmReferenceType!.nullability) - return self.wasmRefNull(type: type) case .Index(_), .none: break // Unimplemented @@ -4355,17 +4367,17 @@ public class ProgramBuilder { @discardableResult public func wasmI31Get(_ refI31: Variable, isSigned: Bool) -> Variable { - return b.emit(WasmI31Get(isSigned: isSigned), withInputs: [refI31], types: [.wasmI31Ref]).output + return b.emit(WasmI31Get(isSigned: isSigned), withInputs: [refI31]).output } @discardableResult public func wasmAnyConvertExtern(_ ref: Variable) -> Variable { - b.emit(WasmAnyConvertExtern(), withInputs: [ref], types: [.wasmExternRef]).output + b.emit(WasmAnyConvertExtern(), withInputs: [ref]).output } @discardableResult public func wasmExternConvertAny(_ ref: Variable) -> Variable { - b.emit(WasmExternConvertAny(), withInputs: [ref], types: [.wasmAnyRef]).output + b.emit(WasmExternConvertAny(), withInputs: [ref]).output } } @@ -4434,13 +4446,14 @@ public class ProgramBuilder { @discardableResult public func addElementSegment(elements: [Variable]) -> Variable { - let inputTypes = Array(repeating: getEntryTypeForTable(elementType: ILType.wasmFuncRef), count: elements.count) + let inputTypes = Array(repeating: getEntryTypeForTable(elementType: ILType.wasmFuncRef()), count: elements.count) return b.emit(WasmDefineElementSegment(size: UInt32(elements.count)), withInputs: elements, types: inputTypes).output } + // TODO(pawkra): support tables of shared elements. public func getEntryTypeForTable(elementType: ILType) -> ILType { switch elementType { - case .wasmFuncRef: + case .wasmFuncRef(): return .wasmFunctionDef() | .function() default: return .object() @@ -4516,6 +4529,7 @@ public class ProgramBuilder { /// Produces a WasmGlobal that is valid to create in the given Context. public func randomWasmGlobal(forContext context: Context) -> WasmGlobal { + // TODO(pawkra): enable shared element types. switch context { case .javascript: // These are valid in JS according to: https://webassembly.github.io/spec/js-api/#globals. @@ -4545,21 +4559,23 @@ public class ProgramBuilder { // TODO(mliedtke): The list of types should be shared with function signature generation // etc. We should also support non-nullable references but that requires being able // to generate valid ones which currently isn't the case for most of them. + // TODO(pawkra): enable shared types. return (0.. WasmSignature { // TODO: generalize this to support more types. Also add support for simd128 and // (null)exnref, note however that these types raise exceptions when used from JS. + // TODO(pawkra): enable shared types. let valueTypes: [ILType] = [.wasmi32, .wasmi64, .wasmf32, .wasmf64] - let abstractRefTypes: [ILType] = [.wasmExternRef, .wasmAnyRef, .wasmI31Ref] - let nullTypes: [ILType] = [.wasmNullRef, .wasmNullExternRef, .wasmNullFuncRef] + let abstractRefTypes: [ILType] = [.wasmExternRef(), .wasmAnyRef(), .wasmI31Ref()] + let nullTypes: [ILType] = [.wasmNullRef(), .wasmNullExternRef(), .wasmNullFuncRef()] let randomType = { chooseUniform( from: chooseBiased(from: [nullTypes, abstractRefTypes, valueTypes], factor: 1.5)) @@ -4574,9 +4590,10 @@ public class ProgramBuilder { // abstract heap types. To be able to emit them, generateRandomWasmVar() needs to be able // to generate a sequence that produces such a non-nullable value which might be difficult // for some types as of now. + // TODO(pawkra): enable shared types. (0.. [Variable] { @@ -5339,3 +5356,4 @@ public class ProgramBuilder { } } + diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerator.swift b/Sources/Fuzzilli/CodeGen/CodeGenerator.swift index 174e72924..61d9e181e 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerator.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerator.swift @@ -105,7 +105,7 @@ public class GeneratorStub: Contributor { if type.Is(.wasmTypeDef()) { type.wasmTypeDefinition?.description is WasmArrayTypeDescription } else if type.Is(.anyNonNullableIndexRef) { - type.Is(.wasmArrayRef) + type.Is(.wasmArrayRef()) } else { false } @@ -113,7 +113,7 @@ public class GeneratorStub: Contributor { if type.Is(.wasmTypeDef()) { type.wasmTypeDefinition?.description is WasmStructTypeDescription } else if type.Is(.anyNonNullableIndexRef) { - type.Is(.wasmStructRef) + type.Is(.wasmStructRef()) } else { false } @@ -121,7 +121,7 @@ public class GeneratorStub: Contributor { if type.Is(.wasmTypeDef()) { type.wasmTypeDefinition?.description is WasmSignatureTypeDescription } else if type.Is(.anyNonNullableIndexRef) { - type.Is(.wasmFuncRef) + type.Is(.wasmFuncRef()) } else { false } diff --git a/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift b/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift index 4f859ba4d..3c257d5f2 100644 --- a/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift +++ b/Sources/Fuzzilli/CodeGen/ProgramTemplates.swift @@ -99,7 +99,7 @@ public let ProgramTemplates = [ let signature = b.type(of: f!).signature ?? Signature.forUnknownFunction // As we do not yet know what types we have in the Wasm module when we try to call this, let Fuzzilli know that it could potentially use all Wasm types here. - let allWasmTypes: WeightedList = WeightedList([(.wasmi32, 1), (.wasmi64, 1), (.wasmf32, 1), (.wasmf64, 1), (.wasmExternRef, 1), (.wasmFuncRef, 1)]) + let allWasmTypes: WeightedList = WeightedList([(.wasmi32, 1), (.wasmi64, 1), (.wasmf32, 1), (.wasmf64, 1), (.wasmExternRef(), 1), (.wasmFuncRef(), 1)]) var wasmSignature = ProgramBuilder.convertJsSignatureToWasmSignature(signature, availableTypes: allWasmTypes) let wrapped = b.wrapSuspending(function: f!) @@ -152,7 +152,7 @@ public let ProgramTemplates = [ let tagToThrow = chooseUniform(from: wasmTags) let throwParamTypes = b.type(of: tagToThrow).wasmTagType!.parameters let tagToCatchForRethrow = chooseUniform(from: tags) - let catchBlockOutputTypes = b.type(of: tagToCatchForRethrow).wasmTagType!.parameters + [.wasmExnRef] + let catchBlockOutputTypes = b.type(of: tagToCatchForRethrow).wasmTagType!.parameters + [.wasmExnRef()] let module = b.buildWasmModule { wasmModule in // Wasm function that throws a tag, catches a tag (the same or a different one) to @@ -173,7 +173,7 @@ public let ProgramTemplates = [ return catchBlockOutputTypes.map(function.findOrGenerateWasmVar) } b.build(n: 10) - function.wasmBuildThrowRef(exception: b.randomVariable(ofType: .wasmExnRef)!) + function.wasmBuildThrowRef(exception: b.randomVariable(ofType: .wasmExnRef())!) return [] } } @@ -206,7 +206,7 @@ public let ProgramTemplates = [ return calleeSig.outputTypes.map(function.findOrGenerateWasmVar) }} - let table = wasmModule.addTable(elementType: .wasmFuncRef, + let table = wasmModule.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: callees.enumerated().map { (index, callee) in .init(indexInTable: index, signature: calleeSig) diff --git a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift index 9ee6a3375..9de2cbfaa 100644 --- a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift @@ -270,6 +270,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ value: newValue) }, + // TODO(pawkra): add shared variant. CodeGenerator("WasmRefNullGenerator", inContext: .single(.wasmFunction)) { b in let function = b.currentWasmModule.currentWasmFunction if let typeDef = (b.findVariable { b.type(of: $0).Is(.wasmTypeDef()) }), @@ -280,7 +281,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ function.wasmRefNull( type: .wasmRef( .Abstract( - chooseUniform(from: WasmAbstractHeapType.allCases)), + HeapTypeInfo(chooseUniform(from: WasmAbstractHeapType.allCases), shared: false)), nullability: true)) } }, @@ -296,15 +297,18 @@ public let WasmCodeGenerators: [CodeGenerator] = [ b.currentWasmModule.currentWasmFunction.wasmRefI31(value) }, - CodeGenerator("WasmI31GetGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmI31Ref)) { b, ref in + // TODO(pawkra): add shared variant. + CodeGenerator("WasmI31GetGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmI31Ref())) { b, ref in b.currentWasmModule.currentWasmFunction.wasmI31Get(ref, isSigned: Bool.random()) }, - CodeGenerator("WasmAnyConvertExternGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmExternRef)) { b, ref in + // TODO(pawkra): add shared variant. + CodeGenerator("WasmAnyConvertExternGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmExternRef())) { b, ref in b.currentWasmModule.currentWasmFunction.wasmAnyConvertExtern(ref) }, - CodeGenerator("WasmExternConvertAnyGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmAnyRef)) { b, ref in + // TODO(pawkra): add shared variant. + CodeGenerator("WasmExternConvertAnyGenerator", inContext: .single(.wasmFunction), inputs: .required(.wasmAnyRef())) { b, ref in b.currentWasmModule.currentWasmFunction.wasmExternConvertAny(ref) }, @@ -596,7 +600,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ // TODO(manoskouk): Generalize these. let minSize = 10 let maxSize: Int? = nil - let elementType = ILType.wasmFuncRef + let elementType = ILType.wasmFuncRef() let definedEntryIndices: [Int] var definedEntries: [WasmTableType.IndexInTableAndWasmSignature] = [] @@ -609,7 +613,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ // Currently, only generate entries for funcref tables. // TODO(manoskouk): Generalize this. - if elementType == .wasmFuncRef { + if elementType == .wasmFuncRef() { if b.randomVariable(ofType: expectedEntryType) != nil { // There is at least one function in scope. Add some initial entries to the table. // TODO(manoskouk): Generalize this. @@ -638,7 +642,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ }, CodeGenerator("WasmDefineElementSegmentGenerator", inContext: .single(.wasm)) { b in - let expectedEntryType = b.currentWasmModule.getEntryTypeForTable(elementType: ILType.wasmFuncRef) + let expectedEntryType = b.currentWasmModule.getEntryTypeForTable(elementType: .wasmFuncRef()) if b.randomVariable(ofType: expectedEntryType) == nil { return } @@ -669,7 +673,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ inputs: .required(.object(ofGroup: "WasmTable")) ) { b, table in let tableType = b.type(of: table).wasmTableType! - if !tableType.elementType.Is(.wasmFuncRef) { return } + if !tableType.elementType.Is(.wasmFuncRef()) { return } guard let indexedSignature = tableType.knownEntries.randomElement() else { return } @@ -729,7 +733,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ ) { b, table in let function = b.currentWasmModule.currentWasmFunction let tableType = b.type(of: table).wasmTableType! - if !tableType.elementType.Is(.wasmFuncRef) { return } + if !tableType.elementType.Is(.wasmFuncRef()) { return } guard let indexedSignature = (tableType.knownEntries.filter { @@ -1551,7 +1555,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ CodeGenerator( "WasmThrowRefGenerator", inContext: .single(.wasmFunction), - inputs: .required(.wasmExnRef) + inputs: .required(.wasmExnRef()) ) { b, exception in let function = b.currentWasmModule.currentWasmFunction function.wasmBuildThrowRef(exception: exception) @@ -1647,7 +1651,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ [] } if withExnRef { - outputTypes.append(.wasmExnRef) + outputTypes.append(.wasmExnRef()) } function.wasmBeginBlock(with: [] => outputTypes, args: []) return outputTypes diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index ede66726a..03c0014ba 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -2282,7 +2282,7 @@ public extension ObjectGroup { name: "WebAssembly", instanceType: nil, properties: [ - "JSTag": .object(ofGroup: "WasmTag", withWasmType: WasmTagType([.wasmExternRef], isJSTag: true)), + "JSTag": .object(ofGroup: "WasmTag", withWasmType: WasmTagType([.wasmExternRef()], isJSTag: true)), "Module": .jsWebAssemblyModuleConstructor, "Global": .jsWebAssemblyGlobalConstructor, "Instance": .jsWebAssemblyInstanceConstructor, diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index 86f98e4a7..9b3faab7d 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -388,8 +388,8 @@ extension Instruction: ProtobufConvertible { $0.nullability = underlyingWasmType.wasmReferenceType!.nullability } } - case .Abstract(let heapType): - let kind = switch heapType { + case .Abstract(let heapTypeInfo): + let kind = switch heapTypeInfo.heapType { case .WasmExn: Fuzzilli_Protobuf_WasmReferenceTypeKind.exnref case .WasmI31: @@ -1683,37 +1683,40 @@ extension Instruction: ProtobufConvertible { fatalError("Unrecognized wasm value type \(value)") } case .refType(_): - let refKind: WasmReferenceType.Kind = switch wasmType.refType.kind { - case .index: - .Index() + if wasmType.refType.kind == .index { + return .wasmRef(.Index(), nullability: wasmType.refType.nullability) + } + let heapType: WasmAbstractHeapType = switch wasmType.refType.kind { case .externref: - .Abstract(.WasmExtern) + .WasmExtern case .funcref: - .Abstract(.WasmFunc) + .WasmFunc case .exnref: - .Abstract(.WasmExn) + .WasmExn case .i31Ref: - .Abstract(.WasmI31) + .WasmI31 case .anyref: - .Abstract(.WasmAny) + .WasmAny case .eqref: - .Abstract(.WasmEq) + .WasmEq case .structref: - .Abstract(.WasmStruct) + .WasmStruct case .arrayref: - .Abstract(.WasmArray) + .WasmArray case .noneref: - .Abstract(.WasmNone) + .WasmNone case .noexternref: - .Abstract(.WasmNoExtern) + .WasmNoExtern case .nofuncref: - .Abstract(.WasmNoFunc) + .WasmNoFunc case .noexnref: - .Abstract(.WasmNoExn) + .WasmNoExn + case .index: + fatalError("Unexpected index type.") case .UNRECOGNIZED(let value): fatalError("Unrecognized wasm reference type \(value)") } - return .wasmRef(refKind, nullability: wasmType.refType.nullability) + return .wasmRef(heapType, nullability: wasmType.refType.nullability) case .none: fatalError("Absent wasm type") } diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index ae3e96826..1d2cbeec4 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -907,17 +907,20 @@ public struct JSTyper: Analyzer { case .wasmRefIsNull(_): setType(of: instr.output, to: .wasmi32) case .wasmRefI31(_): - setType(of: instr.output, to: .wasmRefI31) + // TODO(pawkra): support shared variant. + setType(of: instr.output, to: .wasmRefI31()) case .wasmI31Get(_): setType(of: instr.output, to: .wasmi32) case .wasmAnyConvertExtern(_): + // TODO(pawkra): forward shared bit & update the comment // any.convert_extern forwards the nullability bit from the input. let null = type(of: instr.input(0)).wasmReferenceType!.nullability - setType(of: instr.output, to: .wasmRef(.Abstract(.WasmAny), nullability: null)) + setType(of: instr.output, to: .wasmRef(.WasmAny, shared: false, nullability: null)) case .wasmExternConvertAny(_): - // extern.convert_any forwards the nullability bit from the input. + // TODO(pawkra): forward shared bit & update the comment + // extern.convert_any forwards the nullability from the input. let null = type(of: instr.input(0)).wasmReferenceType!.nullability - setType(of: instr.output, to: .wasmRef(.Abstract(.WasmExtern), nullability: null)) + setType(of: instr.output, to: .wasmRef(.WasmExtern, shared: false, nullability: null)) case .wasmDefineAdHocSignatureType(let op): startTypeGroup() addSignatureType(def: instr.output, signature: op.signature, inputs: instr.inputs) @@ -1854,7 +1857,7 @@ public struct JSTyper: Analyzer { set(instr.output, .wasmTable(wasmTableType: WasmTableType(elementType: op.tableType.elementType, limits: op.tableType.limits, isTable64: op.tableType.isTable64, knownEntries: []))) case .createWasmJSTag(_): - set(instr.output, .object(ofGroup: "WasmTag", withWasmType: WasmTagType([.wasmExternRef], isJSTag: true))) + set(instr.output, .object(ofGroup: "WasmTag", withWasmType: WasmTagType([.wasmExternRef()], isJSTag: true))) case .createWasmTag(let op): set(instr.output, .object(ofGroup: "WasmTag", withWasmType: WasmTagType(op.parameterTypes))) diff --git a/Sources/Fuzzilli/FuzzIL/TypeSystem.swift b/Sources/Fuzzilli/FuzzIL/TypeSystem.swift index a0aa62eed..b463f8432 100644 --- a/Sources/Fuzzilli/FuzzIL/TypeSystem.swift +++ b/Sources/Fuzzilli/FuzzIL/TypeSystem.swift @@ -247,23 +247,33 @@ public struct ILType: Hashable { public static let wasmi64 = ILType(definiteType: .wasmi64) public static let wasmf32 = ILType(definiteType: .wasmf32) public static let wasmf64 = ILType(definiteType: .wasmf64) - public static let wasmExternRef = ILType.wasmRef(.Abstract(.WasmExtern), nullability: true) - public static let wasmRefExtern = ILType.wasmRef(.Abstract(.WasmExtern), nullability: false) - public static let wasmFuncRef = ILType.wasmRef(.Abstract(.WasmFunc), nullability: true) - public static let wasmExnRef = ILType.wasmRef(.Abstract(.WasmExn), nullability: true) - public static let wasmI31Ref = ILType.wasmRef(.Abstract(.WasmI31), nullability: true) - public static let wasmRefI31 = ILType.wasmRef(.Abstract(.WasmI31), nullability: false) - public static let wasmAnyRef = ILType.wasmRef(.Abstract(.WasmAny), nullability: true) - public static let wasmRefAny = ILType.wasmRef(.Abstract(.WasmAny), nullability: false) - public static let wasmNullRef = ILType.wasmRef(.Abstract(.WasmNone), nullability: true) - public static let wasmNullExternRef = ILType.wasmRef(.Abstract(.WasmNoExtern), nullability: true) - public static let wasmNullFuncRef = ILType.wasmRef(.Abstract(.WasmNoFunc), nullability: true) - public static let wasmEqRef = ILType.wasmRef(.Abstract(.WasmEq), nullability: true) - public static let wasmStructRef = ILType.wasmRef(.Abstract(.WasmStruct), nullability: true) - public static let wasmArrayRef = ILType.wasmRef(.Abstract(.WasmArray), nullability: true) + public static func wasmExternRef(shared: Bool = false) -> ILType { wasmRef(.WasmExtern, shared: shared, nullability: true) } + public static func wasmRefExtern(shared: Bool = false) -> ILType { wasmRef(.WasmExtern, shared: shared, nullability: false) } + public static func wasmFuncRef(shared: Bool = false) -> ILType { wasmRef(.WasmFunc, shared: shared, nullability: true) } + public static func wasmExnRef(shared: Bool = false) -> ILType { wasmRef(.WasmExn, shared: shared, nullability: true) } + public static func wasmI31Ref(shared: Bool = false) -> ILType { wasmRef(.WasmI31, shared: shared, nullability: true) } + public static func wasmRefI31(shared: Bool = false) -> ILType { wasmRef(.WasmI31, shared: shared, nullability: false) } + public static func wasmAnyRef(shared: Bool = false) -> ILType { wasmRef(.WasmAny, shared: shared, nullability: true) } + public static func wasmRefAny(shared: Bool = false) -> ILType { wasmRef(.WasmAny, shared: shared, nullability: false) } + public static func wasmNullRef(shared: Bool = false) -> ILType { wasmRef(.WasmNone, shared: shared, nullability: true) } + public static func wasmNullExternRef(shared: Bool = false) -> ILType { wasmRef(.WasmNoExtern, shared: shared, nullability: true) } + public static func wasmNullFuncRef(shared: Bool = false) -> ILType { wasmRef(.WasmNoFunc, shared: shared, nullability: true) } + public static func wasmEqRef(shared: Bool = false) -> ILType { wasmRef(.WasmEq, shared: shared, nullability: true) } + public static func wasmStructRef(shared: Bool = false) -> ILType { wasmRef(.WasmStruct, shared: shared, nullability: true) } + public static func wasmArrayRef(shared: Bool = false) -> ILType { wasmRef(.WasmArray, shared: shared, nullability: true) } public static let wasmSimd128 = ILType(definiteType: .wasmSimd128) public static let wasmGenericRef = ILType(definiteType: .wasmRef) + public static func allNullableAbstractWasmRefTypes() -> [ILType] { + var refTypes: [ILType] = [] + for sharedRef in [true, false] { + for heapType in WasmAbstractHeapType.allCases { + refTypes.append(wasmRef(heapType, shared: sharedRef, nullability: true)) + } + } + return refTypes + } + static func wasmTypeDef(description: WasmTypeDescription? = nil) -> ILType { let typeDef = WasmTypeDefinition() typeDef.description = description @@ -275,6 +285,10 @@ public struct ILType: Hashable { wasmTypeDef(description: .selfReference) } + static func wasmRef(_ heapType: WasmAbstractHeapType, shared: Bool = false, nullability: Bool = true) -> ILType { + .wasmRef(.Abstract(.init(heapType, shared: shared)), nullability: nullability) + } + static func wasmRef(_ kind: WasmReferenceType.Kind, nullability: Bool) -> ILType { return ILType(definiteType: .wasmRef, ext: TypeExtension( properties: [], methods: [], signature: nil, @@ -286,7 +300,7 @@ public struct ILType: Hashable { } // The union of all primitive wasm types - public static let wasmPrimitive = .wasmi32 | .wasmi64 | .wasmf32 | .wasmf64 | .wasmExternRef | .wasmFuncRef | .wasmI31Ref | .wasmSimd128 | .wasmGenericRef + public static let wasmPrimitive = .wasmi32 | .wasmi64 | .wasmf32 | .wasmf64 | .wasmSimd128 | .wasmGenericRef public static let wasmNumericalPrimitive = .wasmi32 | .wasmi64 | .wasmf32 | .wasmf64 @@ -1098,8 +1112,9 @@ extension ILType: CustomStringConvertible { } let nullPrefix = refType.nullability ? "null " : "" switch refType.kind { - case .Abstract(let heapType): - return ".wasmRef(.Abstract(\(nullPrefix)\(heapType)))" + case .Abstract(let heapTypeInfo): + let sharedPrefix = heapTypeInfo.shared ? "shared " : "" + return ".wasmRef(.Abstract(\(nullPrefix)\(sharedPrefix)\(heapTypeInfo.heapType)))" case .Index(let indexRef): if let desc = indexRef.get() { return ".wasmRef(\(nullPrefix)Index \(desc.format(abbreviate: abbreviate)))" @@ -1400,9 +1415,8 @@ public class WasmTypeDefinition: WasmTypeExtension { } // TODO: Add continuation types for core stack switching. -// TODO: Add shared bit for shared-everything-threads. // TODO: Add internal string type for JS string builtins. -enum WasmAbstractHeapType: CaseIterable, Comparable { +public enum WasmAbstractHeapType: CaseIterable, Comparable { // Note: The union, intersection, ... implementations are inspired by Binaryen's implementation, // so when extending the type system, feel free to use that implemenation as an orientation. // https://github.com/WebAssembly/binaryen/blob/main/src/wasm/wasm-type.cpp @@ -1484,8 +1498,8 @@ enum WasmAbstractHeapType: CaseIterable, Comparable { if self == other { return self } - if self.getBottom() != other.getBottom() { - return nil + if !self.inSameHierarchy(other) { + return nil // Incompatible heap types. } if self.subsumes(other) { return other @@ -1499,6 +1513,57 @@ enum WasmAbstractHeapType: CaseIterable, Comparable { func subsumes(_ other: Self) -> Bool { union(other) == self } + + public static func allNonBottomTypes() -> [WasmAbstractHeapType] { + return WasmAbstractHeapType.allCases.filter { !$0.isBottom() } + } +} + +public class HeapTypeInfo : Hashable { + public let heapType: WasmAbstractHeapType + public let shared: Bool + + init(_ heapType: WasmAbstractHeapType, shared: Bool) { + self.heapType = heapType + self.shared = shared + } + + public static func ==(lhs: HeapTypeInfo, rhs: HeapTypeInfo) -> Bool { + return lhs.heapType == rhs.heapType && lhs.shared == rhs.shared + } + + func union(_ other: HeapTypeInfo) -> HeapTypeInfo? { + if (shared != other.shared) { + return nil; + } + if let unionHeapType = heapType.union(other.heapType) { + return HeapTypeInfo(unionHeapType, shared: shared) + } + return nil + } + + func intersection(_ other: HeapTypeInfo) -> HeapTypeInfo? { + if (shared != other.shared) { + return nil; + } + if let intersectionHeapType = heapType.intersection(other.heapType) { + return HeapTypeInfo(intersectionHeapType, shared: shared) + } + return nil + } + + func subsumes(_ other: HeapTypeInfo) -> Bool { + if (shared != other.shared) { + return false; + } + return heapType.subsumes(other.heapType) + } + + + public func hash(into hasher: inout Hasher) { + hasher.combine(heapType) + hasher.combine(shared) + } } // A wrapper around a WasmTypeDescription without owning the WasmTypeDescription. @@ -1523,7 +1588,7 @@ public class WasmReferenceType: WasmTypeExtension { // corresponding WasmTypeDefinition extension attached to the type of the operation // defining the wasm-gc type (and is kept alive by the JSTyper). case Index(UnownedWasmTypeDescription = UnownedWasmTypeDescription()) - case Abstract(WasmAbstractHeapType) + case Abstract(HeapTypeInfo) func union(_ other: Self) -> Self? { switch self { @@ -2077,10 +2142,10 @@ class WasmTypeDescription: Hashable, CustomStringConvertible { // The "closest" super type that is an abstract type (.WasmArray for arrays, .WasmStruct for // structs). It is nil for unresolved forward/self references for which the concrete abstract // super type is still undecided. - public let abstractHeapSupertype: WasmAbstractHeapType? + public let abstractHeapSupertype: HeapTypeInfo? // TODO(gc): We will also need to support subtyping of struct and array types at some point. - init(typeGroupIndex: Int, superType: WasmAbstractHeapType? = nil) { + init(typeGroupIndex: Int, superType: HeapTypeInfo? = nil) { self.typeGroupIndex = typeGroupIndex self.abstractHeapSupertype = superType } @@ -2110,7 +2175,8 @@ class WasmSignatureTypeDescription: WasmTypeDescription { init(signature: WasmSignature, typeGroupIndex: Int) { self.signature = signature - super.init(typeGroupIndex: typeGroupIndex, superType: .WasmFunc) + // TODO(pawkra): support shared variant. + super.init(typeGroupIndex: typeGroupIndex, superType: HeapTypeInfo.init(.WasmFunc, shared: false)) } override func format(abbreviate: Bool) -> String { @@ -2131,7 +2197,8 @@ class WasmArrayTypeDescription: WasmTypeDescription { init(elementType: ILType, mutability: Bool, typeGroupIndex: Int) { self.elementType = elementType self.mutability = mutability - super.init(typeGroupIndex: typeGroupIndex, superType: .WasmArray) + // TODO(pawkra): support shared variant. + super.init(typeGroupIndex: typeGroupIndex, superType: HeapTypeInfo.init(.WasmArray, shared: false)) } override func format(abbreviate: Bool) -> String { @@ -2162,7 +2229,8 @@ class WasmStructTypeDescription: WasmTypeDescription { init(fields: [Field], typeGroupIndex: Int) { self.fields = fields - super.init(typeGroupIndex: typeGroupIndex, superType: .WasmStruct) + // TODO(pawkra): support shared variant. + super.init(typeGroupIndex: typeGroupIndex, superType: HeapTypeInfo.init(.WasmStruct, shared: false)) } override func format(abbreviate: Bool) -> String { diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index 307ff0346..e5e519fc8 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -695,6 +695,7 @@ public enum WasmGlobal { case wasmf64(Float64) // Empty reference // TODO(gc): Add support for globals with non-nullable references. + // TODO(pawkra): add support for shared references. case externref case exnref case i31ref @@ -716,11 +717,11 @@ public enum WasmGlobal { case .wasmf64: return .wasmf64 case .externref: - return .wasmExternRef + return .wasmExternRef() case .exnref: - return .wasmExnRef + return .wasmExnRef() case .i31ref: - return .wasmI31Ref + return .wasmI31Ref() case .imported(let type): assert(type.wasmGlobalType != nil) return type.wasmGlobalType!.valueType @@ -729,7 +730,7 @@ public enum WasmGlobal { } } - func typeString() -> String { +func typeString() -> String { switch self { case .wasmi64(_): return "i64" @@ -750,8 +751,9 @@ public enum WasmGlobal { } } + // Returns a JS string representing the initial value. - func valueToString() -> String { +func valueToString() -> String { switch self { case .wasmi64(let val): return "\(val)n" @@ -801,9 +803,10 @@ final class WasmDefineTable: WasmOperation { self.definedEntries = definedEntries // TODO(manoskouk): Find a way to define non-function tables with initializers. - assert(elementType == .wasmFuncRef || definedEntries.isEmpty) + let isWasmFuncRef = elementType == .wasmFuncRef() + assert(isWasmFuncRef || definedEntries.isEmpty) - super.init(numInputs: elementType == .wasmFuncRef ? definedEntries.count : 0, + super.init(numInputs: isWasmFuncRef ? definedEntries.count : 0, numOutputs: 1, attributes: [.isMutable], requiredContext: [.wasm]) diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift index fd96d0caf..040a3dc9d 100644 --- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift +++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift @@ -1589,11 +1589,12 @@ public class JavaScriptLifter: Lifter { let LET = w.varKeyword let type: String switch op.tableType.elementType { - case .wasmExternRef: + case .wasmExternRef(): type = "externref" - case .wasmFuncRef: + case .wasmFuncRef(): type = "anyfunc" // TODO(mliedtke): add tables for i31ref. + // TODO(pawkra): add shared ref variants. default: fatalError("Unknown table type") } @@ -1628,21 +1629,21 @@ public class JavaScriptLifter: Lifter { return "\"i64\"" case .wasmSimd128: return "\"v128\"" - case .wasmExternRef: - return "\"externref\"" - case .wasmFuncRef: + case ILType.wasmExternRef(): + return "\"externref\"" + case ILType.wasmFuncRef(): return "\"anyfunc\"" - case .wasmAnyRef: + case ILType.wasmAnyRef(): return "\"anyref\"" - case .wasmEqRef: + case ILType.wasmEqRef(): return "\"eqref\"" - case .wasmI31Ref: + case ILType.wasmI31Ref(): return "\"i31ref\"" - case .wasmStructRef: + case ILType.wasmStructRef(): return "\"structref\"" - case .wasmArrayRef: + case ILType.wasmArrayRef(): return "\"arrayref\"" - case .wasmExnRef: + case ILType.wasmExnRef(): return "\"exnref\"" default: diff --git a/Sources/Fuzzilli/Lifting/WasmLifter.swift b/Sources/Fuzzilli/Lifting/WasmLifter.swift index bcfb03a07..a13968ef1 100644 --- a/Sources/Fuzzilli/Lifting/WasmLifter.swift +++ b/Sources/Fuzzilli/Lifting/WasmLifter.swift @@ -527,33 +527,26 @@ public class WasmLifter { self.bytecode += [0x1, 0x0, 0x0, 0x0] } - private func encodeAbstractHeapType(_ heapType: WasmAbstractHeapType) -> Data { - switch (heapType) { - case .WasmExtern: - return Data([0x6F]) - case .WasmFunc: - return Data([0x70]) - case .WasmAny: - return Data([0x6E]) - case .WasmEq: - return Data([0x6D]) - case .WasmI31: - return Data([0x6C]) - case .WasmStruct: - return Data([0x6B]) - case .WasmArray: - return Data([0x6A]) - case .WasmExn: - return Data([0x69]) - case .WasmNone: - return Data([0x71]) - case .WasmNoExtern: - return Data([0x72]) - case .WasmNoFunc: - return Data([0x73]) - case .WasmNoExn: - return Data([0x74]) - } + private func encodeAbstractHeapType(_ heapTypeInfo: HeapTypeInfo) -> Data { + // Base of v8 implementation. See + // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/wasm-constants.h?q=symbol:ValueTypeCode + let sharedFlagPrefix: [UInt8] = heapTypeInfo.shared ? [0x65] : [] + let opCode: UInt8 = + switch (heapTypeInfo.heapType) { + case .WasmExtern: 0x6F + case .WasmFunc: 0x70 + case .WasmAny: 0x6E + case .WasmEq: 0x6D + case .WasmI31: 0x6C + case .WasmStruct: 0x6B + case .WasmArray: 0x6A + case .WasmExn: 0x69 + case .WasmNone: 0x71 + case .WasmNoExtern: 0x72 + case .WasmNoFunc: 0x73 + case .WasmNoExn: 0x74 + } + return Data(sharedFlagPrefix + [opCode]) } private func encodeWasmGCType(_ description: WasmTypeDescription?) throws -> Data { @@ -568,28 +561,27 @@ public class WasmLifter { let isNullable = refType.nullability let nullabilityByte: UInt8 = isNullable ? 0x63 : 0x64 - switch refType.kind { - case .Index(let description): - return try Data([nullabilityByte]) + encodeWasmGCType(description.get()) - case .Abstract(let heapType): - return Data([nullabilityByte]) + encodeAbstractHeapType(heapType) - } + return try Data([nullabilityByte]) + encodeHeapType(refType.kind) } // HINT: If you crash here, you might not have specified an encoding for your new type in `ILTypeMapping`. return ILTypeMapping[type] ?? ILTypeMapping[defaultType!]! } - private func encodeHeapType(_ type: ILType, defaultType: ILType? = nil) throws -> Data { + private func encodeHeapType(_ refKind: WasmReferenceType.Kind) throws -> Data { + switch refKind { + case .Index(let description): + return try encodeWasmGCType(description.get()) + case .Abstract(let heapTypeInfo): + return encodeAbstractHeapType(heapTypeInfo) + } + } + + // Convienience method to avoid casting to wasmReferenceType on callers side. + private func encodeHeapType(_ type: ILType) throws -> Data { if let refType = type.wasmReferenceType { - switch refType.kind { - case .Index(let description): - return try encodeWasmGCType(description.get()) - case .Abstract(let heapType): - return encodeAbstractHeapType(heapType) - } + return try encodeHeapType(refType.kind) } - // HINT: If you crash here, you might not have specified an encoding for your new type in `ILTypeMapping`. - return ILTypeMapping[type] ?? ILTypeMapping[defaultType!]! + fatalError("This function supports only wasmReferenceType.") } private func buildTypeEntry(for desc: WasmTypeDescription, data: inout Data) throws { @@ -663,7 +655,7 @@ public class WasmLifter { } temp += Leb128.unsignedEncode(signature.outputTypes.count) for outputType in signature.outputTypes { - temp += try encodeType(outputType, defaultType: .wasmExternRef) + temp += try encodeType(outputType, defaultType: .wasmExternRef()) } } @@ -1077,13 +1069,13 @@ public class WasmLifter { case .wasmi64(let val): temporaryInstruction = Instruction(Consti64(value: val), output: Variable()) case .externref: - temp += try! Data([0xD0]) + encodeHeapType(.wasmExternRef) + Data([0x0B]) + temp += try! Data([0xD0]) + encodeHeapType(.wasmExternRef()) + Data([0x0B]) continue case .exnref: - temp += try! Data([0xD0]) + encodeHeapType(.wasmExnRef) + Data([0x0B]) + temp += try! Data([0xD0]) + encodeHeapType(.wasmExnRef()) + Data([0x0B]) continue case .i31ref: - temp += try! Data([0xD0]) + encodeHeapType(.wasmI31Ref) + Data([0x0B]) + temp += try! Data([0xD0]) + encodeHeapType(.wasmI31Ref()) + Data([0x0B]) continue case .refFunc(_), .imported(_): @@ -1505,7 +1497,8 @@ public class WasmLifter { self.exports.append(.global(instr)) case .wasmDefineTable(let tableDef): self.exports.append(.table(instr)) - if tableDef.elementType == .wasmFuncRef { + // TODO(pawkra): support shared refs. + if tableDef.elementType == .wasmFuncRef() { for (value, definedEntry) in zip(instr.inputs, tableDef.definedEntries) { if !typer.type(of: value).Is(.wasmFunctionDef()) { // Check if we need to import the inputs. diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift index 94e7e297c..4f8b7b843 100644 --- a/Sources/Fuzzilli/Mutators/OperationMutator.swift +++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift @@ -369,23 +369,21 @@ public class OperationMutator: BaseInstructionMutator { case .wasmDefineGlobal(let op): // We never change the type of the global, only the value as changing the type will break the following code pretty much instantly. - let wasmGlobal: WasmGlobal - switch op.wasmGlobal.toType() { - case .wasmf32: - wasmGlobal = .wasmf32(Float32(b.randomFloat())) - case .wasmf64: - wasmGlobal = .wasmf64(b.randomFloat()) - case .wasmi32: - wasmGlobal = .wasmi32(Int32(truncatingIfNeeded: b.randomInt())) - case .wasmi64: - wasmGlobal = .wasmi64(b.randomInt()) - case .wasmExternRef, - .wasmExnRef, - .wasmI31Ref: - wasmGlobal = op.wasmGlobal - default: - fatalError("unexpected/unimplemented Value Type!") - } + let wasmGlobal:WasmGlobal = + switch op.wasmGlobal.toType() { + case .wasmf32: + .wasmf32(Float32(b.randomFloat())) + case .wasmf64: + .wasmf64(b.randomFloat()) + case .wasmi32: + .wasmi32(Int32(truncatingIfNeeded: b.randomInt())) + case .wasmi64: + .wasmi64(b.randomInt()) + case ILType.wasmExternRef(), ILType.wasmExnRef(), ILType.wasmI31Ref(): + op.wasmGlobal + default: + fatalError("unexpected/unimplemented Value Type!") + } newOp = WasmDefineGlobal(wasmGlobal: wasmGlobal, isMutable: probability(0.5)) case .wasmDefineTable(let op): // TODO: change table size? diff --git a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift index d53a07dac..620302af2 100644 --- a/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8CommonProfile.swift @@ -561,7 +561,7 @@ public let WasmDeoptFuzzer = WasmProgramTemplate("WasmDeoptFuzzer") { b in } let table = wasmModule.addTable( - elementType: .wasmFuncRef, + elementType: .wasmFuncRef(), minSize: numCallees, definedEntries: (0.. functionSig.outputType let m = b.buildWasmModule { m in - let allWasmTypes: WeightedList = WeightedList([(.wasmi32, 1), (.wasmi64, 1), (.wasmf32, 1), (.wasmf64, 1), (.wasmExternRef, 1), (.wasmFuncRef, 1)]) + let allWasmTypes: WeightedList = WeightedList([(.wasmi32, 1), (.wasmi64, 1), (.wasmf32, 1), (.wasmf64, 1), (.wasmExternRef(), 1), (.wasmFuncRef(), 1)]) let wasmSignature = ProgramBuilder.convertJsSignatureToWasmSignature(wrappedSig, availableTypes: allWasmTypes) m.addWasmFunction(with: wasmSignature) {fbuilder, _, _ in let args = b.randomWasmArguments(forWasmSignature: wasmSignature) diff --git a/Tests/FuzzilliTests/JSTyperTests.swift b/Tests/FuzzilliTests/JSTyperTests.swift index 181f128f3..b5ebb6779 100644 --- a/Tests/FuzzilliTests/JSTyperTests.swift +++ b/Tests/FuzzilliTests/JSTyperTests.swift @@ -1534,7 +1534,7 @@ class JSTyperTests: XCTestCase { } b.doReturn(obj) } - let wasmSignature = [] => [.wasmExternRef] + let wasmSignature = [] => [.wasmExternRef()] let typeDesc = b.type(of: typeGroup[0]).wasmTypeDefinition!.description! @@ -1574,7 +1574,7 @@ class JSTyperTests: XCTestCase { } // Function three - wasmModule.addWasmFunction(with: [.wasmExternRef] => [.wasmi32, .wasmi64]) { function, label, _ in + wasmModule.addWasmFunction(with: [.wasmExternRef()] => [.wasmi32, .wasmi64]) { function, label, _ in return [function.consti32(1), function.consti64(2)] } @@ -1585,7 +1585,7 @@ class JSTyperTests: XCTestCase { // Function five - wasmModule.addWasmFunction(with: [] => [.wasmExternRef]) { function, label, _ in + wasmModule.addWasmFunction(with: [] => [.wasmExternRef()]) { function, label, _ in // This forces an import and we should see a re-exported function on the module. return [function.wasmJsCall(function: plainFunction, withArgs: [], withWasmSignature: wasmSignature)!] } @@ -1795,7 +1795,7 @@ class JSTyperTests: XCTestCase { let wasmTableConstructor = b.getProperty("Table", of: wasm) let wasmTable = b.construct(wasmTableConstructor) // In theory this needs arguments. XCTAssertFalse(b.type(of: wasmTable).Is(.object(ofGroup: "WasmTable"))) - let realWasmTable = b.createWasmTable(elementType: .wasmAnyRef, limits: .init(min: 0), isTable64: false) + let realWasmTable = b.createWasmTable(elementType: .wasmAnyRef(), limits: .init(min: 0), isTable64: false) XCTAssert(b.type(of: realWasmTable).Is(.object(ofGroup: "WasmTable"))) XCTAssert(b.type(of: realWasmTable).Is(ObjectGroup.wasmTable.instanceType)) let tablePrototype = b.getProperty("prototype", of: wasmTableConstructor) diff --git a/Tests/FuzzilliTests/LifterTest.swift b/Tests/FuzzilliTests/LifterTest.swift index 258ed4a02..2744756fb 100644 --- a/Tests/FuzzilliTests/LifterTest.swift +++ b/Tests/FuzzilliTests/LifterTest.swift @@ -3271,7 +3271,7 @@ class LifterTests: XCTestCase { let fuzzer = makeMockFuzzer() let b = fuzzer.makeBuilder() - let table = b.createWasmTable(elementType: .wasmFuncRef, limits: Limits(min: 1), isTable64: true) + let table = b.createWasmTable(elementType: .wasmFuncRef(), limits: Limits(min: 1), isTable64: true) XCTAssertTrue(b.type(of: table).Is(.object(ofGroup: "WasmTable"))) let f = b.buildPlainFunction(with: .parameters(n: 0)) {_ in diff --git a/Tests/FuzzilliTests/LiveTests.swift b/Tests/FuzzilliTests/LiveTests.swift index 4ffb8c5d9..8a6dce290 100644 --- a/Tests/FuzzilliTests/LiveTests.swift +++ b/Tests/FuzzilliTests/LiveTests.swift @@ -133,15 +133,16 @@ class LiveTests: XCTestCase { b.buildTryCatchFinally { // TODO(manoskouk): Once we support wasm-gc types in signatures, we'll need // something more sophisticated. + // TODO(pawkra): support shared refs. let args = wasmSignature.parameterTypes.map { switch $0 { case .wasmi64: return b.loadBigInt(123) - case .wasmFuncRef: + case ILType.wasmFuncRef(): return jsFunction - case .wasmNullExternRef, .wasmNullFuncRef, .wasmNullRef: + case ILType.wasmNullExternRef(), ILType.wasmNullFuncRef(), ILType.wasmNullRef(): return b.loadNull() - case .wasmExternRef, .wasmAnyRef: + case ILType.wasmExternRef(), ILType.wasmAnyRef(): return b.createObject(with: [:]) default: return b.loadInt(321) diff --git a/Tests/FuzzilliTests/ProgramBuilderTest.swift b/Tests/FuzzilliTests/ProgramBuilderTest.swift index 938bde343..97d2c1788 100644 --- a/Tests/FuzzilliTests/ProgramBuilderTest.swift +++ b/Tests/FuzzilliTests/ProgramBuilderTest.swift @@ -2863,21 +2863,21 @@ class ProgramBuilderTests: XCTestCase { let arrayI32Type = b.type(of: arrayI32) XCTAssert(arrayI32Type.Is(.wasmRef(.Index(), nullability: true))) XCTAssert(arrayI32Type.Is(.wasmRef(.Index(), nullability: false))) - XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmArray), nullability: true))) - XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmArray), nullability: false))) - XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmEq), nullability: true))) - XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmEq), nullability: false))) - XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmAny), nullability: true))) - XCTAssert(arrayI32Type.Is(.wasmRef(.Abstract(.WasmAny), nullability: false))) - XCTAssertFalse(arrayI32Type.Is(.wasmRef(.Abstract(.WasmStruct), nullability: true))) - XCTAssertFalse(arrayI32Type.Is(.wasmRef(.Abstract(.WasmStruct), nullability: false))) - XCTAssertFalse(arrayI32Type.Is(.wasmRef(.Abstract(.WasmExn), nullability: false))) + XCTAssert(arrayI32Type.Is(.wasmRef(.WasmArray, nullability: true))) + XCTAssert(arrayI32Type.Is(.wasmRef(.WasmArray, nullability: false))) + XCTAssert(arrayI32Type.Is(.wasmRef(.WasmEq, nullability: true))) + XCTAssert(arrayI32Type.Is(.wasmRef(.WasmEq, nullability: false))) + XCTAssert(arrayI32Type.Is(.wasmRef(.WasmAny, nullability: true))) + XCTAssert(arrayI32Type.Is(.wasmRef(.WasmAny, nullability: false))) + XCTAssertFalse(arrayI32Type.Is(.wasmRef(.WasmStruct, nullability: true))) + XCTAssertFalse(arrayI32Type.Is(.wasmRef(.WasmStruct, nullability: false))) + XCTAssertFalse(arrayI32Type.Is(.wasmRef(.WasmExn, nullability: false))) let arrayI32B = function.wasmArrayNewFixed(arrayType: arrayDefI32B, elements: []) let arrayI32BType = b.type(of: arrayI32B) XCTAssertFalse(arrayI32BType.Is(arrayI32Type)) XCTAssertFalse(arrayI32Type.Is(arrayI32BType)) - let refArrayType = ILType.wasmRef(.Abstract(.WasmArray), nullability: false) + let refArrayType = ILType.wasmRef(.WasmArray, nullability: false) XCTAssertEqual(arrayI32Type.union(with: arrayI32BType), refArrayType) XCTAssertEqual(arrayI32BType.union(with: arrayI32Type), refArrayType) XCTAssertEqual(arrayI32Type.intersection(with: arrayI32BType), .nothing) @@ -2887,14 +2887,14 @@ class ProgramBuilderTests: XCTestCase { let structType = b.type(of: structVar) XCTAssert(structType.Is(.wasmRef(.Index(), nullability: true))) XCTAssert(structType.Is(.wasmRef(.Index(), nullability: false))) - XCTAssert(structType.Is(.wasmRef(.Abstract(.WasmStruct), nullability: false))) - XCTAssert(structType.Is(.wasmRef(.Abstract(.WasmEq), nullability: false))) - XCTAssert(structType.Is(.wasmRef(.Abstract(.WasmAny), nullability: false))) - XCTAssertFalse(structType.Is(.wasmRef(.Abstract(.WasmArray), nullability: true))) - XCTAssertFalse(structType.Is(.wasmRef(.Abstract(.WasmArray), nullability: false))) - XCTAssertFalse(structType.Is(.wasmRef(.Abstract(.WasmExn), nullability: false))) - - let refEqType = ILType.wasmRef(.Abstract(.WasmEq), nullability: false) + XCTAssert(structType.Is(.wasmRef(.WasmStruct, nullability: false))) + XCTAssert(structType.Is(.wasmRef(.WasmEq, nullability: false))) + XCTAssert(structType.Is(.wasmRef(.WasmAny, nullability: false))) + XCTAssertFalse(structType.Is(.wasmRef(.WasmArray, nullability: true))) + XCTAssertFalse(structType.Is(.wasmRef(.WasmArray, nullability: false))) + XCTAssertFalse(structType.Is(.wasmRef(.WasmExn, nullability: false))) + + let refEqType = ILType.wasmRef(.WasmEq, nullability: false) XCTAssertEqual(structType.union(with: arrayI32Type), refEqType) XCTAssertEqual(arrayI32Type.union(with: structType), refEqType) XCTAssertEqual(structType.intersection(with: arrayI32Type), .nothing) @@ -2903,25 +2903,25 @@ class ProgramBuilderTests: XCTestCase { let i31 = function.wasmRefI31(function.consti32(42)) let i31Type = b.type(of: i31) XCTAssertFalse(i31Type.Is(.wasmRef(.Index(), nullability: true))) - XCTAssert(i31Type.Is(.wasmRef(.Abstract(.WasmEq), nullability: false))) - XCTAssert(i31Type.Is(.wasmRef(.Abstract(.WasmAny), nullability: false))) - XCTAssertFalse(i31Type.Is(.wasmRef(.Abstract(.WasmArray), nullability: false))) - XCTAssertFalse(i31Type.Is(.wasmRef(.Abstract(.WasmStruct), nullability: false))) - XCTAssertFalse(i31Type.Is(.wasmRef(.Abstract(.WasmExn), nullability: false))) + XCTAssert(i31Type.Is(.wasmRef(.WasmEq, nullability: false))) + XCTAssert(i31Type.Is(.wasmRef(.WasmAny, nullability: false))) + XCTAssertFalse(i31Type.Is(.wasmRef(.WasmArray, nullability: false))) + XCTAssertFalse(i31Type.Is(.wasmRef(.WasmStruct, nullability: false))) + XCTAssertFalse(i31Type.Is(.wasmRef(.WasmExn, nullability: false))) XCTAssertEqual(structType.union(with: i31Type), refEqType) XCTAssertEqual(arrayI32Type.union(with: i31Type), refEqType) XCTAssertEqual(i31Type.union(with: refEqType), refEqType) XCTAssertEqual(refArrayType.union(with: i31Type), refEqType) - let refStructType = ILType.wasmRef(.Abstract(.WasmStruct), nullability: false) + let refStructType = ILType.wasmRef(.WasmStruct, nullability: false) XCTAssertEqual(i31Type.union(with: refStructType), refEqType) XCTAssertEqual(i31Type.intersection(with: refEqType), i31Type) XCTAssertEqual(refEqType.intersection(with: i31Type), i31Type) - let refNone = ILType.wasmRef(.Abstract(.WasmNone), nullability: false) + let refNone = ILType.wasmRef(.WasmNone, nullability: false) XCTAssertEqual(i31Type.intersection(with: refArrayType), refNone) XCTAssertEqual(refStructType.intersection(with: i31Type), refNone) - XCTAssertEqual(i31Type.intersection(with: .wasmExnRef), .nothing) + XCTAssertEqual(i31Type.intersection(with: .wasmExnRef()), .nothing) return [] } diff --git a/Tests/FuzzilliTests/TypeSystemTest.swift b/Tests/FuzzilliTests/TypeSystemTest.swift index 5e6f0fd77..677032925 100644 --- a/Tests/FuzzilliTests/TypeSystemTest.swift +++ b/Tests/FuzzilliTests/TypeSystemTest.swift @@ -1110,11 +1110,12 @@ class TypeSystemTests: XCTestCase { let strObjOrFuncObj = (ILType.string + ILType.object(withProperties: ["foo"])) | (ILType.function([.rest(.jsAnything)] => .float) + ILType.object(withProperties: ["foo"])) XCTAssertEqual(strObjOrFuncObj.description, ".string + .object(withProperties: [\"foo\"]) | .object(withProperties: [\"foo\"]) + .function()") - let nullExn = ILType.wasmRef(.Abstract(.WasmExn), nullability: true) - let nonNullAny = ILType.wasmRef(.Abstract(.WasmAny), nullability: false) - XCTAssertEqual(nullExn.description, ".wasmRef(.Abstract(null WasmExn))") + let nullExn = ILType.wasmRef(.WasmExn, shared: true, nullability: true) + let nonNullAny = ILType.wasmRef(.WasmAny, shared: false, nullability: false) + XCTAssertEqual(nullExn.description, ".wasmRef(.Abstract(null shared WasmExn))") XCTAssertEqual(nonNullAny.description, ".wasmRef(.Abstract(WasmAny))") + // TODO(pawkra): add shared variant. let arrayDesc = WasmArrayTypeDescription(elementType: .wasmi32, mutability: false, typeGroupIndex: 0) let arrayRef = ILType.wasmIndexRef(arrayDesc, nullability: true) XCTAssertEqual(arrayRef.description, ".wasmRef(null Index 0 Array[immutable .wasmi32])") @@ -1148,11 +1149,11 @@ class TypeSystemTests: XCTestCase { ".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) + signature: [.wasmi32, arrayRef] => [structRef, .wasmNullRef(shared: true)], 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))]])") + "[.wasmRef(Index 1 Struct), .wasmRef(.Abstract(null shared 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 @@ -1163,7 +1164,7 @@ class TypeSystemTests: XCTestCase { } func testWasmSubsumptionRules() { - let wasmTypes: [ILType] = [.wasmi32, .wasmi64, .wasmf32, .wasmf64, .wasmFuncRef, .wasmExternRef, .wasmI31Ref, .wasmExnRef] + let wasmTypes: [ILType] = [.wasmi32, .wasmi64, .wasmf32, .wasmf64] + ILType.allNullableAbstractWasmRefTypes() // Make sure that no Wasm type is subsumed by (JS-)anything. for t in wasmTypes { XCTAssertEqual(t <= .jsAnything, false) @@ -1209,14 +1210,16 @@ class TypeSystemTests: XCTestCase { // Test nullability rules for abstract Wasm types. for heapType: WasmAbstractHeapType in WasmAbstractHeapType.allCases { - let nullable = ILType.wasmRef(.Abstract(heapType), nullability: true) - let nonNullable = ILType.wasmRef(.Abstract(heapType), nullability: false) - XCTAssert(nonNullable.Is(nullable)) - XCTAssertFalse(nullable.Is(nonNullable)) - XCTAssertEqual(nullable.union(with: nonNullable), nullable) - XCTAssertEqual(nonNullable.union(with: nullable), nullable) - XCTAssertEqual(nullable.intersection(with: nonNullable), nonNullable) - XCTAssertEqual(nonNullable.intersection(with: nullable), nonNullable) + for shared in [true, false] { + let nullable = ILType.wasmRef(heapType, shared: shared, nullability: true) + let nonNullable = ILType.wasmRef(heapType, shared: shared, nullability: false) + XCTAssert(nonNullable.Is(nullable)) + XCTAssertFalse(nullable.Is(nonNullable)) + XCTAssertEqual(nullable.union(with: nonNullable), nullable) + XCTAssertEqual(nonNullable.union(with: nullable), nullable) + XCTAssertEqual(nullable.intersection(with: nonNullable), nonNullable) + XCTAssertEqual(nonNullable.intersection(with: nullable), nonNullable) + } } } @@ -1276,7 +1279,6 @@ class TypeSystemTests: XCTestCase { XCTAssertEqual(type.intersection(type.getBottom()), type.getBottom()) } - // Testing a few combinations. XCTAssertEqual(WasmAbstractHeapType.WasmAny.union(.WasmEq), .WasmAny) XCTAssertEqual(WasmAbstractHeapType.WasmStruct.union(.WasmArray), .WasmEq) XCTAssertEqual(WasmAbstractHeapType.WasmI31.union(.WasmArray), .WasmEq) @@ -1287,35 +1289,47 @@ class TypeSystemTests: XCTestCase { XCTAssertEqual(WasmAbstractHeapType.WasmAny.intersection(.WasmArray), .WasmArray) // Tests on the whole ILType. - let ref = {t in ILType.wasmRef(.Abstract(t), nullability: false)} - let refNull = {t in ILType.wasmRef(.Abstract(t), nullability: true)} - for type in allTypes { - let refT = ref(type) - let refNullT = refNull(type) - XCTAssertEqual(refT.union(with: refNullT), refNullT) - XCTAssertEqual(refNullT.union(with: refT), refNullT) - XCTAssertEqual(refT.union(with: refT), refT) - XCTAssertEqual(refNullT.union(with: refNullT), refNullT) - XCTAssertEqual(refT.intersection(with: refT), refT) - XCTAssertEqual(refNullT.intersection(with: refNullT), refNullT) - XCTAssertEqual(refT.intersection(with: refNullT), refT) - XCTAssertEqual(refNullT.intersection(with: refT), refT) + for shared in [true, false] { + let ref: (WasmAbstractHeapType) -> ILType = {t in ILType.wasmRef(t, shared: shared, nullability: false,)} + let refNull = {t in ILType.wasmRef(t, shared: shared, nullability: true)} + + for type in allTypes { + let refT = ref(type) + let refNullT = refNull(type) + XCTAssertEqual(refT.union(with: refNullT), refNullT) + XCTAssertEqual(refNullT.union(with: refT), refNullT) + XCTAssertEqual(refT.union(with: refT), refT) + XCTAssertEqual(refNullT.union(with: refNullT), refNullT) + XCTAssertEqual(refT.intersection(with: refT), refT) + XCTAssertEqual(refNullT.intersection(with: refNullT), refNullT) + XCTAssertEqual(refT.intersection(with: refNullT), refT) + XCTAssertEqual(refNullT.intersection(with: refT), refT) + } + XCTAssertEqual(ref(.WasmAny).union(with: refNull(.WasmEq)), refNull(.WasmAny)) + XCTAssertEqual(ref(.WasmStruct).union(with: ref(.WasmArray)), ref(.WasmEq)) + // We should never do this for the type information of any Variable as .wasmGenericRef + // cannot be encoded in the Wasm module and any instruction that leads to such a static type + // is "broken". However, we will still need to allow this union type if we want to be able + // to request a .required(.wasmGenericRef) for operations like WasmRefIsNull. + XCTAssertEqual(ref(.WasmI31).union(with: refNull(.WasmExn)), .wasmGenericRef) + + XCTAssertEqual(ref(.WasmAny).intersection(with: refNull(.WasmEq)), ref(.WasmEq)) + XCTAssertEqual(refNull(.WasmI31).intersection(with: refNull(.WasmStruct)), refNull(.WasmNone)) + // Note that `ref none` is a perfectly valid type in Wasm but such a reference can never be + // constructed. + XCTAssertEqual(ref(.WasmArray).intersection(with: refNull(.WasmStruct)), ref(.WasmNone)) + XCTAssertEqual(refNull(.WasmArray).intersection(with: ref(.WasmAny)), ref(.WasmArray)) } - XCTAssertEqual(ref(.WasmAny).union(with: refNull(.WasmEq)), refNull(.WasmAny)) - XCTAssertEqual(ref(.WasmStruct).union(with: ref(.WasmArray)), ref(.WasmEq)) - // We should never do this for the type information of any Variable as .wasmGenericRef - // cannot be encoded in the Wasm module and any instruction that leads to such a static type - // is "broken". However, we will still need to allow this union type if we want to be able - // to request a .required(.wasmGenericRef) for operations like WasmRefIsNull. - XCTAssertEqual(ref(.WasmI31).union(with: refNull(.WasmExn)), .wasmGenericRef) - - XCTAssertEqual(ref(.WasmAny).intersection(with: refNull(.WasmEq)), ref(.WasmEq)) - XCTAssertEqual(refNull(.WasmI31).intersection(with: refNull(.WasmStruct)), refNull(.WasmNone)) - // Note that `ref none` is a perfectly valid type in Wasm but such a reference can never be - // constructed. - XCTAssertEqual(ref(.WasmArray).intersection(with: refNull(.WasmStruct)), ref(.WasmNone)) - XCTAssertEqual(refNull(.WasmArray).intersection(with: ref(.WasmAny)), ref(.WasmArray)) + let ref = {t, shared in ILType.wasmRef(t, shared: shared, nullability: false,)} + let refNull = {t, shared in ILType.wasmRef(t, shared: shared, nullability: true)} + // Shared and unshared ref hierarchies are disjoint. + for (lhsShared, rhsShared) in [(true, false), (false, true)] { + for type in allTypes { + XCTAssertEqual(ref(type, lhsShared).union(with: ref(type, rhsShared)), .wasmGenericRef) + XCTAssertEqual(refNull(type, lhsShared).union(with: refNull(type, rhsShared)), .wasmGenericRef) + } + } } func testUnboundFunctionSubsumptionRules() { @@ -1433,15 +1447,10 @@ class TypeSystemTests: XCTestCase { .wasmf32, .wasmi64, .wasmf64, - .wasmFuncRef, - .wasmExternRef, - .wasmExnRef, - .wasmI31Ref, - .wasmRefI31, .wasmFunctionDef([.wasmi32] => [.wasmi64]), .wasmFunctionDef([.wasmf32] => [.wasmi32]), - .wasmFunctionDef([.wasmExternRef] => [.wasmExternRef]), + .wasmFunctionDef([.wasmExternRef()] => [.wasmExternRef()]), .wasmMemory(limits: Limits(min: 10)), .wasmMemory(limits: Limits(min: 10, max: 20)), - ] + ] + ILType.allNullableAbstractWasmRefTypes() } diff --git a/Tests/FuzzilliTests/WasmTableTests.swift b/Tests/FuzzilliTests/WasmTableTests.swift index c3e59c9b7..2e4f9335f 100644 --- a/Tests/FuzzilliTests/WasmTableTests.swift +++ b/Tests/FuzzilliTests/WasmTableTests.swift @@ -22,7 +22,7 @@ class WasmTableTests: XCTestCase { let js = buildAndLiftProgram { b in let module = b.buildWasmModule { wasmModule in - let table = wasmModule.addTable(elementType: .wasmFuncRef, minSize: 10, maxSize: 20, isTable64: false) + let table = wasmModule.addTable(elementType: .wasmFuncRef(), minSize: 10, maxSize: 20, isTable64: false) wasmModule.addWasmFunction(with: [] => [.wasmi32]) { f, _, _ in let size = f.wasmTableSize(table: table) @@ -31,7 +31,7 @@ class WasmTableTests: XCTestCase { expectedOutput += "10\n" wasmModule.addWasmFunction(with: [] => [.wasmi32, .wasmi32]) { f, _, _ in - let initialValue = f.wasmRefNull(type: .wasmFuncRef) + let initialValue = f.wasmRefNull(type: .wasmFuncRef()) let growBy = f.consti32(5) let oldSize = f.wasmTableGrow(table: table, with: initialValue, by: growBy) let newSize = f.wasmTableSize(table: table) diff --git a/Tests/FuzzilliTests/WasmTests.swift b/Tests/FuzzilliTests/WasmTests.swift index 987599fb4..5fad1a6d9 100644 --- a/Tests/FuzzilliTests/WasmTests.swift +++ b/Tests/FuzzilliTests/WasmTests.swift @@ -44,16 +44,16 @@ func testForErrorOutput(program: String, runner: JavaScriptExecutor, errorMessag class WasmSignatureConversionTests: XCTestCase { func testJsSignatureConversion() { - XCTAssertEqual(ProgramBuilder.convertJsSignatureToWasmSignature([.number] => .integer, availableTypes: WeightedList([(.wasmi32, 1), (.wasmFuncRef, 1), (.wasmExternRef, 1)])), [.wasmi32] => [.wasmi32]) - XCTAssertEqual(ProgramBuilder.convertJsSignatureToWasmSignature([.number] => .integer, availableTypes: WeightedList([(.wasmf32, 1), (.wasmFuncRef, 1), (.wasmExternRef, 1)])), [.wasmf32] => [.wasmi32]) + XCTAssertEqual(ProgramBuilder.convertJsSignatureToWasmSignature([.number] => .integer, availableTypes: WeightedList([(.wasmi32, 1), (.wasmFuncRef(), 1), (.wasmExternRef(), 1)])), [.wasmi32] => [.wasmi32]) + XCTAssertEqual(ProgramBuilder.convertJsSignatureToWasmSignature([.number] => .integer, availableTypes: WeightedList([(.wasmf32, 1), (.wasmFuncRef(), 1), (.wasmExternRef(), 1)])), [.wasmf32] => [.wasmi32]) } func testWasmSignatureConversion() { XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmi32, .wasmi64] => [.wasmf32]), [.integer, .bigint] => .float) - XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmi32, .wasmExnRef] => [.wasmf64]), [.integer, .jsAnything] => .float) - XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmExternRef, .wasmFuncRef] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) - XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmRef(.Index(), nullability: false), .wasmFuncRef] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) - XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmRef(.Abstract(.WasmExtern), nullability: false), .wasmFuncRef] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) + XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmi32, .wasmExnRef()] => [.wasmf64]), [.integer, .jsAnything] => .float) + XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmExternRef(), .wasmFuncRef()] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) + XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmRef(.Index(), nullability: false), .wasmFuncRef()] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) + XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmRef(.WasmExtern, nullability: false), .wasmFuncRef()] => [.wasmf64, .wasmf64]), [.jsAnything, .function()] => .jsArray) // TODO(cffsmith): Change this once we know how we want to represent .wasmSimd128 types in JS. XCTAssertEqual(ProgramBuilder.convertWasmSignatureToJsSignature([.wasmSimd128] => [.wasmSimd128]), [.jsAnything] => .jsAnything) } @@ -452,7 +452,7 @@ class WasmFoundationTests: XCTestCase { let module = b.buildWasmModule { wasmModule in // Note that globals of exnref can only be defined in wasm, not in JS. let global = wasmModule.addGlobal(wasmGlobal: .exnref, isMutable: true) - XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmExnRef, isMutable: true))) + XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: .wasmExnRef(), isMutable: true))) wasmModule.addWasmFunction(with: [] => [.wasmi32]) { function, label, args in let value = function.wasmLoadGlobal(globalVariable: global) @@ -461,12 +461,12 @@ class WasmFoundationTests: XCTestCase { // Throw an exception, catch it and store it in the global. wasmModule.addWasmFunction(with: [] => []) { function, label, args in - let exnref = function.wasmBuildBlockWithResults(with: [] => [.wasmExnRef], args: []) { catchLabel, _ in + let exnref = function.wasmBuildBlockWithResults(with: [] => [.wasmExnRef()], args: []) { catchLabel, _ in function.wasmBuildTryTable(with: [] => [], args: [catchLabel], catches: [.AllRef]) { _, _ in function.WasmBuildThrow(tag: tagi32, inputs: [function.consti32(42)]) return [] } - return [function.wasmRefNull(type: .wasmExnRef)] + return [function.wasmRefNull(type: .wasmExnRef())] }[0] function.wasmStoreGlobal(globalVariable: global, to: exnref) return [] @@ -474,12 +474,12 @@ class WasmFoundationTests: XCTestCase { // Rethrow the exception stored in the global, catch it and extract the integer. wasmModule.addWasmFunction(with: [] => [.wasmi32]) { function, label, args in - let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef], args: []) { catchLabel, _ in + let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef()], args: []) { catchLabel, _ in function.wasmBuildTryTable(with: [] => [], args: [tagi32, catchLabel], catches: [.Ref]) { _, _ in function.wasmBuildThrowRef(exception: function.wasmLoadGlobal(globalVariable: global)) return [] } - return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef)] + return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef())] } return [caughtValues[0]] } @@ -510,12 +510,12 @@ class WasmFoundationTests: XCTestCase { let otherModule = b.buildWasmModule { wasmModule in // Rethrow the exception stored in the global, catch it and extract the integer. wasmModule.addWasmFunction(with: [] => [.wasmi32]) { function, label, args in - let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef], args: []) { catchLabel, _ in + let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef()], args: []) { catchLabel, _ in function.wasmBuildTryTable(with: [] => [], args: [tagi32, catchLabel], catches: [.Ref]) { _, _ in function.wasmBuildThrowRef(exception: function.wasmLoadGlobal(globalVariable: global)) return [] } - return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef)] + return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef())] } return [caughtValues[0]] } @@ -539,13 +539,13 @@ class WasmFoundationTests: XCTestCase { let module = b.buildWasmModule { wasmModule in let global = wasmModule.addGlobal(wasmGlobal: .externref, isMutable: true) - XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmExternRef, isMutable: true))) + XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: .wasmExternRef(), isMutable: true))) - wasmModule.addWasmFunction(with: [] => [.wasmExternRef]) { function, label, args in + wasmModule.addWasmFunction(with: [] => [.wasmExternRef()]) { function, label, args in [function.wasmLoadGlobal(globalVariable: global)] } - wasmModule.addWasmFunction(with: [.wasmExternRef] => []) { function, label, args in + wasmModule.addWasmFunction(with: [.wasmExternRef()] => []) { function, label, args in function.wasmStoreGlobal(globalVariable: global, to: args[0]) return [] } @@ -578,8 +578,9 @@ class WasmFoundationTests: XCTestCase { let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() + // TODO(pawkra): add shared ref variant. let global: Variable = b.createWasmGlobal(value: .externref, isMutable: true) - XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmExternRef, isMutable: true))) + XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmExternRef(), isMutable: true))) let outputFunc = b.createNamedVariable(forBuiltin: "output") // The initial value is "undefined" (because we didn't provide an explicit initialization). @@ -604,13 +605,13 @@ class WasmFoundationTests: XCTestCase { let module = b.buildWasmModule { wasmModule in let global = wasmModule.addGlobal(wasmGlobal: .i31ref, isMutable: true) - XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmI31Ref, isMutable: true))) + XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: .wasmI31Ref(), isMutable: true))) - wasmModule.addWasmFunction(with: [] => [.wasmI31Ref]) { function, label, args in + wasmModule.addWasmFunction(with: [] => [.wasmI31Ref()]) { function, label, args in [function.wasmLoadGlobal(globalVariable: global)] } - wasmModule.addWasmFunction(with: [.wasmI31Ref] => []) { function, label, args in + wasmModule.addWasmFunction(with: [.wasmI31Ref()] => []) { function, label, args in function.wasmStoreGlobal(globalVariable: global, to: args[0]) return [] } @@ -642,8 +643,9 @@ class WasmFoundationTests: XCTestCase { let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() + // TODO(pawkra): add shared ref variant. let global: Variable = b.createWasmGlobal(value: .i31ref, isMutable: true) - XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmI31Ref, isMutable: true))) + XCTAssertEqual(b.type(of: global), .object(ofGroup: "WasmGlobal", withProperties: ["value"], withMethods: ["valueOf"], withWasmType: WasmGlobalType(valueType: ILType.wasmI31Ref(), isMutable: true))) let outputFunc = b.createNamedVariable(forBuiltin: "output") // The initial value is "null" (because we didn't provide an explicit initialization). @@ -667,8 +669,8 @@ class WasmFoundationTests: XCTestCase { let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() - let javaScriptTable = b.createWasmTable(elementType: .wasmExternRef, limits: Limits(min: 5, max: 25), isTable64: isTable64) - XCTAssertEqual(b.type(of: javaScriptTable), .wasmTable(wasmTableType: WasmTableType(elementType: .wasmExternRef, limits: Limits(min: 5, max: 25), isTable64: isTable64, knownEntries: []))) + let javaScriptTable = b.createWasmTable(elementType: .wasmExternRef(), limits: Limits(min: 5, max: 25), isTable64: isTable64) + XCTAssertEqual(b.type(of: javaScriptTable), .wasmTable(wasmTableType: WasmTableType(elementType: .wasmExternRef(), limits: Limits(min: 5, max: 25), isTable64: isTable64, knownEntries: []))) let object = b.createObject(with: ["a": b.loadInt(41), "b": b.loadInt(42)]) @@ -676,9 +678,9 @@ class WasmFoundationTests: XCTestCase { b.callMethod("set", on: javaScriptTable, withArgs: [isTable64 ? b.loadBigInt(1) : b.loadInt(1), object]) let module = b.buildWasmModule { wasmModule in - let tableRef = wasmModule.addTable(elementType: .wasmExternRef, minSize: 2, isTable64: isTable64) + let tableRef = wasmModule.addTable(elementType: .wasmExternRef(), minSize: 2, isTable64: isTable64) - wasmModule.addWasmFunction(with: [] => [.wasmExternRef]) { function, _, _ in + wasmModule.addWasmFunction(with: [] => [.wasmExternRef()]) { function, _, _ in let offset = isTable64 ? function.consti64(0) : function.consti32(0) var ref = function.wasmTableGet(tableRef: tableRef, idx: offset) let offset1 = isTable64 ? function.consti64(1) : function.consti32(1) @@ -728,7 +730,7 @@ class WasmFoundationTests: XCTestCase { let wasmFunction = wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmi32]) { function, label, params in [function.wasmi32BinOp(params[0], function.consti32(1), binOpKind: .Add)] } - wasmModule.addTable(elementType: .wasmFuncRef, + wasmModule.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: [.init(indexInTable: 0, signature: [.wasmi32] => [.wasmi32]), .init(indexInTable: 1, signature: [] => [.wasmi64])], definedEntryValues: [wasmFunction, jsFunction], @@ -746,7 +748,7 @@ class WasmFoundationTests: XCTestCase { XCTAssertEqual(b.type(of: importedFunction), .function([] => .bigint)) // This is the table type that we expect to see on the exports based on the dynamic object group typing. - let tableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef, limits: Limits(min: 10), isTable64: isTable64, knownEntries: [ + let tableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef(), limits: Limits(min: 10), isTable64: isTable64, knownEntries: [ .init(indexInTable: 0, signature: [.wasmi32] => [.wasmi32]), .init(indexInTable: 1, signature: [] => [.wasmi64]) @@ -794,7 +796,7 @@ class WasmFoundationTests: XCTestCase { let wasmFunction = wasmModule.addWasmFunction(with: [.wasmi64] => [.wasmi64, .wasmi64]) { function, label, params in return [params[0], function.consti64(1)] } - let table = wasmModule.addTable(elementType: .wasmFuncRef, + let table = wasmModule.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: [.init(indexInTable: 0, signature: [.wasmi64] => [.wasmi64, .wasmi64]), .init(indexInTable: 1, signature: [.wasmi64] => [.wasmi64])], definedEntryValues: [wasmFunction, jsFunction], @@ -844,7 +846,7 @@ class WasmFoundationTests: XCTestCase { let wasmFunction = wasmModule.addWasmFunction(with: [.wasmi64] => [.wasmi64, .wasmi64]) { function, label, params in return [params[0], function.consti64(1)] } - wasmModule.addTable(elementType: .wasmFuncRef, + wasmModule.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: [.init(indexInTable: 0, signature: [.wasmi64] => [.wasmi64, .wasmi64]), .init(indexInTable: 1, signature: [.wasmi64] => [.wasmi64])], definedEntryValues: [wasmFunction, jsFunction], @@ -852,7 +854,7 @@ class WasmFoundationTests: XCTestCase { } let table = b.getProperty("wt0", of: module.loadExports()) - let tableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef, limits: Limits(min: 10), isTable64: false, knownEntries: [ + let tableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef(), limits: Limits(min: 10), isTable64: false, knownEntries: [ .init(indexInTable: 0, signature: [.wasmi64] => [.wasmi64, .wasmi64]), .init(indexInTable: 1, signature: [.wasmi64] => [.wasmi64]) ])) @@ -867,7 +869,7 @@ class WasmFoundationTests: XCTestCase { fn.wasmCallIndirect(signature: [.wasmi64] => [.wasmi64], table: table, functionArgs: [params[1]], tableIndex: params[0]) } - wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmFuncRef]) { function, label, params in + wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmFuncRef()]) { function, label, params in [function.wasmTableGet(tableRef: table, idx: params[0])] } @@ -882,7 +884,7 @@ class WasmFoundationTests: XCTestCase { let reexportedTable = b.getProperty("iwt0", of: exports) // This is the table type that we expect to see on the exports based on the dynamic object group typing. - let reexportedTableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef, limits: Limits(min: 10), isTable64: false, knownEntries: [ + let reexportedTableType = ILType.wasmTable(wasmTableType: WasmTableType(elementType: .wasmFuncRef(), limits: Limits(min: 10), isTable64: false, knownEntries: [ .init(indexInTable: 0, signature: [.wasmi64] => [.wasmi64, .wasmi64]), .init(indexInTable: 1, signature: [.wasmi64] => [.wasmi64]) @@ -998,7 +1000,7 @@ class WasmFoundationTests: XCTestCase { let wasmFunction = wasmModule.addWasmFunction(with: [.wasmi64] => [.wasmi64, .wasmi64]) { function, label, params in return [params[0], function.consti64(1)] } - let table = wasmModule.addTable(elementType: .wasmFuncRef, + let table = wasmModule.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: [.init(indexInTable: 0, signature: [.wasmi64] => [.wasmi64, .wasmi64]), .init(indexInTable: 1, signature: [.wasmi64] => [.wasmi64])], definedEntryValues: [wasmFunction, jsFunction], @@ -3764,8 +3766,8 @@ class WasmFoundationTests: XCTestCase { let tagi32 = b.createWasmTag(parameterTypes: [.wasmi32]) let module = b.buildWasmModule { wasmModule in wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmi32]) { function, label, args in - function.wasmBuildBlockWithResults(with: [] => [.wasmExnRef], args: []) { catchAllRefLabel, _ in - let catchRefI32 = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef], args: []) { catchRefLabel, _ in + function.wasmBuildBlockWithResults(with: [] => [.wasmExnRef()], args: []) { catchAllRefLabel, _ in + let catchRefI32 = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef()], args: []) { catchRefLabel, _ in function.wasmBuildTryTable(with: [] => [], args: [tagi32, catchRefLabel, catchAllRefLabel], catches: [.Ref, .AllRef]) { _, _ in function.wasmBuildIfElse(function.wasmi32EqualZero(args[0]), hint: .None) { function.WasmBuildThrow(tag: tagVoid, inputs: []) @@ -3774,10 +3776,10 @@ class WasmFoundationTests: XCTestCase { } return [] } - return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef)] + return [function.consti32(-1), function.wasmRefNull(type: .wasmExnRef())] } function.wasmReturn(catchRefI32[0]) - return [function.wasmRefNull(type: .wasmExnRef)] + return [function.wasmRefNull(type: .wasmExnRef())] } return [function.consti32(100)] } @@ -3867,10 +3869,8 @@ class WasmFoundationTests: XCTestCase { let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() - // Assumption: All types but the bottom (null) types are supported in the JS API. - let supportedTypes = WasmAbstractHeapType.allCases.filter {!$0.isBottom()}.map { heapType in - ILType.wasmRef(.Abstract(heapType), nullability:true) - } + // Assumption: All types apart from bottom (null) & shared types are supported in the JS API. + let supportedTypes = WasmAbstractHeapType.allNonBottomTypes().map {ILType.wasmRef($0, nullability: true)} b.createWasmTag(parameterTypes: supportedTypes) let prog = b.finalize() let jsProg = fuzzer.lifter.lift(prog, withOptions: [.includeComments]) @@ -3896,12 +3896,12 @@ class WasmFoundationTests: XCTestCase { let module = b.buildWasmModule { wasmModule in // Inner function that throws, catches and then rethrows the value. let callee = wasmModule.addWasmFunction(with: [.wasmi32] => []) { function, label, args in - let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef], args: []) { catchRefLabel, _ in + let caughtValues = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef()], args: []) { catchRefLabel, _ in function.wasmBuildTryTable(with: [] => [], args: [tagi32, catchRefLabel], catches: [.Ref]) { _, _ in function.WasmBuildThrow(tag: tagi32, inputs: [args[0]]) return [] } - return [function.consti32(0), function.wasmRefNull(type: .wasmExnRef)] + return [function.consti32(0), function.wasmRefNull(type: .wasmExnRef())] } // Print the caught i32 value. function.wasmJsCall(function: printInteger, withArgs: [caughtValues[0]], withWasmSignature: [.wasmi32] => []) @@ -3974,7 +3974,7 @@ class WasmFoundationTests: XCTestCase { trueValue: function.consti64(123), falseValue: function.consti64(321))] } - wasmModule.addWasmFunction(with: [.wasmi32, .wasmExternRef, .wasmExternRef] => [.wasmExternRef]) { function, label, args in + wasmModule.addWasmFunction(with: [.wasmi32, .wasmExternRef(), .wasmExternRef()] => [.wasmExternRef()]) { function, label, args in [function.wasmSelect(on: args[0], trueValue: args[1], falseValue: args[2])] } } @@ -4150,13 +4150,13 @@ class WasmFoundationTests: XCTestCase { let f1 = module.addWasmFunction(with: [] => [.wasmi64]) { f, _, _ in return [f.consti64(1)]} let f2 = module.addWasmFunction(with: [] => [.wasmi64]) { f, _, _ in return [f.consti64(2)]} let f3 = module.addWasmFunction(with: [] => [.wasmi64]) { f, _, _ in return [f.consti64(3)]} - - module.addTable(elementType: .wasmFuncRef, + // TODO(pawkra): add shared ref variant. + module.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: [], definedEntryValues: [], isTable64: isTable64) - let table2 = module.addTable(elementType: .wasmFuncRef, + let table2 = module.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: [], definedEntryValues: [], @@ -4198,12 +4198,13 @@ class WasmFoundationTests: XCTestCase { let f2 = module.addWasmFunction(with: [] => [.wasmi64]) { f, _, _ in return [f.consti64(2)]} let f3 = module.addWasmFunction(with: [] => [.wasmi64]) { f, _, _ in return [f.consti64(3)]} - let table1 = module.addTable(elementType: .wasmFuncRef, + // TODO(pawkra): add shared ref variant. + let table1 = module.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: [], definedEntryValues: [], isTable64: isTable64) - let table2 = module.addTable(elementType: .wasmFuncRef, + let table2 = module.addTable(elementType: .wasmFuncRef(), minSize: 10, definedEntries: (0..<4).map { WasmTableType.IndexInTableAndWasmSignature.init(indexInTable: $0, signature: [] => [.wasmi64]) }, definedEntryValues: [f3, f3, f1, f2], @@ -4470,8 +4471,9 @@ class WasmGCTests: XCTestCase { return [arrayi32, signature] } + // TODO(pawkra): add shared ref variant. let module = b.buildWasmModule { wasmModule in - wasmModule.addWasmFunction(with: [] => [.wasmFuncRef]) { function, label, args 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. @@ -4766,15 +4768,18 @@ class WasmGCTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "1\n0\n") } - func testRefNullAbstractTypes() throws { - let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--experimental-wasm-exnref"]) + func refNullAbstractTypes(sharedRef: Bool) throws { + let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--experimental-wasm-exnref", "--experimental-wasm-shared"]) let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() + let unsupportedHeapType: Set = sharedRef ? [.WasmFunc, .WasmNoFunc, .WasmExn, .WasmNoExn] : [] + let supportedHeapTypes = Array(Set(WasmAbstractHeapType.allCases).subtracting(unsupportedHeapType)) + let module = b.buildWasmModule { wasmModule in - for heapType in WasmAbstractHeapType.allCases { - let valueType = ILType.wasmRef(.Abstract(heapType), nullability: true) + for heapType in supportedHeapTypes { + let valueType = ILType.wasmRef(heapType, shared: sharedRef, nullability: true) if heapType.isUsableInJS() { // ref.null wasmModule.addWasmFunction(with: [] => [valueType]) { function, label, args in @@ -4790,8 +4795,8 @@ class WasmGCTests: XCTestCase { let exports = module.loadExports() let outputFunc = b.createNamedVariable(forBuiltin: "output") - let exportedFctCount = WasmAbstractHeapType.allCases.count - + WasmAbstractHeapType.allCases.count {$0.isUsableInJS()} + let exportedFctCount = supportedHeapTypes.count + + supportedHeapTypes.count {$0.isUsableInJS()} for i in 0.. [.wasmI31Ref]) { function, label, args in + wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmI31Ref()]) { function, label, args in [function.wasmRefI31(args[0])] } - wasmModule.addWasmFunction(with: [.wasmI31Ref] => [.wasmi32, .wasmi32]) { function, label, args in + wasmModule.addWasmFunction(with: [.wasmI31Ref()] => [.wasmi32, .wasmi32]) { function, label, args in [function.wasmI31Get(args[0], isSigned: true), function.wasmI31Get(args[0], isSigned: false)] } @@ -4845,29 +4858,29 @@ class WasmGCTests: XCTestCase { let b = fuzzer.makeBuilder() let module = b.buildWasmModule { wasmModule in - wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmRefExtern]) { function, label, args in + wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmRefExtern()]) { function, label, args in // As ref.i31 produces a non null `ref i31`, the result of extern.convert_any is a // non-nullable `ref extern`. let result = function.wasmExternConvertAny(function.wasmRefI31(args[0])) - XCTAssertEqual(b.type(of: result), .wasmRefExtern) + XCTAssertEqual(b.type(of: result), .wasmRefExtern()) return [result] } - wasmModule.addWasmFunction(with: [.wasmRefExtern] => [.wasmRefAny]) { function, label, args in + wasmModule.addWasmFunction(with: [.wasmRefExtern()] => [.wasmRefAny()]) { function, label, args in let result = function.wasmAnyConvertExtern(args[0]) - XCTAssertEqual(b.type(of: result), .wasmRefAny) + XCTAssertEqual(b.type(of: result), .wasmRefAny()) return [result] } - wasmModule.addWasmFunction(with: [] => [.wasmExternRef]) { function, label, args in - let result = function.wasmExternConvertAny(function.wasmRefNull(type: .wasmNullRef)) - XCTAssertEqual(b.type(of: result), .wasmExternRef) + wasmModule.addWasmFunction(with: [] => [.wasmExternRef()]) { function, label, args in + let result = function.wasmExternConvertAny(function.wasmRefNull(type: .wasmNullRef())) + XCTAssertEqual(b.type(of: result), .wasmExternRef()) return [result] } - wasmModule.addWasmFunction(with: [] => [.wasmAnyRef]) { function, label, args in - let result = function.wasmAnyConvertExtern(function.wasmRefNull(type: .wasmNullExternRef)) - XCTAssertEqual(b.type(of: result), .wasmAnyRef) + wasmModule.addWasmFunction(with: [] => [.wasmAnyRef()]) { function, label, args in + let result = function.wasmAnyConvertExtern(function.wasmRefNull(type: .wasmNullExternRef())) + XCTAssertEqual(b.type(of: result), .wasmAnyRef()) return [result] } } @@ -6418,8 +6431,8 @@ class WasmJSPITests: XCTestCase { // Now lets build the module let module = b.buildWasmModule { m in - m.addWasmFunction(with: [.wasmExternRef] => [.wasmi32]) { f, label, args in - [f.wasmJsCall(function: importFunction, withArgs: args, withWasmSignature: [.wasmExternRef] => [.wasmi32])!] + m.addWasmFunction(with: [.wasmExternRef()] => [.wasmi32]) { f, label, args in + [f.wasmJsCall(function: importFunction, withArgs: args, withWasmSignature: [.wasmExternRef()] => [.wasmi32])!] } } From 125ad0c7bbc47138a28b27e35cebc42bed320116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Gro=C3=9F?= Date: Wed, 17 Dec 2025 15:34:07 +0100 Subject: [PATCH 44/89] Add testcases for recently added SIGILL-related crashers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See crrev.com/c/7269586 for context. Here we add the fuzzilli-side testcases for the new crash types and also extend the ASAN_OPTIONS with `handle_sigill=1` so we get ASAN splats for non-ud2 SIGILL crashes. Bug: 42202821 Change-Id: I08d7d42e8ef9869cd1a228ce15654b23c956ded3 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8857196 Reviewed-by: Michael Achenbach Commit-Queue: Samuel Groß --- Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift b/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift index 1df53793d..dce89be75 100644 --- a/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift @@ -102,9 +102,10 @@ let v8SandboxProfile = Profile( // ASan options. // - abort_on_error=true: We need asan to exit in a way that's detectable for Fuzzilli as a crash + // - handle_sigill=true: It seems by default ASAN doesn't handle SIGILL, but we want that to have stack traces // - symbolize=false: Symbolization can tak a _very_ long time (> 1s), which may cause crashing samples to time out before the stack trace has been captured (in which case Fuzzilli will discard the sample) // - redzone=128: This value is used by Clusterfuzz for reproducing testcases so we should use the same value - processEnv: ["ASAN_OPTIONS" : "abort_on_error=1:symbolize=false:redzone=128", "UBSAN_OPTIONS" : "abort_on_error=1:symbolize=false:redzone=128"], + processEnv: ["ASAN_OPTIONS" : "abort_on_error=1:handle_sigill=1:symbolize=false:redzone=128", "UBSAN_OPTIONS" : "abort_on_error=1:symbolize=false:redzone=128"], maxExecsBeforeRespawn: 1000, @@ -503,6 +504,8 @@ let v8SandboxProfile = Profile( ("fuzzilli('FUZZILLI_CRASH', 6)", .shouldCrash), // This should crash due to calling abort_with_sandbox_violation(). ("fuzzilli('FUZZILLI_CRASH', 9)", .shouldCrash), + // This should crash due to executing an invalid machine code instruction. + ("fuzzilli('FUZZILLI_CRASH', 11)", .shouldCrash), // Crashes that are not sandbox violations and so should be filtered out by the crash filter. // This triggers an IMMEDIATE_CRASH. @@ -511,6 +514,8 @@ let v8SandboxProfile = Profile( ("fuzzilli('FUZZILLI_CRASH', 1)", .shouldNotCrash), // This triggers a std::vector OOB access that should be caught by the libc++ hardening. ("fuzzilli('FUZZILLI_CRASH', 5)", .shouldNotCrash), + // This triggers a `ud 2` which might for example be used for release asserts and so should be ignored. + ("fuzzilli('FUZZILLI_CRASH', 10)", .shouldNotCrash), ], additionalCodeGenerators: [ From 676f94c36f076f6ec58e5ebe998ea9829fd38af5 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Thu, 18 Dec 2025 11:46:04 +0100 Subject: [PATCH 45/89] Add a test with semi-conditional returns in inlined function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: 468928010 Change-Id: I00d34c83bf727b1efece464787910ecfdc3a61fe Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8861036 Commit-Queue: Michael Achenbach Reviewed-by: Samuel Groß --- Tests/FuzzilliTests/MinimizerTest.swift | 73 +++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/Tests/FuzzilliTests/MinimizerTest.swift b/Tests/FuzzilliTests/MinimizerTest.swift index 8d9a29664..cae11e8de 100644 --- a/Tests/FuzzilliTests/MinimizerTest.swift +++ b/Tests/FuzzilliTests/MinimizerTest.swift @@ -651,6 +651,79 @@ class MinimizerTests: XCTestCase { XCTAssertEqual(expectedProgram, actualProgram) } + func testInliningWithSemiConditionalReturn() { + /* + Show that inlining with conditional returns migth not be + semantically equivalent. + + Input: + function f1() { + if (true) { + return 1; + } + return 2; + } + let v1 = f1(); + {result: v1} + + Is inlined to: + var v1 = undefined; + if (true) { + v1 = 1; + } + v1 = 2; + + {result: 2} + */ + let evaluator = EvaluatorForMinimizationTests() + let fuzzer = makeMockFuzzer(evaluator: evaluator) + let b = fuzzer.makeBuilder() + + // Build input program to be minimized. + var a1 = b.loadBool(true) + var a2 = b.loadInt(1) + var a3 = b.loadInt(2) + b.loadString("unused") + let f = b.buildPlainFunction(with: .parameters(n: 0)) { args in + b.buildIf(a1, ifBody: { + b.doReturn(a2) + }) + b.doReturn(a3) + } + + var r = b.callFunction(f, withArgs: []) + var o = b.createObject(with: [:]) + evaluator.nextInstructionIsImportant(in: b) + b.setProperty("result", of: o, to: r) + + let originalProgram = b.finalize() + + // Need to keep various things alive, see also the comment in testBasicInlining + evaluator.operationIsImportant(LoadInteger.self) + evaluator.operationIsImportant(LoadBoolean.self) + evaluator.operationIsImportant(BeginIf.self) + evaluator.operationIsImportant(Reassign.self) + evaluator.keepReturnsInFunctions = true + + // Build expected output program. + a1 = b.loadBool(true) + a2 = b.loadInt(1) + a3 = b.loadInt(2) + r = b.loadUndefined() + b.buildIf(a1, ifBody: { + b.reassign(r, to: a2) + }) + b.reassign(r, to: a3) + o = b.createObject(with: [:]) + b.setProperty("result", of: o, to: a3) + + let expectedProgram = b.finalize() + + // Perform minimization and check that the two programs are equal. + let actualProgram = minimize(originalProgram, with: fuzzer) + XCTAssertEqual(expectedProgram, actualProgram) + } + func testMultiInlining() { let evaluator = EvaluatorForMinimizationTests() let fuzzer = makeMockFuzzer(evaluator: evaluator) From a776a26b5b5d3f8b653cde0adbc951d6d35319c7 Mon Sep 17 00:00:00 2001 From: Pawel Krawczyk Date: Thu, 18 Dec 2025 12:53:46 +0000 Subject: [PATCH 46/89] Shared references - wasmRefI31 Bug: 448349112 Change-Id: Icef73c9f72668e31b48d6c71699b0392f20a5fb2 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8832118 Auto-Submit: Pawel Krawczyk Reviewed-by: Danylo Mocherniuk Commit-Queue: Pawel Krawczyk --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 4 +- .../Fuzzilli/CodeGen/WasmCodeGenerators.swift | 3 +- Sources/Fuzzilli/FuzzIL/Instruction.swift | 10 +++-- Sources/Fuzzilli/FuzzIL/JSTyper.swift | 5 +-- Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 4 +- Sources/Fuzzilli/Lifting/WasmLifter.swift | 4 +- Sources/Fuzzilli/Protobuf/operations.pb.swift | 19 ++++++-- Sources/Fuzzilli/Protobuf/operations.proto | 1 + Tests/FuzzilliTests/ProgramBuilderTest.swift | 1 + Tests/FuzzilliTests/WasmTests.swift | 43 ++++++++++++++++++- 10 files changed, 77 insertions(+), 17 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 3a5080ca9..5957975e2 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -4361,8 +4361,8 @@ public class ProgramBuilder { } @discardableResult - public func wasmRefI31(_ number: Variable) -> Variable { - return b.emit(WasmRefI31(), withInputs: [number], types: [.wasmi32]).output + public func wasmRefI31(_ number: Variable, shared: Bool = false) -> Variable { + return b.emit(WasmRefI31(isShared: shared), withInputs: [number], types: [.wasmi32]).output } @discardableResult diff --git a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift index 9de2cbfaa..130545440 100644 --- a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift @@ -293,8 +293,9 @@ public let WasmCodeGenerators: [CodeGenerator] = [ b.currentWasmModule.currentWasmFunction.wasmRefIsNull(ref) }, + // TODO(pawkra): add shared variant. CodeGenerator("WasmRefI31Generator", inContext: .single(.wasmFunction), inputs: .required(.wasmi32)) { b, value in - b.currentWasmModule.currentWasmFunction.wasmRefI31(value) + b.currentWasmModule.currentWasmFunction.wasmRefI31(value, shared: false) }, // TODO(pawkra): add shared variant. diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index 9b3faab7d..cc52480ec 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -1617,8 +1617,10 @@ extension Instruction: ProtobufConvertible { } case .wasmRefIsNull(_): $0.wasmRefIsNull = Fuzzilli_Protobuf_WasmRefIsNull() - case .wasmRefI31(_): - $0.wasmRefI31 = Fuzzilli_Protobuf_WasmRefI31() + case .wasmRefI31(let op): + $0.wasmRefI31 = Fuzzilli_Protobuf_WasmRefI31.with { + $0.isShared = op.isShared + } case .wasmI31Get(let op): $0.wasmI31Get = Fuzzilli_Protobuf_WasmI31Get.with { $0.isSigned = op.isSigned @@ -2601,8 +2603,8 @@ extension Instruction: ProtobufConvertible { op = p.hasType ? WasmRefNull(type: WasmTypeEnumToILType(p.type)) : WasmRefNull(type: nil) case .wasmRefIsNull(_): op = WasmRefIsNull() - case .wasmRefI31(_): - op = WasmRefI31() + case .wasmRefI31(let p): + op = WasmRefI31(isShared: p.isShared) case .wasmI31Get(let p): op = WasmI31Get(isSigned: p.isSigned) case .wasmAtomicLoad(let p): diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index 1d2cbeec4..29e75d80e 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -906,9 +906,8 @@ public struct JSTyper: Analyzer { } case .wasmRefIsNull(_): setType(of: instr.output, to: .wasmi32) - case .wasmRefI31(_): - // TODO(pawkra): support shared variant. - setType(of: instr.output, to: .wasmRefI31()) + case .wasmRefI31(let op): + setType(of: instr.output, to: .wasmRefI31(shared: op.isShared)) case .wasmI31Get(_): setType(of: instr.output, to: .wasmi32) case .wasmAnyConvertExtern(_): diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index e5e519fc8..508922c58 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -2276,8 +2276,10 @@ class WasmRefIsNull: WasmOperation { class WasmRefI31: WasmOperation { override var opcode: Opcode { .wasmRefI31(self) } + let isShared: Bool - init() { + init(isShared: Bool) { + self.isShared = isShared super.init(numInputs: 1, numOutputs: 1, requiredContext: [.wasmFunction]) } } diff --git a/Sources/Fuzzilli/Lifting/WasmLifter.swift b/Sources/Fuzzilli/Lifting/WasmLifter.swift index a13968ef1..56eaddcfe 100644 --- a/Sources/Fuzzilli/Lifting/WasmLifter.swift +++ b/Sources/Fuzzilli/Lifting/WasmLifter.swift @@ -2181,8 +2181,8 @@ public class WasmLifter { return try Data([0xD0]) + encodeHeapType(typer.type(of: wasmInstruction.output)) case .wasmRefIsNull(_): return Data([0xD1]) - case .wasmRefI31(_): - return Data([Prefix.GC.rawValue, 0x1C]) + case .wasmRefI31(let op): + return Data([Prefix.GC.rawValue, op.isShared ? 0x1F : 0x1C]) case .wasmI31Get(let op): let opCode: UInt8 = op.isSigned ? 0x1D : 0x1E return Data([Prefix.GC.rawValue, opCode]) diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index 883ab8d90..4cbf6f735 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -5923,6 +5923,8 @@ public struct Fuzzilli_Protobuf_WasmRefI31: Sendable { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. + public var isShared: Bool = false + public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} @@ -15427,18 +15429,29 @@ extension Fuzzilli_Protobuf_WasmRefIsNull: SwiftProtobuf.Message, SwiftProtobuf. extension Fuzzilli_Protobuf_WasmRefI31: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmRefI31" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap() + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}isShared\0") public mutating func decodeMessage(decoder: inout D) throws { - // Load everything into unknown fields - while try decoder.nextFieldNumber() != nil {} + 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.decodeSingularBoolField(value: &self.isShared) }() + default: break + } + } } public func traverse(visitor: inout V) throws { + if self.isShared != false { + try visitor.visitSingularBoolField(value: self.isShared, fieldNumber: 1) + } try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Fuzzilli_Protobuf_WasmRefI31, rhs: Fuzzilli_Protobuf_WasmRefI31) -> Bool { + if lhs.isShared != rhs.isShared {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 d26a6d56b..247ba5c8e 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -1563,6 +1563,7 @@ message WasmRefIsNull { } message WasmRefI31 { + bool isShared = 1; } message WasmI31Get { diff --git a/Tests/FuzzilliTests/ProgramBuilderTest.swift b/Tests/FuzzilliTests/ProgramBuilderTest.swift index 97d2c1788..52f339565 100644 --- a/Tests/FuzzilliTests/ProgramBuilderTest.swift +++ b/Tests/FuzzilliTests/ProgramBuilderTest.swift @@ -2828,6 +2828,7 @@ class ProgramBuilderTests: XCTestCase { XCTAssertEqual(typesB.count, 1) } + // TODO(pawkra): check shared subtyping once we support more shared refs. func testWasmGCSubtyping() { let env = JavaScriptEnvironment() let config = Configuration(logLevel: .error) diff --git a/Tests/FuzzilliTests/WasmTests.swift b/Tests/FuzzilliTests/WasmTests.swift index 5fad1a6d9..c219e6175 100644 --- a/Tests/FuzzilliTests/WasmTests.swift +++ b/Tests/FuzzilliTests/WasmTests.swift @@ -4817,7 +4817,7 @@ class WasmGCTests: XCTestCase { try refNullAbstractTypes(sharedRef: false) } - func testi31Ref() throws { + func testi31RefFromJs() throws { let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--experimental-wasm-shared"]) let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) @@ -4851,6 +4851,47 @@ class WasmGCTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "42\n-42\n42,42\n-42,2147483606\n") } + func i31Ref(shared: Bool) throws { + let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: shared ? ["--experimental-wasm-shared", "--experimental-wasm-stringref"] : []) + let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) + let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) + let b: ProgramBuilder = fuzzer.makeBuilder() + let i31RefType = ILType.wasmI31Ref(shared: shared) + + let module = b.buildWasmModule { wasmModule in + let createReferenceOf = wasmModule.addWasmFunction(with: [.wasmi32] => [i31RefType]) { function, label, args in + [function.wasmRefI31(args[0], shared: shared)] + } + let dereference = wasmModule.addWasmFunction(with: [i31RefType] => [.wasmi32, .wasmi32]) { function, label, args in + [function.wasmI31Get(args[0], isSigned: true), + function.wasmI31Get(args[0], isSigned: false)] + } + wasmModule.addWasmFunction(with: [] => [.wasmi32, .wasmi32]) { function, label, args in + let const = function.consti32(-42) + let constRef = function.wasmCallDirect(signature: [.wasmi32] => [i31RefType], function: createReferenceOf, functionArgs: [const]) + let dereferencedConst = function.wasmCallDirect(signature: [i31RefType] => [.wasmi32, .wasmi32], function: dereference, functionArgs: constRef) + return dereferencedConst + } + } + + let exports = module.loadExports() + let outputFunc = b.createNamedVariable(forBuiltin: "output") + let result = b.callMethod(module.getExportedMethod(at: 2), on: exports, withArgs: [b.loadInt(42)]) + b.callFunction(outputFunc, withArgs: [result]) + + let prog = b.finalize() + let jsProg = fuzzer.lifter.lift(prog) + testForOutput(program: jsProg, runner: runner, outputString: "-42,2147483606\n") + } + + func testi31RefShared() throws { + try i31Ref(shared: true) + } + + func testi31RefUnshared() throws { + try i31Ref(shared: false) + } + func testExternAnyConversions() throws { let runner = try GetJavaScriptExecutorOrSkipTest() let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) From 34c9d61e9bd98dc3b19082e98ae3f24e01e45308 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Wed, 17 Dec 2025 16:39:43 +0100 Subject: [PATCH 47/89] Escape double quotes when lifting string literals to JS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Though Fuzzilli has no way to introduce a double quote character into a string, it can get them when tests are imported from JS. We assume the string representations in FuzzIL are raw (i.e. not escaped) and now escape the double quote character when lifting to JS. Bug: 469712158 Change-Id: I33e843b39959538fc5a1f0aacaae522af63ec1c0 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8857197 Reviewed-by: Samuel Groß Reviewed-by: Pawel Krawczyk Commit-Queue: Michael Achenbach --- Sources/Fuzzilli/Lifting/JavaScriptLifter.swift | 3 ++- Tests/FuzzilliTests/LifterTest.swift | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift index 040a3dc9d..d299c10f2 100644 --- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift +++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift @@ -302,7 +302,8 @@ public class JavaScriptLifter: Lifter { w.assign(expr, to: instr.output) case .loadString(let op): - w.assign(StringLiteral.new("\"\(op.value)\""), to: instr.output) + let escaped = op.value.replacingOccurrences(of: "\"", with:"\\\"") + w.assign(StringLiteral.new("\"\(escaped)\""), to: instr.output) case .loadRegExp(let op): let flags = op.flags.asString() diff --git a/Tests/FuzzilliTests/LifterTest.swift b/Tests/FuzzilliTests/LifterTest.swift index 2744756fb..0af2c8a0e 100644 --- a/Tests/FuzzilliTests/LifterTest.swift +++ b/Tests/FuzzilliTests/LifterTest.swift @@ -3635,4 +3635,21 @@ class LifterTests: XCTestCase { """ XCTAssertEqual(actual, expected) } + + func testStringEscape() { + let fuzzer = makeMockFuzzer() + let b = fuzzer.makeBuilder() + let v1 = b.loadString("Hello \"World\"") + let p = b.createNamedVariable(forBuiltin: "print") + b.callFunction(p, withArgs: [v1]) + let program = b.finalize() + let actual = fuzzer.lifter.lift(program) + + let expected = """ + print("Hello \\"World\\""); + + """ + + XCTAssertEqual(actual, expected) + } } From beddc758c4fcc37d363afbac3c76680983e74959 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Tue, 30 Dec 2025 14:54:55 +0100 Subject: [PATCH 48/89] Add assertion to detect inconsistent code generators This guards an optional unwrapping that frequently fails in production with an assertion with more debug output. Bug: 470273473 Change-Id: I17cbc86c698ff629708d9fd893e2bec80a79b6d8 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8888616 Commit-Queue: Michael Achenbach Reviewed-by: Danylo Mocherniuk --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 5957975e2..627f5bcb6 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -841,11 +841,11 @@ public class ProgramBuilder { 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)! + let variable = self.randomVariable(ofTypeOrSubtype: type) + assert(variable != nil, + "The generator \(generator.name) is supposed to generate type \(type). " + + "Either the generator or its annotation is wrong.") + return variable! } // 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. From 51dcc33c0a8be424c3fe0a7f232233ddc6c1616b Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Mon, 5 Jan 2026 08:41:33 +0100 Subject: [PATCH 49/89] Convert assertion to fatalError to catch issues with generators This is a follow-up to commit beddc758c4fcc37d363afbac3c76680983e74959 Bug: 470273473 Change-Id: Ia3bc85a8632efe75a009f5c11ec47b3611f42998 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8901816 Auto-Submit: Matthias Liedtke Commit-Queue: Michael Achenbach Reviewed-by: Michael Achenbach --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 10 +++++----- Sources/Fuzzilli/Protobuf/gen_programproto.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 627f5bcb6..959f91aa8 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -841,11 +841,11 @@ public class ProgramBuilder { if generators.count > 0 { let generator = generators.randomElement() let _ = self.complete(generator: generator, withBudget: 10) - let variable = self.randomVariable(ofTypeOrSubtype: type) - assert(variable != nil, - "The generator \(generator.name) is supposed to generate type \(type). " + - "Either the generator or its annotation is wrong.") - return variable! + guard let variable = self.randomVariable(ofTypeOrSubtype: type) else { + fatalError("The generator \(generator.name) is supposed to generate type " + + "\(type). Either the generator or its annotation is wrong.") + } + return variable } // 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. diff --git a/Sources/Fuzzilli/Protobuf/gen_programproto.py b/Sources/Fuzzilli/Protobuf/gen_programproto.py index 0018ac2a6..7447d6caf 100644 --- a/Sources/Fuzzilli/Protobuf/gen_programproto.py +++ b/Sources/Fuzzilli/Protobuf/gen_programproto.py @@ -9,7 +9,7 @@ OPCODESSWIFT_FILE = "../FuzzIL/Opcodes.swift" -START = f"// Copyright {datetime.date.today().year} Google LLC" +START = f"// Copyright 2025 Google LLC" START += """ // // Licensed under the Apache License, Version 2.0 (the "License"); From b71ac3d72cac9ad7b9f152034f98c5f6d4c97ecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hendrik=20W=C3=BCthrich?= Date: Thu, 8 Jan 2026 01:47:05 -0800 Subject: [PATCH 50/89] Revert "Add ManyArgumentsCall CodeGenerator" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 36d62582f60991d3194bd2e820440d503a7f3d07. Reason for revert: The code we want to target is unreachable due to hitting syntax errors from the parser before we could ever compile and bailout. Original change's description: > Add ManyArgumentsCall CodeGenerator > > Calling apply() with an array like this generator does will create a > function call with as many arguments as the size of the array. > It is meant to cover the discrepencies in max argument counts between > turboshaft and maglev. > > Bug: b/455503442 > Change-Id: Ia605368687970369e168796273486d75de4cc811 > Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8839116 > Reviewed-by: Matthias Liedtke > Commit-Queue: Hendrik Wüthrich Bug: b/455503442 Change-Id: Ie18ec8668485fe8518b14a7d95ba6dea74886364 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8910656 Reviewed-by: Matthias Liedtke Commit-Queue: Hendrik Wüthrich --- .../CodeGen/CodeGeneratorWeights.swift | 1 - Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 19 ------------------- 2 files changed, 20 deletions(-) diff --git a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift index 18a364cd4..9da0a7720 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift @@ -129,7 +129,6 @@ public let codeGeneratorWeights = [ "ComputedPropertyConfigurationGenerator": 10, "FunctionCallGenerator": 30, "FunctionCallWithSpreadGenerator": 3, - "ManyArgumentsCall": 3, "ConstructorCallGenerator": 20, "ConstructorCallWithSpreadGenerator": 3, "MethodCallGenerator": 30, diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 40d6f737f..6b387d0ab 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -1879,25 +1879,6 @@ public let CodeGenerators: [CodeGenerator] = [ f, withArgs: arguments, spreading: spreads, guard: needGuard) }, - - CodeGenerator("ManyArgumentsCall", inputs: .preferred(.function())) { b, f in - // These sizes are around the max arguments for maglev (2^16 - 10) - // and turboshaft (2^16). - let sizes: [Int64] = [65524, 65525, 65526, 65534, 65535, 65536] - let size = b.loadInt(chooseUniform(from: sizes)) - let constructor = b.createNamedVariable(forBuiltin: "Array") - let largeArray = b.construct(constructor, withArgs: [size]) - - let needGuard = b.type(of: f).MayNotBe(.function()) - - if probability(0.5) { - let receiver = probability(0.5) ? b.loadNull() : b.randomVariable(forUseAs: .object()) - b.callMethod("apply", on: f, withArgs: [receiver, largeArray], guard: needGuard) - } else { - b.callFunction(f, withArgs: [largeArray], spreading: [true], guard: needGuard) - } - }, - CodeGenerator( "ConstructorCallWithSpreadGenerator", inputs: .preferred(.constructor()) ) { b, c in From c97d5f61685f0719bea5990365309847c0e73c3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Fl=C3=BCckiger?= Date: Mon, 5 Jan 2026 11:36:37 +0100 Subject: [PATCH 51/89] Enable experimental features in tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I6d1d5fad1fee59368e3b277e43db96f56e8ff903 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8902196 Commit-Queue: Olivier Flückiger Reviewed-by: Matthias Liedtke --- Tests/FuzzilliTests/EnvironmentTest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/FuzzilliTests/EnvironmentTest.swift b/Tests/FuzzilliTests/EnvironmentTest.swift index d8dbcf271..736164e73 100644 --- a/Tests/FuzzilliTests/EnvironmentTest.swift +++ b/Tests/FuzzilliTests/EnvironmentTest.swift @@ -25,7 +25,7 @@ class EnvironmentTests: XCTestCase { /// Test all the builtin objects that are reachable from the global this. /// (This does not include anything that needs a constructor to be called.) func testJSEnvironmentLive() throws { - let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--harmony", "--experimental-wasm-rab-integration", "--wasm-test-streaming"]) + let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--harmony", "--experimental-wasm-rab-integration", "--wasm-test-streaming", "--js-staging"]) let jsProg = buildAndLiftProgram(withLiftingOptions: [.includeComments]) { b in let jsEnvironment = b.fuzzer.environment var seenTypeGroups = Set() From 972d56028be3bd5623b6ed51d7a3967a28293802 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Thu, 8 Jan 2026 13:45:53 +0100 Subject: [PATCH 52/89] Fix inlining of functions also used as disposable variables. Inlining can crash if a function is also used as a disposable variable in some other function. This also adds a minimizer test that demonstrates the crash in patchset 10. Bug: 468928010 Change-Id: Ic9554163e536b0cfb909783ec401062014270cf8 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8861857 Commit-Queue: Michael Achenbach Reviewed-by: Matthias Liedtke --- .../Minimization/InliningReducer.swift | 8 +++- Tests/FuzzilliTests/MinimizerTest.swift | 45 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/Sources/Fuzzilli/Minimization/InliningReducer.swift b/Sources/Fuzzilli/Minimization/InliningReducer.swift index be6bed5d6..c9555538d 100644 --- a/Sources/Fuzzilli/Minimization/InliningReducer.swift +++ b/Sources/Fuzzilli/Minimization/InliningReducer.swift @@ -118,10 +118,14 @@ struct InliningReducer: Reducer { // Can't inline functions that are passed as arguments to other functions. deleteCandidates(instr.inputs.dropFirst()) + case .loadDisposableVariable: + // Can't inline functions that are also used as a disposable variable. + deleteCandidates(instr.inputs) + fallthrough case .loadNewTarget, - .loadArguments, - .loadDisposableVariable: + .loadArguments: // Can't inline functions if they access their arguments or new.target. + // Or if they create a disposable variable. if let function = activeSubroutineDefinitions.last! { candidates.removeValue(forKey: function) } diff --git a/Tests/FuzzilliTests/MinimizerTest.swift b/Tests/FuzzilliTests/MinimizerTest.swift index cae11e8de..497508250 100644 --- a/Tests/FuzzilliTests/MinimizerTest.swift +++ b/Tests/FuzzilliTests/MinimizerTest.swift @@ -724,6 +724,51 @@ class MinimizerTests: XCTestCase { XCTAssertEqual(expectedProgram, actualProgram) } + // Ensure we don't crash when a candidate function is also used as a + // disposable variable. + func testInliningWithDisposableVariable() { + let evaluator = EvaluatorForMinimizationTests() + let fuzzer = makeMockFuzzer(evaluator: evaluator) + let b = fuzzer.makeBuilder() + + // Build input program to be minimized. + let a1 = b.loadBool(true) + + // The function to be inlined. + let f = b.buildPlainFunction(with: .parameters(n: 0)) { args in + b.doReturn(a1) + } + + // The call and result. + evaluator.nextInstructionIsImportant(in: b) + let r = b.callFunction(f, withArgs: []) + let o = b.createObject(with: [:]) + evaluator.nextInstructionIsImportant(in: b) + b.setProperty("result", of: o, to: r) + + // Another function with a disposable variable refering + // to the first function. + evaluator.nextInstructionIsImportant(in: b) + b.buildPlainFunction(with: .parameters(n: 0)) { args in + evaluator.nextInstructionIsImportant(in: b) + b.loadDisposableVariable(f) + b.doReturn(a1) + } + + let originalProgram = b.finalize() + + // Need to keep various things alive, see also the comment in testBasicInlining + evaluator.operationIsImportant(LoadBoolean.self) + evaluator.operationIsImportant(Reassign.self) + evaluator.keepReturnsInFunctions = true + + // Perform minimization and check that the two programs are equal. + // We expect the no changes as the inlining candidate was used as + // a disposable variable. + let actualProgram = minimize(originalProgram, with: fuzzer) + XCTAssertEqual(originalProgram, actualProgram) + } + func testMultiInlining() { let evaluator = EvaluatorForMinimizationTests() let fuzzer = makeMockFuzzer(evaluator: evaluator) From 948e9fdcbe3dfe156f07641d2aeac819cc8b8811 Mon Sep 17 00:00:00 2001 From: Dominik Klemba Date: Thu, 8 Jan 2026 14:47:28 +0000 Subject: [PATCH 53/89] [bugfix] Remove incorrect produced type from the list We cannot guarantee that `.integer` is produced because the `.length` property could be overwritten, and nothing else produces integers. We don't want `produces: [.jsAnything]` as it doesn't have value. Bug: 470273473 Change-Id: Ib3c78e05ea2845ed3e7966b1e10aaa51f3a0e5b5 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8911216 Reviewed-by: Matthias Liedtke Auto-Submit: Dominik Klemba Commit-Queue: Matthias Liedtke --- Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 6b387d0ab..9d814ac62 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -301,8 +301,7 @@ public let CodeGenerators: [CodeGenerator] = [ CodeGenerator("TypedArrayLastIndexGenerator", inContext: .single(.javascript), - inputs: .required(.object(withProperties: ["buffer", "length"])), - produces: [.integer, .jsAnything] + inputs: .required(.object(withProperties: ["buffer", "length"])) ) { b, view in let len = b.getProperty("length", of: view) let index = b.binary(len, b.loadInt(1), with: .Sub) From d37a8ed52d349593d065c814415e866673a6e9e5 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Fri, 26 Dec 2025 11:05:41 -0800 Subject: [PATCH 54/89] [intl] Add fuzz support for Intl.Locale This fills in some support for Intl.Locale. There's still work to be done, but this covers most of the API. Bug: 450083673 Change-Id: I7b2f899b7d2a8ff44a10a7ecea5f8a906a6a6964 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8881013 Reviewed-by: Matthias Liedtke Commit-Queue: Manish Goregaokar --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 66 +++++++++++++ Sources/Fuzzilli/CodeGen/CodeGenerators.swift | 2 +- .../Environment/JavaScriptEnvironment.swift | 93 +++++++++++++++++-- 3 files changed, 153 insertions(+), 8 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 959f91aa8..fae586123 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -5286,12 +5286,24 @@ public class ProgramBuilder { @discardableResult static func constructIntlLocaleString() -> String { + // TODO(Manishearth) Generate more complicated locale strings, not just language strings + return constructIntlLanguageString() + } + + + @discardableResult + static func constructIntlLanguageString() -> String { // TODO(Manishearth) Generate more interesting locales than just the builtins 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"] + // https://en.wikipedia.org/wiki/ISO_15924#List_of_codes + // Unfortunately this list grows over time, but constructIntlScript can at least randomly generate other four-char script codes + fileprivate static let allScripts = ["Adlm", "Afak", "Aghb", "Ahom", "Arab", "Aran", "Armi", "Armn", "Avst", "Bali", "Bamu", "Bass", "Batk", "Beng", "Berf", "Bhks", "Blis", "Bopo", "Brah", "Brai", "Bugi", "Buhd", "Cakm", "Cans", "Cari", "Cham", "Cher", "Chis", "Chrs", "Cirt", "Copt", "Cpmn", "Cprt", "Cyrl", "Cyrs", "Deva", "Diak", "Dogr", "Dsrt", "Dupl", "Egyd", "Egyh", "Egyp", "Elba", "Elym", "Ethi", "Gara", "Geok", "Geor", "Glag", "Gong", "Gonm", "Goth", "Gran", "Grek", "Gujr", "Gukh", "Guru", "Hanb", "Hang", "Hani", "Hano", "Hans", "Hant", "Hatr", "Hebr", "Hira", "Hluw", "Hmng", "Hmnp", "Hntl", "Hrkt", "Hung", "Inds", "Ital", "Jamo", "Java", "Jpan", "Jurc", "Kali", "Kana", "Kawi", "Khar", "Khmr", "Khoj", "Kitl", "Kits", "Knda", "Kore", "Kpel", "Krai", "Kthi", "Lana", "Laoo", "Latf", "Latg", "Latn", "Leke", "Lepc", "Limb", "Lina", "Linb", "Lisu", "Loma", "Lyci", "Lydi", "Mahj", "Maka", "Mand", "Mani", "Marc", "Maya", "Medf", "Mend", "Merc", "Mero", "Mlym", "Modi", "Mong", "Moon", "Mroo", "Mtei", "Mult", "Mymr", "Nagm", "Nand", "Narb", "Nbat", "Newa", "Nkdb", "Nkgb", "Nkoo", "Nshu", "Ogam", "Olck", "Onao", "Orkh", "Orya", "Osge", "Osma", "Ougr", "Palm", "Pauc", "Pcun", "Pelm", "Perm", "Phag", "Phli", "Phlp", "Phlv", "Phnx", "Piqd", "Plrd", "Prti", "Psin", "Qaaa-Qabx", "Ranj", "Rjng", "Rohg", "Roro", "Runr", "Samr", "Sara", "Sarb", "Saur", "Seal", "Sgnw", "Shaw", "Shrd", "Shui", "Sidd", "Sidt", "Sind", "Sinh", "Sogd", "Sogo", "Sora", "Soyo", "Sund", "Sunu", "Sylo", "Syrc", "Syre", "Syrj", "Syrn", "Tagb", "Takr", "Tale", "Talu", "Taml", "Tang", "Tavt", "Tayo", "Telu", "Teng", "Tfng", "Tglg", "Thaa", "Thai", "Tibt", "Tirh", "Tnsa", "Todr", "Tols", "Toto", "Tutg", "Ugar", "Vaii", "Visp", "Vith", "Wara", "Wcho", "Wole", "Xpeo", "Xsux", "Yezi", "Yiii", "Zanb", "Zinh", "Zmth", "Zsye", "Zsym", "Zxxx", "Zyyy", "Zzzz"] + fileprivate static let allAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + fileprivate static let allAlphaNum = allAlpha + "0123456789" @discardableResult static func constructIntlUnit() -> String { @@ -5304,6 +5316,60 @@ public class ProgramBuilder { } } + + @discardableResult + static func constructIntlScriptString() -> String { + if probability(0.7) { + return chooseUniform(from: allScripts) + } else { + return String((0..<4).map { _ in allAlpha.randomElement()! }) + } + } + + @discardableResult + static func constructIntlRegionString() -> String { + // either two letters or three digits + if probability(0.5) { + return String((0..<2).map { _ in allAlpha.randomElement()! }) + } else { + return String(format: "%03d", Int.random(in: 0...999)) + } + } + + @discardableResult + private static func constructSingleIntlVariantString() -> String { + // 5-8 alphanumerics or a digit and 3 alphanumerics + if probability(0.5) { + let count = Int.random(in: 5...8) + return String((0.. String { + if probability(0.9) { + return constructSingleIntlVariantString() + } else { + return "\(constructSingleIntlVariantString())-\(constructIntlVariantString())" + } + } + + @discardableResult + func constructIntlLocale() -> Variable { + let intl = createNamedVariable(forBuiltin: "Intl") + let constructor = getProperty("Locale", of: intl) + + var args: [Variable] = [] + args.append(findOrGenerateType(.jsIntlLocaleString)) + if probability(0.7) { + args.append(createOptionsBag(.jsIntlLocaleSettings)) + } + return construct(constructor, withArgs: args) + } + // Generic generators for Intl types. private func constructIntlType(type: String, optionsBag: OptionsBag) -> Variable { let intl = createNamedVariable(forBuiltin: "Intl") diff --git a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift index 9d814ac62..07943c72a 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGenerators.swift @@ -309,7 +309,7 @@ public let CodeGenerators: [CodeGenerator] = [ }, CodeGenerator("BuiltinIntlGenerator") { b in - let _ = chooseUniform(from: [b.constructIntlDateTimeFormat, b.constructIntlCollator, b.constructIntlListFormat, b.constructIntlNumberFormat, b.constructIntlPluralRules, b.constructIntlRelativeTimeFormat, b.constructIntlSegmenter])() + let _ = chooseUniform(from: [b.constructIntlDateTimeFormat, b.constructIntlCollator, b.constructIntlListFormat, b.constructIntlLocale, 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 03c0014ba..562009562 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -433,6 +433,9 @@ public class JavaScriptEnvironment: ComponentBase { registerObjectGroup(.jsIntlListFormat) registerObjectGroup(.jsIntlListFormatConstructor) registerObjectGroup(.jsIntlListFormatPrototype) + registerObjectGroup(.jsIntlLocale) + registerObjectGroup(.jsIntlLocaleConstructor) + registerObjectGroup(.jsIntlLocalePrototype) registerObjectGroup(.jsIntlNumberFormat) registerObjectGroup(.jsIntlNumberFormatConstructor) registerObjectGroup(.jsIntlNumberFormatPrototype) @@ -476,6 +479,7 @@ public class JavaScriptEnvironment: ComponentBase { registerEnumeration(OptionsBag.jsIntlFullLongMediumShort) registerEnumeration(OptionsBag.jsIntlCollatorUsageEnum) registerEnumeration(OptionsBag.jsIntlCollationEnum) + registerEnumeration(OptionsBag.jsIntlCollationTypeEnum) registerEnumeration(OptionsBag.jsIntlCaseFirstEnum) registerEnumeration(OptionsBag.jsIntlCollatorSensitivityEnum) registerEnumeration(OptionsBag.jsIntlListFormatTypeEnum) @@ -511,6 +515,7 @@ public class JavaScriptEnvironment: ComponentBase { registerOptionsBag(.jsIntlDateTimeFormatSettings) registerOptionsBag(.jsIntlCollatorSettings) registerOptionsBag(.jsIntlListFormatSettings) + registerOptionsBag(.jsIntlLocaleSettings) registerOptionsBag(.jsIntlNumberFormatSettings) registerOptionsBag(.jsIntlPluralRulesSettings) registerOptionsBag(.jsIntlRelativeTimeFormatSettings) @@ -536,8 +541,12 @@ public class JavaScriptEnvironment: ComponentBase { return ProgramBuilder.randomUTCOffsetString(mayHaveSeconds: true) } }) - addNamedStringGenerator(forType: .jsIntlLocaleLike, with: { ProgramBuilder.constructIntlLocaleString() }) - addNamedStringGenerator(forType: .jsIntlUnit, with: { ProgramBuilder.constructIntlUnit() }) + addNamedStringGenerator(forType: .jsIntlLocaleString, with: { ProgramBuilder.constructIntlLocaleString() }) + addNamedStringGenerator(forType: .jsIntlLanguageString, with: { ProgramBuilder.constructIntlLanguageString() }) + addNamedStringGenerator(forType: .jsIntlScriptString, with: { ProgramBuilder.constructIntlScriptString() }) + addNamedStringGenerator(forType: .jsIntlRegionString, with: { ProgramBuilder.constructIntlRegionString() }) + addNamedStringGenerator(forType: .jsIntlVariantString, with: { ProgramBuilder.constructIntlVariantString() }) + addNamedStringGenerator(forType: .jsIntlUnitString, 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, @@ -3012,7 +3021,10 @@ extension OptionsBag { // Intl extension ILType { // Intl types - static let jsIntlObject = ILType.object(ofGroup: "Intl", withProperties: ["DateTimeFormat", "Collator", "ListFormat", "NumberFormat", "PluralRules", "RelativeTimeFormat", "Segmenter"], withMethods: ["getCanonicalLocales", "supportedValuesOf"]) + static let jsIntlObject = ILType.object(ofGroup: "Intl", withProperties: ["DateTimeFormat", "Collator", "ListFormat", "Locale", "NumberFormat", "PluralRules", "RelativeTimeFormat", "Segmenter"], withMethods: ["getCanonicalLocales", "supportedValuesOf"]) + + static let jsIntlLocale = ILType.object(ofGroup: "Intl.Locale", withProperties: ["baseName", "calendar", "caseFirst", "collation", "hourCycle", "language", "numberingSystem", "numeric", "region", "script", "variants"], withMethods: ["getCalendars", "getCollations", "getHourCycles", "getNumberingSystems", "getTextInfo", "getTimeZones", "getWeekInfo", "maximize", "minimize", "toString"]) + static let jsIntlLocaleConstructor = ILType.functionAndConstructor([.plain(.jsIntlLocaleString), .opt(OptionsBag.jsIntlLocaleSettings.group.instanceType)] => .jsIntlLocale) + .object(ofGroup: "IntlLocaleConstructor", withProperties: ["prototype"], withMethods: []) 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"]) @@ -3037,8 +3049,14 @@ extension ILType { static let jsIntlSegmenterSegments = ILType.object(ofGroup: "IntlSegmenterSegments", withProperties: [], withMethods: ["containing"]) - static let jsIntlLocaleLike = ILType.namedString(ofName: "IntlLocaleString") - static let jsIntlUnit = ILType.namedString(ofName: "IntlUnitString") + static let jsIntlLocaleString = ILType.namedString(ofName: "IntlLocaleString") + static let jsIntlLanguageString = ILType.namedString(ofName: "IntlLanguageString") + static let jsIntlScriptString = ILType.namedString(ofName: "IntlScriptString") + static let jsIntlRegionString = ILType.namedString(ofName: "IntlRegionString") + static let jsIntlVariantString = ILType.namedString(ofName: "IntlVariantString") + // TODO: locale-likes are actually supposed to be a locale string, an Intl.Locale object, or an array of the same + static let jsIntlLocaleLike = ILType.jsIntlLocaleString + static let jsIntlUnitString = ILType.namedString(ofName: "IntlUnitString") } extension ObjectGroup { @@ -3051,6 +3069,7 @@ extension ObjectGroup { "Collator" : .jsIntlCollatorConstructor, "DateTimeFormat" : .jsIntlDateTimeFormatConstructor, "ListFormat" : .jsIntlListFormatConstructor, + "Locale" : .jsIntlLocaleConstructor, "NumberFormat" : .jsIntlNumberFormatConstructor, "PluralRules" : .jsIntlPluralRulesConstructor, "RelativeTimeFormat" : .jsIntlRelativeTimeFormatConstructor, @@ -3157,6 +3176,48 @@ extension ObjectGroup { ] ) + static let jsIntlLocale = ObjectGroup( + name: "Intl.Locale", + instanceType: .jsIntlLocale, + properties: [ + "baseName": .string, + "calendar": .string, + "caseFirst": .string, + "collation": .string, + "hourCycle": .string, + "language": .string, + "numberingSystem": .string, + "numeric": .string, + "region": .string, + "script": .string, + "variants": .string, + ], + methods: [ + "getCalendars": [] => .jsArray, + "getCollations": [] => .jsArray, + "getHourCycles": [] => .jsArray, + "getNumberingSystems": [] => .jsArray, + "getTextInfo": [] => .jsArray, + "getTimeZones": [] => .jsArray, + "getWeekInfo": [] => .object(), + "maximize": [] => .jsIntlLocale, + "minimize": [] => .jsIntlLocale, + "toString": [] => .string, + ] + ) + + static let jsIntlLocalePrototype = createPrototypeObjectGroup(jsIntlLocale) + + static let jsIntlLocaleConstructor = ObjectGroup( + name: "IntlLocaleConstructor", + constructorPath: "Intl.Locale", + instanceType: .jsIntlLocaleConstructor, + properties: [ + "prototype" : jsIntlLocalePrototype.instanceType, + ], + methods: [:] + ) + 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 { @@ -3301,6 +3362,7 @@ extension OptionsBag { 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 jsIntlCollationTypeEnum = ILType.enumeration(ofName: "IntlCollationType", withValues: ["compat", "emoji", "eor", "phonebk", "pinyin", "searchjl", "stroke", "trad", "unihan", "zhuyin"]) 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"]) @@ -3319,7 +3381,7 @@ extension OptionsBag { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#parameters static let jsIntlDateTimeFormatSettings = OptionsBag( - name: "IntlLocaleSettings", + name: "IntlDateTimeFormatSettings", properties: [ // Locale options "localeMatcher": jsIntlLocaleMatcherEnum, @@ -3371,6 +3433,23 @@ extension OptionsBag { ] ) + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/Locale#options + static let jsIntlLocaleSettings = OptionsBag( + name: "IntlLocaleSettings", + properties: [ + "language": .jsIntlLanguageString, + "script": .jsIntlScriptString, + "region": .jsIntlRegionString, + "variants": .jsIntlVariantString, + "calendar": .jsTemporalCalendarEnum, + "collation": jsIntlCollationTypeEnum, + "numberingSystem": jsIntlNumberingSystemEnum, + "caseFirst": jsIntlCaseFirstEnum, + "hourCycle": jsIntlHourCycleEnum, + "numeric": .boolean, + ] + ) + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options static let jsIntlNumberFormatSettings = OptionsBag( name: "IntlNumberFormatSettings", @@ -3383,7 +3462,7 @@ extension OptionsBag { "currency": jsIntlCurrencySystemEnum, "currencyDisplay": jsIntlCurrencyDisplayEnum, "currencySign": jsIntlCurrencySignEnum, - "unit": .jsIntlUnit, + "unit": .jsIntlUnitString, "unitDisplay": jsIntlLongShortNarrowEnum, // digit options "minimumIntegerDigits": .integer, From 7b9f695a9e56008850ef0c03698c0ff14cdc055d Mon Sep 17 00:00:00 2001 From: Danylo Mocherniuk Date: Fri, 9 Jan 2026 09:25:14 +0000 Subject: [PATCH 55/89] [dumpling] Add RelateTool and DiffOracle DiffOracle is a library that allows to see if there was a difference between optimized and unoptimized runs. RelateTool is designed as a CLI tool to compare optimized vs unoptimized runs. Usage: swift run -c release RelateTool --d8=... --poc=... Bug: 441467877 Change-Id: Ie8850e8534ae3a890f93be77ba2d0961f51a129e Co-authored-by: Mathias Payer Co-authored-by: Liam Wachter Co-authored-by: Flavio Toffalini Co-authored-by: Christian Wressnegger Co-authored-by: Julian Gremminger Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8759816 Reviewed-by: Matthias Liedtke Commit-Queue: Danylo Mocherniuk --- Package.swift | 8 + .../Fuzzilli/DumplingDiffOracle/Oracle.swift | 229 +++++++++++++ Sources/RelateTool/main.swift | 114 +++++++ Tests/FuzzilliTests/DiffOracleTests.swift | 314 ++++++++++++++++++ 4 files changed, 665 insertions(+) create mode 100644 Sources/Fuzzilli/DumplingDiffOracle/Oracle.swift create mode 100644 Sources/RelateTool/main.swift create mode 100644 Tests/FuzzilliTests/DiffOracleTests.swift diff --git a/Package.swift b/Package.swift index 216cf67c3..62f6609c4 100644 --- a/Package.swift +++ b/Package.swift @@ -70,6 +70,14 @@ let package = Package( .executableTarget(name: "FuzzILTool", dependencies: ["Fuzzilli"]), + // Tool that runs d8 in Dumpling mode. First time it runs with Maglev + // and Turbofan. Second time without. In both runs frames are dumped + // in certain points to the files. The dumps are later compared for + // equality. If they are not equal, it means that there's likely a bug + // in V8. + .executableTarget(name: "RelateTool", + dependencies: ["Fuzzilli"]), + .testTarget(name: "FuzzilliTests", dependencies: ["Fuzzilli"], resources: [.copy("CompilerTests")]), diff --git a/Sources/Fuzzilli/DumplingDiffOracle/Oracle.swift b/Sources/Fuzzilli/DumplingDiffOracle/Oracle.swift new file mode 100644 index 000000000..74a988db3 --- /dev/null +++ b/Sources/Fuzzilli/DumplingDiffOracle/Oracle.swift @@ -0,0 +1,229 @@ +// Copyright 2026 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. + +// The tool implemented in this file compares two Dumpling dumps for +// equality. Each Dumpling dump consists of multiple frame dumps. +// Example frame dump: +// ---I +// b:34 +// f:500 +// n:3 +// m:5 +// x:40 +// r0:30 +// r3:some_string +// a0:1 +// a1:2 +// +// A frame dump always starts with a header which is one of +// ---I (for interpreter), ---S (for Sparkplug), ---M (for Maglev), +// ---T (for Turbofan), ---D (deopt Turbofan: this is frame produced by actual +// Turbofan deopt, not by Dumpling). +// Next line with 'b' might follow (see when it and other lines might get omitted +// in the NOTE below), it denotes bytecode offset of a dump (within a function). +// Next line with 'f' might follow, it denotes JS function id of a dump. +// Next line with 'x' might follow, it denotes the dump of the accumulator. +// Next lines with 'n' and 'm' might follow, which denote parameters of a function +// and register count of a frame respectfully. +// Next multiple lines 'ai' might follow, where 'a' denotes that this is a dump +// of a function parameter and 'i' denotes the number of the parameter. +// Next multiple lines 'ri' might follow, where 'r' denotes that this is a dump +// of a register and 'i' denotes the number of the register. +// Lastly an empty line always ends the frame dump. +// +// NOTE: frame dumps are implemented incrementally to not write too much data +// to the file. +// IOW let's say we dumped 2 frames with full format as follows: +// ---I +// b:30 +// f:40 +// a0:1 +// a1:2 +// +// ---I +// b:35 +// f:40 +// a0:1 +// a1:1 +// +// In order to save space a file will contain just: +// ---I +// b:30 +// f:40 +// a0:1 +// a1:2 +// +// ---I +// b:35 +// a1:1 + + +import Foundation + +// This class is implementing one public function relate(optimizedOutput, unoptimizedOutput). +// `relate` compares optimizedOutput and unoptimizedOutput for equality. +public final class DiffOracle { + private enum FrameType { + case interpreter + case sparkplug + case maglev + case turbofan + case deoptTurbofan + } + + private struct Frame: Equatable { + let bytecodeOffset: Int + let accumulator: String + let arguments: [String] + let registers: [String] + let functionId: Int + let frameType: FrameType + + // 'reference' is the value from Unoptimized frame. + func matches(reference: Frame) -> Bool { + + guard self.bytecodeOffset == reference.bytecodeOffset, + self.functionId == reference.functionId, + self.arguments.count == reference.arguments.count, + self.registers.count == reference.registers.count else { + return false + } + + // Logic: 'self' is the Optimized frame. It is allowed to have "". + func isMatch(_ optValue: String, unoptValue: String) -> Bool { + return optValue == "" || optValue == unoptValue + } + + if !isMatch(self.accumulator, unoptValue: reference.accumulator) { + return false + } + + if !zip(self.arguments, reference.arguments).allSatisfy(isMatch) { + return false + } + + if !zip(self.registers, reference.registers).allSatisfy(isMatch) { + return false + } + + return true + } + } + + private static func parseDiffFrame(_ frameArr: ArraySlice, _ prevFrame: Frame?) -> Frame { + func parseValue(prefix: String, defaultValue: T, index: inout Int, conversion: (Substring) -> T) -> T { + if index < frameArr.endIndex && frameArr[index].starts(with: prefix) { + let value = conversion(frameArr[index].dropFirst(prefix.count)) + index += 1 + return value + } + return defaultValue + } + var i = frameArr.startIndex + func parseFrameType(_ type: Substring) -> FrameType { + switch type { + case "---I": .interpreter + case "---S": .sparkplug + case "---M": .maglev + case "---T": .turbofan + case "---D": .deoptTurbofan + default: + fatalError("Unknown frame type") + } + } + + let frameType: FrameType = parseFrameType(frameArr[i]) + + i += 1 + + let bytecodeOffset = parseValue(prefix: "b:", defaultValue: prevFrame?.bytecodeOffset ?? -1, index: &i){ Int($0)! } + let functionId = parseValue(prefix: "f:", defaultValue: prevFrame?.functionId ?? -1, index: &i){ Int($0)! } + let accumulator = parseValue(prefix: "x:", defaultValue: prevFrame?.accumulator ?? "", index: &i){ String($0) } + let argCount = parseValue(prefix: "n:", defaultValue: prevFrame?.arguments.count ?? -1, index: &i){ Int($0)! } + let regCount = parseValue(prefix: "m:", defaultValue: prevFrame?.registers.count ?? -1, index: &i){ Int($0)! } + + func updateValues(prefix: String, totalCount: Int, oldValues: [String]) -> [String] { + var newValues = oldValues + + if newValues.count > totalCount { + newValues.removeLast(newValues.count - totalCount) + } else if newValues.count < totalCount { + let missingCount = totalCount - newValues.count + let defaults = Array(repeating: "", count: missingCount) + newValues.append(contentsOf: defaults) + } + + while i < frameArr.endIndex && frameArr[i].starts(with: prefix) { + let data = frameArr[i].dropFirst(1).split(separator: ":", maxSplits: 1) + let number = Int(data[0])! + let value = String(data[1]) + newValues[number] = value + i += 1 + } + return newValues + + } + + let arguments = updateValues(prefix: "a", totalCount: argCount, oldValues: prevFrame?.arguments ?? []) + let registers = updateValues(prefix: "r", totalCount: regCount, oldValues: prevFrame?.registers ?? []) + + let frame = Frame(bytecodeOffset: bytecodeOffset, + accumulator: accumulator, + arguments: arguments, + registers: registers, + functionId: functionId, + frameType: frameType) + return frame + } + + private static func parseFullFrames(_ stdout: String) -> [Frame] { + var frameArray: [Frame] = [] + var prevFrame: Frame? = nil + + let split = stdout.split(separator: "\n", omittingEmptySubsequences: false) + let frames = split.split(separator: "") + + for frame in frames { + assert(frame.first?.starts(with: "---") == true, "Invalid frame header found: \(frame.first ?? "nil")") + + prevFrame = parseDiffFrame(frame, prevFrame) + frameArray.append(prevFrame!) + } + return frameArray + } + + public static func relate(_ optIn: String, with unoptIn: String) -> Bool { + let optFrames = parseFullFrames(optIn) + let unoptFrames = parseFullFrames(unoptIn) + var unoptFramesLeft = ArraySlice(unoptFrames) + + for optFrame in optFrames { + guard let unoptIndex = unoptFramesLeft.firstIndex(where: optFrame.matches) else { + print(optFrame as AnyObject) + print("--------------------------") + print("[") + for unoptFrame in unoptFrames { + if unoptFrame.bytecodeOffset == optFrame.bytecodeOffset { + print(unoptFrame as AnyObject) + } + } + print("]") + return false + } + // Remove all skipped frames and the found frame. + unoptFramesLeft = unoptFramesLeft[(unoptIndex + 1)...] + } + return true + } +} diff --git a/Sources/RelateTool/main.swift b/Sources/RelateTool/main.swift new file mode 100644 index 000000000..33a9e718a --- /dev/null +++ b/Sources/RelateTool/main.swift @@ -0,0 +1,114 @@ +// Copyright 2026 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 Foundation +import Fuzzilli + +public struct V8DifferentialConfig { + public static let commonArgs: [String] = [ + "--expose-gc", + "--omit-quit", + "--allow-natives-for-differential-fuzzing", + "--fuzzing", + "--future", + "--harmony", + "--predictable", + "--trace", + "--print-bytecode", + "--correctness-fuzzer-suppressions", + "--no-lazy-feedback-allocation", + ] + + public static let differentialArgs: [String] = [ + "--no-sparkplug", + "--jit-fuzzing", + "--maglev-dumping", + "--turbofan-dumping", + "--turbofan-dumping-print-deopt-frames" + ] + + public static let referenceArgs: [String] = [ + "--no-turbofan", + "--no-maglev", + "--sparkplug-dumping", + "--interpreter-dumping" + ] +} + +struct Relater { + let d8Path: String + let pocPath: String + let dumpFilePath: String + + private func runV8(args: [String]) throws { + let process = Process() + process.executableURL = URL(fileURLWithPath: d8Path) + process.arguments = args + [pocPath] + + let pipe = Pipe() + process.standardOutput = pipe + process.standardError = pipe + + try process.run() + process.waitUntilExit() + } + + private func readDumpFile() throws -> String { + return try String(contentsOfFile: dumpFilePath, encoding: .utf8) + } + + private func cleanDumpFile() { + try? FileManager.default.removeItem(atPath: dumpFilePath) + } + + /// Main execution flow. + func run() { + do { + cleanDumpFile() + let optArgs = V8DifferentialConfig.commonArgs + V8DifferentialConfig.differentialArgs + try runV8(args: optArgs) + let optDumps = try readDumpFile() + + cleanDumpFile() + let refArgs = V8DifferentialConfig.commonArgs + V8DifferentialConfig.referenceArgs + try runV8(args: refArgs) + let unOptDumps = try readDumpFile() + + let result = DiffOracle.relate(optDumps, with: unOptDumps) + print("Differential check result: \(result)") + + if !result { + exit(1) + } + + } catch { + print("Error during relate: \(error)") + exit(1) + } + } +} + +let args = Arguments.parse(from: CommandLine.arguments) + +guard let jsShellPath = args["--d8"], + let pocPath = args["--poc"] else { + print("Usage: --d8 --poc [--dump ]") + exit(1) +} + +// Parse optional dump path, default to /tmp/output_dump.txt +let dumpPath = args["--dump"] ?? "/tmp/output_dump.txt" + +let relater = Relater(d8Path: jsShellPath, pocPath: pocPath, dumpFilePath: dumpPath) +relater.run() diff --git a/Tests/FuzzilliTests/DiffOracleTests.swift b/Tests/FuzzilliTests/DiffOracleTests.swift new file mode 100644 index 000000000..4b6176aa7 --- /dev/null +++ b/Tests/FuzzilliTests/DiffOracleTests.swift @@ -0,0 +1,314 @@ +// Copyright 2026 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 + +final class DiffOracleTests: XCTestCase { + + func testSimpleIdentity() { + let dump = """ + ---I + b:10 + f:1 + x:100 + n:1 + m:1 + a0:10 + r0:20 + + """ + // Should match itself + XCTAssertTrue(DiffOracle.relate(dump, with: dump)) + } + + func testIncrementalParsingLogic() { + // This tests if the second frame correctly inherits 'f:1', 'n:1', 'm:1' from the first frame + // and only updates 'b' and 'x'. + let unopt = """ + ---I + b:10 + f:1 + x:100 + n:1 + m:1 + a0:10 + r0:20 + + ---I + b:20 + x:200 + + """ + + // Even if we explicitly write out the full state in the "Opt" input, + // it should match the incremental "Unopt" input if parsing works correctly. + let optFull = """ + ---I + b:10 + f:1 + x:100 + n:1 + m:1 + a0:10 + r0:20 + + ---I + b:20 + f:1 + x:200 + n:1 + m:1 + a0:10 + r0:20 + + """ + + XCTAssertTrue(DiffOracle.relate(optFull, with: unopt)) + } + + + func testOptimizedOutAccumulator() { + let unopt = """ + ---I + b:10 + f:1 + x:SecretValue + n:0 + m:0 + + """ + + let opt = """ + ---I + b:10 + f:1 + x: + n:0 + m:0 + + """ + + // in Opt should match distinct value in Unopt + XCTAssertTrue(DiffOracle.relate(opt, with: unopt)) + } + + func testOptimizedOutArgument() { + let unopt = """ + ---I + b:10 + f:1 + x:0 + n:2 + m:0 + a0:RealVal + a1:OtherVal + + """ + + let opt = """ + ---I + b:10 + f:1 + x:0 + n:2 + m:0 + a0: + a1:OtherVal + + """ + + XCTAssertTrue(DiffOracle.relate(opt, with: unopt)) + } + + func testArgumentMismatch() { + let unopt = """ + ---I + b:10 + f:1 + x:0 + n:1 + m:0 + a0:ValueA + + """ + + let opt = """ + ---I + b:10 + f:1 + x:0 + n:1 + m:0 + a0:ValueB + + """ + + XCTAssertFalse(DiffOracle.relate(opt, with: unopt)) + } + + func testRegisterMismatch() { + let unopt = """ + ---I + b:10 + f:1 + x:0 + n:0 + m:1 + r0:ValueA + + """ + + let opt = """ + ---I + b:10 + f:1 + x:0 + n:0 + m:1 + r0:ValueB + + """ + + XCTAssertFalse(DiffOracle.relate(opt, with: unopt)) + } + + + func testSkipsUnoptimizedFrames() { + // Scenario: Unoptimized dump has extra intermediate steps (frames at offset 20 and 30). + // Optimized dump only snapshots offset 10 and 40. This is valid. + let unopt = """ + ---I + b:10 + f:1 + n:0 + m:0 + + ---I + b:20 + + ---I + b:30 + + ---I + b:40 + + """ + + let opt = """ + ---I + b:10 + f:1 + n:0 + m:0 + + ---I + b:40 + + """ + + XCTAssertTrue(DiffOracle.relate(opt, with: unopt)) + } + + func testOrderMatters() { + // Scenario: Opt dump tries to match b:40 BEFORE b:10. This is invalid. + // The relatation consumes the unopt stream forward. + let unopt = """ + ---I + b:10 + f:1 + n:0 + m:0 + + ---I + b:40 + + """ + + let opt = """ + ---I + b:40 + f:1 + n:0 + m:0 + + ---I + b:10 + + """ + + XCTAssertFalse(DiffOracle.relate(opt, with: unopt)) + } + + func testBytecodeOffsetMismatch() { + let unopt = """ + ---I + b:10 + f:1 + n:0 + m:0 + + """ + + let opt = """ + ---I + b:99 + f:1 + n:0 + m:0 + + """ + + XCTAssertFalse(DiffOracle.relate(opt, with: unopt)) + } + + func testArrayResizeUpdateValues() { + // Tests the logic in `updateValues` where it handles missing or excess values + // when n counts change between frames. + + let unopt = """ + ---I + b:10 + f:1 + n:1 + m:0 + a0:A + + ---I + b:20 + n:2 + a1:B + + """ + + // This opt dump expects a0 to still be A (carried over) and a1 to be B. + let opt = """ + ---M + b:10 + f:1 + n:1 + m:0 + a0:A + + ---M + b:20 + n:2 + a0:A + a1:B + + """ + + XCTAssertTrue(DiffOracle.relate(opt, with: unopt)) + } +} From 954de4ec5362e79130ab2ac5795a2d7e2fa22cf3 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Mon, 5 Jan 2026 08:38:44 +0100 Subject: [PATCH 56/89] [test] Cleanup i31 tests Change-Id: I63cb4c1f7aef06240729a3879eb20ab3ef452549 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8911776 Auto-Submit: Matthias Liedtke Reviewed-by: Pawel Krawczyk Commit-Queue: Matthias Liedtke --- Tests/FuzzilliTests/WasmTests.swift | 42 ++++------------------------- 1 file changed, 5 insertions(+), 37 deletions(-) diff --git a/Tests/FuzzilliTests/WasmTests.swift b/Tests/FuzzilliTests/WasmTests.swift index c219e6175..0b291643f 100644 --- a/Tests/FuzzilliTests/WasmTests.swift +++ b/Tests/FuzzilliTests/WasmTests.swift @@ -4817,17 +4817,18 @@ class WasmGCTests: XCTestCase { try refNullAbstractTypes(sharedRef: false) } - func testi31RefFromJs() throws { + func i31Ref(shared: Bool) throws { let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--experimental-wasm-shared"]) let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) let b = fuzzer.makeBuilder() + let i31RefT = ILType.wasmI31Ref(shared: shared) let module = b.buildWasmModule { wasmModule in - wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmI31Ref()]) { function, label, args in - [function.wasmRefI31(args[0])] + wasmModule.addWasmFunction(with: [.wasmi32] => [i31RefT]) { function, label, args in + [function.wasmRefI31(args[0], shared: shared)] } - wasmModule.addWasmFunction(with: [.wasmI31Ref()] => [.wasmi32, .wasmi32]) { function, label, args in + wasmModule.addWasmFunction(with: [i31RefT] => [.wasmi32, .wasmi32]) { function, label, args in [function.wasmI31Get(args[0], isSigned: true), function.wasmI31Get(args[0], isSigned: false)] } @@ -4851,39 +4852,6 @@ class WasmGCTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "42\n-42\n42,42\n-42,2147483606\n") } - func i31Ref(shared: Bool) throws { - let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: shared ? ["--experimental-wasm-shared", "--experimental-wasm-stringref"] : []) - let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) - let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) - let b: ProgramBuilder = fuzzer.makeBuilder() - let i31RefType = ILType.wasmI31Ref(shared: shared) - - let module = b.buildWasmModule { wasmModule in - let createReferenceOf = wasmModule.addWasmFunction(with: [.wasmi32] => [i31RefType]) { function, label, args in - [function.wasmRefI31(args[0], shared: shared)] - } - let dereference = wasmModule.addWasmFunction(with: [i31RefType] => [.wasmi32, .wasmi32]) { function, label, args in - [function.wasmI31Get(args[0], isSigned: true), - function.wasmI31Get(args[0], isSigned: false)] - } - wasmModule.addWasmFunction(with: [] => [.wasmi32, .wasmi32]) { function, label, args in - let const = function.consti32(-42) - let constRef = function.wasmCallDirect(signature: [.wasmi32] => [i31RefType], function: createReferenceOf, functionArgs: [const]) - let dereferencedConst = function.wasmCallDirect(signature: [i31RefType] => [.wasmi32, .wasmi32], function: dereference, functionArgs: constRef) - return dereferencedConst - } - } - - let exports = module.loadExports() - let outputFunc = b.createNamedVariable(forBuiltin: "output") - let result = b.callMethod(module.getExportedMethod(at: 2), on: exports, withArgs: [b.loadInt(42)]) - b.callFunction(outputFunc, withArgs: [result]) - - let prog = b.finalize() - let jsProg = fuzzer.lifter.lift(prog) - testForOutput(program: jsProg, runner: runner, outputString: "-42,2147483606\n") - } - func testi31RefShared() throws { try i31Ref(shared: true) } From 0785e9ecc881b0efb64fb32e6fffecdf5be77df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Fl=C3=BCckiger?= Date: Mon, 12 Jan 2026 17:49:28 +0100 Subject: [PATCH 57/89] Add support for iterator sequencing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: 434977727 Change-Id: I24617b4353f7205c8654ce9f555ed043fb5e4b8c Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8902197 Reviewed-by: Michael Achenbach Commit-Queue: Olivier Flückiger --- Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index 562009562..05d07890c 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -1036,7 +1036,7 @@ public extension ILType { static let jsIterator = ILType.iterable + ILType.object(ofGroup: "Iterator", withProperties: ["value", "done"], withMethods: ["next", "return", "throw", "map", "filter", "take", "drop", "flatMap", "reduce", "toArray", "forEach", "some", "every", "find"]) /// Type of the JavaScript Iterator constructor builtin. - static let jsIteratorConstructor = ILType.object(ofGroup: "IteratorConstructor", withProperties: ["prototype"], withMethods: ["from"]) + static let jsIteratorConstructor = ILType.object(ofGroup: "IteratorConstructor", withProperties: ["prototype"], withMethods: ["from", "concat"]) /// Type of a JavaScript generator object. static let jsGenerator = ILType.iterable + ILType.object(ofGroup: "Generator", withMethods: ["next", "return", "throw"]) @@ -1544,6 +1544,7 @@ public extension ObjectGroup { ], methods: [ "from" : [.jsAnything] => .jsIterator, + "concat" : [.jsAnything...] => .jsIterator, ] ) From da6f981650637848a31ce74bd2f4af21385e08dc Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Tue, 13 Jan 2026 17:09:39 +0100 Subject: [PATCH 58/89] Make transpilator script support mjsunit Bug: 442444727 Change-Id: Ia254954f25df8284a58d43cce0c27383027e01db Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8926738 Commit-Queue: Michael Achenbach Reviewed-by: Matthias Liedtke --- Tools/transpile_tests/transpile_tests.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Tools/transpile_tests/transpile_tests.py b/Tools/transpile_tests/transpile_tests.py index 02b913a18..6ee3959d5 100644 --- a/Tools/transpile_tests/transpile_tests.py +++ b/Tools/transpile_tests/transpile_tests.py @@ -38,6 +38,9 @@ class DefaultMetaDataParser: """Class instantiated once per test configuration/suite, providing a method to check for supported tests based on their metadata. """ + def __init__(self, _): + pass + def is_supported(self, abspath, relpath): return any(relpath.name.endswith(ext) for ext in ['.js', '.mjs']) @@ -67,6 +70,13 @@ def is_supported(self, abspath, relpath): TEST_CONFIGS = { + 'mjsunit': { + 'path': 'test/mjsunit', + 'excluded_suffixes': [], + 'levels': 2, + 'expand_level_paths': [], + 'metadata_parser': DefaultMetaDataParser, + }, 'test262': { 'path': 'test/test262/data/test', 'excluded_suffixes': ['_FIXTURE.js'], @@ -83,7 +93,7 @@ def is_supported(self, abspath, relpath): 'language/statements/class', ], 'metadata_parser': Test262MetaDataParser, - } + }, } From 614a606f3f11d33e611d43a0301f2ccadf910d0a Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Tue, 13 Jan 2026 15:40:35 +0100 Subject: [PATCH 59/89] [environment] Refactor excluded properties on prototype There is an ever-growing list of properties that exist on a receiver which don't exist on the corresponding prototype object of the constructor or are not usable on it. So far, there are two cases for it: 1) They simply do not come from the prototype, e.g. iterator instances have properties that are part of the Iterator protocol, like `next`, while Iterator.prototype.next does not exist. 2) The property does exist on the prototype object, however it is not usable on it. There are a few properties that aren't actually regular properties but get accessors. These get accessors cannot be used on the prototype object as they will throw on access, e.g. `Intl.Collator.prototype.compare`. For nicer documentation, move these excluded properties to the call that registers the prototype object on the environment. Change-Id: I6e6163e0424a3e1f7e213ea2700d2dc8c883cd31 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8926737 Commit-Queue: Matthias Liedtke Reviewed-by: Michael Achenbach --- .../Environment/JavaScriptEnvironment.swift | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index 05d07890c..921e07878 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -1324,24 +1324,17 @@ public extension ObjectGroup { // them. Instead Fuzzilli generates GetProperty operations for them which will then be typed as // an `ILType.unboundFunction` which knows the required receiver type (in the example `Date`), // so Fuzzilli can generate sequences like `Date.prototype.getTime.call(new Date())`. - static func createPrototypeObjectGroup(_ receiver: ObjectGroup) -> ObjectGroup { + static func createPrototypeObjectGroup(_ receiver: ObjectGroup, excludeProperties: [String] = []) -> ObjectGroup { let name = receiver.name + ".prototype" var properties = Dictionary(uniqueKeysWithValues: receiver.methods.map { ($0.0, ILType.unboundFunction($0.1.first, receiver: receiver.instanceType)) }) - // 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 - // TODO(mliedtke): Find a nicer interface than manually excluding some - // magic combinations here. - if receiver.name == "Intl.DateTimeFormat" || receiver.name == "Intl.NumberFormat" { - properties.removeValue(forKey: "format") - } else if receiver.name == "Intl.Collator" { - properties.removeValue(forKey: "compare") - } else if receiver.name == "Iterator" { - properties.removeValue(forKey: "next") - properties.removeValue(forKey: "return") - properties.removeValue(forKey: "throw") + // Some properties of the instance type do not come from the prototype, e.g. Iterator.next + // which comes from the Iterator protocol. + // Other properties are get accessors instead of regular functions, and error when accessed + // on the prototype. We hide them from the prototype object to avoid generating patterns + // like `let v0 = Intl.DateTimeFormat.prototype.format`. + for property in excludeProperties { + properties.removeValue(forKey: property) } return ObjectGroup( name: name, @@ -1534,7 +1527,9 @@ public extension ObjectGroup { ] ) - static let jsIteratorPrototype = createPrototypeObjectGroup(jsIterator) + // next, return and throw are part of the Iterator protocol, not Iterator.prototype. + static let jsIteratorPrototype = createPrototypeObjectGroup(jsIterator, + excludeProperties: ["next", "return", "throw"]) static let jsIteratorConstructor = ObjectGroup( name: "IteratorConstructor", @@ -3092,7 +3087,10 @@ extension ObjectGroup { ] ) - static let jsIntlCollatorPrototype = createPrototypeObjectGroup(jsIntlCollator) + // Exclude compare as it is a get accessor, not a property, meaning that + // Intl.Collator.prototype.compare throws unconditionally (even without calling it). + static let jsIntlCollatorPrototype = createPrototypeObjectGroup(jsIntlCollator, + excludeProperties: ["compare"]) static let jsIntlCollatorConstructor = ObjectGroup( name: "IntlCollatorConstructor", @@ -3136,7 +3134,10 @@ extension ObjectGroup { ] ) - static let jsIntlDateTimeFormatPrototype = createPrototypeObjectGroup(jsIntlDateTimeFormat) + // Exclude format as it is a get accessor, not a property, meaning that + // Intl.DateTimeFormat.prototype.format throws unconditionally (even without calling it). + static let jsIntlDateTimeFormatPrototype = createPrototypeObjectGroup(jsIntlDateTimeFormat, + excludeProperties: ["format"]) static let jsIntlDateTimeFormatConstructor = ObjectGroup( name: "IntlDateTimeFormatConstructor", @@ -3246,7 +3247,10 @@ extension ObjectGroup { ] ) - static let jsIntlNumberFormatPrototype = createPrototypeObjectGroup(jsIntlNumberFormat) + // Exclude format as it is a get accessor, not a property, meaning that + // Intl.NumberFormat.prototype.format throws unconditionally (even without calling it). + static let jsIntlNumberFormatPrototype = createPrototypeObjectGroup(jsIntlNumberFormat, + excludeProperties: ["format"]) static let jsIntlNumberFormatConstructor = ObjectGroup( name: "IntlNumberFormatConstructor", From a63c74073085d4e619a5622cf563ce7755f4ff3e Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Fri, 9 Jan 2026 16:19:01 +0100 Subject: [PATCH 60/89] [wasm] Use a wasm-gc signature as input for if-else-endif Similar to commit 3b241b0b0ea992ce6e99433a44b18bdfdaa92ba1 this change makes wasm's if-else control flow blocks use wasm-gc signatures as inputs instead of having the signature stored as a property inside the operation. This allows using wasm-gc index types inside these signatures. Bug: 445356784 Change-Id: I8315f64be536a1882ab9d4d39a3b7b72eb690456 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8914456 Reviewed-by: Pawel Krawczyk Commit-Queue: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 50 +++++++++++------ .../Fuzzilli/CodeGen/WasmCodeGenerators.swift | 45 ++++++++-------- Sources/Fuzzilli/FuzzIL/Instruction.swift | 19 +++---- Sources/Fuzzilli/FuzzIL/JSTyper.swift | 17 +++--- Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 27 +++++----- Sources/Fuzzilli/Lifting/FuzzILLifter.swift | 4 +- Sources/Fuzzilli/Lifting/WasmLifter.swift | 6 +-- .../Fuzzilli/Minimization/BlockReducer.swift | 18 ++++--- .../Fuzzilli/Mutators/OperationMutator.swift | 4 +- Sources/Fuzzilli/Protobuf/operations.pb.swift | 53 ++++++++----------- Sources/Fuzzilli/Protobuf/operations.proto | 9 ++-- 11 files changed, 131 insertions(+), 121 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index fae586123..ed04ebae9 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -3973,35 +3973,53 @@ public class ProgramBuilder { withInputs: labels + args + [on]) } + // TODO(mliedtke): It would be nice to remove this overload to simplify the codebase. public func wasmBuildIfElse(_ condition: Variable, hint: WasmBranchHint = .None, ifBody: () -> Void, elseBody: (() -> Void)? = nil) { - b.emit(WasmBeginIf(hint: hint), withInputs: [condition]) + let signatureDef = b.wasmDefineAdHocSignatureType(signature: [] => []) + b.emit(WasmBeginIf(hint: hint), withInputs: [signatureDef, condition]) ifBody() if let elseBody { - b.emit(WasmBeginElse()) + b.emit(WasmBeginElse(), withInputs: [signatureDef]) elseBody() } - b.emit(WasmEndIf()) + b.emit(WasmEndIf(), withInputs: [signatureDef]) } public func wasmBuildIfElse(_ condition: Variable, signature: WasmSignature, args: [Variable], inverted: Bool, ifBody: (Variable, [Variable]) -> Void, elseBody: ((Variable, [Variable]) -> Void)? = nil) { - let beginBlock = b.emit(WasmBeginIf(with: signature, inverted: inverted), - withInputs: args + [condition], - types: signature.parameterTypes + [.wasmi32]) + assert(signature.outputTypes.count == 0) + let signatureDef = b.wasmDefineAdHocSignatureType(signature: signature) + let beginBlock = b.emit(WasmBeginIf(parameterCount: signature.parameterTypes.count, inverted: inverted), + withInputs: [signatureDef] + args + [condition], + types: [.wasmTypeDef()] + signature.parameterTypes + [.wasmi32]) ifBody(beginBlock.innerOutput(0), Array(beginBlock.innerOutputs(1...))) if let elseBody { - let elseBlock = b.emit(WasmBeginElse(with: signature)) + let elseBlock = b.emit(WasmBeginElse(parameterCount: signature.parameterTypes.count, outputCount: signature.outputTypes.count), + withInputs: [signatureDef], + types: [.wasmTypeDef()]) elseBody(elseBlock.innerOutput(0), Array(elseBlock.innerOutputs(1...))) } - b.emit(WasmEndIf()) + b.emit(WasmEndIf(), withInputs: [signatureDef], types: [.wasmTypeDef()]) } + // TODO(mliedtke): Instead of taking a WasmSignature also allow taking an existing signature + // definition (Variable) as input and add a code generator for it. @discardableResult public func wasmBuildIfElseWithResult(_ condition: Variable, hint: WasmBranchHint = .None, signature: WasmSignature, args: [Variable], ifBody: (Variable, [Variable]) -> [Variable], elseBody: (Variable, [Variable]) -> [Variable]) -> [Variable] { - let beginBlock = b.emit(WasmBeginIf(with: signature, hint: hint), withInputs: args + [condition], types: signature.parameterTypes + [.wasmi32]) + let signatureDef = b.wasmDefineAdHocSignatureType(signature: signature) + let beginBlock = b.emit( + WasmBeginIf(parameterCount: signature.parameterTypes.count), + withInputs: [signatureDef] + args + [condition], + types: [.wasmTypeDef()] + signature.parameterTypes + [.wasmi32]) let trueResults = ifBody(beginBlock.innerOutput(0), Array(beginBlock.innerOutputs(1...))) - let elseBlock = b.emit(WasmBeginElse(with: signature), withInputs: trueResults, types: signature.outputTypes) + let elseBlock = b.emit( + WasmBeginElse(parameterCount: signature.parameterTypes.count, outputCount: signature.outputTypes.count), + withInputs: [signatureDef] + trueResults, + types: [.wasmTypeDef()] + signature.outputTypes) let falseResults = elseBody(elseBlock.innerOutput(0), Array(elseBlock.innerOutputs(1...))) - return Array(b.emit(WasmEndIf(outputTypes: signature.outputTypes), withInputs: falseResults, types: signature.outputTypes).outputs) + return Array(b.emit( + WasmEndIf(outputCount: signature.outputTypes.count), + withInputs: [signatureDef] + falseResults, + types: [.wasmTypeDef()] + signature.outputTypes).outputs) } @discardableResult @@ -4010,6 +4028,7 @@ public class ProgramBuilder { return wasmBuildLoopImpl(signature: signature, signatureDef: signatureDef, args: args, body: body) } + // TODO(mliedtke): Also use this inside the code generator. @discardableResult public func wasmBuildLoop(with signatureDef: Variable, args: [Variable], body: (Variable, [Variable]) -> [Variable]) -> [Variable] { let signature = b.type(of: signatureDef).wasmFunctionSignatureDefSignature @@ -4596,11 +4615,11 @@ public class ProgramBuilder { + WasmAbstractHeapType.allCases.map {.wasmRef($0, nullability: true)})} } - public func randomWasmBlockArguments(upTo n: Int) -> [Variable] { + public func randomWasmBlockArguments(upTo n: Int, allowingGcTypes: Bool = false) -> [Variable] { (0.. outputTypes) let loopBegin = b.emit(WasmBeginLoop(parameterCount: parameters.count), withInputs: [signature] + args) @@ -1455,22 +1449,24 @@ public let WasmCodeGenerators: [CodeGenerator] = [ inputs: .required(.wasmi32), provides: [.wasmFunction] ) { b, condition in + let signature = b.wasmDefineAdHocSignatureType(signature: [] => []) + b.runtimeData.push("ifSignature", signature) b.emit( WasmBeginIf(hint: b.randomWasmBranchHint()), - withInputs: [condition]) + withInputs: [signature, condition]) }, GeneratorStub( "WasmBeginElseGenerator", inContext: .single(.wasmFunction), provides: [.wasmFunction] ) { b in - b.emit(WasmBeginElse()) + b.emit(WasmBeginElse(), withInputs: [b.runtimeData.popAndPush("ifSignature")]) }, GeneratorStub( "WasmEndIfElseGenerator", inContext: .single(.wasmFunction) ) { b in - b.emit(WasmEndIf()) + b.emit(WasmEndIf(), withInputs: [b.runtimeData.pop("ifSignature")]) }, ]), @@ -1483,14 +1479,16 @@ public let WasmCodeGenerators: [CodeGenerator] = [ inputs: .required(.wasmi32), provides: [.wasmFunction] ) { b, condition in - let args = b.randomWasmBlockArguments(upTo: 5) + let args = b.randomWasmBlockArguments(upTo: 5, allowingGcTypes: true) let parameters = args.map(b.type) let outputTypes = b.randomWasmBlockOutputTypes(upTo: 5) + let signature = b.wasmDefineAdHocSignatureType(signature: parameters => outputTypes) + b.runtimeData.push("ifSignature", signature) b.emit( WasmBeginIf( - with: parameters => outputTypes, + parameterCount: parameters.count, hint: b.randomWasmBranchHint()), - withInputs: args + [condition]) + withInputs: [signature] + args + [condition]) }, GeneratorStub( "WasmBeginElseGenerator", @@ -1498,22 +1496,23 @@ public let WasmCodeGenerators: [CodeGenerator] = [ provides: [.wasmFunction] ) { b in let function = b.currentWasmFunction - let signature = b.currentWasmSignature - let trueResults = signature.outputTypes.map( - function.findOrGenerateWasmVar) - b.emit(WasmBeginElse(with: signature), withInputs: trueResults) + let signature = b.runtimeData.popAndPush("ifSignature") + let wasmSignature = b.type(of: signature).wasmFunctionSignatureDefSignature + let trueResults = wasmSignature.outputTypes.map(function.findOrGenerateWasmVar) + b.emit(WasmBeginElse(parameterCount: wasmSignature.parameterTypes.count, + outputCount: wasmSignature.outputTypes.count), + withInputs: [signature] + trueResults) }, GeneratorStub( "WasmEndIfGenerator", inContext: .single(.wasmFunction) ) { 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) + let signature = b.runtimeData.pop("ifSignature") + let wasmSignature = b.type(of: signature).wasmFunctionSignatureDefSignature + let falseResults = wasmSignature.outputTypes.map(function.findOrGenerateWasmVar) + b.emit(WasmEndIf(outputCount: wasmSignature.outputTypes.count), + withInputs: [signature] + falseResults) }, ]), diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index cc52480ec..728619b03 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -1466,19 +1466,18 @@ extension Instruction: ProtobufConvertible { } case .wasmBeginIf(let op): $0.wasmBeginIf = Fuzzilli_Protobuf_WasmBeginIf.with { - $0.parameterTypes = op.signature.parameterTypes.map(ILTypeToWasmTypeEnum) - $0.outputTypes = op.signature.outputTypes.map(ILTypeToWasmTypeEnum) + $0.parameterCount = Int32(op.parameterCount) $0.inverted = op.inverted $0.hint = convertEnum(op.hint, WasmBranchHint.allCases) } case .wasmBeginElse(let op): $0.wasmBeginElse = Fuzzilli_Protobuf_WasmBeginElse.with { - $0.parameterTypes = op.signature.parameterTypes.map(ILTypeToWasmTypeEnum) - $0.outputTypes = op.signature.outputTypes.map(ILTypeToWasmTypeEnum) + $0.parameterCount = Int32(op.parameterCount) + $0.outputCount = Int32(op.outputCount) } case .wasmEndIf(let op): $0.wasmEndIf = Fuzzilli_Protobuf_WasmEndIf.with { - $0.outputTypes = op.outputTypes.map(ILTypeToWasmTypeEnum) + $0.outputCount = Int32(op.outputCount) } case .wasmNop(_): fatalError("Should never be serialized") @@ -2503,15 +2502,11 @@ extension Instruction: ProtobufConvertible { case .wasmBranchTable(let p): op = WasmBranchTable(labelTypes: p.parameters.map(WasmTypeEnumToILType), valueCount: Int(p.valueCount)) case .wasmBeginIf(let p): - let parameters = p.parameterTypes.map(WasmTypeEnumToILType) - let outputs = p.outputTypes.map(WasmTypeEnumToILType) - op = WasmBeginIf(with: parameters => outputs, hint: try convertEnum(p.hint, WasmBranchHint.allCases), inverted: p.inverted) + op = WasmBeginIf(parameterCount: Int(p.parameterCount), hint: try convertEnum(p.hint, WasmBranchHint.allCases), inverted: p.inverted) case .wasmBeginElse(let p): - let parameters = p.parameterTypes.map(WasmTypeEnumToILType) - let outputs = p.outputTypes.map(WasmTypeEnumToILType) - op = WasmBeginElse(with: parameters => outputs) + op = WasmBeginElse(parameterCount: Int(p.parameterCount), outputCount: Int(p.outputCount)) case .wasmEndIf(let p): - op = WasmEndIf(outputTypes: p.outputTypes.map(WasmTypeEnumToILType)) + op = WasmEndIf(outputCount: Int(p.outputCount)) case .wasmNop(_): fatalError("Should never be deserialized!") case .wasmUnreachable(_): diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index 29e75d80e..4cf4888e2 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -818,14 +818,17 @@ public struct JSTyper: Analyzer { wasmTypeBeginBlock(instr, op.signature) case .wasmEndBlock(let op): wasmTypeEndBlock(instr, op.outputTypes) - case .wasmBeginIf(let op): - wasmTypeBeginBlock(instr, op.signature) - case .wasmBeginElse(let op): + case .wasmBeginIf(_): + let signature = type(of: instr.input(0)).wasmFunctionSignatureDefSignature + wasmTypeBeginBlock(instr, signature) + case .wasmBeginElse(_): + let signature = type(of: instr.input(0)).wasmFunctionSignatureDefSignature // The else block is both end and begin block. - wasmTypeEndBlock(instr, op.signature.outputTypes) - wasmTypeBeginBlock(instr, op.signature) - case .wasmEndIf(let op): - wasmTypeEndBlock(instr, op.outputTypes) + wasmTypeEndBlock(instr, signature.outputTypes) + wasmTypeBeginBlock(instr, signature) + case .wasmEndIf(_): + let signature = type(of: instr.input(0)).wasmFunctionSignatureDefSignature + wasmTypeEndBlock(instr, signature.outputTypes) case .wasmBeginLoop(_): // Note that different to all other blocks the loop's label parameters are the input types // of the block, not the result types (because a branch to a loop label jumps to the diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index 508922c58..3f47b2d30 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -1277,43 +1277,46 @@ public enum WasmBranchHint: CaseIterable { final class WasmBeginIf: WasmOperation { override var opcode: Opcode { .wasmBeginIf(self) } - let signature: WasmSignature let inverted: Bool let hint: WasmBranchHint - init(with signature: WasmSignature = [] => [], hint: WasmBranchHint = .None, inverted: Bool = false) { - self.signature = signature + init(parameterCount: Int = 0, hint: WasmBranchHint = .None, inverted: Bool = false) { self.inverted = inverted self.hint = hint // Note that the condition is the last input! This is due to how lifting works for the wasm // 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. + // Inputs: The signature, the arguments and the condition. // 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]) + super.init(numInputs: 2 + parameterCount, numInnerOutputs: 1 + parameterCount, attributes: [.isBlockStart, .propagatesSurroundingContext, .isMutable], requiredContext: [.wasmFunction]) } + + var parameterCount: Int {numInputs - 2} } final class WasmBeginElse: WasmOperation { override var opcode: Opcode { .wasmBeginElse(self) } - let signature: WasmSignature - init(with signature: WasmSignature = [] => []) { - self.signature = signature + // The parameterCount and outputCount of the wasm signature. + init(parameterCount: Int = 0, outputCount: Int = 0) { // 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]) + super.init(numInputs: 1 + outputCount, numInnerOutputs: 1 + parameterCount, attributes: [.isBlockStart, .isBlockEnd, .propagatesSurroundingContext], requiredContext: [.wasmFunction]) } + + var parameterCount: Int {numInnerOutputs - 1} + var outputCount: Int {numInputs - 1} } final class WasmEndIf: WasmOperation { override var opcode: Opcode { .wasmEndIf(self) } - let outputTypes: [ILType] - init(outputTypes: [ILType] = []) { - self.outputTypes = outputTypes - super.init(numInputs: outputTypes.count, numOutputs: outputTypes.count, attributes: [.isBlockEnd], requiredContext: [.wasmFunction]) + init(outputCount: Int = 0) { + super.init(numInputs: 1 + outputCount, numOutputs: outputCount, attributes: [.isBlockEnd], requiredContext: [.wasmFunction]) } + + var outputCount: Int {numInputs - 1} } final class WasmBeginLoop: WasmOperation { diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift index f7985fe99..4df1a6fce 100644 --- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift +++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift @@ -1219,14 +1219,12 @@ public class FuzzILLifter: Lifter { case .Likely: "likely " case .Unlikely: "unlikely " } - w.emit("WasmBeginIf \(op.inverted ? "inverted " : "")\(hint)(\(op.signature)) [\(inputs)] -> L:\(instr.innerOutput(0)) [\(liftCallArguments(instr.innerOutputs(1...)))]") + w.emit("WasmBeginIf \(op.inverted ? "inverted " : "")\(hint) [\(inputs)] -> L:\(instr.innerOutput(0)) [\(liftCallArguments(instr.innerOutputs(1...)))]") w.increaseIndentionLevel() case .wasmBeginElse(_): w.decreaseIndentionLevel() let inputs = instr.inputs.map(lift).joined(separator: ", ") - // Note that the signature is printed by the WasmBeginIf, so we skip it here for better - // readability. w.emit("WasmBeginElse [\(inputs)] -> L:\(instr.innerOutput(0)) [\(liftCallArguments(instr.innerOutputs(1...)))]") w.increaseIndentionLevel() diff --git a/Sources/Fuzzilli/Lifting/WasmLifter.swift b/Sources/Fuzzilli/Lifting/WasmLifter.swift index 56eaddcfe..754a12795 100644 --- a/Sources/Fuzzilli/Lifting/WasmLifter.swift +++ b/Sources/Fuzzilli/Lifting/WasmLifter.swift @@ -1235,8 +1235,7 @@ public class WasmLifter { self.currentFunction!.labelBranchDepthMapping[instr.innerOutput(0)] = self.currentFunction!.variableAnalyzer.wasmBranchDepth // Needs typer analysis return true - case .wasmBeginIf(let op): - registerSignature(op.signature) + case .wasmBeginIf(_): self.currentFunction!.labelBranchDepthMapping[instr.innerOutput(0)] = self.currentFunction!.variableAnalyzer.wasmBranchDepth // Needs typer analysis return true @@ -1970,7 +1969,8 @@ public class WasmLifter { return Data([0x0E]) + Leb128.unsignedEncode(op.valueCount) + depths.map(Leb128.unsignedEncode).joined() case .wasmBeginIf(let op): currentFunction!.addBranchHint(op.hint) - let beginIf = Data([0x04] + Leb128.unsignedEncode(try getSignatureIndex(op.signature))) + let signatureDesc = typer.getTypeDescription(of: wasmInstruction.input(0)) + let beginIf = Data([0x04] + Leb128.unsignedEncode(typeDescToIndex[signatureDesc]!)) // Invert the condition with an `i32.eqz` (resulting in 0 becoming 1 and everything else becoming 0). return op.inverted ? Data([0x45]) + beginIf : beginIf case .wasmBeginElse(_): diff --git a/Sources/Fuzzilli/Minimization/BlockReducer.swift b/Sources/Fuzzilli/Minimization/BlockReducer.swift index 3fd0a0ad0..a25e18d12 100644 --- a/Sources/Fuzzilli/Minimization/BlockReducer.swift +++ b/Sources/Fuzzilli/Minimization/BlockReducer.swift @@ -479,8 +479,9 @@ struct BlockReducer: Reducer { let ifBlock = group.block(0) let beginIf = helper.code[ifBlock.head].op as! WasmBeginIf + let endIf = helper.code[group.tail].op as! WasmEndIf // Now try to turn if-else into just if. - if group.numBlocks == 2 && beginIf.signature.outputTypes.isEmpty { + if group.numBlocks == 2 && endIf.outputCount == 0 { // First try to remove the else block. let elseBlock = group.block(1) let rangeToNop = Array(elseBlock.head ..< elseBlock.tail) @@ -490,7 +491,7 @@ struct BlockReducer: Reducer { } // Then try to remove the if block. This requires inverting the condition of the if. - let invertedIf = WasmBeginIf(with: beginIf.signature, inverted: !beginIf.inverted) + let invertedIf = WasmBeginIf(parameterCount: beginIf.parameterCount, inverted: !beginIf.inverted) var replacements = [(Int, Instruction)]() // The new WasmBeginIf will take the original inputs but produces the inner outputs // of the original WasmBeginElse block, so that users of them are rewired correctly. @@ -509,7 +510,7 @@ struct BlockReducer: Reducer { } // If we have outputs or the innerOutputs of the WasmBeginIf / WasmBeginElse are used, // a more "sophisticated" reduction is needed. - if group.numBlocks == 2 && (!beginIf.signature.parameterTypes.isEmpty || !beginIf.signature.outputTypes.isEmpty) { + if group.numBlocks == 2 && (beginIf.parameterCount != 0 || endIf.outputCount != 0) { let elseBlock = group.block(1) let beginIfInstr = helper.code[ifBlock.head] @@ -522,11 +523,11 @@ struct BlockReducer: Reducer { do { // First try to replace the if-else with the if body. // "Shortcut" bypassing the WasmBeginIf by directly using its inputs. var varReplacements = Dictionary( - uniqueKeysWithValues: zip(beginIfInstr.innerOutputs.dropFirst(), beginIfInstr.inputs)) + uniqueKeysWithValues: zip(beginIfInstr.innerOutputs.dropFirst(), beginIfInstr.inputs.dropFirst())) // Replace all usages of the WasmEndIf outputs with the results of the if true // block which are the inputs into the WasmBeginElse block. varReplacements.merge( - zip(helper.code[elseBlock.tail].outputs, helper.code[elseBlock.head].inputs.map {varReplacements[$0] ?? $0}), + zip(helper.code[elseBlock.tail].outputs, helper.code[elseBlock.head].inputs.dropFirst().map {varReplacements[$0] ?? $0}), uniquingKeysWith: {_, _ in fatalError("duplicate variables")}) var newCode = Code() for (i, instr) in helper.code.enumerated() { @@ -547,10 +548,13 @@ struct BlockReducer: Reducer { // "Shortcut" bypassing the WasmBeginElse by directly using the inputs into the // WasmBeginIf. var varReplacements = Dictionary( - uniqueKeysWithValues: zip(beginElseInstr.innerOutputs.dropFirst(), beginIfInstr.inputs)) + uniqueKeysWithValues: zip(beginElseInstr.innerOutputs.dropFirst(), beginIfInstr.inputs.dropFirst())) // Replace all usages of the WasmEndIf outputs with the results of the else block // which are the inputs into the WasmEndIf block. - varReplacements.merge(zip(helper.code[elseBlock.tail].outputs, helper.code[elseBlock.tail].inputs.map {varReplacements[$0] ?? $0}), uniquingKeysWith: {_, _ in fatalError("duplicate variables")}) + varReplacements.merge( + zip(helper.code[elseBlock.tail].outputs, + helper.code[elseBlock.tail].inputs.dropFirst().map {varReplacements[$0] ?? $0}), + uniquingKeysWith: {_, _ in fatalError("duplicate variables")}) var newCode = Code() for (i, instr) in helper.code.enumerated() { if i == elseBlock.tail || (i >= ifBlock.head && i <= ifBlock.tail) { diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift index 4f8b7b843..1098ea1b3 100644 --- a/Sources/Fuzzilli/Mutators/OperationMutator.swift +++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift @@ -369,7 +369,7 @@ public class OperationMutator: BaseInstructionMutator { case .wasmDefineGlobal(let op): // We never change the type of the global, only the value as changing the type will break the following code pretty much instantly. - let wasmGlobal:WasmGlobal = + let wasmGlobal:WasmGlobal = switch op.wasmGlobal.toType() { case .wasmf32: .wasmf32(Float32(b.randomFloat())) @@ -499,7 +499,7 @@ public class OperationMutator: BaseInstructionMutator { case .wasmBranchIf(let op): newOp = WasmBranchIf(labelTypes: op.labelTypes, hint: chooseUniform(from: WasmBranchHint.allCases)) case .wasmBeginIf(let op): - newOp = WasmBeginIf(with: op.signature, hint: chooseUniform(from: WasmBranchHint.allCases), inverted: Bool.random()) + newOp = WasmBeginIf(parameterCount: op.parameterCount, hint: chooseUniform(from: WasmBranchHint.allCases), inverted: Bool.random()) case .wasmArrayGet(let op): // Switch signedness. (This only matters for packed types i8 and i16.) newOp = WasmArrayGet(isSigned: !op.isSigned) diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index 4cbf6f735..00cd84c4e 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -5408,9 +5408,7 @@ public struct Fuzzilli_Protobuf_WasmBeginIf: Sendable { // `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 parameterCount: Int32 = 0 public var inverted: Bool = false @@ -5426,9 +5424,9 @@ public struct Fuzzilli_Protobuf_WasmBeginElse: Sendable { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var parameterTypes: [Fuzzilli_Protobuf_WasmILType] = [] + public var parameterCount: Int32 = 0 - public var outputTypes: [Fuzzilli_Protobuf_WasmILType] = [] + public var outputCount: Int32 = 0 public var unknownFields = SwiftProtobuf.UnknownStorage() @@ -5440,7 +5438,7 @@ public struct Fuzzilli_Protobuf_WasmEndIf: Sendable { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var outputTypes: [Fuzzilli_Protobuf_WasmILType] = [] + public var outputCount: Int32 = 0 public var unknownFields = SwiftProtobuf.UnknownStorage() @@ -14275,7 +14273,7 @@ extension Fuzzilli_Protobuf_WasmReassign: SwiftProtobuf.Message, SwiftProtobuf._ extension Fuzzilli_Protobuf_WasmBeginIf: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmBeginIf" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameterTypes\0\u{1}outputTypes\0\u{1}inverted\0\u{1}hint\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameterCount\0\u{2}\u{2}inverted\0\u{1}hint\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -14283,8 +14281,7 @@ extension Fuzzilli_Protobuf_WasmBeginIf: 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.decodeRepeatedMessageField(value: &self.parameterTypes) }() - case 2: try { try decoder.decodeRepeatedMessageField(value: &self.outputTypes) }() + case 1: try { try decoder.decodeSingularInt32Field(value: &self.parameterCount) }() case 3: try { try decoder.decodeSingularBoolField(value: &self.inverted) }() case 4: try { try decoder.decodeSingularEnumField(value: &self.hint) }() default: break @@ -14293,11 +14290,8 @@ extension Fuzzilli_Protobuf_WasmBeginIf: SwiftProtobuf.Message, SwiftProtobuf._M } 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) + if self.parameterCount != 0 { + try visitor.visitSingularInt32Field(value: self.parameterCount, fieldNumber: 1) } if self.inverted != false { try visitor.visitSingularBoolField(value: self.inverted, fieldNumber: 3) @@ -14309,8 +14303,7 @@ extension Fuzzilli_Protobuf_WasmBeginIf: SwiftProtobuf.Message, SwiftProtobuf._M } public static func ==(lhs: Fuzzilli_Protobuf_WasmBeginIf, rhs: Fuzzilli_Protobuf_WasmBeginIf) -> Bool { - if lhs.parameterTypes != rhs.parameterTypes {return false} - if lhs.outputTypes != rhs.outputTypes {return false} + if lhs.parameterCount != rhs.parameterCount {return false} if lhs.inverted != rhs.inverted {return false} if lhs.hint != rhs.hint {return false} if lhs.unknownFields != rhs.unknownFields {return false} @@ -14320,7 +14313,7 @@ extension Fuzzilli_Protobuf_WasmBeginIf: SwiftProtobuf.Message, SwiftProtobuf._M extension Fuzzilli_Protobuf_WasmBeginElse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmBeginElse" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameterTypes\0\u{1}outputTypes\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameterCount\0\u{1}outputCount\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -14328,26 +14321,26 @@ extension Fuzzilli_Protobuf_WasmBeginElse: SwiftProtobuf.Message, SwiftProtobuf. // 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) }() + case 1: try { try decoder.decodeSingularInt32Field(value: &self.parameterCount) }() + case 2: try { try decoder.decodeSingularInt32Field(value: &self.outputCount) }() default: break } } } public func traverse(visitor: inout V) throws { - if !self.parameterTypes.isEmpty { - try visitor.visitRepeatedMessageField(value: self.parameterTypes, fieldNumber: 1) + if self.parameterCount != 0 { + try visitor.visitSingularInt32Field(value: self.parameterCount, fieldNumber: 1) } - if !self.outputTypes.isEmpty { - try visitor.visitRepeatedMessageField(value: self.outputTypes, fieldNumber: 2) + if self.outputCount != 0 { + try visitor.visitSingularInt32Field(value: self.outputCount, fieldNumber: 2) } try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Fuzzilli_Protobuf_WasmBeginElse, rhs: Fuzzilli_Protobuf_WasmBeginElse) -> Bool { - if lhs.parameterTypes != rhs.parameterTypes {return false} - if lhs.outputTypes != rhs.outputTypes {return false} + if lhs.parameterCount != rhs.parameterCount {return false} + if lhs.outputCount != rhs.outputCount {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -14355,7 +14348,7 @@ extension Fuzzilli_Protobuf_WasmBeginElse: SwiftProtobuf.Message, SwiftProtobuf. extension Fuzzilli_Protobuf_WasmEndIf: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmEndIf" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}outputTypes\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}outputCount\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -14363,21 +14356,21 @@ extension Fuzzilli_Protobuf_WasmEndIf: SwiftProtobuf.Message, SwiftProtobuf._Mes // 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.outputTypes) }() + case 1: try { try decoder.decodeSingularInt32Field(value: &self.outputCount) }() default: break } } } public func traverse(visitor: inout V) throws { - if !self.outputTypes.isEmpty { - try visitor.visitRepeatedMessageField(value: self.outputTypes, fieldNumber: 1) + if self.outputCount != 0 { + try visitor.visitSingularInt32Field(value: self.outputCount, fieldNumber: 1) } try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Fuzzilli_Protobuf_WasmEndIf, rhs: Fuzzilli_Protobuf_WasmEndIf) -> Bool { - if lhs.outputTypes != rhs.outputTypes {return false} + if lhs.outputCount != rhs.outputCount {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 247ba5c8e..a369be39d 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -1337,19 +1337,18 @@ message WasmReassign { } message WasmBeginIf { - repeated WasmILType parameterTypes = 1; - repeated WasmILType outputTypes = 2; + int32 parameterCount = 1; bool inverted = 3; WasmBranchHint hint = 4; } message WasmBeginElse { - repeated WasmILType parameterTypes = 1; - repeated WasmILType outputTypes = 2; + int32 parameterCount = 1; + int32 outputCount = 2; } message WasmEndIf { - repeated WasmILType outputTypes = 1; + int32 outputCount = 1; } message WasmNop { From 492e592032a25b56522a5860d283fe32a327f48d Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Fri, 9 Jan 2026 18:21:27 +0100 Subject: [PATCH 61/89] [wasm] Use a wasm-gc signature for wasm blocks Bug: 445356784 Change-Id: Ic309942aac909ffa6397fc889fd4cd8fe86e6b4e Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8914596 Commit-Queue: Matthias Liedtke Reviewed-by: Pawel Krawczyk --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 34 +++++------ .../Fuzzilli/CodeGen/WasmCodeGenerators.swift | 57 ++++++++++--------- Sources/Fuzzilli/FuzzIL/Instruction.swift | 11 ++-- Sources/Fuzzilli/FuzzIL/JSTyper.swift | 10 ++-- Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 18 +++--- Sources/Fuzzilli/Lifting/FuzzILLifter.swift | 4 +- Sources/Fuzzilli/Lifting/WasmLifter.swift | 8 +-- .../Fuzzilli/Minimization/BlockReducer.swift | 18 ++---- Sources/Fuzzilli/Protobuf/operations.pb.swift | 31 ++++------ Sources/Fuzzilli/Protobuf/operations.proto | 5 +- Tests/FuzzilliTests/MinimizerTest.swift | 2 +- 11 files changed, 91 insertions(+), 107 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index ed04ebae9..4c285c31e 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -3921,25 +3921,28 @@ public class ProgramBuilder { // The first innerOutput of this block is a label variable, which is just there to explicitly mark control-flow and allow branches. public func wasmBuildBlock(with signature: WasmSignature, args: [Variable], body: (Variable, [Variable]) -> ()) { assert(signature.outputTypes.count == 0) - let instr = b.emit(WasmBeginBlock(with: signature), withInputs: args, types: signature.parameterTypes) + let signatureDef = b.wasmDefineAdHocSignatureType(signature: signature) + let instr = b.emit( + WasmBeginBlock(parameterCount: signature.parameterTypes.count), + withInputs: [signatureDef] + args, + types: [.wasmTypeDef()] + signature.parameterTypes) body(instr.innerOutput(0), Array(instr.innerOutputs(1...))) - b.emit(WasmEndBlock(outputTypes: [])) + b.emit(WasmEndBlock(outputCount: 0), withInputs: [signatureDef]) } @discardableResult public func wasmBuildBlockWithResults(with signature: WasmSignature, args: [Variable], body: (Variable, [Variable]) -> [Variable]) -> [Variable] { - let instr = b.emit(WasmBeginBlock(with: signature), withInputs: args, types: signature.parameterTypes) + let signatureDef = b.wasmDefineAdHocSignatureType(signature: signature) + let instr = b.emit( + WasmBeginBlock(parameterCount: signature.parameterTypes.count), + withInputs: [signatureDef] + args, + types: [.wasmTypeDef()] + signature.parameterTypes) let results = body(instr.innerOutput(0), Array(instr.innerOutputs(1...))) - return Array(b.emit(WasmEndBlock(outputTypes: signature.outputTypes), withInputs: results, types: signature.outputTypes).outputs) - } - - // Convenience function to begin a wasm block. Note that this does not emit an end block. - func wasmBeginBlock(with signature: WasmSignature, args: [Variable]) { - b.emit(WasmBeginBlock(with: signature), withInputs: args, types: signature.parameterTypes) - } - // Convenience function to end a wasm block. - func wasmEndBlock(outputTypes: [ILType], args: [Variable]) { - b.emit(WasmEndBlock(outputTypes: outputTypes), withInputs: args, types: outputTypes) + return Array(b.emit( + WasmEndBlock(outputCount: signature.outputTypes.count), + withInputs: [signatureDef] + results, + types: [.wasmTypeDef()] + signature.outputTypes + ).outputs) } private func checkArgumentsMatchLabelType(label: ILType, args: [Variable]) { @@ -4909,8 +4912,6 @@ public class ProgramBuilder { break case .beginWasmFunction(let op): activeWasmModule!.functions.append(WasmFunction(forBuilder: self, withSignature: op.signature)) - case .wasmBeginBlock(let op): - activeWasmModule!.blockSignatures.push(op.signature) case .wasmBeginTry(let op): activeWasmModule!.blockSignatures.push(op.signature) case .wasmBeginTryDelegate(let op): @@ -4919,8 +4920,7 @@ public class ProgramBuilder { activeWasmModule!.blockSignatures.push(op.signature) case .wasmEndTry(_), .wasmEndTryDelegate(_), - .wasmEndTryTable(_), - .wasmEndBlock(_): + .wasmEndTryTable(_): activeWasmModule!.blockSignatures.pop() default: diff --git a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift index a5ae351b6..eefef55ec 100644 --- a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift @@ -1216,13 +1216,15 @@ public let WasmCodeGenerators: [CodeGenerator] = [ inContext: .single(.wasmFunction), provides: [.wasmFunction] ) { b in - b.emit(WasmBeginBlock(with: [] => [])) + let signature = b.wasmDefineAdHocSignatureType(signature: [] => []) + b.runtimeData.push("blockSignature", signature) + b.emit(WasmBeginBlock(parameterCount: 0), withInputs: [signature]) }, GeneratorStub( "WasmEndBlockGenerator", inContext: .single(.wasmFunction) ) { b in - b.emit(WasmEndBlock(outputTypes: [])) + b.emit(WasmEndBlock(outputCount: 0), withInputs: [b.runtimeData.pop("blockSignature")]) }, ]), @@ -1234,24 +1236,22 @@ public let WasmCodeGenerators: [CodeGenerator] = [ inContext: .single(.wasmFunction), provides: [.wasmFunction] ) { b in - let args = b.randomWasmBlockArguments(upTo: 5) + let args = b.randomWasmBlockArguments(upTo: 5, allowingGcTypes: true) let parameters = args.map(b.type) let outputTypes = b.randomWasmBlockOutputTypes(upTo: 5) - let signature = parameters => outputTypes - b.emit(WasmBeginBlock(with: signature), withInputs: args) + let signature = b.wasmDefineAdHocSignatureType(signature: parameters => outputTypes) + b.runtimeData.push("blockSignature", signature) + b.emit(WasmBeginBlock(parameterCount: parameters.count), withInputs: [signature] + args) }, - GeneratorStub( - "WasmEndBlockGenerator", - inContext: .single(.wasmFunction) - ) { b in - let signature = b.currentWasmSignature + GeneratorStub("WasmEndBlockGenerator", inContext: .single(.wasmFunction)) { b in + let signature = b.runtimeData.pop("blockSignature") + let wasmSignature = b.type(of: signature).wasmFunctionSignatureDefSignature let function = b.currentWasmFunction - let outputs = signature.outputTypes.map( - function.findOrGenerateWasmVar) + let outputs = wasmSignature.outputTypes.map(function.findOrGenerateWasmVar) b.emit( - WasmEndBlock(outputTypes: signature.outputTypes), - withInputs: outputs) + WasmEndBlock(outputCount: wasmSignature.outputTypes.count), + withInputs: [signature] + outputs) }, ]), @@ -1607,8 +1607,9 @@ public let WasmCodeGenerators: [CodeGenerator] = [ let extraBlockCount = Int.random(in: 1...5) let valueCount = Int.random(in: 0...20) let signature = [] => parameterTypes + let signatureDef = b.wasmDefineAdHocSignatureType(signature: signature) (0.. outputTypes, args: []) - return outputTypes + let signature = [] => outputTypes + let signatureDef = b.wasmDefineAdHocSignatureType(signature: signature) + b.emit(WasmBeginBlock(parameterCount: 0), withInputs: [signatureDef]) + return signatureDef } // 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 labels = signatureDefs.map { signatureDef in + b.randomVariable(ofType: .label(b.type(of: signatureDef).wasmFunctionSignatureDefSignature.outputTypes))! } let catches = zip(tags, withExnRef).map { tag, withExnRef -> WasmBeginTryTable.CatchKind in @@ -1682,12 +1684,11 @@ public let WasmCodeGenerators: [CodeGenerator] = [ b.buildRecursive(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) + signatureDefs.reversed().enumerated().forEach { n, signatureDef in + let wasmSignature = b.type(of: signatureDef).wasmFunctionSignatureDefSignature + let results = wasmSignature.outputTypes.map(function.findOrGenerateWasmVar) + b.emit(WasmEndBlock(outputCount: wasmSignature.outputTypes.count), + withInputs: [signatureDef] + results) b.buildRecursive(n: defaultCodeGenerationAmount) } } diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index 728619b03..19e2f917b 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -1386,12 +1386,11 @@ extension Instruction: ProtobufConvertible { } case .wasmBeginBlock(let op): $0.wasmBeginBlock = Fuzzilli_Protobuf_WasmBeginBlock.with { - $0.parameterTypes = op.signature.parameterTypes.map(ILTypeToWasmTypeEnum) - $0.outputTypes = op.signature.outputTypes.map(ILTypeToWasmTypeEnum) + $0.parameterCount = Int32(op.parameterCount) } case .wasmEndBlock(let op): $0.wasmEndBlock = Fuzzilli_Protobuf_WasmEndBlock.with { - $0.outputTypes = op.outputTypes.map(ILTypeToWasmTypeEnum) + $0.outputCount = Int32(op.numOutputs) } case .wasmBeginLoop(let op): $0.wasmBeginLoop = Fuzzilli_Protobuf_WasmBeginLoop.with { @@ -2453,11 +2452,9 @@ extension Instruction: ProtobufConvertible { let outputs = p.outputTypes.map(WasmTypeEnumToILType) op = EndWasmFunction(signature: parameters => outputs) case .wasmBeginBlock(let p): - let parameters = p.parameterTypes.map(WasmTypeEnumToILType) - let outputs = p.outputTypes.map(WasmTypeEnumToILType) - op = WasmBeginBlock(with: parameters => outputs) + op = WasmBeginBlock(parameterCount: Int(p.parameterCount)) case .wasmEndBlock(let p): - op = WasmEndBlock(outputTypes: p.outputTypes.map(WasmTypeEnumToILType)) + op = WasmEndBlock(outputCount: Int(p.outputCount)) case .wasmBeginLoop(let p): op = WasmBeginLoop(parameterCount: Int(p.parameterCount)) case .wasmEndLoop(let p): diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index 4cf4888e2..99a0e3216 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -814,10 +814,12 @@ public struct JSTyper: Analyzer { dynamicObjectGroupManager.addWasmFunction(withSignature: ProgramBuilder.convertWasmSignatureToJsSignature(op.signature), forDefinition: instr, forVariable: instr.output) case .wasmSelect(_): setType(of: instr.output, to: type(of: instr.input(0))) - case .wasmBeginBlock(let op): - wasmTypeBeginBlock(instr, op.signature) - case .wasmEndBlock(let op): - wasmTypeEndBlock(instr, op.outputTypes) + case .wasmBeginBlock(_): + let signature = type(of: instr.input(0)).wasmFunctionSignatureDefSignature + wasmTypeBeginBlock(instr, signature) + case .wasmEndBlock(_): + let signature = type(of: instr.input(0)).wasmFunctionSignatureDefSignature + wasmTypeEndBlock(instr, signature.outputTypes) case .wasmBeginIf(_): let signature = type(of: instr.input(0)).wasmFunctionSignatureDefSignature wasmTypeBeginBlock(instr, signature) diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index 3f47b2d30..e0424b20a 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -1250,22 +1250,20 @@ final class WasmSelect: WasmOperation { final class WasmBeginBlock: WasmOperation { override var opcode: Opcode { .wasmBeginBlock(self) } - let signature: WasmSignature - - init(with signature: WasmSignature) { - self.signature = signature - super.init(numInputs: signature.parameterTypes.count, numInnerOutputs: signature.parameterTypes.count + 1, attributes: [.isBlockStart, .propagatesSurroundingContext], requiredContext: [.wasmFunction]) + init(parameterCount: Int) { + // Inputs: The signature plus the arguments. + // Inner outputs: The label plus the arguments. + super.init(numInputs: 1 + parameterCount, numInnerOutputs: 1 + parameterCount, attributes: [.isBlockStart, .propagatesSurroundingContext], requiredContext: [.wasmFunction]) } + + var parameterCount: Int {numInputs - 1} } final class WasmEndBlock: WasmOperation { override var opcode: Opcode { .wasmEndBlock(self) } - let outputTypes: [ILType] - - init(outputTypes: [ILType]) { - self.outputTypes = outputTypes - super.init(numInputs: outputTypes.count, numOutputs: outputTypes.count, attributes: [.isBlockEnd, .resumesSurroundingContext], requiredContext: [.wasmFunction]) + init(outputCount: Int) { + super.init(numInputs: 1 + outputCount, numOutputs: outputCount, attributes: [.isBlockEnd, .resumesSurroundingContext], requiredContext: [.wasmFunction]) } } diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift index 4df1a6fce..f88825865 100644 --- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift +++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift @@ -1081,10 +1081,10 @@ public class FuzzILLifter: Lifter { let inputs = instr.inputs.map(lift).joined(separator: ", ") w.emit("WasmReturnCallIndirect(\(op.signature)) \(inputs)") - case .wasmBeginBlock(let op): + case .wasmBeginBlock(_): // TODO(cffsmith): Maybe lift labels as e.g. L7 or something like that? let inputs = instr.inputs.map(lift).joined(separator: ", ") - w.emit("WasmBeginBlock (\(op.signature)) [\(inputs)] -> L:\(instr.innerOutput(0)) [\(liftCallArguments(instr.innerOutputs(1...)))]") + w.emit("WasmBeginBlock [\(inputs)] -> L:\(instr.innerOutput(0)) [\(liftCallArguments(instr.innerOutputs(1...)))]") w.increaseIndentionLevel() case .wasmEndBlock(let op): diff --git a/Sources/Fuzzilli/Lifting/WasmLifter.swift b/Sources/Fuzzilli/Lifting/WasmLifter.swift index 754a12795..ec001c82e 100644 --- a/Sources/Fuzzilli/Lifting/WasmLifter.swift +++ b/Sources/Fuzzilli/Lifting/WasmLifter.swift @@ -1230,8 +1230,7 @@ public class WasmLifter { assert(instr.op is WasmOperation) switch instr.op.opcode { - case .wasmBeginBlock(let op): - registerSignature(op.signature) + case .wasmBeginBlock(_): self.currentFunction!.labelBranchDepthMapping[instr.innerOutput(0)] = self.currentFunction!.variableAnalyzer.wasmBranchDepth // Needs typer analysis return true @@ -1899,10 +1898,11 @@ public class WasmLifter { } else { throw WasmLifter.CompileError.failedIndexLookUp } - case .wasmBeginBlock(let op): + case .wasmBeginBlock(_): // A Block can "produce" (push) an item on the value stack, just like a function. Similarly, a block can also have parameters. // Ref: https://webassembly.github.io/spec/core/binary/instructions.html#binary-blocktype - return Data([0x02] + Leb128.unsignedEncode(getSignatureIndexStrict(op.signature))) + let signatureDesc = typer.getTypeDescription(of: wasmInstruction.input(0)) + return Data([0x02] + Leb128.unsignedEncode(typeDescToIndex[signatureDesc]!)) case .wasmBeginLoop(_): let signatureDesc = typer.getTypeDescription(of: wasmInstruction.input(0)) return Data([0x03] + Leb128.unsignedEncode(typeDescToIndex[signatureDesc]!)) diff --git a/Sources/Fuzzilli/Minimization/BlockReducer.swift b/Sources/Fuzzilli/Minimization/BlockReducer.swift index a25e18d12..aec2abf3c 100644 --- a/Sources/Fuzzilli/Minimization/BlockReducer.swift +++ b/Sources/Fuzzilli/Minimization/BlockReducer.swift @@ -95,20 +95,13 @@ struct BlockReducer: Reducer { .wasmBeginTryTable: reduceGenericBlockGroup(group, with: helper) - case .wasmBeginBlock: - // TODO(mliedtke): Deduplicate with .wasmBeginLoop once blocks also use wasm-gc - // signatures. + case .wasmBeginBlock, + .wasmBeginLoop: let rewroteProgram = reduceGenericWasmBlockGroup(group, with: helper) if rewroteProgram { return } - case .wasmBeginLoop: - let rewroteProgram = reduceGenericWasmBlockGroup(group, with: helper, usesSignature: true) - if rewroteProgram { - return - } - case .wasmBeginCatchAll, .wasmBeginCatch: // These instructions are handled in the reduceWasmTryCatch. @@ -318,7 +311,7 @@ struct BlockReducer: Reducer { // Reduce a wasm block. In some cases this reduction fully rewrites the program // invalidating pre-computed BlockGroups. If that happens, the function returns true indicating // that following reductions need to rerun the Blockgroups analysis. - private func reduceGenericWasmBlockGroup(_ group: BlockGroup, with helper: MinimizationHelper, usesSignature: Bool = false) -> Bool { + private func reduceGenericWasmBlockGroup(_ group: BlockGroup, with helper: MinimizationHelper) -> Bool { // Try to remove just the block. var candidates = group.blockInstructionIndices if helper.tryNopping(candidates) { @@ -341,8 +334,9 @@ struct BlockReducer: Reducer { let beginInstr = helper.code[group.head] let endInstr = helper.code[group.tail] - let blockInputs = usesSignature ? beginInstr.inputs.dropFirst() : beginInstr.inputs - let endInstrInputs = usesSignature ? endInstr.inputs.dropFirst() : endInstr.inputs + // Drop the signature from the inputs before remapping. + let blockInputs = beginInstr.inputs.dropFirst() + let endInstrInputs = endInstr.inputs.dropFirst() var varReplacements = Dictionary( uniqueKeysWithValues: zip(beginInstr.innerOutputs.dropFirst(), blockInputs)) varReplacements.merge(zip(endInstr.outputs, endInstrInputs.map {varReplacements[$0] ?? $0}), diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index 00cd84c4e..aca566377 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -5147,9 +5147,7 @@ public struct Fuzzilli_Protobuf_WasmBeginBlock: Sendable { // `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 parameterCount: Int32 = 0 public var unknownFields = SwiftProtobuf.UnknownStorage() @@ -5161,7 +5159,7 @@ public struct Fuzzilli_Protobuf_WasmEndBlock: Sendable { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var outputTypes: [Fuzzilli_Protobuf_WasmILType] = [] + public var outputCount: Int32 = 0 public var unknownFields = SwiftProtobuf.UnknownStorage() @@ -13651,7 +13649,7 @@ extension Fuzzilli_Protobuf_WasmMemoryFill: SwiftProtobuf.Message, SwiftProtobuf extension Fuzzilli_Protobuf_WasmBeginBlock: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmBeginBlock" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameterTypes\0\u{1}outputTypes\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameterCount\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -13659,26 +13657,21 @@ extension Fuzzilli_Protobuf_WasmBeginBlock: SwiftProtobuf.Message, SwiftProtobuf // 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) }() + case 1: try { try decoder.decodeSingularInt32Field(value: &self.parameterCount) }() 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) + if self.parameterCount != 0 { + try visitor.visitSingularInt32Field(value: self.parameterCount, fieldNumber: 1) } try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Fuzzilli_Protobuf_WasmBeginBlock, rhs: Fuzzilli_Protobuf_WasmBeginBlock) -> Bool { - if lhs.parameterTypes != rhs.parameterTypes {return false} - if lhs.outputTypes != rhs.outputTypes {return false} + if lhs.parameterCount != rhs.parameterCount {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -13686,7 +13679,7 @@ extension Fuzzilli_Protobuf_WasmBeginBlock: SwiftProtobuf.Message, SwiftProtobuf extension Fuzzilli_Protobuf_WasmEndBlock: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmEndBlock" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}outputTypes\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}outputCount\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -13694,21 +13687,21 @@ extension Fuzzilli_Protobuf_WasmEndBlock: SwiftProtobuf.Message, SwiftProtobuf._ // 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.outputTypes) }() + case 1: try { try decoder.decodeSingularInt32Field(value: &self.outputCount) }() default: break } } } public func traverse(visitor: inout V) throws { - if !self.outputTypes.isEmpty { - try visitor.visitRepeatedMessageField(value: self.outputTypes, fieldNumber: 1) + if self.outputCount != 0 { + try visitor.visitSingularInt32Field(value: self.outputCount, fieldNumber: 1) } try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Fuzzilli_Protobuf_WasmEndBlock, rhs: Fuzzilli_Protobuf_WasmEndBlock) -> Bool { - if lhs.outputTypes != rhs.outputTypes {return false} + if lhs.outputCount != rhs.outputCount {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 a369be39d..d55891336 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -1238,12 +1238,11 @@ message WasmMemoryFill { } message WasmBeginBlock { - repeated WasmILType parameterTypes = 1; - repeated WasmILType outputTypes = 2; + int32 parameterCount = 1; } message WasmEndBlock { - repeated WasmILType outputTypes = 1; + int32 outputCount = 1; } message WasmBeginLoop { diff --git a/Tests/FuzzilliTests/MinimizerTest.swift b/Tests/FuzzilliTests/MinimizerTest.swift index 497508250..b70df09bf 100644 --- a/Tests/FuzzilliTests/MinimizerTest.swift +++ b/Tests/FuzzilliTests/MinimizerTest.swift @@ -1947,7 +1947,7 @@ class MinimizerTests: XCTestCase { } } - // Don't minimize the block fi the label is used by an important instruction. + // Don't minimize the block if the label is used by an important instruction. func testWasmBlockMinimizationLabelUsed() throws { try runWasmMinimization { evaluator, b in b.buildWasmModule { wasmModule in From df38fe33220688ac156d3891563113e156fe680f Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Fri, 9 Jan 2026 18:37:38 +0100 Subject: [PATCH 62/89] [cleanup] Remove one of the wasmBuildIfElse overloads Change-Id: Id1322a5847527125d3282da5d80e861422bd7f45 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8914597 Commit-Queue: Matthias Liedtke Reviewed-by: Pawel Krawczyk --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 14 +--------- Tests/FuzzilliTests/MinimizerTest.swift | 4 +-- Tests/FuzzilliTests/WasmTests.swift | 32 +++++++++++----------- 3 files changed, 19 insertions(+), 31 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 4c285c31e..286204b1c 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -3976,19 +3976,7 @@ public class ProgramBuilder { withInputs: labels + args + [on]) } - // TODO(mliedtke): It would be nice to remove this overload to simplify the codebase. - public func wasmBuildIfElse(_ condition: Variable, hint: WasmBranchHint = .None, ifBody: () -> Void, elseBody: (() -> Void)? = nil) { - let signatureDef = b.wasmDefineAdHocSignatureType(signature: [] => []) - b.emit(WasmBeginIf(hint: hint), withInputs: [signatureDef, condition]) - ifBody() - if let elseBody { - b.emit(WasmBeginElse(), withInputs: [signatureDef]) - elseBody() - } - b.emit(WasmEndIf(), withInputs: [signatureDef]) - } - - public func wasmBuildIfElse(_ condition: Variable, signature: WasmSignature, args: [Variable], inverted: Bool, ifBody: (Variable, [Variable]) -> Void, elseBody: ((Variable, [Variable]) -> Void)? = nil) { + public func wasmBuildIfElse(_ condition: Variable, signature: WasmSignature = [] => [], args: [Variable] = [], hint: WasmBranchHint = .None, inverted: Bool = false, ifBody: (Variable, [Variable]) -> Void, elseBody: ((Variable, [Variable]) -> Void)? = nil) { assert(signature.outputTypes.count == 0) let signatureDef = b.wasmDefineAdHocSignatureType(signature: signature) let beginBlock = b.emit(WasmBeginIf(parameterCount: signature.parameterTypes.count, inverted: inverted), diff --git a/Tests/FuzzilliTests/MinimizerTest.swift b/Tests/FuzzilliTests/MinimizerTest.swift index b70df09bf..22807afe5 100644 --- a/Tests/FuzzilliTests/MinimizerTest.swift +++ b/Tests/FuzzilliTests/MinimizerTest.swift @@ -1672,10 +1672,10 @@ class MinimizerTests: XCTestCase { try runWasmMinimization { evaluator, b in b.buildWasmModule { wasmModule in wasmModule.addWasmFunction(with: [.wasmi32] => []) { function, label, args in - function.wasmBuildIfElse(args[0], hint: .None) { + function.wasmBuildIfElse(args[0], signature: [] => []) { _, _ in evaluator.nextInstructionIsImportant(in: b) function.consti64(43) - } elseBody: { + } elseBody: { _, _ in evaluator.nextInstructionIsImportant(in: b) function.consti64(42) } diff --git a/Tests/FuzzilliTests/WasmTests.swift b/Tests/FuzzilliTests/WasmTests.swift index 0b291643f..21ac40213 100644 --- a/Tests/FuzzilliTests/WasmTests.swift +++ b/Tests/FuzzilliTests/WasmTests.swift @@ -2731,7 +2731,7 @@ class WasmFoundationTests: XCTestCase { function.wasmBranchIf(condition, to: loopLabel, args: [incFirst, incSecond]) return [incFirst, incSecond] } - function.wasmBuildIfElse(function.wasmi32CompareOp(loopResult[1], function.consti32(20), using: .Ne), hint: .None) { + function.wasmBuildIfElse(function.wasmi32CompareOp(loopResult[1], function.consti32(20), using: .Ne)) { label, args in function.wasmUnreachable() } return [loopResult[0]] @@ -2762,13 +2762,13 @@ class WasmFoundationTests: XCTestCase { let comp = function.wasmi32CompareOp(variable, condVariable, using: .Lt_s) - function.wasmBuildIfElse(comp, hint: .None, ifBody: { + function.wasmBuildIfElse(comp) { label, args in let tmp = function.wasmi32BinOp(variable, condVariable, binOpKind: .Add) function.wasmReassign(variable: result, to: tmp) - }, elseBody: { + } elseBody: { label, args in let tmp = function.wasmi32BinOp(variable, condVariable, binOpKind: .Sub) function.wasmReassign(variable: result, to: tmp) - }) + } return [result] } @@ -2897,13 +2897,13 @@ class WasmFoundationTests: XCTestCase { let module = b.buildWasmModule { wasmModule in wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmi32]) { function, label, args in - function.wasmBuildIfElse(function.wasmi32EqualZero(args[0]), hint: .Unlikely) { + function.wasmBuildIfElse(function.wasmi32EqualZero(args[0]), hint: .Unlikely) { _, _ in let one = function.wasmJsCall(function: jsReturnOne, withArgs: [], withWasmSignature: [] => [.wasmi32])! function.wasmReturn(one) } let cond = function.wasmi32CompareOp(args[0], function.consti32(4), using: .Gt_s) - function.wasmBuildIfElse(cond, hint: .Likely) { + function.wasmBuildIfElse(cond, hint: .Likely) { _, _ in function.wasmReturn(function.consti32(2)) } function.wasmBranchIf( @@ -3190,9 +3190,9 @@ class WasmFoundationTests: XCTestCase { */ wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmi32]) { function, label, param in function.wasmBuildLegacyTry(with: [] => [], args: []) { label, _ in - function.wasmBuildIfElse(param[0], hint: .None) { + function.wasmBuildIfElse(param[0], hint: .None) { _, _ in function.WasmBuildThrow(tag: definedTag, inputs: [param[0]]) - } elseBody: { + } elseBody: { _, _ in function.WasmBuildThrow(tag: importedTag, inputs: [function.consti32(123)]) } function.wasmUnreachable() @@ -3280,13 +3280,13 @@ class WasmFoundationTests: XCTestCase { wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmi32]) { function, label, args in let contant42 = function.consti64(42) let result = function.wasmBuildLegacyTryWithResult(with: [.wasmi32] => [.wasmi32, .wasmi64], args: args, body: { label, args in - function.wasmBuildIfElse(function.wasmi32EqualZero(args[0]), hint: .None) { + function.wasmBuildIfElse(function.wasmi32EqualZero(args[0])) { _, _ in function.WasmBuildThrow(tag: tagVoid, inputs: []) } - function.wasmBuildIfElse(function.wasmi32CompareOp(args[0], function.consti32(1), using: .Eq), hint: .None) { + function.wasmBuildIfElse(function.wasmi32CompareOp(args[0], function.consti32(1), using: .Eq)) { _, _ in function.WasmBuildThrow(tag: tagi32, inputs: [function.consti32(100)]) } - function.wasmBuildIfElse(function.wasmi32CompareOp(args[0], function.consti32(2), using: .Eq), hint: .None) { + function.wasmBuildIfElse(function.wasmi32CompareOp(args[0], function.consti32(2), using: .Eq)) { _, _ in function.WasmBuildThrow(tag: tagi32Other, inputs: [function.consti32(200)]) } return [args[0], contant42] @@ -3302,7 +3302,7 @@ class WasmFoundationTests: XCTestCase { ], catchAllBody: { _ in return [function.consti32(900), contant42] }) - function.wasmBuildIfElse(function.wasmi64CompareOp(result[1], contant42, using: .Ne), hint: .None) { + function.wasmBuildIfElse(function.wasmi64CompareOp(result[1], contant42, using: .Ne), hint: .None) { _, _ in function.wasmUnreachable() } return [result[0]] @@ -3730,9 +3730,9 @@ class WasmFoundationTests: XCTestCase { function.wasmBuildBlock(with: [] => [], args: []) { catchAllNoRefLabel, _ in let catchNoRefI32 = function.wasmBuildBlockWithResults(with: [] => [.wasmi32], args: []) { catchNoRefLabel, _ in function.wasmBuildTryTable(with: [] => [], args: [tagi32, catchNoRefLabel, catchAllNoRefLabel], catches: [.NoRef, .AllNoRef]) { _, _ in - function.wasmBuildIfElse(function.wasmi32EqualZero(args[0]), hint: .None) { + function.wasmBuildIfElse(function.wasmi32EqualZero(args[0])) { _, _ in function.WasmBuildThrow(tag: tagVoid, inputs: []) - } elseBody: { + } elseBody: { _, _ in function.WasmBuildThrow(tag: tagi32, inputs: [args[0]]) } return [] @@ -3769,9 +3769,9 @@ class WasmFoundationTests: XCTestCase { function.wasmBuildBlockWithResults(with: [] => [.wasmExnRef()], args: []) { catchAllRefLabel, _ in let catchRefI32 = function.wasmBuildBlockWithResults(with: [] => [.wasmi32, .wasmExnRef()], args: []) { catchRefLabel, _ in function.wasmBuildTryTable(with: [] => [], args: [tagi32, catchRefLabel, catchAllRefLabel], catches: [.Ref, .AllRef]) { _, _ in - function.wasmBuildIfElse(function.wasmi32EqualZero(args[0]), hint: .None) { + function.wasmBuildIfElse(function.wasmi32EqualZero(args[0])) { _, _ in function.WasmBuildThrow(tag: tagVoid, inputs: []) - } elseBody: { + } elseBody: { _, _ in function.WasmBuildThrow(tag: tagi32, inputs: [args[0]]) } return [] From fcd3227ad23db2141b233b1916e48d89a229d7f9 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Mon, 12 Jan 2026 17:14:11 +0100 Subject: [PATCH 63/89] [cleanup] Rename GeneratorRuntimeData.popAndPush() to peek() Change-Id: Ia6616629177ee5f941377471a998d59e1ab31d06 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8922297 Reviewed-by: Pawel Krawczyk Commit-Queue: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 5 +++-- Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift | 4 ++-- Tests/FuzzilliTests/ProgramBuilderTest.swift | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 286204b1c..e392e7d95 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -35,8 +35,9 @@ public class ProgramBuilder { return data[key]!.pop() } - // Fetch the most recent value for this key and push it back. - public func popAndPush(_ key: String) -> Variable { + // Fetch the most recent value for this key but keep it. (As the entries act as a stack, + // another CodeGeneratorStub will still need to pop the entry later on.) + public func peek(_ key: String) -> Variable { assert(data[key] != nil) return data[key]!.top } diff --git a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift index eefef55ec..f4c36b7b1 100644 --- a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift @@ -1460,7 +1460,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ inContext: .single(.wasmFunction), provides: [.wasmFunction] ) { b in - b.emit(WasmBeginElse(), withInputs: [b.runtimeData.popAndPush("ifSignature")]) + b.emit(WasmBeginElse(), withInputs: [b.runtimeData.peek("ifSignature")]) }, GeneratorStub( "WasmEndIfElseGenerator", @@ -1496,7 +1496,7 @@ public let WasmCodeGenerators: [CodeGenerator] = [ provides: [.wasmFunction] ) { b in let function = b.currentWasmFunction - let signature = b.runtimeData.popAndPush("ifSignature") + let signature = b.runtimeData.peek("ifSignature") let wasmSignature = b.type(of: signature).wasmFunctionSignatureDefSignature let trueResults = wasmSignature.outputTypes.map(function.findOrGenerateWasmVar) b.emit(WasmBeginElse(parameterCount: wasmSignature.parameterTypes.count, diff --git a/Tests/FuzzilliTests/ProgramBuilderTest.swift b/Tests/FuzzilliTests/ProgramBuilderTest.swift index 52f339565..473a33590 100644 --- a/Tests/FuzzilliTests/ProgramBuilderTest.swift +++ b/Tests/FuzzilliTests/ProgramBuilderTest.swift @@ -3237,7 +3237,7 @@ class ProgramBuilderRuntimeDataTests: XCTestCase { counter += 1 }, GeneratorStub("AddOne") { b in - let value = b.runtimeData.popAndPush("value") + let value = b.runtimeData.peek("value") b.binary(value, b.loadInt(1), with: .Add) }, GeneratorStub("SubOne") { b in From 47c7c39a2d201c9f9ac76877bddce5a868c57344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doga=20Y=C3=BCksel?= Date: Tue, 13 Jan 2026 10:39:14 +0000 Subject: [PATCH 64/89] Add WasmStructNew operation and generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds support for wasm struct.new operation to be able to generate structs with initial non-default values. Bug: 474940922 Change-Id: Ic8f1cc8d7f9dc24dc73b342fb3d55c35e1a33446 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8921896 Reviewed-by: Matthias Liedtke Reviewed-by: Dominik Klemba Commit-Queue: Doga Yüksel --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 16 +++++++-- .../CodeGen/CodeGeneratorWeights.swift | 1 + .../Fuzzilli/CodeGen/WasmCodeGenerators.swift | 19 +++++++++++ Sources/Fuzzilli/FuzzIL/Instruction.swift | 4 +++ Sources/Fuzzilli/FuzzIL/JSTyper.swift | 3 +- Sources/Fuzzilli/FuzzIL/Opcodes.swift | 1 + Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 8 +++++ Sources/Fuzzilli/Lifting/FuzzILLifter.swift | 4 +++ .../Fuzzilli/Lifting/JavaScriptLifter.swift | 1 + Sources/Fuzzilli/Lifting/WasmLifter.swift | 4 +++ .../Fuzzilli/Mutators/OperationMutator.swift | 3 +- Sources/Fuzzilli/Protobuf/operations.pb.swift | 29 ++++++++++++++++ Sources/Fuzzilli/Protobuf/operations.proto | 3 ++ Sources/Fuzzilli/Protobuf/program.pb.swift | 29 ++++++++++++++-- Sources/Fuzzilli/Protobuf/program.proto | 1 + Tests/FuzzilliTests/WasmTests.swift | 34 +++++++++++++++++++ 16 files changed, 153 insertions(+), 7 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index e392e7d95..33e887a0a 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -4180,9 +4180,14 @@ public class ProgramBuilder { } else { return nil } - case .Index(_), - .none: - break // Unimplemented + case .Index(_): + if (type.wasmReferenceType!.nullability) { + return self.wasmRefNull(typeDef: b.jsTyper.getWasmTypeDef(for: type)) + } else { + break + } + case .none: + break } } else { return nil @@ -4338,6 +4343,11 @@ public class ProgramBuilder { types: [.wasmGenericRef, .wasmi32, arrayDesc.elementType.unpacked()]) } + @discardableResult + public func wasmStructNew(structType: Variable, fields: [Variable]) -> Variable { + return b.emit(WasmStructNew(fieldCount: fields.count), withInputs: [structType] + fields).output + } + @discardableResult public func wasmStructNewDefault(structType: Variable) -> Variable { return b.emit(WasmStructNewDefault(), withInputs: [structType]).output diff --git a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift index 9da0a7720..22596f473 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift @@ -365,6 +365,7 @@ public let codeGeneratorWeights = [ "WasmArrayLengthGenerator": 5, "WasmArrayGetGenerator": 5, "WasmArraySetGenerator": 5, + "WasmStructNewGenerator": 5, "WasmStructNewDefaultGenerator": 5, "WasmStructGetGenerator": 5, "WasmStructSetGenerator": 5, diff --git a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift index f4c36b7b1..0fdbc2ab0 100644 --- a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift @@ -210,6 +210,25 @@ public let WasmCodeGenerators: [CodeGenerator] = [ function.wasmArraySet(array: array, index: index, element: element) }, + CodeGenerator( + "WasmStructNewGenerator", inContext: .single(.wasmFunction), + inputs: .requiredComplex(.init(.wasmTypeDef(), .IsWasmStruct)), + producesComplex: [.init(.anyNonNullableIndexRef, .IsWasmStruct)] + ) { b, structType in + guard let typeDesc = b.type(of: structType).wasmTypeDefinition?.description as? WasmStructTypeDescription + else { + fatalError("Invalid type description for \(b.type(of: structType))") + } + let function = b.currentWasmModule.currentWasmFunction + var initial_fields: [Variable] = [] + for field in typeDesc.fields { + let fieldType = field.type.unpacked() + let fieldValue = function.findOrGenerateWasmVar(ofType: fieldType) + initial_fields.append(fieldValue) + } + function.wasmStructNew(structType: structType, fields: initial_fields) + }, + CodeGenerator( "WasmStructNewDefaultGenerator", inContext: .single(.wasmFunction), inputs: .requiredComplex(.init(.wasmTypeDef(), .IsWasmStruct)), diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index 19e2f917b..36dc2e9be 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -1598,6 +1598,8 @@ extension Instruction: ProtobufConvertible { $0.wasmArraySet = Fuzzilli_Protobuf_WasmArraySet() case .wasmStructNewDefault(_): $0.wasmStructNewDefault = Fuzzilli_Protobuf_WasmStructNewDefault() + case .wasmStructNew(_): + $0.wasmStructNew = Fuzzilli_Protobuf_WasmStructNew() case .wasmStructGet(let op): $0.wasmStructGet = Fuzzilli_Protobuf_WasmStructGet.with { $0.fieldIndex = Int32(op.fieldIndex) @@ -2585,6 +2587,8 @@ extension Instruction: ProtobufConvertible { op = WasmArrayGet(isSigned: p.isSigned) case .wasmArraySet(_): op = WasmArraySet() + case .wasmStructNew(_): + op = WasmStructNew(fieldCount: inouts.count - 2) case .wasmStructNewDefault(_): op = WasmStructNewDefault() case .wasmStructGet(let p): diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index 99a0e3216..1fd9f2377 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -896,7 +896,8 @@ public struct JSTyper: Analyzer { setType(of: instr.output, to: typeDesc.elementType.unpacked()) case .wasmArraySet(_): break - case .wasmStructNewDefault(_): + case .wasmStructNew(_), + .wasmStructNewDefault(_): setReferenceType(of: instr.output, typeDef: instr.input(0), nullability: false) case .wasmStructGet(let op): let typeDesc = getTypeDescription(of: instr.input(0)) as! WasmStructTypeDescription diff --git a/Sources/Fuzzilli/FuzzIL/Opcodes.swift b/Sources/Fuzzilli/FuzzIL/Opcodes.swift index d28dd189f..8e2110cae 100644 --- a/Sources/Fuzzilli/FuzzIL/Opcodes.swift +++ b/Sources/Fuzzilli/FuzzIL/Opcodes.swift @@ -364,4 +364,5 @@ enum Opcode { case createNamedDisposableVariable(CreateNamedDisposableVariable) case createNamedAsyncDisposableVariable(CreateNamedAsyncDisposableVariable) case wasmDefineAdHocSignatureType(WasmDefineAdHocSignatureType) + case wasmStructNew(WasmStructNew) } diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index e0424b20a..6df5b99c1 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -2224,6 +2224,14 @@ class WasmArraySet: WasmOperation { } } +class WasmStructNew: WasmOperation { + override var opcode: Opcode { .wasmStructNew(self) } + + init(fieldCount: Int) { + super.init(numInputs: fieldCount + 1, numOutputs: 1, requiredContext: [.wasmFunction]) + } +} + class WasmStructNewDefault: WasmOperation { override var opcode: Opcode { .wasmStructNewDefault(self) } diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift index f88825865..f6c7cfc5b 100644 --- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift +++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift @@ -1310,6 +1310,10 @@ public class FuzzILLifter: Lifter { let inputs = instr.inputs.map(lift).joined(separator: ", ") w.emit("WasmArraySet [\(inputs)]") + case .wasmStructNew(_): + let inputs = instr.inputs.map(lift).joined(separator: ", ") + w.emit("\(output()) <- WasmStructNew [\(inputs)]") + case .wasmStructNewDefault(_): w.emit("\(output()) <- WasmStructNewDefault [\(input(0))]") diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift index d299c10f2..1d74e006a 100644 --- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift +++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift @@ -1783,6 +1783,7 @@ public class JavaScriptLifter: Lifter { .wasmArrayLen(_), .wasmArrayGet(_), .wasmArraySet(_), + .wasmStructNew(_), .wasmStructNewDefault(_), .wasmStructGet(_), .wasmStructSet(_), diff --git a/Sources/Fuzzilli/Lifting/WasmLifter.swift b/Sources/Fuzzilli/Lifting/WasmLifter.swift index ec001c82e..a97485fec 100644 --- a/Sources/Fuzzilli/Lifting/WasmLifter.swift +++ b/Sources/Fuzzilli/Lifting/WasmLifter.swift @@ -2162,6 +2162,10 @@ public class WasmLifter { let typeDesc = typer.getTypeDescription(of: wasmInstruction.input(0)) let arrayIndex = Leb128.unsignedEncode(typeDescToIndex[typeDesc]!) return Data([Prefix.GC.rawValue, 0x0E]) + arrayIndex + case .wasmStructNew(_): + let typeDesc = typer.getTypeDescription(of: wasmInstruction.input(0)) + let structIndex = Leb128.unsignedEncode(typeDescToIndex[typeDesc]!) + return Data([Prefix.GC.rawValue, 0x00]) + structIndex case .wasmStructNewDefault(_): let typeDesc = typer.getTypeDescription(of: wasmInstruction.input(0)) let structIndex = Leb128.unsignedEncode(typeDescToIndex[typeDesc]!) diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift index 1098ea1b3..af493a681 100644 --- a/Sources/Fuzzilli/Mutators/OperationMutator.swift +++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift @@ -727,7 +727,8 @@ public class OperationMutator: BaseInstructionMutator { .wasmDefineElementSegment(_), .wasmDropElementSegment(_), .wasmTableInit(_), - .wasmTableCopy(_): + .wasmTableCopy(_), + .wasmStructNew(_): let mutability = instr.isOperationMutable ? "mutable" : "immutable" fatalError("Unexpected operation \(instr.op.opcode), marked as \(mutability)") } diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index aca566377..2632630b4 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -5857,6 +5857,16 @@ public struct Fuzzilli_Protobuf_WasmStructNewDefault: Sendable { public init() {} } +public struct Fuzzilli_Protobuf_WasmStructNew: 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_WasmStructGet: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -15295,6 +15305,25 @@ extension Fuzzilli_Protobuf_WasmStructNewDefault: SwiftProtobuf.Message, SwiftPr } } +extension Fuzzilli_Protobuf_WasmStructNew: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".WasmStructNew" + 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_WasmStructNew, rhs: Fuzzilli_Protobuf_WasmStructNew) -> Bool { + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension Fuzzilli_Protobuf_WasmStructGet: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmStructGet" public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}fieldIndex\0\u{1}isSigned\0") diff --git a/Sources/Fuzzilli/Protobuf/operations.proto b/Sources/Fuzzilli/Protobuf/operations.proto index d55891336..46758340b 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -1544,6 +1544,9 @@ message WasmArraySet { message WasmStructNewDefault { } +message WasmStructNew { +} + message WasmStructGet { int32 fieldIndex = 1; bool isSigned = 2; diff --git a/Sources/Fuzzilli/Protobuf/program.pb.swift b/Sources/Fuzzilli/Protobuf/program.pb.swift index 293a03607..c85ec56f6 100644 --- a/Sources/Fuzzilli/Protobuf/program.pb.swift +++ b/Sources/Fuzzilli/Protobuf/program.pb.swift @@ -2729,6 +2729,14 @@ public struct Fuzzilli_Protobuf_Instruction: Sendable { set {operation = .wasmDefineAdHocSignatureType(newValue)} } + public var wasmStructNew: Fuzzilli_Protobuf_WasmStructNew { + get { + if case .wasmStructNew(let v)? = operation {return v} + return Fuzzilli_Protobuf_WasmStructNew() + } + set {operation = .wasmStructNew(newValue)} + } + public var unknownFields = SwiftProtobuf.UnknownStorage() public enum OneOf_Operation: Equatable, Sendable { @@ -3067,7 +3075,7 @@ public struct Fuzzilli_Protobuf_Instruction: Sendable { case createNamedDisposableVariable(Fuzzilli_Protobuf_CreateNamedDisposableVariable) case createNamedAsyncDisposableVariable(Fuzzilli_Protobuf_CreateNamedAsyncDisposableVariable) case wasmDefineAdHocSignatureType(Fuzzilli_Protobuf_WasmDefineAdHocSignatureType) - + case wasmStructNew(Fuzzilli_Protobuf_WasmStructNew) } public init() {} @@ -3115,7 +3123,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}beginClassInstanceComputedMethod\0\u{1}endClassInstanceComputedMethod\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}beginClassStaticComputedMethod\0\u{1}endClassStaticComputedMethod\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}wasmDefineAdHocSignatureType\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}beginClassInstanceComputedMethod\0\u{1}endClassInstanceComputedMethod\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}beginClassStaticComputedMethod\0\u{1}endClassStaticComputedMethod\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}wasmDefineAdHocSignatureType\0\u{1}wasmStructNew\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -7474,6 +7482,19 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M self.operation = .wasmDefineAdHocSignatureType(v) } }() + case 337: try { + var v: Fuzzilli_Protobuf_WasmStructNew? + var hadOneofValue = false + if let current = self.operation { + hadOneofValue = true + if case .wasmStructNew(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.operation = .wasmStructNew(v) + } + }() default: break } } @@ -8828,6 +8849,10 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M guard case .wasmDefineAdHocSignatureType(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 336) }() + case .wasmStructNew?: try { + guard case .wasmStructNew(let v)? = self.operation else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 337) + }() case nil: break } try unknownFields.traverse(visitor: &visitor) diff --git a/Sources/Fuzzilli/Protobuf/program.proto b/Sources/Fuzzilli/Protobuf/program.proto index db2587934..1c6d2fbba 100644 --- a/Sources/Fuzzilli/Protobuf/program.proto +++ b/Sources/Fuzzilli/Protobuf/program.proto @@ -360,6 +360,7 @@ message Instruction { CreateNamedDisposableVariable createNamedDisposableVariable = 334; CreateNamedAsyncDisposableVariable createNamedAsyncDisposableVariable = 335; WasmDefineAdHocSignatureType wasmDefineAdHocSignatureType = 336; + WasmStructNew wasmStructNew = 337; } } diff --git a/Tests/FuzzilliTests/WasmTests.swift b/Tests/FuzzilliTests/WasmTests.swift index 21ac40213..23ef57bb6 100644 --- a/Tests/FuzzilliTests/WasmTests.swift +++ b/Tests/FuzzilliTests/WasmTests.swift @@ -4417,6 +4417,40 @@ class WasmGCTests: XCTestCase { testForOutput(program: jsProg, runner: runner, outputString: "42\n") } + func testStructNew() throws { + let runner = try GetJavaScriptExecutorOrSkipTest() + let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) + let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) + let b = fuzzer.makeBuilder() + + let types = b.wasmDefineTypeGroup { + let structOfi32 = b.wasmDefineStructType(fields: [WasmStructTypeDescription.Field(type: .wasmi32, mutability: true)], indexTypes: []) + let structOfStruct = b.wasmDefineStructType(fields: [WasmStructTypeDescription.Field(type: .wasmRef(.Index(), nullability: true), mutability: true)], indexTypes: [structOfi32]) + return [structOfi32, structOfStruct] + } + let structOfi32 = types[0] + let structOfStruct = types[1] + + let module = b.buildWasmModule { wasmModule in + wasmModule.addWasmFunction(with: [.wasmi32] => [.wasmi32]) { function, label, args in + let innerStruct = function.wasmStructNew(structType: structOfi32, fields: [args[0]]) + let outerStruct = function.wasmStructNew(structType: structOfStruct, fields: [innerStruct]) + let retrievedInnerStruct = function.wasmStructGet(theStruct: outerStruct, fieldIndex: 0) + let retrievedValue = function.wasmStructGet(theStruct: retrievedInnerStruct, fieldIndex: 0) + return [retrievedValue] + } + } + + let exports = module.loadExports() + let outputFunc = b.createNamedVariable(forBuiltin: "output") + let wasmOut = b.callMethod(module.getExportedMethod(at: 0), on: exports, withArgs: [b.loadInt(42)]) + b.callFunction(outputFunc, withArgs: [b.callMethod("toString", on: wasmOut)]) + + let program = b.finalize() + let jsProgram = fuzzer.lifter.lift(program) + testForOutput(program: jsProgram, runner: runner, outputString: "42\n") + } + func testStructPacked() throws { let runner = try GetJavaScriptExecutorOrSkipTest() let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) From 1f1871962fbf8cdb86099a9fb72c41ab5e13111f Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Tue, 13 Jan 2026 14:44:34 +0100 Subject: [PATCH 65/89] [cleanup] Simplify ProgramBuilder.generateRandomWasmVar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Id6f1609bf09df512aced5db86f979e3709647446 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8926736 Reviewed-by: Doga Yüksel Commit-Queue: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 33e887a0a..5f39ab5e3 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -4177,20 +4177,14 @@ public class ProgramBuilder { // TODO(pawkra): support other non-nullable types. if (type.wasmReferenceType!.nullability) { return self.wasmRefNull(type: type) - } else { - return nil } case .Index(_): if (type.wasmReferenceType!.nullability) { return self.wasmRefNull(typeDef: b.jsTyper.getWasmTypeDef(for: type)) - } else { - break } case .none: break } - } else { - return nil } return nil } From f418a5e040e090b9453b74f77644da701450e8a2 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Thu, 15 Jan 2026 13:53:56 +0100 Subject: [PATCH 66/89] [wasm] Print better error message when failing in findOrGenerateWasmVar Bug: 475996631 Change-Id: I2fed02882da99abdaaca11d5bed21ebd0f0ff833 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8932836 Auto-Submit: Matthias Liedtke Reviewed-by: Michael Achenbach Commit-Queue: Michael Achenbach --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 5f39ab5e3..9f1ccbe64 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -4191,7 +4191,10 @@ public class ProgramBuilder { } public func findOrGenerateWasmVar(ofType type: ILType) -> Variable { - b.randomVariable(ofType: type) ?? generateRandomWasmVar(ofType: type)! + if let result = b.randomVariable(ofType: type) ?? generateRandomWasmVar(ofType: type) { + return result + } + fatalError("Could not find or generate wasm variable of type \(type)") } public func wasmUnreachable() { From c3fe975530af5307fd770c69537639b8c2b1c525 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Wed, 14 Jan 2026 16:20:40 +0100 Subject: [PATCH 67/89] [js] Fix input requirements resolution for JS types So far we didn't try to resolve input requirements for JS types by scheduling code generators that produce these inputs. This change fixes that and also fixes the test case and the compile warning it produces for the unused Swift variable due to commented out code. Change-Id: I7c090fc164b00df5ef31353447ae4f993538c437 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8929438 Reviewed-by: Dominik Klemba Commit-Queue: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 38 +++++++++----------- Tests/FuzzilliTests/ProgramBuilderTest.swift | 16 +++++---- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 9f1ccbe64..1e0956bf9 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -2225,31 +2225,27 @@ public class ProgramBuilder { private func createRequiredInputVariables(for requirements: Set) { for requirement in requirements { let type = requirement.type - 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 (findVariable {requirement.fulfilled(by: self.type(of: $0))} == 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(where: requirement.fulfilled) - } - - // Cannot build type here. - if usableGenerators.isEmpty { - // Continue here though, as we might be able to create Variables for other types. - continue - } + if type.Is(.wasmAnything) && context.contains(.wasmFunction) { + // Check if we can produce it with findOrGenerateWasmVar + let _ = currentWasmFunction.generateRandomWasmVar(ofType: type) + } + if (findVariable {requirement.fulfilled(by: self.type(of: $0))} == nil) { - let generator = usableGenerators.randomElement() + // 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(where: requirement.fulfilled) + } - let _ = complete(generator: generator, withBudget: 5) + // 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) } } } diff --git a/Tests/FuzzilliTests/ProgramBuilderTest.swift b/Tests/FuzzilliTests/ProgramBuilderTest.swift index 473a33590..69ef921eb 100644 --- a/Tests/FuzzilliTests/ProgramBuilderTest.swift +++ b/Tests/FuzzilliTests/ProgramBuilderTest.swift @@ -3016,11 +3016,13 @@ class ProgramBuilderTests: XCTestCase { func testTypedArrayFromBufferGenerator() { let fuzzer = makeMockFuzzer() - let numPrograms = 50 + let numPrograms = 30 for _ in 0...numPrograms { let b = fuzzer.makeBuilder() - b.buildPrefix() + // Instead of loading a prefix, emit a single integer, so that we have a "prefix" but + // the prefix does not fulfill the requirements for the generator, yet. + b.loadInt(123) let generator = fuzzer.codeGenerators.filter { $0.name == "TypedArrayFromBufferGenerator" @@ -3033,11 +3035,13 @@ class ProgramBuilderTests: XCTestCase { let N = 30 // We might generate a lot more than 30 instructions to fulfill the constraints. let numGeneratedInstructions = b.complete(generator: syntheticGenerator!, withBudget: N) - + XCTAssertGreaterThan(numGeneratedInstructions, 0) + // All generator input requirements are fulfilled. + XCTAssert(generator.parts.allSatisfy { + $0.inputs.constraints.allSatisfy { b.randomVariable(ofType: $0.type) != nil }}) + // All generator `produces` guarantees are fulfilled. + XCTAssert(generator.produces.allSatisfy { b.randomVariable(ofType: $0.type) != nil }) let _ = b.finalize() - - // XCTAssertGreaterThan(numGeneratedInstructions, 0) - // TODO(tacet): Fails in around 5% of times, we should figure out how to fix it. } } From 8fe974b63af7bb0650d43d86c3a020c2bdcd3057 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Thu, 15 Jan 2026 15:51:42 +0100 Subject: [PATCH 68/89] [wasm] Fix generateRandomWasmVar for wasm index ref in cases where it doesn't have a wasmReferenceType WasmTypeExtension. The better printing in commit f418a5e040e090b9453b74f77644da701450e8a2 doesn't do anything if we don't ever reach it because we already crash earlier. This change adapts generateRandomWasmVar to return nil if it receives an index reference type without the expected type extension. If this still causes crashes, we'd then get the better error message from above. Bug: 475996631 Change-Id: I86f89855724f09de3875770e2380257c07d54062 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8932837 Commit-Queue: Michael Achenbach Reviewed-by: Michael Achenbach Commit-Queue: Matthias Liedtke Auto-Submit: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 1e0956bf9..7c51c954b 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -4175,7 +4175,7 @@ public class ProgramBuilder { return self.wasmRefNull(type: type) } case .Index(_): - if (type.wasmReferenceType!.nullability) { + if (type.wasmReferenceType?.nullability ?? false) { return self.wasmRefNull(typeDef: b.jsTyper.getWasmTypeDef(for: type)) } case .none: From 1968a9719129f4f84aa71cf603e48c342548a67d Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Fri, 16 Jan 2026 11:22:41 +0100 Subject: [PATCH 69/89] [wasm] Add one more fatalError to investigate recent failures Bug: 475996631 Change-Id: If8b92877f88b5852184d69477bc508d6c039c294 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8932857 Reviewed-by: Michael Achenbach Commit-Queue: Matthias Liedtke Auto-Submit: Matthias Liedtke --- Sources/Fuzzilli/FuzzIL/JSTyper.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index 1fd9f2377..e78852875 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -446,7 +446,10 @@ public struct JSTyper: Analyzer { guard case .Index(let desc) = type.wasmReferenceType!.kind else { fatalError("\(type) is not an index type") } - return wasmTypeDefMap[desc.get()!]! + guard let desc = desc.get(), let typeDef = wasmTypeDefMap[desc] else { + fatalError("missing type definition link for type \(type), desc \(desc)") + } + return typeDef } mutating func addSignatureType(def: Variable, signature: WasmSignature, inputs: ArraySlice) { From 86744448158e49a294c0ae993ee962f265a9e045 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Fri, 16 Jan 2026 15:46:46 +0100 Subject: [PATCH 70/89] [wasm] Fix WasmTypeGroupReducer to expose all unremoved types While the WasmTypeGroupReducer shall remove all inputs which are not used from the WasmEndTypeGroup (so that these types can be removed in a following iteration), it should still expose all types which are used inside the type group, so that the JSTyper still continues to handle them correctly. This will hopefully fix the current crashes we are observing for types missing the linkage from a wasm index reference type to the corresponding type definition variable in the JSTyper. Bug: 475996631 Change-Id: I571a44fabee3f302c8f53fad14d6f62263d0a8ca Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8935617 Commit-Queue: Matthias Liedtke Reviewed-by: Michael Achenbach Auto-Submit: Matthias Liedtke --- .../Minimization/WasmTypeGroupReducer.swift | 16 +++++- Tests/FuzzilliTests/MinimizerTest.swift | 49 +++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/Sources/Fuzzilli/Minimization/WasmTypeGroupReducer.swift b/Sources/Fuzzilli/Minimization/WasmTypeGroupReducer.swift index 96d6d4870..df6c149db 100644 --- a/Sources/Fuzzilli/Minimization/WasmTypeGroupReducer.swift +++ b/Sources/Fuzzilli/Minimization/WasmTypeGroupReducer.swift @@ -22,12 +22,24 @@ struct WasmTypeGroupReducer: Reducer { uses[input]? += 1 } + + guard instr.op is WasmTypeOperation else { continue } + // Define the usages for all WasmTypeOperation so that we also count usages of a type + // inside a type group (i.e. by other type operations). + for output in instr.outputs { + uses[output] = 0 + } + // For now, we only consider EndTypeGroup instructions. guard case .wasmEndTypeGroup = instr.op.opcode else { continue } candidates.append(instr.index) - for output in instr.outputs { - uses[output] = 0 + for (input, output) in zip(instr.inputs, instr.outputs) { + // Subtract 1 as the input in the WasmEndTypeGroup itself is not a reason to keep + // the type. However, if the type is used inside the type group, it also needs to be + // exposed by the type group. Right now the JSTyper requires that all types defined + // in a type group are exposed by their WasmEndTypeGroup instruction. + uses[output]! += uses[input]! - 1 } } diff --git a/Tests/FuzzilliTests/MinimizerTest.swift b/Tests/FuzzilliTests/MinimizerTest.swift index 22807afe5..58dd10ec7 100644 --- a/Tests/FuzzilliTests/MinimizerTest.swift +++ b/Tests/FuzzilliTests/MinimizerTest.swift @@ -2126,6 +2126,55 @@ class MinimizerTests: XCTestCase { } + func testWasmTypeGroupTypeOnlyUsedInDependency() throws { + let evaluator = EvaluatorForMinimizationTests() + let fuzzer = makeMockFuzzer(evaluator: evaluator) + let b = fuzzer.makeBuilder() + + // Build input program to be minimized. + do { + let typeGroup = b.wasmDefineTypeGroup { + // This signature is only used by the structType which is exposed from this type + // group and then used in the struct.new_default inside the wasm function. Still, + // due to this indirect usage it must still be kept alive. + let signature = b.wasmDefineSignatureType(signature: [.wasmi32] => [.wasmi32], indexTypes: []) + let structType = b.wasmDefineStructType(fields: [.init(type: .wasmRef(.Index(), nullability: true), mutability: true)], indexTypes: [signature]) + return [signature, structType] + } + + b.buildWasmModule { wasmModule in + wasmModule.addWasmFunction(with: [] => [.wasmAnyRef()]) { function, label, args in + evaluator.nextInstructionIsImportant(in: b) + return [function.wasmStructNewDefault(structType: typeGroup[1])] + } + } + } + let originalProgram = b.finalize() + + // Build expected output program. + do { + let typeGroup = b.wasmDefineTypeGroup { + let signature = b.wasmDefineSignatureType(signature: [.wasmi32] => [.wasmi32], indexTypes: []) + let structType = b.wasmDefineStructType(fields: [.init(type: .wasmRef(.Index(), nullability: true), mutability: true)], indexTypes: [signature]) + return [signature, structType] + } + + b.buildWasmModule { wasmModule in + wasmModule.addWasmFunction(with: [] => [.wasmAnyRef()]) { function, label, args in + return [function.wasmStructNewDefault(structType: typeGroup[1])] + } + } + } + let expectedProgram = b.finalize() + + // Perform minimization and check that the two programs are equal. + let actualProgram = minimize(originalProgram, with: fuzzer) + XCTAssertEqual(expectedProgram, actualProgram, + "Expected:\n\(FuzzILLifter().lift(expectedProgram.code))\n\n" + + "Actual:\n\(FuzzILLifter().lift(actualProgram.code))") + } + + func testWasmTypeGroupNestedTypesAndTypeGroupDependencies() throws { let evaluator = EvaluatorForMinimizationTests() let fuzzer = makeMockFuzzer(evaluator: evaluator) From 83d4faca2511e3810ac261647810fc46a01d3eb6 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Fri, 16 Jan 2026 16:14:09 +0100 Subject: [PATCH 71/89] Print corpus import durations Bug: 442444727 Change-Id: I2b829da00393a63f40ac2791091ff73bd288aa24 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8935636 Reviewed-by: Matthias Liedtke Commit-Queue: Michael Achenbach --- Sources/FuzzilliCli/main.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Sources/FuzzilliCli/main.swift b/Sources/FuzzilliCli/main.swift index 10612275b..89c2e87e9 100644 --- a/Sources/FuzzilliCli/main.swift +++ b/Sources/FuzzilliCli/main.swift @@ -641,6 +641,7 @@ fuzzer.sync { // Resume a previous fuzzing session ... if resume, let path = storagePath { + let start = Date() var corpus = loadCorpus(from: path + "/old_corpus") logger.info("Scheduling import of \(corpus.count) programs from previous fuzzing run.") @@ -650,6 +651,10 @@ fuzzer.sync { fuzzer.registerEventListener(for: fuzzer.events.CorpusImportComplete) { // Delete the old corpus directory as soon as the corpus import is complete. try? FileManager.default.removeItem(atPath: path + "/old_corpus") + + let duration = Date().timeIntervalSince(start) + let humanReadableDuration = Duration.seconds(duration).formatted(.time(pattern: .hourMinuteSecond)) + logger.info("Corpus import after resume took \((String(format: "%.0f", duration)))s (\(humanReadableDuration)).") } fuzzer.scheduleCorpusImport(corpus, importMode: .interestingOnly(shouldMinimize: false)) // We assume that the programs are already minimized @@ -658,11 +663,19 @@ fuzzer.sync { // ... or import an existing corpus. if let path = corpusImportPath { assert(!resume) + let start = Date() let corpus = loadCorpus(from: path) guard !corpus.isEmpty else { logger.fatal("Cannot import an empty corpus.") } logger.info("Scheduling corpus import of \(corpus.count) programs with mode \(corpusImportModeName).") + + fuzzer.registerEventListener(for: fuzzer.events.CorpusImportComplete) { + let duration = Date().timeIntervalSince(start) + let humanReadableDuration = Duration.seconds(duration).formatted(.time(pattern: .hourMinuteSecond)) + logger.info("Existing corpus import took \((String(format: "%.0f", duration)))s (\(humanReadableDuration)).") + } + fuzzer.scheduleCorpusImport(corpus, importMode: corpusImportMode) } From 6faf7876262188452399fc43be4015018f12f7b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doga=20Y=C3=BCksel?= Date: Fri, 16 Jan 2026 11:22:09 +0000 Subject: [PATCH 72/89] [wasm] Add WasmRefEq operation and generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds support for ref.eq instruction to be generated Bug: 474940922 Change-Id: I7b88ceffed5252878132406da30a570be01f13ad Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8933276 Reviewed-by: Matthias Liedtke Commit-Queue: Doga Yüksel --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 6 +++ .../CodeGen/CodeGeneratorWeights.swift | 1 + .../Fuzzilli/CodeGen/WasmCodeGenerators.swift | 8 ++++ Sources/Fuzzilli/FuzzIL/Instruction.swift | 4 ++ Sources/Fuzzilli/FuzzIL/JSTyper.swift | 2 + Sources/Fuzzilli/FuzzIL/Opcodes.swift | 1 + Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 8 ++++ Sources/Fuzzilli/Lifting/FuzzILLifter.swift | 3 ++ .../Fuzzilli/Lifting/JavaScriptLifter.swift | 1 + Sources/Fuzzilli/Lifting/WasmLifter.swift | 2 + .../Fuzzilli/Mutators/OperationMutator.swift | 3 +- Sources/Fuzzilli/Protobuf/operations.pb.swift | 29 ++++++++++++++ Sources/Fuzzilli/Protobuf/operations.proto | 3 ++ Sources/Fuzzilli/Protobuf/program.pb.swift | 29 +++++++++++++- Sources/Fuzzilli/Protobuf/program.proto | 1 + Tests/FuzzilliTests/WasmTests.swift | 38 +++++++++++++++++++ 16 files changed, 137 insertions(+), 2 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 7c51c954b..0322618a1 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -4374,6 +4374,12 @@ public class ProgramBuilder { return b.emit(WasmRefIsNull(), withInputs: [ref], types: [.wasmGenericRef]).output } + @discardableResult + // TODO(pawkra): Support shared references. + public func wasmRefEq(_ lhs: Variable, _ rhs: Variable) -> Variable { + return b.emit(WasmRefEq(), withInputs: [lhs, rhs], types: [.wasmEqRef(), .wasmEqRef()]).output + } + @discardableResult public func wasmRefI31(_ number: Variable, shared: Bool = false) -> Variable { return b.emit(WasmRefI31(isShared: shared), withInputs: [number], types: [.wasmi32]).output diff --git a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift index 22596f473..6d8698fc6 100644 --- a/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift +++ b/Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift @@ -371,6 +371,7 @@ public let codeGeneratorWeights = [ "WasmStructSetGenerator": 5, "WasmRefNullGenerator": 5, "WasmRefIsNullGenerator": 5, + "WasmRefEqGenerator": 5, "WasmRefI31Generator": 5, "WasmI31GetGenerator": 5, "WasmAnyConvertExternGenerator": 5, diff --git a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift index 0fdbc2ab0..ee8abfc22 100644 --- a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift @@ -312,6 +312,14 @@ public let WasmCodeGenerators: [CodeGenerator] = [ b.currentWasmModule.currentWasmFunction.wasmRefIsNull(ref) }, + CodeGenerator( + "WasmRefEqGenerator", inContext: .single(.wasmFunction), + inputs: .required(.wasmEqRef(), .wasmEqRef()), + produces: [.wasmi32] + ) { b, lhs, rhs in + b.currentWasmModule.currentWasmFunction.wasmRefEq(lhs, rhs) + }, + // TODO(pawkra): add shared variant. CodeGenerator("WasmRefI31Generator", inContext: .single(.wasmFunction), inputs: .required(.wasmi32)) { b, value in b.currentWasmModule.currentWasmFunction.wasmRefI31(value, shared: false) diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index 36dc2e9be..bb3d4b0f6 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -1617,6 +1617,8 @@ extension Instruction: ProtobufConvertible { } case .wasmRefIsNull(_): $0.wasmRefIsNull = Fuzzilli_Protobuf_WasmRefIsNull() + case .wasmRefEq(_): + $0.wasmRefEq = Fuzzilli_Protobuf_WasmRefEq() case .wasmRefI31(let op): $0.wasmRefI31 = Fuzzilli_Protobuf_WasmRefI31.with { $0.isShared = op.isShared @@ -2599,6 +2601,8 @@ extension Instruction: ProtobufConvertible { op = p.hasType ? WasmRefNull(type: WasmTypeEnumToILType(p.type)) : WasmRefNull(type: nil) case .wasmRefIsNull(_): op = WasmRefIsNull() + case .wasmRefEq(_): + op = WasmRefEq() case .wasmRefI31(let p): op = WasmRefI31(isShared: p.isShared) case .wasmI31Get(let p): diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index e78852875..eeff27465 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -915,6 +915,8 @@ public struct JSTyper: Analyzer { } case .wasmRefIsNull(_): setType(of: instr.output, to: .wasmi32) + case .wasmRefEq(_): + setType(of: instr.output, to: .wasmi32) case .wasmRefI31(let op): setType(of: instr.output, to: .wasmRefI31(shared: op.isShared)) case .wasmI31Get(_): diff --git a/Sources/Fuzzilli/FuzzIL/Opcodes.swift b/Sources/Fuzzilli/FuzzIL/Opcodes.swift index 8e2110cae..6faff452a 100644 --- a/Sources/Fuzzilli/FuzzIL/Opcodes.swift +++ b/Sources/Fuzzilli/FuzzIL/Opcodes.swift @@ -365,4 +365,5 @@ enum Opcode { case createNamedAsyncDisposableVariable(CreateNamedAsyncDisposableVariable) case wasmDefineAdHocSignatureType(WasmDefineAdHocSignatureType) case wasmStructNew(WasmStructNew) + case wasmRefEq(WasmRefEq) } diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index 6df5b99c1..dfe1fd6f1 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -2283,6 +2283,14 @@ class WasmRefIsNull: WasmOperation { } } +class WasmRefEq: WasmOperation { + override var opcode: Opcode { .wasmRefEq(self) } + + init() { + super.init(numInputs: 2, numOutputs: 1, requiredContext: [.wasmFunction]) + } +} + class WasmRefI31: WasmOperation { override var opcode: Opcode { .wasmRefI31(self) } let isShared: Bool diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift index f6c7cfc5b..8fbb32fb6 100644 --- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift +++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift @@ -1330,6 +1330,9 @@ public class FuzzILLifter: Lifter { case .wasmRefIsNull(_): w.emit("\(output()) <- WasmRefIsNull \(input(0))") + case .wasmRefEq(_): + w.emit("\(output()) <- WasmRefEq \(input(0)) \(input(1))") + case .wasmRefI31(_): w.emit("\(output()) <- WasmRefI31 \(input(0))") diff --git a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift index 1d74e006a..9fb9ba5fe 100644 --- a/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift +++ b/Sources/Fuzzilli/Lifting/JavaScriptLifter.swift @@ -1789,6 +1789,7 @@ public class JavaScriptLifter: Lifter { .wasmStructSet(_), .wasmRefNull(_), .wasmRefIsNull(_), + .wasmRefEq(_), .wasmRefI31(_), .wasmI31Get(_), .wasmAnyConvertExtern(_), diff --git a/Sources/Fuzzilli/Lifting/WasmLifter.swift b/Sources/Fuzzilli/Lifting/WasmLifter.swift index a97485fec..d3cbe2635 100644 --- a/Sources/Fuzzilli/Lifting/WasmLifter.swift +++ b/Sources/Fuzzilli/Lifting/WasmLifter.swift @@ -2185,6 +2185,8 @@ public class WasmLifter { return try Data([0xD0]) + encodeHeapType(typer.type(of: wasmInstruction.output)) case .wasmRefIsNull(_): return Data([0xD1]) + case .wasmRefEq(_): + return Data([0xD3]) case .wasmRefI31(let op): return Data([Prefix.GC.rawValue, op.isShared ? 0x1F : 0x1C]) case .wasmI31Get(let op): diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift index af493a681..cefbf9d92 100644 --- a/Sources/Fuzzilli/Mutators/OperationMutator.swift +++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift @@ -728,7 +728,8 @@ public class OperationMutator: BaseInstructionMutator { .wasmDropElementSegment(_), .wasmTableInit(_), .wasmTableCopy(_), - .wasmStructNew(_): + .wasmStructNew(_), + .wasmRefEq(_): let mutability = instr.isOperationMutable ? "mutable" : "immutable" fatalError("Unexpected operation \(instr.op.opcode), marked as \(mutability)") } diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index 2632630b4..a6011ecc8 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -5924,6 +5924,16 @@ public struct Fuzzilli_Protobuf_WasmRefIsNull: Sendable { public init() {} } +public struct Fuzzilli_Protobuf_WasmRefEq: 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_WasmRefI31: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -15442,6 +15452,25 @@ extension Fuzzilli_Protobuf_WasmRefIsNull: SwiftProtobuf.Message, SwiftProtobuf. } } +extension Fuzzilli_Protobuf_WasmRefEq: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".WasmRefEq" + 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_WasmRefEq, rhs: Fuzzilli_Protobuf_WasmRefEq) -> Bool { + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension Fuzzilli_Protobuf_WasmRefI31: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmRefI31" public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}isShared\0") diff --git a/Sources/Fuzzilli/Protobuf/operations.proto b/Sources/Fuzzilli/Protobuf/operations.proto index 46758340b..f761715e1 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -1563,6 +1563,9 @@ message WasmRefNull { message WasmRefIsNull { } +message WasmRefEq { +} + message WasmRefI31 { bool isShared = 1; } diff --git a/Sources/Fuzzilli/Protobuf/program.pb.swift b/Sources/Fuzzilli/Protobuf/program.pb.swift index c85ec56f6..11b662941 100644 --- a/Sources/Fuzzilli/Protobuf/program.pb.swift +++ b/Sources/Fuzzilli/Protobuf/program.pb.swift @@ -2737,6 +2737,14 @@ public struct Fuzzilli_Protobuf_Instruction: Sendable { set {operation = .wasmStructNew(newValue)} } + public var wasmRefEq: Fuzzilli_Protobuf_WasmRefEq { + get { + if case .wasmRefEq(let v)? = operation {return v} + return Fuzzilli_Protobuf_WasmRefEq() + } + set {operation = .wasmRefEq(newValue)} + } + public var unknownFields = SwiftProtobuf.UnknownStorage() public enum OneOf_Operation: Equatable, Sendable { @@ -3076,6 +3084,8 @@ public struct Fuzzilli_Protobuf_Instruction: Sendable { case createNamedAsyncDisposableVariable(Fuzzilli_Protobuf_CreateNamedAsyncDisposableVariable) case wasmDefineAdHocSignatureType(Fuzzilli_Protobuf_WasmDefineAdHocSignatureType) case wasmStructNew(Fuzzilli_Protobuf_WasmStructNew) + case wasmRefEq(Fuzzilli_Protobuf_WasmRefEq) + } public init() {} @@ -3123,7 +3133,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}beginClassInstanceComputedMethod\0\u{1}endClassInstanceComputedMethod\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}beginClassStaticComputedMethod\0\u{1}endClassStaticComputedMethod\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}wasmDefineAdHocSignatureType\0\u{1}wasmStructNew\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}beginClassInstanceComputedMethod\0\u{1}endClassInstanceComputedMethod\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}beginClassStaticComputedMethod\0\u{1}endClassStaticComputedMethod\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}wasmDefineAdHocSignatureType\0\u{1}wasmStructNew\0\u{1}wasmRefEq\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -7495,6 +7505,19 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M self.operation = .wasmStructNew(v) } }() + case 338: try { + var v: Fuzzilli_Protobuf_WasmRefEq? + var hadOneofValue = false + if let current = self.operation { + hadOneofValue = true + if case .wasmRefEq(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + self.operation = .wasmRefEq(v) + } + }() default: break } } @@ -8853,6 +8876,10 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M guard case .wasmStructNew(let v)? = self.operation else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 337) }() + case .wasmRefEq?: try { + guard case .wasmRefEq(let v)? = self.operation else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 338) + }() case nil: break } try unknownFields.traverse(visitor: &visitor) diff --git a/Sources/Fuzzilli/Protobuf/program.proto b/Sources/Fuzzilli/Protobuf/program.proto index 1c6d2fbba..6fc3e2981 100644 --- a/Sources/Fuzzilli/Protobuf/program.proto +++ b/Sources/Fuzzilli/Protobuf/program.proto @@ -361,6 +361,7 @@ message Instruction { CreateNamedAsyncDisposableVariable createNamedAsyncDisposableVariable = 335; WasmDefineAdHocSignatureType wasmDefineAdHocSignatureType = 336; WasmStructNew wasmStructNew = 337; + WasmRefEq wasmRefEq = 338; } } diff --git a/Tests/FuzzilliTests/WasmTests.swift b/Tests/FuzzilliTests/WasmTests.swift index 23ef57bb6..b2b51e9d3 100644 --- a/Tests/FuzzilliTests/WasmTests.swift +++ b/Tests/FuzzilliTests/WasmTests.swift @@ -4851,6 +4851,44 @@ class WasmGCTests: XCTestCase { try refNullAbstractTypes(sharedRef: false) } + func testRefEq() throws { + let runner = try GetJavaScriptExecutorOrSkipTest() + let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) + let fuzzer = makeMockFuzzer(config: liveTestConfig, environment: JavaScriptEnvironment()) + let b = fuzzer.makeBuilder() + + let arrayType = b.wasmDefineTypeGroup {b.wasmDefineArrayType(elementType: .wasmi32, mutability: true)}[0] + + let module = b.buildWasmModule { wasmModule in + wasmModule.addWasmFunction(with: [.wasmEqRef(), .wasmEqRef()] => [.wasmi32]) { function, label, args in + return [function.wasmRefEq(args[0], args[1])] + } + wasmModule.addWasmFunction(with: [] => [.wasmEqRef()]) { function, label, args in + return [function.wasmArrayNewFixed(arrayType: arrayType, elements: [])] + } + } + + let exports = module.loadExports() + let outputFunc = b.createNamedVariable(forBuiltin: "output") + + let array = b.callMethod(module.getExportedMethod(at: 1), on: exports, withArgs: []) + let otherArray = b.callMethod(module.getExportedMethod(at: 1), on: exports, withArgs: []) + + let wasmRefEq = module.getExportedMethod(at: 0) + let cases = [ + b.callMethod(wasmRefEq, on: exports, withArgs: [b.loadInt(1), b.loadInt(1)]), + b.callMethod(wasmRefEq, on: exports, withArgs: [b.loadInt(0), b.loadInt(1)]), + b.callMethod(wasmRefEq, on: exports, withArgs: [array, array]), + b.callMethod(wasmRefEq, on: exports, withArgs: [array, otherArray]) + ] + + for input in cases { b.callFunction(outputFunc, withArgs: [input]) } + + let prog = b.finalize() + let jsProg = fuzzer.lifter.lift(prog) + testForOutput(program: jsProg, runner: runner, outputString: "1\n0\n1\n0\n") + } + func i31Ref(shared: Bool) throws { let runner = try GetJavaScriptExecutorOrSkipTest(type: .any, withArguments: ["--experimental-wasm-shared"]) let liveTestConfig = Configuration(logLevel: .error, enableInspection: true) From 00086f688fe42450174e4e517e9ba4989544b06e Mon Sep 17 00:00:00 2001 From: Danylo Mocherniuk Date: Mon, 19 Jan 2026 09:06:09 +0000 Subject: [PATCH 73/89] Actually emit variables for unused expressions. Seems like the previous change didn't do that because configuration objects never got this boolean. Bug: 422361840 Change-Id: I9a4fd2af616b7dd5dd27126fe10004374a41992a Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8940976 Commit-Queue: Matthias Liedtke Reviewed-by: Matthias Liedtke --- Sources/FuzzilliCli/main.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Sources/FuzzilliCli/main.swift b/Sources/FuzzilliCli/main.swift index 89c2e87e9..08be33264 100644 --- a/Sources/FuzzilliCli/main.swift +++ b/Sources/FuzzilliCli/main.swift @@ -547,7 +547,8 @@ let mainConfig = Configuration(arguments: CommandLine.arguments, staticCorpus: staticCorpus, tag: tag, isWasmEnabled: enableWasm, - storagePath: storagePath) + storagePath: storagePath, + forDifferentialFuzzing: forDifferentialFuzzing) let fuzzer = makeFuzzer(with: mainConfig) @@ -699,7 +700,8 @@ let workerConfig = Configuration(arguments: CommandLine.arguments, staticCorpus: staticCorpus, tag: tag, isWasmEnabled: enableWasm, - storagePath: storagePath) + storagePath: storagePath, + forDifferentialFuzzing: forDifferentialFuzzing) for _ in 1.. Date: Mon, 19 Jan 2026 12:26:06 +0000 Subject: [PATCH 74/89] [dumpling] Implement differential fuzzing Bug: 441467877 Change-Id: I7278380605e40ca79b4dc889cb8b6734aa7c4327 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8908076 Reviewed-by: Matthias Liedtke Commit-Queue: Danylo Mocherniuk --- Sources/Fuzzilli/Base/Contributor.swift | 16 ++- Sources/Fuzzilli/Base/Events.swift | 14 +++ Sources/Fuzzilli/Configuration.swift | 38 +++++- Sources/Fuzzilli/Engines/FuzzEngine.swift | 4 + Sources/Fuzzilli/Engines/HybridEngine.swift | 3 + .../Evaluation/ProgramCoverageEvaluator.swift | 4 +- Sources/Fuzzilli/Execution/Execution.swift | 67 +++++++++++ Sources/Fuzzilli/Fuzzer.swift | 86 ++++++++++++- Sources/Fuzzilli/Modules/Storage.swift | 16 +++ .../Mutators/RuntimeAssistedMutator.swift | 4 +- Sources/Fuzzilli/Util/MockFuzzer.swift | 1 + .../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 | 7 ++ Sources/FuzzilliCli/Profiles/QjsProfile.swift | 2 + .../FuzzilliCli/Profiles/QtjsProfile.swift | 2 + Sources/FuzzilliCli/Profiles/Serenity.swift | 1 + .../Profiles/SpidermonkeyProfile.swift | 2 + .../Profiles/V8DumplingProfile.swift | 113 ++++++++++++++++++ .../Profiles/V8HoleFuzzingProfile.swift | 2 + Sources/FuzzilliCli/Profiles/V8Profile.swift | 2 + .../Profiles/V8SandboxProfile.swift | 2 + Sources/FuzzilliCli/Profiles/XSProfile.swift | 2 + Sources/FuzzilliCli/main.swift | 41 +++++-- 26 files changed, 423 insertions(+), 14 deletions(-) create mode 100644 Sources/FuzzilliCli/Profiles/V8DumplingProfile.swift diff --git a/Sources/Fuzzilli/Base/Contributor.swift b/Sources/Fuzzilli/Base/Contributor.swift index c0e23bd2f..87b668464 100644 --- a/Sources/Fuzzilli/Base/Contributor.swift +++ b/Sources/Fuzzilli/Base/Contributor.swift @@ -28,6 +28,8 @@ public class Contributor: Hashable { private var timedOutSamples = 0 // Number of crashing programs produced. private var crashingSamples = 0 + // The number of programs resulting in valid differential produced. + private var differentialSamples = 0 // The number of times this contributor has been invoked private(set) var invocationCount = 0 // The number of times this contributor has actually emitted more than 0 instructions @@ -46,6 +48,10 @@ public class Contributor: Hashable { validSamples += 1 } + func generatedDifferentialSample() { + differentialSamples += 1 + } + func generatedInterestingSample() { interestingSamples += 1 } @@ -82,8 +88,12 @@ public class Contributor: Hashable { return crashingSamples } + public var differentialsFound: Int { + return differentialSamples + } + public var totalSamples: Int { - return validSamples + interestingSamples + invalidSamples + timedOutSamples + crashingSamples + return validSamples + interestingSamples + invalidSamples + timedOutSamples + crashingSamples + differentialSamples } // If this is low, that means the CodeGenerator has dynamic requirements that are not met most of the time. @@ -151,4 +161,8 @@ extension Contributors { public func generatedTimeOutSample() { forEach { $0.generatedTimeOutSample() } } + + public func generatedDifferentialSample() { + forEach { $0.generatedDifferentialSample() } + } } diff --git a/Sources/Fuzzilli/Base/Events.swift b/Sources/Fuzzilli/Base/Events.swift index 7006829bd..84a6832ec 100644 --- a/Sources/Fuzzilli/Base/Events.swift +++ b/Sources/Fuzzilli/Base/Events.swift @@ -55,6 +55,9 @@ public class Events { /// Signals that a crashing program has been found. Dispatched after the crashing program has been minimized. public let CrashFound = Event<(program: Program, behaviour: CrashBehaviour, isUnique: Bool, origin: ProgramOrigin)>() + /// Signals that a differential program was found. Dispatched after the differential program has been minimized. + public let DifferentialFound = Event<(program: Program, behaviour: CrashBehaviour, isUnique: Bool, origin: ProgramOrigin)>() + /// Signals that a program causing a timeout has been found. public let TimeOutFound = Event() @@ -140,4 +143,15 @@ public enum ExecutionPurpose { case runtimeAssistedMutation /// Any other reason. case other + + // This variable is added because we currently don't want to differentially execute + // a sample if the purpose is unknown (`.other`), `.startup` or `.runtimeAssistedMutation`. + public var supportsDifferentialRun: Bool { + switch self { + case .fuzzing, .checkForDeterministicBehavior, .programImport, .minimization: + return true + case .startup, .runtimeAssistedMutation, .other: + return false + } + } } diff --git a/Sources/Fuzzilli/Configuration.swift b/Sources/Fuzzilli/Configuration.swift index 121e9cbb8..28c7eaad4 100644 --- a/Sources/Fuzzilli/Configuration.swift +++ b/Sources/Fuzzilli/Configuration.swift @@ -12,7 +12,36 @@ // See the License for the specific language governing permissions and // limitations under the License. +// TODO(mdanylo): this should be part of the protocol between Fuzzilli and V8. +public struct DifferentialConfig { + public let dumpFilenamePattern: String + + public init(dumpFilenamePattern: String) { + self.dumpFilenamePattern = dumpFilenamePattern + } + + public func getDumpFilename(isOptimized: Bool) -> String { + let subDirectory = isOptimized ? "optimizedDump" : "unoptimizedDump" + return String(format: dumpFilenamePattern, subDirectory) + } + + public func getDumpFilenameParameter(isOptimized: Bool) -> String { + return "--dump-out-filename=\(getDumpFilename(isOptimized: isOptimized))" + } + + public static func create(for instanceId: Int, storagePath: String) -> DifferentialConfig { + // TODO(mdanylo): it would be cool to add random hash to the filename if we store dumps + // in the filesystem for debugging. + let fileName = "output_dump_\(instanceId).txt" + let pattern = "\(storagePath)/%@/\(fileName)" + return DifferentialConfig(dumpFilenamePattern: pattern) + } +} + public struct Configuration { + /// The config specific to differential fuzzing + public let diffConfig: DifferentialConfig? + /// The commandline arguments used by this instance. public let arguments: [String] @@ -91,7 +120,9 @@ public struct Configuration { tag: String? = nil, isWasmEnabled: Bool = false, storagePath: String? = nil, - forDifferentialFuzzing: Bool = false) { + forDifferentialFuzzing: Bool = false, + instanceId: Int = -1, + dumplingEnabled: Bool = false) { self.arguments = arguments self.timeout = timeout self.logLevel = logLevel @@ -106,6 +137,11 @@ public struct Configuration { self.isWasmEnabled = isWasmEnabled self.storagePath = storagePath self.forDifferentialFuzzing = forDifferentialFuzzing + self.diffConfig = dumplingEnabled ? DifferentialConfig.create(for: instanceId, storagePath: storagePath!) : nil + } + + public func getInstanceSpecificArguments(forReferenceRunner: Bool) -> [String] { + return diffConfig.map { [$0.getDumpFilenameParameter(isOptimized: !forReferenceRunner)] } ?? [] } } diff --git a/Sources/Fuzzilli/Engines/FuzzEngine.swift b/Sources/Fuzzilli/Engines/FuzzEngine.swift index 815caad30..1a8602312 100644 --- a/Sources/Fuzzilli/Engines/FuzzEngine.swift +++ b/Sources/Fuzzilli/Engines/FuzzEngine.swift @@ -44,6 +44,10 @@ public class FuzzEngine: ComponentBase { fuzzer.processCrash(program, withSignal: termsig, withStderr: execution.stderr, withStdout: execution.stdout, origin: .local, withExectime: execution.execTime) program.contributors.generatedCrashingSample() + case .differential: + fuzzer.processDifferential(program, withStderr: execution.stderr, withStdout: execution.stdout, origin: .local) + program.contributors.generatedDifferentialSample() + case .succeeded: fuzzer.dispatchEvent(fuzzer.events.ValidProgramFound, data: program) var isInteresting = false diff --git a/Sources/Fuzzilli/Engines/HybridEngine.swift b/Sources/Fuzzilli/Engines/HybridEngine.swift index b59aa1ed4..214ff67cb 100644 --- a/Sources/Fuzzilli/Engines/HybridEngine.swift +++ b/Sources/Fuzzilli/Engines/HybridEngine.swift @@ -21,6 +21,7 @@ public class HybridEngine: FuzzEngine { // The different outcomes of one fuzzing iterations. private enum CodeGenerationOutcome: String, CaseIterable { case success = "Success" + case generatedCodeDifferential = "Generated code differential" case generatedCodeFailed = "Generated code failed" case generatedCodeTimedOut = "Generated code timed out" case generatedCodeCrashed = "Generated code crashed" @@ -112,6 +113,8 @@ public class HybridEngine: FuzzEngine { return recordOutcome(.generatedCodeTimedOut) case .crashed: return recordOutcome(.generatedCodeCrashed) + case .differential: + return recordOutcome(.generatedCodeDifferential) } // Now perform one round of fixup to improve the generated program based on runtime information and in particular remove all try-catch guards that are not needed. diff --git a/Sources/Fuzzilli/Evaluation/ProgramCoverageEvaluator.swift b/Sources/Fuzzilli/Evaluation/ProgramCoverageEvaluator.swift index c365ff1ef..ed17d6d43 100644 --- a/Sources/Fuzzilli/Evaluation/ProgramCoverageEvaluator.swift +++ b/Sources/Fuzzilli/Evaluation/ProgramCoverageEvaluator.swift @@ -192,8 +192,8 @@ public class ProgramCoverageEvaluator: ComponentBase, ProgramEvaluator { return false } - if execution.outcome.isCrash() { - // For crashes, we don't care about the edges that were triggered, just about the outcome itself. + if execution.outcome.isCrash() || execution.outcome.isDifferential() { + // For crashes and differentials, we don't care about the edges that were triggered, just about the outcome itself. return true } diff --git a/Sources/Fuzzilli/Execution/Execution.swift b/Sources/Fuzzilli/Execution/Execution.swift index 5d23c70a2..9297ef296 100644 --- a/Sources/Fuzzilli/Execution/Execution.swift +++ b/Sources/Fuzzilli/Execution/Execution.swift @@ -20,6 +20,12 @@ public enum ExecutionOutcome: CustomStringConvertible, Equatable, Hashable { case failed(Int) case succeeded case timedOut + // This outcome is added to support native differential fuzzing. + // It should get very similar treatment to crashed -> if the run resulted + // in a differential, most likely there's a bug. + // Please note that this feature is unstable yet, so the statement above + // might not always be the case. + case differential public var description: String { switch self { @@ -31,6 +37,8 @@ public enum ExecutionOutcome: CustomStringConvertible, Equatable, Hashable { return "Succeeded" case .timedOut: return "TimedOut" + case .differential: + return "Differential" } } @@ -49,6 +57,14 @@ public enum ExecutionOutcome: CustomStringConvertible, Equatable, Hashable { return false } } + + public func isDifferential() -> Bool { + if case .differential = self { + return true + } else { + return false + } + } } /// The result of executing a program. @@ -59,3 +75,54 @@ public protocol Execution { var fuzzout: String { get } var execTime: TimeInterval { get } } + +/// Struct to capture result of exection in differential mode +struct DiffExecution: Execution { + let outcome: ExecutionOutcome + let execTime: TimeInterval + let stdout: String + let stderr: String + let fuzzout: String + + private init( + outcome: ExecutionOutcome, + execTime: TimeInterval, + stdout: String, + stderr: String, + fuzzout: String + ) { + self.outcome = outcome + self.execTime = execTime + self.stdout = stdout + self.stderr = stderr + self.fuzzout = fuzzout + } + + // TODO(mdanylo): we shouldn't pass dump outputs as a separate parameter, + // instead we should rather make them a part of a REPRL protocol between Fuzzilli and V8. + static func diff(optExec: Execution, unoptExec: Execution, + optDumpOut: String, unoptDumpOut: String) -> Execution { + + assert(optExec.outcome == .succeeded && unoptExec.outcome == .succeeded) + + func formatDiff(label: String, optData: String, unoptData: String) -> String { + return """ + === OPT \(label) === + \(optData) + + === UNOPT \(label) === + \(unoptData) + """ + } + + let relateOutcome = DiffOracle.relate(optDumpOut, with: unoptDumpOut) + + return DiffExecution( + outcome: relateOutcome ? .succeeded : .differential, + execTime: optExec.execTime, + stdout: formatDiff(label: "STDOUT", optData: optExec.stdout, unoptData: unoptExec.stdout), + stderr: formatDiff(label: "STDERR", optData: optExec.stderr, unoptData: unoptExec.stderr), + fuzzout: formatDiff(label: "FUZZOUT", optData: optExec.fuzzout, unoptData: unoptExec.fuzzout) + ) + } +} diff --git a/Sources/Fuzzilli/Fuzzer.swift b/Sources/Fuzzilli/Fuzzer.swift index 3a7f9f605..51aa6a308 100644 --- a/Sources/Fuzzilli/Fuzzer.swift +++ b/Sources/Fuzzilli/Fuzzer.swift @@ -53,6 +53,9 @@ public class Fuzzer { /// The script runner used to execute generated scripts. public let runner: ScriptRunner + /// The script runners used to compare against in differential executions. + public let referenceRunner: ScriptRunner? + /// The fuzzer engine producing new programs from existing ones and executing them. public let engine: FuzzEngine @@ -126,6 +129,10 @@ public class Fuzzer { /// Start time of this fuzzing session private let startTime = Date() + public var isDifferentialFuzzing: Bool { + return referenceRunner != nil + } + /// Returns the uptime of this fuzzer as TimeInterval. public func uptime() -> TimeInterval { return -startTime.timeIntervalSinceNow @@ -175,7 +182,7 @@ public class Fuzzer { /// Constructs a new fuzzer instance with the provided components. public init( - configuration: Configuration, scriptRunner: ScriptRunner, engine: FuzzEngine, mutators: WeightedList, + configuration: Configuration, scriptRunner: ScriptRunner, referenceScriptRunner: ScriptRunner?, engine: FuzzEngine, mutators: WeightedList, codeGenerators: WeightedList, programTemplates: WeightedList, evaluator: ProgramEvaluator, environment: JavaScriptEnvironment, lifter: Lifter, corpus: Corpus, minimizer: Minimizer, queue: DispatchQueue? = nil ) { @@ -196,6 +203,7 @@ public class Fuzzer { self.lifter = lifter self.corpus = corpus self.runner = scriptRunner + self.referenceRunner = referenceScriptRunner self.minimizer = minimizer self.logger = Logger(withLabel: "Fuzzer") self.contextGraph = ContextGraph(for: codeGenerators, withLogger: self.logger) @@ -265,6 +273,9 @@ public class Fuzzer { // Initialize the script runner first so we are able to execute programs. runner.initialize(with: self) + if let referenceRunner { + referenceRunner.initialize(with: self) + } // Then initialize all components. engine.initialize(with: self) @@ -465,6 +476,8 @@ public class Fuzzer { // from another instance triggers a crash in this instance. processCrash(program, withSignal: termsig, withStderr: execution.stderr, withStdout: execution.stdout, origin: origin, withExectime: execution.execTime) + case .differential: + processDifferential(program, withStderr: execution.stderr, withStdout: execution.stdout, origin: origin) case .succeeded: if let aspects = evaluator.evaluate(execution) { @@ -700,6 +713,10 @@ public class Fuzzer { let execution = runner.run(script, withTimeout: timeout ?? config.timeout) dispatchEvent(events.PostExecute, data: execution) + if (isDifferentialFuzzing && purpose.supportsDifferentialRun && execution.outcome == .succeeded) { + return executeDifferentialIfNeeded(execution, program, script, withTimeout: timeout ?? config.timeout) + } + return execution } @@ -815,6 +832,42 @@ public class Fuzzer { } } + /// Process a program that causes difference between optimized and unoptimized executions + func processDifferential(_ program: Program, withStderr stderr: String, withStdout stdout: String, origin: ProgramOrigin) { + func processCommon(_ program: Program) { + let hasDiffInfo = program.comments.at(.footer)?.contains("DIFFERENTIAL INFO") ?? false + if !hasDiffInfo { + let footerMessage = """ + DIFFERENTIAL INFO + ========== + STDERR: + \(stderr) + ARGS: \(runner.processArguments.joined(separator: " ")) + REFERENCE ARGS: \(referenceRunner!.processArguments.joined(separator: " ")) + """ + + program.comments.add(footerMessage, at: .footer) + } + + let execution = execute(program, withTimeout: self.config.timeout * 2, purpose: .checkForDeterministicBehavior) + if case .differential = execution.outcome { + dispatchEvent(events.DifferentialFound, data: (program, .deterministic, true, origin)) + } else { + dispatchEvent(events.DifferentialFound, data: (program, .flaky, true, origin)) + } + } + + if !origin.requiresMinimization() { + return processCommon(program) + } + + fuzzGroup.enter() + minimizer.withMinimizedCopy(program, withAspects: ProgramAspects(outcome: .differential)) { minimizedProgram in + self.fuzzGroup.leave() + processCommon(minimizedProgram) + } + } + /// Constructs a new ProgramBuilder using this fuzzing context. public func makeBuilder(forMutating parent: Program? = nil) -> ProgramBuilder { dispatchPrecondition(condition: .onQueue(queue)) @@ -1063,6 +1116,35 @@ public class Fuzzer { return actualTimeout } + private func executeDifferentialIfNeeded(_ execution: Execution, _ program: Program, _ script: String, withTimeout timeout: UInt32) -> Execution { + do { + let optPath = config.diffConfig!.getDumpFilename(isOptimized: true) + let unoptPath = config.diffConfig!.getDumpFilename(isOptimized: false) + + let optimizedDump = try String(contentsOfFile: optPath, encoding: .utf8) + + if optimizedDump.isEmpty { + return execution + } + + // If we dumped at least one optimized frame, there is a point in unoptimized run. + let unoptExecution = referenceRunner!.run(script, withTimeout: timeout) + + // If unoptExecution didn't succeed just return the outcome, so that we can debug it. + // Comparison with relate tool is meaningless in such a case. + if unoptExecution.outcome != .succeeded { + return unoptExecution + } + + let unoptimizedDump = try String(contentsOfFile: unoptPath, encoding: .utf8) + + return DiffExecution.diff(optExec: execution, unoptExec: unoptExecution, optDumpOut: optimizedDump, unoptDumpOut: unoptimizedDump) + + } catch { + fatalError("Critical failure: Unable to read dump files. Error: \(error)") + } + } + /// A pending corpus import job together with some statistics. private struct CorpusImportJob { private var corpusToImport: [Program] @@ -1124,7 +1206,7 @@ public class Fuzzer { numberOfProgramsRequiringWasmButDisabled += 1 case .failed(let outcome): switch outcome { - case .crashed, .succeeded: + case .crashed, .succeeded, .differential: // This is unexpected so we don't track these. break case .failed: diff --git a/Sources/Fuzzilli/Modules/Storage.swift b/Sources/Fuzzilli/Modules/Storage.swift index 9c9860d57..72c5ad49f 100644 --- a/Sources/Fuzzilli/Modules/Storage.swift +++ b/Sources/Fuzzilli/Modules/Storage.swift @@ -19,12 +19,15 @@ public class Storage: Module { private let storageDir: String private let crashesDir: String private let duplicateCrashesDir: String + private let differentialsDir: String private let corpusDir: String private let statisticsDir: String private let stateFile: String private let failedDir: String private let timeOutDir: String private let diagnosticsDir: String + private let optimizedDumpDir: String + private let unoptimizedDumpDir: String private let statisticsExportInterval: Double? @@ -35,12 +38,15 @@ public class Storage: Module { self.storageDir = storageDir self.crashesDir = storageDir + "/crashes" self.duplicateCrashesDir = storageDir + "/crashes/duplicates" + self.differentialsDir = storageDir + "/differentials" self.corpusDir = storageDir + "/corpus" self.failedDir = storageDir + "/failed" self.timeOutDir = storageDir + "/timeouts" self.statisticsDir = storageDir + "/stats" self.stateFile = storageDir + "/state.bin" self.diagnosticsDir = storageDir + "/diagnostics" + self.optimizedDumpDir = storageDir + "/optimizedDump" + self.unoptimizedDumpDir = storageDir + "/unoptimizedDump" self.statisticsExportInterval = statisticsExportInterval @@ -52,6 +58,7 @@ public class Storage: Module { do { try FileManager.default.createDirectory(atPath: crashesDir, withIntermediateDirectories: true) try FileManager.default.createDirectory(atPath: duplicateCrashesDir, withIntermediateDirectories: true) + try FileManager.default.createDirectory(atPath: differentialsDir, withIntermediateDirectories: true) try FileManager.default.createDirectory(atPath: corpusDir, withIntermediateDirectories: true) try FileManager.default.createDirectory(atPath: statisticsDir, withIntermediateDirectories: true) if fuzzer.config.enableDiagnostics { @@ -59,6 +66,10 @@ public class Storage: Module { try FileManager.default.createDirectory(atPath: timeOutDir, withIntermediateDirectories: true) try FileManager.default.createDirectory(atPath: diagnosticsDir, withIntermediateDirectories: true) } + if fuzzer.isDifferentialFuzzing { + try FileManager.default.createDirectory(atPath: optimizedDumpDir, withIntermediateDirectories: true) + try FileManager.default.createDirectory(atPath: unoptimizedDumpDir, withIntermediateDirectories: true) + } } catch { logger.fatal("Failed to create storage directories. Is \(storageDir) writable by the current user?") } @@ -95,6 +106,11 @@ public class Storage: Module { } } + fuzzer.registerEventListener(for: fuzzer.events.DifferentialFound) { ev in + let filename = "program_\(self.formatDate())_\(ev.program.id)_\(ev.behaviour.rawValue)" + self.storeProgram(ev.program, as: filename, in: self.differentialsDir) + } + fuzzer.registerEventListener(for: fuzzer.events.InterestingProgramFound) { ev in let filename = "program_\(self.formatDate())_\(ev.program.id)" self.storeProgram(ev.program, as: filename, in: self.corpusDir) diff --git a/Sources/Fuzzilli/Mutators/RuntimeAssistedMutator.swift b/Sources/Fuzzilli/Mutators/RuntimeAssistedMutator.swift index a99c33f21..baad473fa 100644 --- a/Sources/Fuzzilli/Mutators/RuntimeAssistedMutator.swift +++ b/Sources/Fuzzilli/Mutators/RuntimeAssistedMutator.swift @@ -114,6 +114,8 @@ public class RuntimeAssistedMutator: Mutator { case .succeeded: // The expected case. break + case .differential: + fatalError("Differential result impossible") } // Process the output to build the mutated program. @@ -386,7 +388,7 @@ extension RuntimeAssistedMutator.Action { case .BitwiseXor: try translateBinaryOperation(.Xor) case .NullCoalesce: - try translateBinaryOperation(.NullCoalesce) + try translateBinaryOperation(.NullCoalesce) case .LeftShift: try translateBinaryOperation(.LShift) case .SignedRightShift: diff --git a/Sources/Fuzzilli/Util/MockFuzzer.swift b/Sources/Fuzzilli/Util/MockFuzzer.swift index 2f9558208..bd524bcf2 100644 --- a/Sources/Fuzzilli/Util/MockFuzzer.swift +++ b/Sources/Fuzzilli/Util/MockFuzzer.swift @@ -129,6 +129,7 @@ public func makeMockFuzzer(config maybeConfiguration: Configuration? = nil, engi // Construct the fuzzer instance. let fuzzer = Fuzzer(configuration: configuration, scriptRunner: runner, + referenceScriptRunner: nil, engine: engine, mutators: mutators, codeGenerators: codeGenerators, diff --git a/Sources/FuzzilliCli/Profiles/DuktapeProfile.swift b/Sources/FuzzilliCli/Profiles/DuktapeProfile.swift index 001ce27a8..bdc685193 100644 --- a/Sources/FuzzilliCli/Profiles/DuktapeProfile.swift +++ b/Sources/FuzzilliCli/Profiles/DuktapeProfile.swift @@ -19,6 +19,8 @@ let duktapeProfile = Profile( ["--reprl"] }, + processArgsReference: nil, + processEnv: ["UBSAN_OPTIONS": "handle_segv=0"], maxExecsBeforeRespawn: 1000, diff --git a/Sources/FuzzilliCli/Profiles/JSCProfile.swift b/Sources/FuzzilliCli/Profiles/JSCProfile.swift index d79b85360..23305fc90 100644 --- a/Sources/FuzzilliCli/Profiles/JSCProfile.swift +++ b/Sources/FuzzilliCli/Profiles/JSCProfile.swift @@ -69,6 +69,8 @@ let jscProfile = Profile( return args }, + processArgsReference: nil, + processEnv: ["UBSAN_OPTIONS":"handle_segv=0"], maxExecsBeforeRespawn: 1000, diff --git a/Sources/FuzzilliCli/Profiles/JerryscriptProfile.swift b/Sources/FuzzilliCli/Profiles/JerryscriptProfile.swift index acc18406d..427a3eea5 100644 --- a/Sources/FuzzilliCli/Profiles/JerryscriptProfile.swift +++ b/Sources/FuzzilliCli/Profiles/JerryscriptProfile.swift @@ -19,6 +19,8 @@ let jerryscriptProfile = Profile( ["--reprl-fuzzilli"] }, + processArgsReference: nil, + processEnv: ["UBSAN_OPTIONS":"handle_segv=0"], maxExecsBeforeRespawn: 1000, diff --git a/Sources/FuzzilliCli/Profiles/NjsProfile.swift b/Sources/FuzzilliCli/Profiles/NjsProfile.swift index 040ef711d..b286196ef 100644 --- a/Sources/FuzzilliCli/Profiles/NjsProfile.swift +++ b/Sources/FuzzilliCli/Profiles/NjsProfile.swift @@ -19,6 +19,8 @@ let njsProfile = Profile( ["fuzz"] }, + processArgsReference: nil, + processEnv: ["UBSAN_OPTIONS": "handle_segv=0"], maxExecsBeforeRespawn: 1000, diff --git a/Sources/FuzzilliCli/Profiles/Profile.swift b/Sources/FuzzilliCli/Profiles/Profile.swift index 0947d9559..8d9d7dcd5 100644 --- a/Sources/FuzzilliCli/Profiles/Profile.swift +++ b/Sources/FuzzilliCli/Profiles/Profile.swift @@ -16,6 +16,8 @@ import Fuzzilli struct Profile { let processArgs: (_ randomize: Bool) -> [String] + // if not nil, then this is profile for differential fuzzing + let processArgsReference: [String]? let processEnv: [String : String] let maxExecsBeforeRespawn: Int // Timeout either by value or interval in milliseconds. @@ -39,6 +41,10 @@ struct Profile { // An optional post-processor that is executed for every sample generated for fuzzing and can modify it. let optionalPostProcessor: FuzzingPostProcessor? + + var isDifferential: Bool { + return processArgsReference != nil + } } let profiles = [ @@ -48,6 +54,7 @@ let profiles = [ "spidermonkey": spidermonkeyProfile, "v8": v8Profile, "v8Sandbox": v8SandboxProfile, + "v8Dumpling": v8DumplingProfile, "duktape": duktapeProfile, "jerryscript": jerryscriptProfile, "xs": xsProfile, diff --git a/Sources/FuzzilliCli/Profiles/QjsProfile.swift b/Sources/FuzzilliCli/Profiles/QjsProfile.swift index aa521f001..dae0ee932 100644 --- a/Sources/FuzzilliCli/Profiles/QjsProfile.swift +++ b/Sources/FuzzilliCli/Profiles/QjsProfile.swift @@ -19,6 +19,8 @@ let qjsProfile = Profile( ["--reprl"] }, + processArgsReference: nil, + processEnv: ["UBSAN_OPTIONS": "handle_segv=0"], maxExecsBeforeRespawn: 1000, diff --git a/Sources/FuzzilliCli/Profiles/QtjsProfile.swift b/Sources/FuzzilliCli/Profiles/QtjsProfile.swift index c536f529b..656a5022c 100644 --- a/Sources/FuzzilliCli/Profiles/QtjsProfile.swift +++ b/Sources/FuzzilliCli/Profiles/QtjsProfile.swift @@ -27,6 +27,8 @@ let qtjsProfile = Profile( ["-reprl"] }, + processArgsReference: nil, + processEnv: ["UBSAN_OPTIONS":"handle_segv=0"], maxExecsBeforeRespawn: 1000, diff --git a/Sources/FuzzilliCli/Profiles/Serenity.swift b/Sources/FuzzilliCli/Profiles/Serenity.swift index 496e833b9..e98f41fd5 100644 --- a/Sources/FuzzilliCli/Profiles/Serenity.swift +++ b/Sources/FuzzilliCli/Profiles/Serenity.swift @@ -16,6 +16,7 @@ import Fuzzilli let serenityProfile = Profile( processArgs: { randomize in return [""] }, + processArgsReference: nil, processEnv: [ "UBSAN_OPTIONS": "handle_segv=0 handle_abrt=0", "ASAN_OPTIONS": "abort_on_error=1", diff --git a/Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift b/Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift index ec131d243..46dc022b8 100644 --- a/Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift +++ b/Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift @@ -70,6 +70,8 @@ let spidermonkeyProfile = Profile( return args }, + processArgsReference: nil, + processEnv: ["UBSAN_OPTIONS": "handle_segv=0"], maxExecsBeforeRespawn: 1000, diff --git a/Sources/FuzzilliCli/Profiles/V8DumplingProfile.swift b/Sources/FuzzilliCli/Profiles/V8DumplingProfile.swift new file mode 100644 index 000000000..0abcb0cf7 --- /dev/null +++ b/Sources/FuzzilliCli/Profiles/V8DumplingProfile.swift @@ -0,0 +1,113 @@ +// Copyright 2026 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 Fuzzilli + +let v8DumplingProfile = Profile( + processArgs: { randomize in + var args = [ + "--expose-gc", + "--expose-externalize-string", + "--omit-quit", + "--allow-natives-syntax", + "--fuzzing", + "--jit-fuzzing", + "--future", + "--harmony", + "--experimental-fuzzing", + "--js-staging", + "--expose-fast-api", + "--predictable", + "--no-sparkplug", + "--maglev-dumping", + "--turbofan-dumping", + "--turbofan-dumping-print-deopt-frames" + ] + + return args + }, + + // TODO(mdanylo): currently we run Fuzzilli in differential fuzzing + // mode if processArgsReference is not nil. We should reconsider + // this decision in the future in favour of something nicer. + processArgsReference: [ + "--sparkplug-dumping", + "--interpreter-dumping", + "--no-maglev", + "--no-turbofan", + "--expose-gc", + "--expose-externalize-string", + "--omit-quit", + "--allow-natives-syntax", + "--fuzzing", + "--jit-fuzzing", + "--future", + "--harmony", + "--experimental-fuzzing", + "--js-staging", + "--expose-fast-api", + "--predictable" + ], + + processEnv: [:], + + maxExecsBeforeRespawn: 1000, + + timeout: Timeout.interval(300, 900), + + codePrefix: """ + """, + + codeSuffix: """ + """, + + ecmaVersion: ECMAScriptVersion.es6, + + startupTests: [ + + ], + + additionalCodeGenerators: [ + (ForceJITCompilationThroughLoopGenerator, 5), + (ForceTurboFanCompilationGenerator, 5), + (ForceMaglevCompilationGenerator, 5), + (TurbofanVerifyTypeGenerator, 10), + + (V8GcGenerator, 10), + ], + + additionalProgramTemplates: WeightedList([ + (MapTransitionFuzzer, 1), + (ValueSerializerFuzzer, 1), + (V8RegExpFuzzer, 1), + (FastApiCallFuzzer, 1), + (LazyDeoptFuzzer, 1), + ]), + + disabledCodeGenerators: [], + + disabledMutators: [], + + additionalBuiltins: [ + "gc" : .function([.opt(gcOptions.instanceType)] => (.undefined | .jsPromise)), + "d8" : .jsD8, + "Worker" : .constructor([.jsAnything, .object()] => .object(withMethods: ["postMessage","getMessage"])), + ], + + additionalObjectGroups: [jsD8, jsD8Test, jsD8FastCAPI, gcOptions], + + additionalEnumerations: [.gcTypeEnum, .gcExecutionEnum], + + optionalPostProcessor: nil +) diff --git a/Sources/FuzzilliCli/Profiles/V8HoleFuzzingProfile.swift b/Sources/FuzzilliCli/Profiles/V8HoleFuzzingProfile.swift index c7c72aa09..218ea99fe 100644 --- a/Sources/FuzzilliCli/Profiles/V8HoleFuzzingProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8HoleFuzzingProfile.swift @@ -36,6 +36,8 @@ let v8HoleFuzzingProfile = Profile( return args }, + processArgsReference: nil, + processEnv: [:], maxExecsBeforeRespawn: 1000, diff --git a/Sources/FuzzilliCli/Profiles/V8Profile.swift b/Sources/FuzzilliCli/Profiles/V8Profile.swift index 3d3e60bf2..9f0c47588 100644 --- a/Sources/FuzzilliCli/Profiles/V8Profile.swift +++ b/Sources/FuzzilliCli/Profiles/V8Profile.swift @@ -19,6 +19,8 @@ let v8Profile = Profile( v8ProcessArgs(randomize: randomize, forSandbox: false) }, + processArgsReference: nil, + // We typically fuzz without any sanitizer instrumentation, but if any sanitizers are active, "abort_on_error=1" must probably be set so that sanitizer errors can be detected. processEnv: [:], diff --git a/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift b/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift index dce89be75..9a171dfd6 100644 --- a/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift +++ b/Sources/FuzzilliCli/Profiles/V8SandboxProfile.swift @@ -100,6 +100,8 @@ let v8SandboxProfile = Profile( v8ProcessArgs(randomize: randomize, forSandbox: true) }, + processArgsReference: nil, + // ASan options. // - abort_on_error=true: We need asan to exit in a way that's detectable for Fuzzilli as a crash // - handle_sigill=true: It seems by default ASAN doesn't handle SIGILL, but we want that to have stack traces diff --git a/Sources/FuzzilliCli/Profiles/XSProfile.swift b/Sources/FuzzilliCli/Profiles/XSProfile.swift index 1cb64c0f1..3de2abb17 100644 --- a/Sources/FuzzilliCli/Profiles/XSProfile.swift +++ b/Sources/FuzzilliCli/Profiles/XSProfile.swift @@ -290,6 +290,8 @@ let xsProfile = Profile( ["-f"] }, + processArgsReference: nil, + processEnv: ["UBSAN_OPTIONS":"handle_segv=0:symbolize=1:print_stacktrace=1:silence_unsigned_overflow=1", "ASAN_OPTIONS": "handle_segv=0:abort_on_error=1:symbolize=1", "MSAN_OPTIONS": "handle_segv=0:abort_on_error=1:symbolize=1", diff --git a/Sources/FuzzilliCli/main.swift b/Sources/FuzzilliCli/main.swift index 08be33264..f49dde3b6 100644 --- a/Sources/FuzzilliCli/main.swift +++ b/Sources/FuzzilliCli/main.swift @@ -328,6 +328,10 @@ if args.unusedOptionals.count > 0 { configError("Invalid arguments: \(args.unusedOptionals)") } +if profile.isDifferential && storagePath == nil { + configError("Differential fuzzing mode requires storage path") +} + // Initialize the logger such that we can print to the screen. let logger = Logger(withLabel: "Cli") @@ -408,8 +412,26 @@ let jsShellArguments = profile.processArgs(argumentRandomization) + additionalAr logger.info("Using the following arguments for the target engine: \(jsShellArguments)") func makeFuzzer(with configuration: Configuration) -> Fuzzer { + let createRunner = { (baseArgs: [String], forReferenceRunner: Bool) -> REPRL in + let finalArgs = baseArgs + configuration.getInstanceSpecificArguments(forReferenceRunner: forReferenceRunner) + return REPRL( + executable: jsShellPath, + processArguments: finalArgs, + processEnvironment: profile.processEnv, + maxExecsBeforeRespawn: profile.maxExecsBeforeRespawn + ) + } + // A script runner to execute JavaScript code in an instrumented JS engine. - let runner = REPRL(executable: jsShellPath, processArguments: jsShellArguments, processEnvironment: profile.processEnv, maxExecsBeforeRespawn: profile.maxExecsBeforeRespawn) + let runner = createRunner(jsShellArguments, false) + + // A script runner used to verify that the samples are indeed differential samples. + let referenceRunner: REPRL? = { + guard profile.isDifferential, let refArgs = profile.processArgsReference else { + return nil + } + return createRunner(refArgs, true) + }() /// The mutation fuzzer responsible for mutating programs from the corpus and evaluating the outcome. let disabledMutators = Set(profile.disabledMutators) @@ -525,6 +547,7 @@ func makeFuzzer(with configuration: Configuration) -> Fuzzer { // Construct the fuzzer instance. return Fuzzer(configuration: configuration, scriptRunner: runner, + referenceScriptRunner: referenceRunner, engine: engine, mutators: mutators, codeGenerators: codeGenerators, @@ -548,7 +571,9 @@ let mainConfig = Configuration(arguments: CommandLine.arguments, tag: tag, isWasmEnabled: enableWasm, storagePath: storagePath, - forDifferentialFuzzing: forDifferentialFuzzing) + forDifferentialFuzzing: forDifferentialFuzzing, + instanceId: 0, + dumplingEnabled: profile.isDifferential) let fuzzer = makeFuzzer(with: mainConfig) @@ -688,9 +713,10 @@ fuzzer.sync { fuzzer.start(runUntil: exitCondition) } -// Add thread worker instances if requested -// Worker instances use a slightly different configuration, mostly just a lower log level. -let workerConfig = Configuration(arguments: CommandLine.arguments, +for i in 1.. Date: Tue, 13 Jan 2026 12:21:01 +0100 Subject: [PATCH 75/89] [wasm] Prepare WasmThrow to use wasm-gc types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By removing the types from the WasmThrow, this prepares Fuzzilli to adapt the tags as a next step to use wasm-gc signatures instead of static parameter types (there might be more dependencies for that). Bug: 445356784 Change-Id: I852a84efd928ed593bbb84105a95ab7a09cde9a7 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8926696 Commit-Queue: Matthias Liedtke Reviewed-by: Doga Yüksel --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 2 +- Sources/Fuzzilli/FuzzIL/Instruction.swift | 10 ++++------ Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 6 ++---- Sources/Fuzzilli/Protobuf/operations.pb.swift | 19 +++---------------- Sources/Fuzzilli/Protobuf/operations.proto | 1 - 5 files changed, 10 insertions(+), 28 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 0322618a1..28aa9d0c9 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -4115,7 +4115,7 @@ public class ProgramBuilder { public func WasmBuildThrow(tag: Variable, inputs: [Variable]) { let tagType = b.type(of: tag).wasmType as! WasmTagType - b.emit(WasmThrow(parameterTypes: tagType.parameters), withInputs: [tag] + inputs, types: [.object(ofGroup: "WasmTag")] + tagType.parameters) + b.emit(WasmThrow(parameterCount: tagType.parameters.count), withInputs: [tag] + inputs, types: [.object(ofGroup: "WasmTag")] + tagType.parameters) } public func wasmBuildThrowRef(exception: Variable) { diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index bb3d4b0f6..2b3fcb2eb 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -1437,10 +1437,8 @@ extension Instruction: ProtobufConvertible { $0.wasmEndTryDelegate = Fuzzilli_Protobuf_WasmEndTryDelegate.with { $0.outputTypes = op.outputTypes.map(ILTypeToWasmTypeEnum) } - case .wasmThrow(let op): - $0.wasmThrow = Fuzzilli_Protobuf_WasmThrow.with { - $0.parameterTypes = op.parameterTypes.map(ILTypeToWasmTypeEnum) - } + case .wasmThrow(_): + $0.wasmThrow = Fuzzilli_Protobuf_WasmThrow() case .wasmThrowRef(_): $0.wasmThrowRef = Fuzzilli_Protobuf_WasmThrowRef() case .wasmRethrow(_): @@ -2488,8 +2486,8 @@ extension Instruction: ProtobufConvertible { op = WasmBeginTryDelegate(with: parameters => outputs) case .wasmEndTryDelegate(let p): op = WasmEndTryDelegate(outputTypes: p.outputTypes.map(WasmTypeEnumToILType)) - case .wasmThrow(let p): - op = WasmThrow(parameterTypes: p.parameterTypes.map(WasmTypeEnumToILType)) + case .wasmThrow(_): + op = WasmThrow(parameterCount: inouts.count - 1) case .wasmThrowRef(_): op = WasmThrowRef() case .wasmRethrow(_): diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index dfe1fd6f1..469ea073a 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -1467,12 +1467,10 @@ final class WasmEndTryDelegate: WasmOperation { final class WasmThrow: WasmOperation { override var opcode: Opcode { .wasmThrow(self) } - public let parameterTypes: [ILType] - init(parameterTypes: [ILType]) { - self.parameterTypes = parameterTypes + init(parameterCount: Int) { // Inputs: the tag to be thrown plus the arguments for each parameter type of the tag. - super.init(numInputs: 1 + parameterTypes.count, attributes: [.isJump], requiredContext: [.wasmFunction]) + super.init(numInputs: 1 + parameterCount, attributes: [.isJump], requiredContext: [.wasmFunction]) } } diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index a6011ecc8..64b1fbb19 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -5301,8 +5301,6 @@ public struct Fuzzilli_Protobuf_WasmThrow: Sendable { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var parameterTypes: [Fuzzilli_Protobuf_WasmILType] = [] - public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} @@ -14054,29 +14052,18 @@ extension Fuzzilli_Protobuf_WasmEndTryDelegate: SwiftProtobuf.Message, SwiftProt extension Fuzzilli_Protobuf_WasmThrow: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmThrow" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameterTypes\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap() 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) }() - default: break - } - } + // Load everything into unknown fields + while try decoder.nextFieldNumber() != nil {} } public func traverse(visitor: inout V) throws { - if !self.parameterTypes.isEmpty { - try visitor.visitRepeatedMessageField(value: self.parameterTypes, fieldNumber: 1) - } try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Fuzzilli_Protobuf_WasmThrow, rhs: Fuzzilli_Protobuf_WasmThrow) -> Bool { - if lhs.parameterTypes != rhs.parameterTypes {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 f761715e1..8398460e6 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -1298,7 +1298,6 @@ message WasmEndTryDelegate { } message WasmThrow { - repeated WasmILType parameterTypes = 1; } message WasmThrowRef { From 66f0b84c97e654dc4626e62fcd215efe670e498e Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Tue, 13 Jan 2026 12:33:12 +0100 Subject: [PATCH 76/89] [wasm] Remove parameterTypes from WasmBranch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: 445356784 Change-Id: I960d64621c3faac93083b44935382a05dee93d84 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8926697 Commit-Queue: Matthias Liedtke Reviewed-by: Doga Yüksel --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 2 +- Sources/Fuzzilli/FuzzIL/Instruction.swift | 10 ++++------ Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 9 ++++----- Sources/Fuzzilli/Lifting/WasmLifter.swift | 2 +- Sources/Fuzzilli/Protobuf/operations.pb.swift | 19 +++---------------- Sources/Fuzzilli/Protobuf/operations.proto | 1 - 6 files changed, 13 insertions(+), 30 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 28aa9d0c9..5d64d2872 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -3954,7 +3954,7 @@ public class ProgramBuilder { public func wasmBranch(to label: Variable, args: [Variable] = []) { let labelType = b.type(of: label) checkArgumentsMatchLabelType(label: labelType, args: args) - b.emit(WasmBranch(labelTypes: labelType.wasmLabelType!.parameters), withInputs: [label] + args) + b.emit(WasmBranch(parameterCount: labelType.wasmLabelType!.parameters.count), withInputs: [label] + args) } public func wasmBranchIf(_ condition: Variable, to label: Variable, args: [Variable] = [], hint: WasmBranchHint = .None) { diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index 2b3fcb2eb..aca5175b8 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -1447,10 +1447,8 @@ extension Instruction: ProtobufConvertible { $0.wasmDefineTag = Fuzzilli_Protobuf_WasmDefineTag.with { $0.parameterTypes = op.parameterTypes.map(ILTypeToWasmTypeEnum) } - case .wasmBranch(let op): - $0.wasmBranch = Fuzzilli_Protobuf_WasmBranch.with { - $0.parameters = op.labelTypes.map(ILTypeToWasmTypeEnum) - } + case .wasmBranch(_): + $0.wasmBranch = Fuzzilli_Protobuf_WasmBranch() case .wasmBranchIf(let op): $0.wasmBranchIf = Fuzzilli_Protobuf_WasmBranchIf.with { $0.parameters = op.labelTypes.map(ILTypeToWasmTypeEnum) @@ -2494,8 +2492,8 @@ extension Instruction: ProtobufConvertible { op = WasmRethrow() case .wasmDefineTag(let p): op = WasmDefineTag(parameterTypes: p.parameterTypes.map(WasmTypeEnumToILType)) - case .wasmBranch(let p): - op = WasmBranch(labelTypes: p.parameters.map(WasmTypeEnumToILType)) + case .wasmBranch(_): + op = WasmBranch(parameterCount: inouts.count - 1) case .wasmBranchIf(let p): op = WasmBranchIf(labelTypes: p.parameters.map(WasmTypeEnumToILType), hint: try convertEnum(p.hint, WasmBranchHint.allCases)) case .wasmBranchTable(let p): diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index 469ea073a..7ce96b954 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -1494,13 +1494,12 @@ final class WasmRethrow: WasmOperation { final class WasmBranch: WasmOperation { override var opcode: Opcode { .wasmBranch(self) } - let labelTypes: [ILType] - - init(labelTypes: [ILType]) { - self.labelTypes = labelTypes - super.init(numInputs: 1 + labelTypes.count, requiredContext: [.wasmFunction]) + init(parameterCount: Int) { + super.init(numInputs: 1 + parameterCount, requiredContext: [.wasmFunction]) } + + var parameterCount: Int {numInputs - 1} } final class WasmBranchIf: WasmOperation { diff --git a/Sources/Fuzzilli/Lifting/WasmLifter.swift b/Sources/Fuzzilli/Lifting/WasmLifter.swift index d3cbe2635..8c81feaa6 100644 --- a/Sources/Fuzzilli/Lifting/WasmLifter.swift +++ b/Sources/Fuzzilli/Lifting/WasmLifter.swift @@ -1957,7 +1957,7 @@ public class WasmLifter { return Data([0x09] + Leb128.unsignedEncode(blockDepth)) case .wasmBranch(let op): let branchDepth = try branchDepthFor(label: wasmInstruction.input(0)) - return Data([0x0C]) + Leb128.unsignedEncode(branchDepth) + Data(op.labelTypes.map {_ in 0x1A}) + return Data([0x0C]) + Leb128.unsignedEncode(branchDepth) + Array(repeating: 0x1a, count: op.parameterCount) case .wasmBranchIf(let op): currentFunction!.addBranchHint(op.hint) let branchDepth = try branchDepthFor(label: wasmInstruction.input(0)) diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index 64b1fbb19..4f3d7604b 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -5343,8 +5343,6 @@ public struct Fuzzilli_Protobuf_WasmBranch: Sendable { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var parameters: [Fuzzilli_Protobuf_WasmILType] = [] - public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} @@ -14139,29 +14137,18 @@ extension Fuzzilli_Protobuf_WasmDefineTag: SwiftProtobuf.Message, SwiftProtobuf. extension Fuzzilli_Protobuf_WasmBranch: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmBranch" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameters\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap() 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.parameters) }() - default: break - } - } + // Load everything into unknown fields + while try decoder.nextFieldNumber() != nil {} } public func traverse(visitor: inout V) throws { - if !self.parameters.isEmpty { - try visitor.visitRepeatedMessageField(value: self.parameters, fieldNumber: 1) - } try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Fuzzilli_Protobuf_WasmBranch, rhs: Fuzzilli_Protobuf_WasmBranch) -> Bool { - if lhs.parameters != rhs.parameters {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 8398460e6..19154bdbc 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -1311,7 +1311,6 @@ message WasmDefineTag { } message WasmBranch { - repeated WasmILType parameters = 1; } enum WasmBranchHint { From 9cc25ac6452c452c0ceafe641bf393a653c739f6 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Tue, 13 Jan 2026 12:42:09 +0100 Subject: [PATCH 77/89] [wasm] Remove paramterTypes from WasmBranchIf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: 445356784 Change-Id: I5d827c480f633e4efe565ac139f91c4fb5e04e79 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8926698 Reviewed-by: Doga Yüksel Commit-Queue: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 4 +++- Sources/Fuzzilli/FuzzIL/Instruction.swift | 3 +-- Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 8 ++++---- Sources/Fuzzilli/Lifting/WasmLifter.swift | 2 +- Sources/Fuzzilli/Mutators/OperationMutator.swift | 2 +- Sources/Fuzzilli/Protobuf/operations.pb.swift | 9 +-------- Sources/Fuzzilli/Protobuf/operations.proto | 1 - 7 files changed, 11 insertions(+), 18 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 5d64d2872..3a535ae1e 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -3961,7 +3961,9 @@ public class ProgramBuilder { let labelType = b.type(of: label) checkArgumentsMatchLabelType(label: labelType, args: args) assert(b.type(of: condition).Is(.wasmi32)) - b.emit(WasmBranchIf(labelTypes: labelType.wasmLabelType!.parameters, hint: hint), withInputs: [label] + args + [condition]) + b.emit( + WasmBranchIf(parameterCount: labelType.wasmLabelType!.parameters.count, hint: hint), + withInputs: [label] + args + [condition]) } public func wasmBranchTable(on: Variable, labels: [Variable], args: [Variable]) { diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index aca5175b8..fde29d97b 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -1451,7 +1451,6 @@ extension Instruction: ProtobufConvertible { $0.wasmBranch = Fuzzilli_Protobuf_WasmBranch() case .wasmBranchIf(let op): $0.wasmBranchIf = Fuzzilli_Protobuf_WasmBranchIf.with { - $0.parameters = op.labelTypes.map(ILTypeToWasmTypeEnum) $0.hint = convertEnum(op.hint, WasmBranchHint.allCases) } case .wasmBranchTable(let op): @@ -2495,7 +2494,7 @@ extension Instruction: ProtobufConvertible { case .wasmBranch(_): op = WasmBranch(parameterCount: inouts.count - 1) case .wasmBranchIf(let p): - op = WasmBranchIf(labelTypes: p.parameters.map(WasmTypeEnumToILType), hint: try convertEnum(p.hint, WasmBranchHint.allCases)) + op = WasmBranchIf(parameterCount: inouts.count - 2, hint: try convertEnum(p.hint, WasmBranchHint.allCases)) case .wasmBranchTable(let p): op = WasmBranchTable(labelTypes: p.parameters.map(WasmTypeEnumToILType), valueCount: Int(p.valueCount)) case .wasmBeginIf(let p): diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index 7ce96b954..525af229d 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -1504,15 +1504,15 @@ final class WasmBranch: WasmOperation { final class WasmBranchIf: WasmOperation { override var opcode: Opcode { .wasmBranchIf(self) } - let labelTypes: [ILType] let hint: WasmBranchHint - init(labelTypes: [ILType], hint: WasmBranchHint) { - self.labelTypes = labelTypes + init(parameterCount: Int, hint: WasmBranchHint) { self.hint = hint // The inputs are the label, the arguments and the condition. - super.init(numInputs: 1 + labelTypes.count + 1, attributes: [.isMutable], requiredContext: [.wasmFunction]) + super.init(numInputs: 1 + parameterCount + 1, attributes: [.isMutable], requiredContext: [.wasmFunction]) } + + var parameterCount: Int {numInputs - 2} } final class WasmBranchTable: WasmOperation { diff --git a/Sources/Fuzzilli/Lifting/WasmLifter.swift b/Sources/Fuzzilli/Lifting/WasmLifter.swift index 8c81feaa6..6012ccab0 100644 --- a/Sources/Fuzzilli/Lifting/WasmLifter.swift +++ b/Sources/Fuzzilli/Lifting/WasmLifter.swift @@ -1961,7 +1961,7 @@ public class WasmLifter { case .wasmBranchIf(let op): currentFunction!.addBranchHint(op.hint) let branchDepth = try branchDepthFor(label: wasmInstruction.input(0)) - return Data([0x0D]) + Leb128.unsignedEncode(branchDepth) + Data(op.labelTypes.map {_ in 0x1A}) + return Data([0x0D]) + Leb128.unsignedEncode(branchDepth) + Array(repeating: 0x1a, count: op.parameterCount) case .wasmBranchTable(let op): let depths = try (0...op.valueCount).map { try branchDepthFor(label: wasmInstruction.input($0)) diff --git a/Sources/Fuzzilli/Mutators/OperationMutator.swift b/Sources/Fuzzilli/Mutators/OperationMutator.swift index cefbf9d92..4ebe42be3 100644 --- a/Sources/Fuzzilli/Mutators/OperationMutator.swift +++ b/Sources/Fuzzilli/Mutators/OperationMutator.swift @@ -497,7 +497,7 @@ public class OperationMutator: BaseInstructionMutator { : Int64.random(in: Int64.min...Int64.max) // most likely out of bounds newOp = WasmSimdLoad(kind: kind, staticOffset: staticOffset) case .wasmBranchIf(let op): - newOp = WasmBranchIf(labelTypes: op.labelTypes, hint: chooseUniform(from: WasmBranchHint.allCases)) + newOp = WasmBranchIf(parameterCount: op.parameterCount, hint: chooseUniform(from: WasmBranchHint.allCases)) case .wasmBeginIf(let op): newOp = WasmBeginIf(parameterCount: op.parameterCount, hint: chooseUniform(from: WasmBranchHint.allCases), inverted: Bool.random()) case .wasmArrayGet(let op): diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index 4f3d7604b..92b565f79 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -5353,8 +5353,6 @@ public struct Fuzzilli_Protobuf_WasmBranchIf: Sendable { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var parameters: [Fuzzilli_Protobuf_WasmILType] = [] - public var hint: Fuzzilli_Protobuf_WasmBranchHint = .branchhintNone public var unknownFields = SwiftProtobuf.UnknownStorage() @@ -14156,7 +14154,7 @@ extension Fuzzilli_Protobuf_WasmBranch: SwiftProtobuf.Message, SwiftProtobuf._Me extension Fuzzilli_Protobuf_WasmBranchIf: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmBranchIf" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameters\0\u{1}hint\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\u{2}hint\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -14164,7 +14162,6 @@ extension Fuzzilli_Protobuf_WasmBranchIf: SwiftProtobuf.Message, SwiftProtobuf._ // 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.parameters) }() case 2: try { try decoder.decodeSingularEnumField(value: &self.hint) }() default: break } @@ -14172,9 +14169,6 @@ extension Fuzzilli_Protobuf_WasmBranchIf: SwiftProtobuf.Message, SwiftProtobuf._ } public func traverse(visitor: inout V) throws { - if !self.parameters.isEmpty { - try visitor.visitRepeatedMessageField(value: self.parameters, fieldNumber: 1) - } if self.hint != .branchhintNone { try visitor.visitSingularEnumField(value: self.hint, fieldNumber: 2) } @@ -14182,7 +14176,6 @@ extension Fuzzilli_Protobuf_WasmBranchIf: SwiftProtobuf.Message, SwiftProtobuf._ } public static func ==(lhs: Fuzzilli_Protobuf_WasmBranchIf, rhs: Fuzzilli_Protobuf_WasmBranchIf) -> Bool { - if lhs.parameters != rhs.parameters {return false} if lhs.hint != rhs.hint {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 19154bdbc..52397695e 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -1320,7 +1320,6 @@ enum WasmBranchHint { } message WasmBranchIf { - repeated WasmILType parameters = 1; WasmBranchHint hint = 2; } From d53f176a3d5ef1c8bba3a2281fae0d60a7ffb020 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Tue, 13 Jan 2026 13:42:42 +0100 Subject: [PATCH 78/89] [wasm] Remove parameter types from wasmBranchTable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: 445356784 Change-Id: Ia1a6b4606ba85e5c6f0093cc8c43cc4726a7b907 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8926699 Reviewed-by: Doga Yüksel Commit-Queue: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 7 ++----- Sources/Fuzzilli/FuzzIL/Instruction.swift | 3 +-- Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 9 +++++---- Sources/Fuzzilli/Protobuf/operations.pb.swift | 9 +-------- Sources/Fuzzilli/Protobuf/operations.proto | 1 - 5 files changed, 9 insertions(+), 20 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 3a535ae1e..94a0a0039 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -3967,11 +3967,8 @@ public class ProgramBuilder { } public func wasmBranchTable(on: Variable, labels: [Variable], args: [Variable]) { - let argumentTypes = args.map({b.type(of: $0)}) - labels.forEach { - checkArgumentsMatchLabelType(label: b.type(of: $0), args: args) - } - b.emit(WasmBranchTable(labelTypes: argumentTypes, valueCount: labels.count - 1), + labels.forEach { checkArgumentsMatchLabelType(label: b.type(of: $0), args: args) } + b.emit(WasmBranchTable(parameterCount: args.count, valueCount: labels.count - 1), withInputs: labels + args + [on]) } diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index fde29d97b..22b3c7c97 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -1455,7 +1455,6 @@ extension Instruction: ProtobufConvertible { } case .wasmBranchTable(let op): $0.wasmBranchTable = Fuzzilli_Protobuf_WasmBranchTable.with { - $0.parameters = op.labelTypes.map(ILTypeToWasmTypeEnum) $0.valueCount = UInt32(op.valueCount) } case .wasmBeginIf(let op): @@ -2496,7 +2495,7 @@ extension Instruction: ProtobufConvertible { case .wasmBranchIf(let p): op = WasmBranchIf(parameterCount: inouts.count - 2, hint: try convertEnum(p.hint, WasmBranchHint.allCases)) case .wasmBranchTable(let p): - op = WasmBranchTable(labelTypes: p.parameters.map(WasmTypeEnumToILType), valueCount: Int(p.valueCount)) + op = WasmBranchTable(parameterCount: inouts.count - Int(p.valueCount) - 2, valueCount: Int(p.valueCount)) case .wasmBeginIf(let p): op = WasmBeginIf(parameterCount: Int(p.parameterCount), hint: try convertEnum(p.hint, WasmBranchHint.allCases), inverted: p.inverted) case .wasmBeginElse(let p): diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index 525af229d..d3112ac60 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -1517,16 +1517,17 @@ final class WasmBranchIf: WasmOperation { final class WasmBranchTable: WasmOperation { override var opcode: Opcode { .wasmBranchTable(self) } - let labelTypes: [ILType] // The number of cases in the br_table. Note that the number of labels is one higher as each // br_table has a default label. let valueCount: Int - init(labelTypes: [ILType], valueCount: Int) { - self.labelTypes = labelTypes + init(parameterCount: Int, valueCount: Int) { self.valueCount = valueCount - super.init(numInputs: valueCount + 1 + labelTypes.count + 1, requiredContext: [.wasmFunction]) + // Inputs: the case labels, the default label, the arguments and the condition. + super.init(numInputs: valueCount + 1 + parameterCount + 1, requiredContext: [.wasmFunction]) } + + var parameterCount: Int {numInputs - valueCount - 2} } // TODO: make this comprehensive, currently only works for locals, or assumes every thing it reassigns to is a local. diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index 92b565f79..ffbb23a80 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -5365,8 +5365,6 @@ public struct Fuzzilli_Protobuf_WasmBranchTable: Sendable { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var parameters: [Fuzzilli_Protobuf_WasmILType] = [] - public var valueCount: UInt32 = 0 public var unknownFields = SwiftProtobuf.UnknownStorage() @@ -14184,7 +14182,7 @@ extension Fuzzilli_Protobuf_WasmBranchIf: SwiftProtobuf.Message, SwiftProtobuf._ extension Fuzzilli_Protobuf_WasmBranchTable: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmBranchTable" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameters\0\u{1}valueCount\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\u{2}valueCount\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -14192,7 +14190,6 @@ extension Fuzzilli_Protobuf_WasmBranchTable: SwiftProtobuf.Message, SwiftProtobu // 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.parameters) }() case 2: try { try decoder.decodeSingularUInt32Field(value: &self.valueCount) }() default: break } @@ -14200,9 +14197,6 @@ extension Fuzzilli_Protobuf_WasmBranchTable: SwiftProtobuf.Message, SwiftProtobu } public func traverse(visitor: inout V) throws { - if !self.parameters.isEmpty { - try visitor.visitRepeatedMessageField(value: self.parameters, fieldNumber: 1) - } if self.valueCount != 0 { try visitor.visitSingularUInt32Field(value: self.valueCount, fieldNumber: 2) } @@ -14210,7 +14204,6 @@ extension Fuzzilli_Protobuf_WasmBranchTable: SwiftProtobuf.Message, SwiftProtobu } public static func ==(lhs: Fuzzilli_Protobuf_WasmBranchTable, rhs: Fuzzilli_Protobuf_WasmBranchTable) -> Bool { - if lhs.parameters != rhs.parameters {return false} if lhs.valueCount != rhs.valueCount {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 52397695e..83d5f156b 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -1324,7 +1324,6 @@ message WasmBranchIf { } message WasmBranchTable { - repeated WasmILType parameters = 1; uint32 valueCount = 2; } From 39b3f84825a71a9e7f90c6dea1652daba7d2bc00 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Tue, 13 Jan 2026 14:21:12 +0100 Subject: [PATCH 79/89] [wasm] Remove parameter types from wasmReassign MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: 445356784 Change-Id: If6049b20eb2a77ce27c04412f571af7626b4216b Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8926700 Reviewed-by: Doga Yüksel Commit-Queue: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 4 +-- Sources/Fuzzilli/FuzzIL/Instruction.swift | 8 ++--- Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 5 +-- Sources/Fuzzilli/Lifting/WasmLifter.swift | 2 +- Sources/Fuzzilli/Protobuf/operations.pb.swift | 32 ++----------------- Sources/Fuzzilli/Protobuf/operations.proto | 1 - 6 files changed, 11 insertions(+), 41 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index 94a0a0039..f9d300212 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -3906,8 +3906,8 @@ public class ProgramBuilder { } public func wasmReassign(variable: Variable, to: Variable) { - assert(b.type(of: variable) == b.type(of: to)) - b.emit(WasmReassign(variableType: b.type(of: variable)), withInputs: [variable, to]) + assert(b.type(of: to).Is(b.type(of: variable))) + b.emit(WasmReassign(), withInputs: [variable, to]) } public enum wasmBlockType { diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index 22b3c7c97..cbd060da5 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -1234,8 +1234,8 @@ extension Instruction: ProtobufConvertible { case .wasmTruncateSatf64Toi64(let op): $0.wasmTruncateSatf64Toi64 = Fuzzilli_Protobuf_WasmTruncateSatf64Toi64.with { $0.isSigned = op.isSigned } - case .wasmReassign(let op): - $0.wasmReassign = Fuzzilli_Protobuf_WasmReassign.with { $0.variableType = ILTypeToWasmTypeEnum(op.variableType) } + case .wasmReassign(_): + $0.wasmReassign = Fuzzilli_Protobuf_WasmReassign() case .wasmDefineGlobal(let op): $0.wasmDefineGlobal = Fuzzilli_Protobuf_WasmDefineGlobal.with { $0.wasmGlobal.isMutable = op.isMutable @@ -2369,8 +2369,8 @@ extension Instruction: ProtobufConvertible { case .wasmTruncateSatf64Toi64(let p): op = WasmTruncateSatf64Toi64(isSigned: p.isSigned) - case .wasmReassign(let p): - op = WasmReassign(variableType: WasmTypeEnumToILType(p.variableType)) + case .wasmReassign(_): + op = WasmReassign() case .wasmDefineGlobal(let p): op = WasmDefineGlobal(wasmGlobal: convertWasmGlobal(p.wasmGlobal), isMutable: p.wasmGlobal.isMutable) case .wasmDefineTable(let p): diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index d3112ac60..94fd8e67e 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -1535,10 +1535,7 @@ final class WasmBranchTable: WasmOperation { final class WasmReassign: WasmOperation { override var opcode: Opcode { .wasmReassign(self) } - let variableType: ILType - - init(variableType: ILType) { - self.variableType = variableType + init() { super.init(numInputs: 2, attributes: [.isNotInputMutable], requiredContext: [.wasmFunction]) } } diff --git a/Sources/Fuzzilli/Lifting/WasmLifter.swift b/Sources/Fuzzilli/Lifting/WasmLifter.swift index 6012ccab0..b5b32b646 100644 --- a/Sources/Fuzzilli/Lifting/WasmLifter.swift +++ b/Sources/Fuzzilli/Lifting/WasmLifter.swift @@ -1980,7 +1980,7 @@ public class WasmLifter { // wasmReassign is quite special, it needs to work for variables stored in various places, e.g. local slots or even globals. As such the lifting here first needs to locate the destination variable. var out = Data() - var storeInstruction = Data() + let storeInstruction: Data // If the variable is a local, we load the stack slot. // Check for the stack location of the `to` variable. if let stackSlot = currentFunction!.getStackSlot(for: wasmInstruction.input(0)) { diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index ffbb23a80..c6d2e9321 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -5377,20 +5377,9 @@ public struct Fuzzilli_Protobuf_WasmReassign: Sendable { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var variableType: Fuzzilli_Protobuf_WasmILType { - get {return _variableType ?? Fuzzilli_Protobuf_WasmILType()} - set {_variableType = newValue} - } - /// Returns true if `variableType` has been explicitly set. - public var hasVariableType: Bool {return self._variableType != nil} - /// Clears the value of `variableType`. Subsequent reads from it will return its default value. - public mutating func clearVariableType() {self._variableType = nil} - public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} - - fileprivate var _variableType: Fuzzilli_Protobuf_WasmILType? = nil } public struct Fuzzilli_Protobuf_WasmBeginIf: Sendable { @@ -14212,33 +14201,18 @@ extension Fuzzilli_Protobuf_WasmBranchTable: SwiftProtobuf.Message, SwiftProtobu extension Fuzzilli_Protobuf_WasmReassign: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmReassign" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}variableType\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap() 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._variableType) }() - default: break - } - } + // Load everything into unknown fields + while try decoder.nextFieldNumber() != nil {} } 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._variableType { - try visitor.visitSingularMessageField(value: v, fieldNumber: 1) - } }() try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Fuzzilli_Protobuf_WasmReassign, rhs: Fuzzilli_Protobuf_WasmReassign) -> Bool { - if lhs._variableType != rhs._variableType {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 83d5f156b..d7e6b28c6 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -1328,7 +1328,6 @@ message WasmBranchTable { } message WasmReassign { - WasmILType variableType = 1; } message WasmBeginIf { From d9c265b6f8176a08d1a51f18f01deda1af3d510d Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Tue, 13 Jan 2026 15:05:44 +0100 Subject: [PATCH 80/89] [wasm] Remove return types from wasmReturn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: 445356784 Change-Id: Ia9ced154e6f1ce465c257e0e17c53782ec13f442 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8926836 Reviewed-by: Doga Yüksel Commit-Queue: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 7 +++---- Sources/Fuzzilli/FuzzIL/Instruction.swift | 10 ++++------ Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 6 ++---- Sources/Fuzzilli/Protobuf/operations.pb.swift | 19 +++---------------- Sources/Fuzzilli/Protobuf/operations.proto | 1 - 5 files changed, 12 insertions(+), 31 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index f9d300212..c21a0d402 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -4203,17 +4203,16 @@ public class ProgramBuilder { } public func wasmReturn(_ values: [Variable]) { - b.emit(WasmReturn(returnTypes: values.map(b.type)), withInputs: values, types: signature.outputTypes) + b.emit(WasmReturn(returnCount: values.count), withInputs: values, types: signature.outputTypes) } public func wasmReturn(_ returnVariable: Variable) { - let returnType = b.type(of: returnVariable) - b.emit(WasmReturn(returnTypes: [returnType]), withInputs: [returnVariable], types: signature.outputTypes) + b.emit(WasmReturn(returnCount: 1), withInputs: [returnVariable], types: signature.outputTypes) } public func wasmReturn() { assert(signature.outputTypes.isEmpty) - b.emit(WasmReturn(returnTypes: []), withInputs: []) + b.emit(WasmReturn(returnCount: 0), withInputs: []) } @discardableResult diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index cbd060da5..ccc76a04c 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -1127,10 +1127,8 @@ extension Instruction: ProtobufConvertible { $0.constf64 = Fuzzilli_Protobuf_Constf64.with { $0.value = op.value } case .constf32(let op): $0.constf32 = Fuzzilli_Protobuf_Constf32.with { $0.value = op.value } - case .wasmReturn(let op): - $0.wasmReturn = Fuzzilli_Protobuf_WasmReturn.with { - $0.returnTypes = op.returnTypes.map(ILTypeToWasmTypeEnum) - } + case .wasmReturn(_): + $0.wasmReturn = Fuzzilli_Protobuf_WasmReturn() case .wasmJsCall(let op): $0.wasmJsCall = Fuzzilli_Protobuf_WasmJsCall.with { $0.parameterTypes = op.functionSignature.parameterTypes.map(ILTypeToWasmTypeEnum) @@ -2279,8 +2277,8 @@ extension Instruction: ProtobufConvertible { op = Constf32(value: p.value) case .constf64(let p): op = Constf64(value: Float64(p.value)) - case .wasmReturn(let p): - op = WasmReturn(returnTypes: p.returnTypes.map(WasmTypeEnumToILType)) + case .wasmReturn(_): + op = WasmReturn(returnCount: inouts.count) case .wasmJsCall(let p): let parameters = p.parameterTypes.map(WasmTypeEnumToILType) let outputs = p.outputTypes.map(WasmTypeEnumToILType) diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index 94fd8e67e..5db0e28c3 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -74,11 +74,9 @@ final class Constf64: WasmOperation { final class WasmReturn: WasmOperation { override var opcode: Opcode { .wasmReturn(self) } - let returnTypes: [ILType] - init(returnTypes: [ILType]) { - self.returnTypes = returnTypes - super.init(numInputs: returnTypes.count, attributes: [.isJump], requiredContext: [.wasmFunction]) + init(returnCount: Int) { + super.init(numInputs: returnCount, attributes: [.isJump], requiredContext: [.wasmFunction]) } } diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index c6d2e9321..38f5660b6 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -4122,8 +4122,6 @@ public struct Fuzzilli_Protobuf_WasmReturn: Sendable { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var returnTypes: [Fuzzilli_Protobuf_WasmILType] = [] - public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} @@ -11529,29 +11527,18 @@ extension Fuzzilli_Protobuf_Constf64: SwiftProtobuf.Message, SwiftProtobuf._Mess extension Fuzzilli_Protobuf_WasmReturn: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmReturn" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}returnTypes\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap() 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.returnTypes) }() - default: break - } - } + // Load everything into unknown fields + while try decoder.nextFieldNumber() != nil {} } public func traverse(visitor: inout V) throws { - if !self.returnTypes.isEmpty { - try visitor.visitRepeatedMessageField(value: self.returnTypes, fieldNumber: 1) - } try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Fuzzilli_Protobuf_WasmReturn, rhs: Fuzzilli_Protobuf_WasmReturn) -> Bool { - if lhs.returnTypes != rhs.returnTypes {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 d7e6b28c6..98beea01d 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -884,7 +884,6 @@ message Constf64 { } message WasmReturn { - repeated WasmILType returnTypes = 1; } // We only serialize the wasm types as the rest would be overkill and are not needed. From 226938a27cb4ac261fdae1bd7ae68cc6f7ac1d6f Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Wed, 14 Jan 2026 14:56:34 +0100 Subject: [PATCH 81/89] [wasm] Remove parameter and return types from WasmCallDirect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: 445356784 Change-Id: Idbe0b038ecd47b371639219edababaf7e33d1054 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8929536 Reviewed-by: Doga Yüksel Commit-Queue: Matthias Liedtke --- Sources/Fuzzilli/Base/ProgramBuilder.swift | 2 +- Sources/Fuzzilli/FuzzIL/Instruction.swift | 8 +++---- Sources/Fuzzilli/FuzzIL/JSTyper.swift | 5 +++-- Sources/Fuzzilli/FuzzIL/WasmOperations.swift | 8 +++---- Sources/Fuzzilli/Lifting/FuzzILLifter.swift | 8 +++---- Sources/Fuzzilli/Protobuf/operations.pb.swift | 22 +++++++++---------- Sources/Fuzzilli/Protobuf/operations.proto | 4 ++-- 7 files changed, 28 insertions(+), 29 deletions(-) diff --git a/Sources/Fuzzilli/Base/ProgramBuilder.swift b/Sources/Fuzzilli/Base/ProgramBuilder.swift index c21a0d402..1f12d1bae 100644 --- a/Sources/Fuzzilli/Base/ProgramBuilder.swift +++ b/Sources/Fuzzilli/Base/ProgramBuilder.swift @@ -3766,7 +3766,7 @@ public class ProgramBuilder { @discardableResult public func wasmCallDirect(signature: WasmSignature, function: Variable, functionArgs: [Variable]) -> [Variable] { - return Array(b.emit(WasmCallDirect(signature: signature), + return Array(b.emit(WasmCallDirect(parameterCount: signature.parameterTypes.count, outputCount: signature.outputTypes.count), withInputs: [function] + functionArgs, types: [.wasmFunctionDef(signature)] + signature.parameterTypes ).outputs) diff --git a/Sources/Fuzzilli/FuzzIL/Instruction.swift b/Sources/Fuzzilli/FuzzIL/Instruction.swift index ccc76a04c..6a7cec37a 100644 --- a/Sources/Fuzzilli/FuzzIL/Instruction.swift +++ b/Sources/Fuzzilli/FuzzIL/Instruction.swift @@ -1313,8 +1313,8 @@ extension Instruction: ProtobufConvertible { } case .wasmCallDirect(let op): $0.wasmCallDirect = Fuzzilli_Protobuf_WasmCallDirect.with { - $0.parameterTypes = op.signature.parameterTypes.map(ILTypeToWasmTypeEnum) - $0.outputTypes = op.signature.outputTypes.map(ILTypeToWasmTypeEnum) + $0.parameterCount = Int32(op.parameterCount) + $0.outputCount = Int32(op.numOutputs) } case .wasmReturnCallDirect(let op): $0.wasmReturnCallDirect = Fuzzilli_Protobuf_WasmReturnCallDirect.with { @@ -2408,9 +2408,7 @@ extension Instruction: ProtobufConvertible { let outputs = p.outputTypes.map(WasmTypeEnumToILType) op = WasmCallIndirect(signature: parameters => outputs) case .wasmCallDirect(let p): - let parameters = p.parameterTypes.map(WasmTypeEnumToILType) - let outputs = p.outputTypes.map(WasmTypeEnumToILType) - op = WasmCallDirect(signature: parameters => outputs) + op = WasmCallDirect(parameterCount: Int(p.parameterCount), outputCount: Int(p.outputCount)) case .wasmReturnCallDirect(let p): let parameters = p.parameterTypes.map(WasmTypeEnumToILType) let outputs = p.outputTypes.map(WasmTypeEnumToILType) diff --git a/Sources/Fuzzilli/FuzzIL/JSTyper.swift b/Sources/Fuzzilli/FuzzIL/JSTyper.swift index eeff27465..0b48a7bf7 100644 --- a/Sources/Fuzzilli/FuzzIL/JSTyper.swift +++ b/Sources/Fuzzilli/FuzzIL/JSTyper.swift @@ -878,8 +878,9 @@ public struct JSTyper: Analyzer { wasmTypeBeginBlock(instr, op.signature) case .wasmEndTryDelegate(let op): wasmTypeEndBlock(instr, op.outputTypes) - case .wasmCallDirect(let op): - for (output, outputType) in zip(instr.outputs, op.signature.outputTypes) { + case .wasmCallDirect(_): + let signature = type(of: instr.input(0)).wasmFunctionDefSignature! + for (output, outputType) in zip(instr.outputs, signature.outputTypes) { setType(of: output, to: outputType) } // We don't need to update the DynamicObjectGroupManager, as all functions that can be called here are .wasmFunctionDef types, this means we have already added them when we saw the EndWasmFunction instruction. diff --git a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift index 5db0e28c3..5b792bc7c 100644 --- a/Sources/Fuzzilli/FuzzIL/WasmOperations.swift +++ b/Sources/Fuzzilli/FuzzIL/WasmOperations.swift @@ -963,12 +963,12 @@ final class WasmCallIndirect: WasmOperation { final class WasmCallDirect: WasmOperation { override var opcode: Opcode { .wasmCallDirect(self) } - let signature: WasmSignature - init(signature: WasmSignature) { - self.signature = signature - super.init(numInputs: 1 + signature.parameterTypes.count, numOutputs: signature.outputTypes.count, requiredContext: [.wasmFunction]) + init(parameterCount: Int, outputCount: Int) { + super.init(numInputs: 1 + parameterCount, numOutputs: outputCount, requiredContext: [.wasmFunction]) } + + var parameterCount: Int {numInputs - 1} } final class WasmReturnCallDirect: WasmOperation { diff --git a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift index 8fbb32fb6..c86becf21 100644 --- a/Sources/Fuzzilli/Lifting/FuzzILLifter.swift +++ b/Sources/Fuzzilli/Lifting/FuzzILLifter.swift @@ -1064,13 +1064,13 @@ public class FuzzILLifter: Lifter { w.emit("\(outputs) <- WasmCallIndirect(\(op.signature)) \(inputs)") } - case .wasmCallDirect(let op): + case .wasmCallDirect(_): let inputs = instr.inputs.map(lift).joined(separator: ", ") - if op.signature.outputTypes.isEmpty { - w.emit("WasmCallDirect(\(op.signature)) \(inputs)") + if instr.outputs.isEmpty { + w.emit("WasmCallDirect \(inputs)") } else { let outputs = instr.outputs.map(lift).joined(separator: ", ") - w.emit("\(outputs) <- WasmCallDirect(\(op.signature)) \(inputs)") + w.emit("\(outputs) <- WasmCallDirect \(inputs)") } case .wasmReturnCallDirect(let op): diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift index 38f5660b6..d9bb295f0 100644 --- a/Sources/Fuzzilli/Protobuf/operations.pb.swift +++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift @@ -5045,9 +5045,9 @@ public struct Fuzzilli_Protobuf_WasmCallDirect: Sendable { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var parameterTypes: [Fuzzilli_Protobuf_WasmILType] = [] + public var parameterCount: Int32 = 0 - public var outputTypes: [Fuzzilli_Protobuf_WasmILType] = [] + public var outputCount: Int32 = 0 public var unknownFields = SwiftProtobuf.UnknownStorage() @@ -13405,7 +13405,7 @@ extension Fuzzilli_Protobuf_WasmCallIndirect: SwiftProtobuf.Message, SwiftProtob extension Fuzzilli_Protobuf_WasmCallDirect: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".WasmCallDirect" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameterTypes\0\u{1}outputTypes\0") + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameterCount\0\u{1}outputCount\0") public mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -13413,26 +13413,26 @@ extension Fuzzilli_Protobuf_WasmCallDirect: SwiftProtobuf.Message, SwiftProtobuf // 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) }() + case 1: try { try decoder.decodeSingularInt32Field(value: &self.parameterCount) }() + case 2: try { try decoder.decodeSingularInt32Field(value: &self.outputCount) }() default: break } } } public func traverse(visitor: inout V) throws { - if !self.parameterTypes.isEmpty { - try visitor.visitRepeatedMessageField(value: self.parameterTypes, fieldNumber: 1) + if self.parameterCount != 0 { + try visitor.visitSingularInt32Field(value: self.parameterCount, fieldNumber: 1) } - if !self.outputTypes.isEmpty { - try visitor.visitRepeatedMessageField(value: self.outputTypes, fieldNumber: 2) + if self.outputCount != 0 { + try visitor.visitSingularInt32Field(value: self.outputCount, fieldNumber: 2) } try unknownFields.traverse(visitor: &visitor) } public static func ==(lhs: Fuzzilli_Protobuf_WasmCallDirect, rhs: Fuzzilli_Protobuf_WasmCallDirect) -> Bool { - if lhs.parameterTypes != rhs.parameterTypes {return false} - if lhs.outputTypes != rhs.outputTypes {return false} + if lhs.parameterCount != rhs.parameterCount {return false} + if lhs.outputCount != rhs.outputCount {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 98beea01d..4968018e3 100644 --- a/Sources/Fuzzilli/Protobuf/operations.proto +++ b/Sources/Fuzzilli/Protobuf/operations.proto @@ -1173,8 +1173,8 @@ message WasmCallIndirect { } message WasmCallDirect { - repeated WasmILType parameterTypes = 1; - repeated WasmILType outputTypes = 2; + int32 parameterCount = 1; + int32 outputCount = 2; } message WasmReturnCallDirect { From 411f1dcc96abc4b219c2287bf84c6132c3defc42 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Tue, 20 Jan 2026 16:14:43 +0100 Subject: [PATCH 82/89] [wasm] Deduplicate WasmLegacyTryCatchWithResultGenerator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One of them should be enough. :) Bug: 445356784 Change-Id: Ib0f215bcd41c2801d2b5d43c6255b17a5d979dd2 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8944236 Auto-Submit: Matthias Liedtke Reviewed-by: Doga Yüksel Commit-Queue: Matthias Liedtke --- .../Fuzzilli/CodeGen/WasmCodeGenerators.swift | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift index ee8abfc22..1a724a3a1 100644 --- a/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift +++ b/Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift @@ -1381,40 +1381,6 @@ public let WasmCodeGenerators: [CodeGenerator] = [ } }, - 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(n: 4) - return outputTypes.map(function.findOrGenerateWasmVar) - }, - catchClauses: tags.enumerated().map { i, tag in - ( - tag, - { _, _, _ in - b.buildRecursive(n: 4) - return outputTypes.map(function.findOrGenerateWasmVar) - } - ) - }, - catchAllBody: { label in - b.buildRecursive(n: 4) - return outputTypes.map(function.findOrGenerateWasmVar) - }) - }, - // TODO split this into a multi-part Generator. CodeGenerator( "WasmLegacyTryCatchWithResultGenerator", inContext: .single(.wasmFunction) From 951080adca25d954c668a85c7df3745ea2ecc664 Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Wed, 21 Jan 2026 18:15:33 +0100 Subject: [PATCH 83/89] Whitespace change to test builders Bug: 442444727 Change-Id: I4639df028436c02f59a26e12e3930bee209ab506 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8949196 Bot-Commit: Rubber Stamper --- WHITESPACE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WHITESPACE b/WHITESPACE index 657b7d229..18c219b08 100644 --- a/WHITESPACE +++ b/WHITESPACE @@ -1,3 +1,3 @@ -You can modify this file to create no-op changelists... +You can modify this file to create no-op changelists.... Try to write something funny. And please don't add trailing whitespace. From 93b589914dde4b5e4a1bf7cb7d2b7c6862342ceb Mon Sep 17 00:00:00 2001 From: Michael Achenbach Date: Thu, 22 Jan 2026 12:37:57 +0100 Subject: [PATCH 84/89] Whitespace change to test builders Bug: 442444727 Change-Id: I77dc4619f6eba65bf7417fbb36609eb42993121c Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8952396 Bot-Commit: Rubber Stamper --- WHITESPACE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WHITESPACE b/WHITESPACE index 18c219b08..7fdb86b20 100644 --- a/WHITESPACE +++ b/WHITESPACE @@ -1,3 +1,3 @@ -You can modify this file to create no-op changelists.... +You can modify this file to create no-op changelists..... Try to write something funny. And please don't add trailing whitespace. From 65c0f459e106208df635d30c984b061b00e794ee Mon Sep 17 00:00:00 2001 From: Danylo Mocherniuk Date: Thu, 22 Jan 2026 09:58:36 +0000 Subject: [PATCH 85/89] [dumpling] Delete patch file. Main functionality was merged into codebase by now. Bug: 441467877 Change-Id: Ibcd2c7873188e52cf0db0dcdfacf8150ee694107 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8947917 Commit-Queue: Dominik Klemba Reviewed-by: Dominik Klemba Commit-Queue: Danylo Mocherniuk --- Sources/Dumpling/dumpling_fuzzilli.patch | 13010 --------------------- 1 file changed, 13010 deletions(-) delete mode 100644 Sources/Dumpling/dumpling_fuzzilli.patch diff --git a/Sources/Dumpling/dumpling_fuzzilli.patch b/Sources/Dumpling/dumpling_fuzzilli.patch deleted file mode 100644 index deb0b6770..000000000 --- a/Sources/Dumpling/dumpling_fuzzilli.patch +++ /dev/null @@ -1,13010 +0,0 @@ -diff --git a/Package.swift b/Package.swift -index 05e5dc1..540b552 100644 ---- a/Package.swift -+++ b/Package.swift -@@ -56,18 +56,22 @@ let package = Package( - .copy("Protobuf/ast.proto"), - .copy("Compiler/Parser")]), - -+ .target(name: "FuzzILTool", -+ dependencies: ["Fuzzilli"]), -+ - .target(name: "REPRLRun", - dependencies: ["libreprl"]), - - .target(name: "FuzzilliCli", - dependencies: ["Fuzzilli"]), -- -- .target(name: "FuzzILTool", -+ -+ .target(name: "RelateTool", - dependencies: ["Fuzzilli"]), - -+ - .testTarget(name: "FuzzilliTests", - dependencies: ["Fuzzilli"], -- resources: [.copy("CompilerTests")]), -+ resources: [ .copy("CompilerTests"), ]), - ], - swiftLanguageVersions: [.v5] - ) -diff --git a/Sources/Fuzzilli/Base/Contributor.swift b/Sources/Fuzzilli/Base/Contributor.swift -index fd0af2a..6497f83 100644 ---- a/Sources/Fuzzilli/Base/Contributor.swift -+++ b/Sources/Fuzzilli/Base/Contributor.swift -@@ -29,6 +29,14 @@ public class Contributor: Hashable { - // Number of crashing programs produced. - private var crashingSamples = 0 - -+ private var differentialSamples = 0 -+ -+ private var turbofanSamples = 0 -+ -+ private var maglevSamples = 0 -+ -+ private var sparkplugSamples = 0 -+ - // Number of times this instance failed to generate/mutate code. - private var failures = 0 - // Total number of instructions added to programs by this contributor. -@@ -58,6 +66,21 @@ public class Contributor: Hashable { - crashingSamples += 1 - } - -+ func generatedDifferentialSample() { -+ differentialSamples += 1 -+ } -+ -+ func generatedTurbofanSample() { -+ turbofanSamples += 1 -+ } -+ -+ func generatedMaglevSample() { -+ maglevSamples += 1 -+ } -+ -+ func generatedSparkplugSample() { -+ sparkplugSamples += 1 -+ } - - func addedInstructions(_ n: Int) { - guard n > 0 else { return } -@@ -72,6 +95,22 @@ public class Contributor: Hashable { - return crashingSamples - } - -+ public var differentialsFound: Int { -+ return differentialSamples -+ } -+ -+ public var turbofanSamplesFound: Int { -+ return turbofanSamples -+ } -+ -+ public var maglevSamplesFound: Int { -+ return maglevSamples -+ } -+ -+ public var sparkplugSamplesFound: Int { -+ return sparkplugSamples -+ } -+ - public var totalSamples: Int { - return validSamples + interestingSamples + invalidSamples + timedOutSamples + crashingSamples - } -@@ -135,4 +174,20 @@ extension Contributors { - public func generatedTimeOutSample() { - forEach { $0.generatedTimeOutSample() } - } -+ -+ public func generatedDifferentialSample() { -+ forEach { $0.generatedDifferentialSample() } -+ } -+ -+ public func generatedTurbofanSample() { -+ forEach { $0.generatedTurbofanSample() } -+ } -+ -+ public func generatedMaglevSample() { -+ forEach { $0.generatedMaglevSample() } -+ } -+ -+ public func generatedSparkplugSample() { -+ forEach { $0.generatedSparkplugSample() } -+ } - } -diff --git a/Sources/Fuzzilli/Base/Events.swift b/Sources/Fuzzilli/Base/Events.swift -index 7063add..4d5d2ad 100644 ---- a/Sources/Fuzzilli/Base/Events.swift -+++ b/Sources/Fuzzilli/Base/Events.swift -@@ -55,6 +55,12 @@ public class Events { - /// Signals that a crashing program has been found. Dispatched after the crashing program has been minimized. - public let CrashFound = Event<(program: Program, behaviour: CrashBehaviour, isUnique: Bool, origin: ProgramOrigin)>() - -+ public let DifferentialFound = Event<(program: Program, behaviour: CrashBehaviour, origin: ProgramOrigin, opt_stdout: String?, unopt_stdout: String, reproducesInNonReplMode: Bool)>() -+ -+ public let JITExecutingProgramFound = Event<(program: Program, compilers: [JITType], is_optimizing: Bool)>() -+ -+ public let RelationPerformed = Event<(program: Program, execution: Execution)>() -+ - /// Signals that a program causing a timeout has been found. - public let TimeOutFound = Event() - -@@ -88,6 +94,12 @@ public class Events { - public let CorpusImportComplete = Event<()>() - } - -+public enum JITType: String { -+ case sparkplug = "sparkplug" -+ case maglev = "maglev" -+ case turbofan = "turbofan" -+} -+ - /// Crash behavior of a program. - public enum CrashBehaviour: String { - case deterministic = "deterministic" -diff --git a/Sources/Fuzzilli/Configuration.swift b/Sources/Fuzzilli/Configuration.swift -index 6429536..b57e3d5 100644 ---- a/Sources/Fuzzilli/Configuration.swift -+++ b/Sources/Fuzzilli/Configuration.swift -@@ -12,6 +12,37 @@ - // See the License for the specific language governing permissions and - // limitations under the License. - -+public struct V8DifferentialConfig { -+ public let commonArgs: [String] = [ -+ "--expose-gc", -+ "--omit-quit", -+ "--allow-natives-for-differential-fuzzing", -+ "--fuzzing", -+ "--future", -+ "--harmony", -+ "--predictable", -+ "--trace", -+ "--correctness-fuzzer-suppressions", -+ "--no-lazy-feedback-allocation", -+ ] -+ -+ public let differentialArgs: [String] = [ -+ "--turbofan-dumping", -+ "--generate-dump-positions", -+ // "--verify-heap-on-jit-dump", -+ "--turbofan-dumping-print-deopt-frames", -+ "--jit-return-dump", -+ "--maglev-dumping", -+ "--no-sparkplug", -+ "--jit-fuzzing", -+ ] -+ -+ public let referenceArgs: [String] = ["--no-turbofan", "--no-maglev", "--load-dump-positions", "--sparkplug-dumping", "--interpreter-dumping"] -+ -+ public init() {} -+} -+ -+ - public struct Configuration { - /// The commandline arguments used by this instance. - public let arguments: [String] -@@ -26,6 +57,15 @@ public struct Configuration { - /// Used to verify that crashes can be detected. - public let crashTests: [String] - -+ /// Code snippets that cause an observable difference of output -+ /// in the target engine. Used to verify that crashes can be detected. -+ public let differentialTests: [String] -+ -+ /// Code snippets that must not cause an observable difference of output -+ /// in the target engine. Used to verify that common sources of -+ /// entropy (Math.random, ...) are deterministic. -+ public let differentialTestsInvariant: [String] -+ - /// The fraction of instruction to keep from the original program when minimizing. - /// This setting is useful to avoid "over-minimization", which can negatively impact the fuzzer's - /// performance if program features are removed that could later be mutated to trigger new -@@ -57,28 +97,48 @@ public struct Configuration { - /// also appended as a comment in the footer of crashing samples. - public let tag: String? - -+ public let relateToolPath: String? -+ -+ public let dumplingDepth: UInt32? -+ -+ public let dumplingPropCount: UInt32? -+ -+ public let storagePath: String? -+ - public init(arguments: [String] = [], - timeout: UInt32 = 250, - skipStartupTests: Bool = false, - logLevel: LogLevel = .info, - crashTests: [String] = [], -+ differentialTests: [String] = [], -+ differentialTestsInvariant: [String] = [], - minimizationLimit: Double = 0.0, - dropoutRate: Double = 0, - collectRuntimeTypes: Bool = false, - enableDiagnostics: Bool = false, - enableInspection: Bool = false, - staticCorpus: Bool = false, -- tag: String? = nil) { -+ tag: String? = nil, -+ relateToolPath: String? = nil, -+ dumplingDepth: UInt32 = 3, -+ dumplingPropCount: UInt32 = 5, -+ storagePath: String? = nil) { - self.arguments = arguments - self.timeout = timeout - self.logLevel = logLevel - self.crashTests = crashTests -+ self.differentialTests = differentialTests -+ self.differentialTestsInvariant = differentialTestsInvariant - self.dropoutRate = dropoutRate - self.minimizationLimit = minimizationLimit - self.enableDiagnostics = enableDiagnostics - self.enableInspection = enableInspection - self.staticCorpus = staticCorpus - self.tag = tag -+ self.relateToolPath = relateToolPath -+ self.dumplingDepth = dumplingDepth -+ self.dumplingPropCount = dumplingPropCount -+ self.storagePath = storagePath - } - } - -diff --git a/Sources/Fuzzilli/DifferentialOracle/Oracle.swift b/Sources/Fuzzilli/DifferentialOracle/Oracle.swift -new file mode 100644 -index 0000000..1d91f18 ---- /dev/null -+++ b/Sources/Fuzzilli/DifferentialOracle/Oracle.swift -@@ -0,0 +1,211 @@ -+import Foundation -+ -+public enum FrameType { -+ case interpreter -+ case sparkplug -+ case maglev -+ case turbofan -+ case deopt -+ case jitReturn -+} -+ -+ -+public struct Frame: Equatable { -+ let bytecode_offset: Int -+ let acc: String -+ let args: [String] -+ let regs: [String] -+ let function_id: Int -+ let frame_type: FrameType -+} -+ -+public enum OracleLogLevel { -+ case none -+ case info -+ case debug -+} -+ -+/// Compare two frames for equality -+/// Warning: It is not communtative! The opt frame must be lhs. -+/// A string value of "" in lhs is equal to every other string -+/// value in acc, args, and regs -+/// - Parameters: -+/// - lhs: The unopt frame -+/// - rhs: The opt frame -+public func == (lhs: Frame, rhs: Frame) -> Bool { -+ return lhs.bytecode_offset == rhs.bytecode_offset && -+ (lhs.acc == rhs.acc || lhs.acc == "") && -+ lhs.args.count == rhs.args.count && -+ lhs.regs.count == rhs.regs.count && -+ (0.." } && -+ (0.." } && -+ lhs.function_id == rhs.function_id -+} -+ -+ -+ -+ -+public func parseDiffFrame(_ frameArr: ArraySlice, _ lastFrame: inout Frame?, -+ _ prevRegs: inout [String], _ prevArgs: inout [String]) -> Frame { -+ func parseValue(prefix: String, defaultValue: T, index: inout Int, conversion: (Substring) -> T) -> T { -+ if index < frameArr.endIndex, frameArr[index].starts(with: prefix) { -+ let value = conversion(frameArr[index].dropFirst(prefix.count)) -+ index += 1 -+ return value -+ } -+ return defaultValue -+ } -+ -+ let frameType: FrameType -+ var i = frameArr.startIndex -+ switch frameArr[i] { -+ case "---I": -+ frameType = .interpreter -+ case "---S": -+ frameType = .sparkplug -+ case "---M": -+ frameType = .maglev -+ case "---T": -+ frameType = .turbofan -+ case "---D": -+ frameType = .deopt -+ case "---R": -+ frameType = .jitReturn -+ default: -+ fatalError("Unknown frame type") -+ } -+ i += 1 -+ -+ let bytecode_offset = parseValue(prefix: "b:", defaultValue: lastFrame?.bytecode_offset ?? 4242, index: &i){ Int($0)! } -+ let function_id = parseValue(prefix: "f:", defaultValue: lastFrame?.function_id ?? 4242, index: &i){ Int($0)! } -+ let arg_count = parseValue(prefix: "n:", defaultValue: lastFrame?.args.count ?? 4242, index: &i){ Int($0)! } -+ let reg_count = parseValue(prefix: "m:", defaultValue: lastFrame?.regs.count ?? 4242, index: &i){ Int($0)! } -+ let acc = parseValue(prefix: "x:", defaultValue: lastFrame?.acc ?? "", index: &i){ String($0) } -+ -+ func updateValues(prefix: String, totalCount: Int, oldValues: [String], prevValues: inout [String]) -> [String] { -+ var newValues: [String] -+ // performance improvement to use Swifts copy on write -+ if oldValues.count == totalCount { -+ newValues = oldValues -+ } -+ else { -+ newValues = [String]() -+ newValues.reserveCapacity(totalCount) -+ // copy up to totalCount from prevValues into newValues -+ for j in 0.. [[Frame]] { -+ var stack: [[Frame]] = [[], []] -+ var linearized: [[Frame]] = [] -+ var lastFrame: Frame? = nil -+ -+ var prevArgs: [String] = [String]() -+ var prevRegs: [String] = [String]() -+ prevArgs.reserveCapacity(64) -+ prevRegs.reserveCapacity(128) -+ -+ let split = stdout.split(separator: "\n", omittingEmptySubsequences: false) -+ var i = 0 -+ -+ while i < split.count { -+ if split[i] == ">" { -+ stack.append([]) -+ } -+ else if split[i] == "<" { -+ linearized.append(stack.removeLast()) -+ } -+ else if split[i].starts(with: "---") { -+ let start = i -+ while (split[i] != "") { -+ i += 1 -+ } -+ let end = i-1 -+ let frame = split[start...end] -+ // append to last stack frame -+ lastFrame = parseDiffFrame(frame, &lastFrame, &prevArgs, &prevRegs) -+ stack[stack.count - 1].append(lastFrame!) -+ } -+ i += 1 -+ } -+ for _ in stack { -+ linearized.append(stack.removeLast()) -+ } -+ return linearized -+} -+ -+public func relate(_ optIn: String, with unoptIn: String, _ logLevel: OracleLogLevel = .none) -> Bool { -+ // Quick and dirty way to not get NaN vs spam -+ let optChunks = parseToLinear(optIn.replacingOccurrences(of: "", with: "NaN")) -+ let unoptChunks = parseToLinear(unoptIn.replacingOccurrences(of: "", with: "NaN")) -+ -+ -+ if logLevel == .debug { -+ print("opt Chunks:") -+ print(optChunks as AnyObject) -+ print("unopt Chunks:") -+ print(unoptChunks as AnyObject) -+ } -+ if optChunks.count != unoptChunks.count { -+ if logLevel != .none { -+ print("Difference in chunk count \(optChunks.count) != \(unoptChunks.count)") -+ } -+ return false -+ } -+ for (optChunk, unoptChunk) in zip(optChunks, unoptChunks) { -+ for optFrame in optChunk { -+ // check if optFrame is in unoptChunk -+ if !unoptChunk.contains(where: {optFrame == $0}) { -+ if logLevel != .none { -+ print(optFrame as AnyObject) -+ print("--------------------------") -+ print("[") -+ for unoptFrame in unoptChunk { -+ if unoptFrame.bytecode_offset == optFrame.bytecode_offset { -+ print(unoptFrame as AnyObject) -+ } -+ } -+ print("]") -+ } -+ return false -+ } -+ } -+ } -+ return true -+} -diff --git a/Sources/Fuzzilli/Engines/FuzzEngine.swift b/Sources/Fuzzilli/Engines/FuzzEngine.swift -index 1b5a610..3a5e76d 100644 ---- a/Sources/Fuzzilli/Engines/FuzzEngine.swift -+++ b/Sources/Fuzzilli/Engines/FuzzEngine.swift -@@ -32,17 +32,27 @@ public class FuzzEngine: ComponentBase { - fatalError("Must be implemented by child classes") - } - -- final func execute(_ program: Program, withTimeout timeout: UInt32? = nil) -> ExecutionOutcome { -+} -+ -+extension FuzzEngine { -+ public func execute(_ program: Program, withTimeout timeout: UInt32? = nil) -> ExecutionOutcome { - let program = postProcessor?.process(program, for: fuzzer) ?? program - - fuzzer.dispatchEvent(fuzzer.events.ProgramGenerated, data: program) - -- let execution = fuzzer.execute(program, withTimeout: timeout, purpose: .fuzzing) -+ let postprocessedProgram: Program -+ postprocessedProgram = program -+ -+ let execution = fuzzer.execute(postprocessedProgram, withTimeout: timeout, purpose: .fuzzing, differentialExecute: .force) - - switch execution.outcome { - case .crashed(let termsig): -- fuzzer.processCrash(program, withSignal: termsig, withStderr: execution.stderr, withStdout: execution.stdout, origin: .local, withExectime: execution.execTime) -- program.contributors.generatedCrashingSample() -+ fuzzer.processCrash(postprocessedProgram, withSignal: termsig, withStderr: execution.stderr, withStdout: execution.stdout, origin: .local, withExectime: execution.execTime) -+ postprocessedProgram.contributors.generatedCrashingSample() -+ -+ case .differential: -+ fuzzer.processDifferential(postprocessedProgram, withStderr: execution.stderr, withStdout: execution.unOptStdout!, origin: .local, optStdout: execution.optStdout!) -+ postprocessedProgram.contributors.generatedDifferentialSample() - - case .succeeded: - fuzzer.dispatchEvent(fuzzer.events.ValidProgramFound, data: program) -@@ -63,19 +73,19 @@ public class FuzzEngine: ComponentBase { - - case .failed(_): - if fuzzer.config.enableDiagnostics { -- program.comments.add("Stdout:\n" + execution.stdout, at: .footer) -+ postprocessedProgram.comments.add("Stdout:\n" + execution.stdout, at: .footer) - } -- fuzzer.dispatchEvent(fuzzer.events.InvalidProgramFound, data: program) -- program.contributors.generatedInvalidSample() -+ fuzzer.dispatchEvent(fuzzer.events.InvalidProgramFound, data: postprocessedProgram) -+ postprocessedProgram.contributors.generatedInvalidSample() - - case .timedOut: -- fuzzer.dispatchEvent(fuzzer.events.TimeOutFound, data: program) -- program.contributors.generatedTimeOutSample() -+ fuzzer.dispatchEvent(fuzzer.events.TimeOutFound, data: postprocessedProgram) -+ postprocessedProgram.contributors.generatedTimeOutSample() - } - - if fuzzer.config.enableDiagnostics { - // Ensure deterministic execution behaviour. This can for example help detect and debug REPRL issues. -- ensureDeterministicExecutionOutcomeForDiagnostic(of: program) -+ ensureDeterministicExecutionOutcomeForDiagnostic(of: postprocessedProgram) - } - - return execution.outcome -diff --git a/Sources/Fuzzilli/Engines/HybridEngine.swift b/Sources/Fuzzilli/Engines/HybridEngine.swift -index f25ab27..4bd0885 100644 ---- a/Sources/Fuzzilli/Engines/HybridEngine.swift -+++ b/Sources/Fuzzilli/Engines/HybridEngine.swift -@@ -24,6 +24,7 @@ public class HybridEngine: FuzzEngine { - case generatedCodeFailed = "Generated code failed" - case generatedCodeTimedOut = "Generated code timed out" - case generatedCodeCrashed = "Generated code crashed" -+ case generatedCodeDifferential = "Generated code differential" - } - private var outcomeCounts = [CodeGenerationOutcome: Int]() - -@@ -112,6 +113,8 @@ public class HybridEngine: FuzzEngine { - return recordOutcome(.generatedCodeTimedOut) - case .crashed: - return recordOutcome(.generatedCodeCrashed) -+ case .differential: -+ return recordOutcome(.generatedCodeDifferential) - } - - // Now perform one round of fixup to improve the generated program based on runtime information and in particular remove all try-catch guards that are not needed. -diff --git a/Sources/Fuzzilli/Evaluation/ProgramAspects.swift b/Sources/Fuzzilli/Evaluation/ProgramAspects.swift -index 7098e72..7cdb6bd 100644 ---- a/Sources/Fuzzilli/Evaluation/ProgramAspects.swift -+++ b/Sources/Fuzzilli/Evaluation/ProgramAspects.swift -@@ -12,12 +12,21 @@ - // See the License for the specific language governing permissions and - // limitations under the License. - -+public enum ExecutionType { -+ case unknown -+ case interpreter -+ case maglev -+ case turbofan -+} -+ - /// Aspects of a program that make it special. - public class ProgramAspects: CustomStringConvertible { - let outcome: ExecutionOutcome -+ public var jitExecution: ExecutionType - -- public init(outcome: ExecutionOutcome) { -+ public init(outcome: ExecutionOutcome, jitExecution: ExecutionType = .unknown) { - self.outcome = outcome -+ self.jitExecution = jitExecution - } - - public var description: String { -diff --git a/Sources/Fuzzilli/Evaluation/ProgramCoverageEvaluator.swift b/Sources/Fuzzilli/Evaluation/ProgramCoverageEvaluator.swift -index c365ff1..0ed5e61 100644 ---- a/Sources/Fuzzilli/Evaluation/ProgramCoverageEvaluator.swift -+++ b/Sources/Fuzzilli/Evaluation/ProgramCoverageEvaluator.swift -@@ -19,11 +19,12 @@ import libcoverage - public class CovEdgeSet: ProgramAspects { - private var numEdges: UInt32 - fileprivate var edges: UnsafeMutablePointer? -+ - -- init(edges: UnsafeMutablePointer?, numEdges: UInt32) { -+ init(edges: UnsafeMutablePointer?, numEdges: UInt32, jitExecution: ExecutionType = .unknown) { - self.numEdges = numEdges - self.edges = edges -- super.init(outcome: .succeeded) -+ super.init(outcome: .succeeded, jitExecution: jitExecution) - } - - deinit { -@@ -192,8 +193,8 @@ public class ProgramCoverageEvaluator: ComponentBase, ProgramEvaluator { - return false - } - -- if execution.outcome.isCrash() { -- // For crashes, we don't care about the edges that were triggered, just about the outcome itself. -+ if execution.outcome.isCrash() || execution.outcome == .differential { -+ // For crashes and differentials?, we don't care about the edges that were triggered, just about the outcome itself. - return true - } - -@@ -237,6 +238,15 @@ public class ProgramCoverageEvaluator: ComponentBase, ProgramEvaluator { - let intersectedCovEdgeSet = secondCovEdgeSet - intersectedCovEdgeSet.setEdges(intersectedEdgeSet) - -+ if execution.compilersUsed.count != 0 { -+ if execution.compilersUsed.contains(.turbofan) { -+ intersectedCovEdgeSet.jitExecution = .turbofan -+ } -+ else if execution.compilersUsed.contains(.maglev) { -+ intersectedCovEdgeSet.jitExecution = .maglev -+ } -+ // dont care about sparkplug here -+ } - return intersectedCovEdgeSet - } - -diff --git a/Sources/Fuzzilli/Execution/Execution.swift b/Sources/Fuzzilli/Execution/Execution.swift -index 746b446..b6e6e76 100644 ---- a/Sources/Fuzzilli/Execution/Execution.swift -+++ b/Sources/Fuzzilli/Execution/Execution.swift -@@ -20,6 +20,7 @@ public enum ExecutionOutcome: CustomStringConvertible, Equatable, Hashable { - case failed(Int) - case succeeded - case timedOut -+ case differential - - public var description: String { - switch self { -@@ -31,6 +32,8 @@ public enum ExecutionOutcome: CustomStringConvertible, Equatable, Hashable { - return "Succeeded" - case .timedOut: - return "TimedOut" -+ case .differential: -+ return "Differential" - } - } - -@@ -43,11 +46,23 @@ public enum ExecutionOutcome: CustomStringConvertible, Equatable, Hashable { - } - } - -+// has to be kept in sync with V8 -+enum JITTypeBits: Int { -+ case turbofanB = 1 // 1 << 0 -+ case maglevB = 2 // 1 << 1 -+ case sparkplugB = 4 // 1 << 2 -+} -+ - /// The result of executing a program. - public protocol Execution { -- var outcome: ExecutionOutcome { get } -+ var outcome: ExecutionOutcome { get set } - var stdout: String { get } - var stderr: String { get } - var fuzzout: String { get } -- var execTime: TimeInterval { get } -+ var execTime: TimeInterval { get set } -+ var compilersUsed: [JITType] { get set } -+ var unOptStdout: String? { get set } -+ var optStdout: String? { get set } -+ var reproducesInNonReplMode: Bool? { get set } -+ var bugOracleTime: TimeInterval? { get set } - } -diff --git a/Sources/Fuzzilli/Execution/REPRL.swift b/Sources/Fuzzilli/Execution/REPRL.swift -index 185d1f8..1ec60d4 100644 ---- a/Sources/Fuzzilli/Execution/REPRL.swift -+++ b/Sources/Fuzzilli/Execution/REPRL.swift -@@ -19,7 +19,7 @@ import libreprl - /// scripts, but resets the global state in between executions. - public class REPRL: ComponentBase, ScriptRunner { - /// Kill and restart the child process after this many script executions -- private let maxExecsBeforeRespawn: Int -+ public let maxExecsBeforeRespawn: Int - - /// Commandline arguments for the executable - public private(set) var processArguments: [String] -@@ -79,7 +79,7 @@ public class REPRL: ComponentBase, ScriptRunner { - env.append(key + "=" + value) - } - -- public func run(_ script: String, withTimeout timeout: UInt32) -> Execution { -+ public func run(_ script: String, withTimeout timeout: UInt32, differentialFuzzingPositionDumpSeed: UInt32) -> Execution { - // Log the current script into the buffer if diagnostics are enabled. - if fuzzer.config.enableDiagnostics { - self.scriptBuffer += script + "\n" -@@ -108,8 +108,11 @@ public class REPRL: ComponentBase, ScriptRunner { - var execTime: UInt64 = 0 // In microseconds - let timeout = UInt64(timeout) * 1000 // In microseconds - var status: Int32 = 0 -+ var encodedJitState: UInt8 = 0 - script.withCString { -- status = reprl_execute(reprlContext, $0, UInt64(script.count), UInt64(timeout), &execTime, freshInstance) -+ status = reprl_execute(reprlContext, $0, UInt64(script.count), UInt64(timeout), &execTime, freshInstance, -+ differentialFuzzingPositionDumpSeed, &encodedJitState) -+ - // If we fail, we retry after a short timeout and with a fresh instance. If we still fail, we give up trying - // to execute this program. If we repeatedly fail to execute any program, we abort. - if status < 0 { -@@ -118,7 +121,9 @@ public class REPRL: ComponentBase, ScriptRunner { - fuzzer.dispatchEvent(fuzzer.events.DiagnosticsEvent, data: (name: "REPRLFail", content: scriptBuffer)) - } - Thread.sleep(forTimeInterval: 1) -- status = reprl_execute(reprlContext, $0, UInt64(script.count), UInt64(timeout), &execTime, 1) -+ status = reprl_execute(reprlContext, $0, UInt64(script.count), UInt64(timeout), &execTime, 1, -+ differentialFuzzingPositionDumpSeed, &encodedJitState) -+ - } - } - -@@ -150,6 +155,16 @@ public class REPRL: ComponentBase, ScriptRunner { - } - execution.execTime = Double(execTime) / 1_000_000 - -+ if encodedJitState & UInt8(JITTypeBits.turbofanB.rawValue) != 0 { -+ execution.compilersUsed.append(.turbofan) -+ } -+ if encodedJitState & UInt8(JITTypeBits.maglevB.rawValue) != 0 { -+ execution.compilersUsed.append(.maglev) -+ } -+ if encodedJitState & UInt8(JITTypeBits.sparkplugB.rawValue) != 0 { -+ execution.compilersUsed.append(.sparkplug) -+ } -+ - return execution - } - } -@@ -163,7 +178,12 @@ class REPRLExecution: Execution { - private let execId: Int - - var outcome = ExecutionOutcome.succeeded -+ var compilersUsed: [JITType] = [] - var execTime: TimeInterval = 0 -+ var unOptStdout: String? = nil -+ var optStdout: String? = nil -+ var reproducesInNonReplMode: Bool? = nil -+ var bugOracleTime: TimeInterval? = nil - - init(from reprl: REPRL) { - self.reprl = reprl -@@ -178,8 +198,14 @@ class REPRLExecution: Execution { - - var stdout: String { - assert(outputStreamsAreValid) -+ let x = String(cString: reprl_fetch_stdout(reprl.reprlContext)) - if cachedStdout == nil { - cachedStdout = String(cString: reprl_fetch_stdout(reprl.reprlContext)) -+ } else if (cachedStdout!) != x { -+ print(cachedStdout!) -+ print("-----------------------------------") -+ print(x) -+ fatalError("repl is bricked!") - } - return cachedStdout! - } -diff --git a/Sources/Fuzzilli/Execution/ScriptRunner.swift b/Sources/Fuzzilli/Execution/ScriptRunner.swift -index 36bccf7..8a7ee2d 100644 ---- a/Sources/Fuzzilli/Execution/ScriptRunner.swift -+++ b/Sources/Fuzzilli/Execution/ScriptRunner.swift -@@ -16,7 +16,7 @@ public protocol ScriptRunner: Component { - var processArguments: [String] { get } - - /// Executes a script, waits for it to complete, and returns the result. -- func run(_ script: String, withTimeout timeout: UInt32) -> Execution -+ func run(_ script: String, withTimeout timeout: UInt32, differentialFuzzingPositionDumpSeed: UInt32) -> Execution - - /// Sets an environment variable for the child process. Must only be called before initialization. - func setEnvironmentVariable(_ key: String, to value: String) -diff --git a/Sources/Fuzzilli/Fuzzer.swift b/Sources/Fuzzilli/Fuzzer.swift -index b854f63..dc93021 100644 ---- a/Sources/Fuzzilli/Fuzzer.swift -+++ b/Sources/Fuzzilli/Fuzzer.swift -@@ -14,6 +14,14 @@ - - import Foundation - -+ -+fileprivate let prependJS = try! String(contentsOfFile: "prepend.js") -+fileprivate let B = 1; -+fileprivate let KB = 1024 * B; -+fileprivate let MB = 1024 * KB; -+ -+fileprivate let execPoison = ["(see crbug.com/", "Aborting on ", "Fatal JavaScript out of memory:"]; -+ - public class Fuzzer { - /// Id of this fuzzer. - public let id: UUID -@@ -36,6 +44,9 @@ public class Fuzzer { - /// The script runner used to execute generated scripts. - public let runner: ScriptRunner - -+ /// The script runners used to compare against in differential executions. -+ public let referenceRunner: ScriptRunner -+ - /// The fuzzer engine producing new programs from existing ones and executing them. - public let engine: FuzzEngine - -@@ -63,6 +74,8 @@ public class Fuzzer { - /// The minimizer to shrink programs that cause crashes or trigger new interesting behaviour. - public let minimizer: Minimizer - -+ private let localId: UInt32 -+ - /// The engine used for initial corpus generation (if performed). - public let corpusGenerationEngine = GenerativeEngine() - -@@ -155,9 +168,9 @@ public class Fuzzer { - - /// Constructs a new fuzzer instance with the provided components. - public init( -- configuration: Configuration, scriptRunner: ScriptRunner, engine: FuzzEngine, mutators: WeightedList, -+ configuration: Configuration, scriptRunner: ScriptRunner, referenceRunner: ScriptRunner, engine: FuzzEngine, mutators: WeightedList, - codeGenerators: WeightedList, programTemplates: WeightedList, evaluator: ProgramEvaluator, -- environment: Environment, lifter: Lifter, corpus: Corpus, minimizer: Minimizer, queue: DispatchQueue? = nil -+ environment: Environment, lifter: Lifter, corpus: Corpus, minimizer: Minimizer, localId: UInt32, queue: DispatchQueue? = nil - ) { - let uniqueId = UUID() - self.id = uniqueId -@@ -175,9 +188,13 @@ public class Fuzzer { - self.lifter = lifter - self.corpus = corpus - self.runner = scriptRunner -+ self.referenceRunner = referenceRunner - self.minimizer = minimizer -+ self.localId = localId - self.logger = Logger(withLabel: "Fuzzer") - -+ // self.removeMetadataFilesIfExist() -+ - // Register this fuzzer instance with its queue so that it is possible to - // obtain a reference to the Fuzzer instance when running on its queue. - // This creates a reference cycle, but Fuzzer instances aren't expected -@@ -185,6 +202,36 @@ public class Fuzzer { - self.queue.setSpecific(key: Fuzzer.dispatchQueueKey, value: self) - } - -+ private func jitMetadataFileURL(_ seed: UInt32) -> URL { -+ let jitMetadataFileName = String(format: "%u_position_dump.json", seed) -+ return URL(fileURLWithPath: "/tmp").appendingPathComponent(jitMetadataFileName) -+ } -+ -+ private func outputMetadataFileURL(_ seed: UInt32) -> URL { -+ let jitMetadataFileName = String(format: "%u_output_dump.txt", seed) -+ return URL(fileURLWithPath: "/tmp").appendingPathComponent(jitMetadataFileName) -+ } -+ -+ private func jitMetadataFileExists(_ seed: UInt32) -> Bool { -+ return FileManager.default.fileExists(atPath: self.jitMetadataFileURL(seed).path) -+ } -+ -+ private func outputMetadataFileExists(_ seed: UInt32) -> Bool { -+ return FileManager.default.fileExists(atPath: self.outputMetadataFileURL(seed).path) -+ } -+ -+ -+ private func removeMetadataFilesIfExist(_ seed: UInt32) { -+ if self.jitMetadataFileExists(seed) { -+ let fileURL = jitMetadataFileURL(seed); -+ try? FileManager.default.removeItem(atPath: fileURL.path) -+ } -+ if self.outputMetadataFileExists(seed) { -+ let fileURL = outputMetadataFileURL(seed); -+ try? FileManager.default.removeItem(atPath: fileURL.path) -+ } -+ } -+ - /// Returns the fuzzer for the active DispatchQueue. - public static var current: Fuzzer? { - return DispatchQueue.getSpecific(key: Fuzzer.dispatchQueueKey) -@@ -235,6 +282,7 @@ public class Fuzzer { - assert(!isInitialized) - - // Initialize the script runner first so we are able to execute programs. -+ referenceRunner.initialize(with: self) - runner.initialize(with: self) - - // Then initialize all components. -@@ -383,6 +431,9 @@ public class Fuzzer { - // from another instance triggers a crash in this instance. - processCrash(program, withSignal: termsig, withStderr: execution.stderr, withStdout: execution.stdout, origin: origin, withExectime: execution.execTime) - -+ case .differential: -+ processDifferential(program, withStderr: execution.stderr, withStdout: execution.unOptStdout!, origin: origin, optStdout: execution.optStdout!) -+ - case .succeeded: - var imported = false - if let aspects = evaluator.evaluate(execution) { -@@ -403,6 +454,18 @@ public class Fuzzer { - return execution.outcome - } - -+ public func importDifferential(_ program: Program, origin: ProgramOrigin) { -+ dispatchPrecondition(condition: .onQueue(queue)) -+ let execution = execute(program, purpose: .other) -+ if case .differential = execution.outcome { -+ processDifferential(program, withStderr: execution.stderr, withStdout: execution.unOptStdout!, origin: origin, optStdout: execution.optStdout!) -+ } else { -+ // Non-deterministic differential -+ dispatchEvent(events.DifferentialFound, data: (program, behaviour: .flaky, origin: origin, opt_stdout: "remote import", unopt_stdout: "remote import", reproducesInNonReplMode: false)) -+ } -+ } -+ -+ - /// Imports a crashing program into this fuzzer. - /// - /// Similar to importProgram, but will make sure to generate a CrashFound event even if the crash does not reproduce. -@@ -449,6 +512,133 @@ public class Fuzzer { - return currentCorpusImportJob.progress() - } - -+ public enum DifferentialExecute { -+ case force -+ case disabled -+ case auto -+ } -+ -+ -+ private func formatDate() -> String { -+ let formatter = DateFormatter() -+ formatter.dateFormat = "yyyyMMddHHmmss" -+ return formatter.string(from: Date()) -+ } -+ -+ func crashReproducesWithoutDumping(_ program: Program, _ script: String) -> Bool { -+ // write script to tmp file -+ let filename = "crash_repro_program_\(self.formatDate())_\(program.id).js" -+ let fileURL = URL(fileURLWithPath: "/tmp").appendingPathComponent(filename) -+ -+ do { -+ try script.write(to: fileURL, atomically: false, encoding: String.Encoding.utf8) -+ } catch { -+ logger.error("Failed to write file \(fileURL): \(error)") -+ try? FileManager.default.removeItem(atPath: fileURL.path) -+ return false -+ } -+ -+ let task = Process() -+ -+ let d8Path = self.runner.processArguments[0] -+ let d8SDumpingFlags = ["--turbofan-dumping", "--generate-dump-positions", "--verify-heap-on-jit-dump", "--maglev-dumping", "--sparkplug-dumping"] -+ let d8FlagsWithoutDumping = self.runner.processArguments.filter { !d8SDumpingFlags.contains($0) && $0 != d8Path } -+ -+ let errorPipe = Pipe() -+ -+ task.executableURL = URL(fileURLWithPath: "/usr/bin/timeout") -+ task.arguments = ["15", d8Path] + d8FlagsWithoutDumping + [fileURL.path] -+ task.standardOutput = FileHandle.nullDevice -+ task.standardError = errorPipe -+ -+ do { -+ try task.run() -+ } catch let error { -+ logger.error(error.localizedDescription) -+ try? FileManager.default.removeItem(atPath: fileURL.path) -+ return false -+ } -+ -+ task.waitUntilExit() -+ -+ if task.isRunning { -+ task.terminate() -+ try? FileManager.default.removeItem(atPath: fileURL.path) -+ return false -+ } -+ -+ try? FileManager.default.removeItem(atPath: fileURL.path) -+ -+ let stderr = String(data: errorPipe.fileHandleForReading.readDataToEndOfFile(), encoding: String.Encoding.utf8) -+ -+ if stderr == nil { -+ return false -+ } -+ for p in execPoison { -+ if stderr!.contains(p) { -+ return false -+ } -+ } -+ return task.terminationStatus != 0 && task.terminationStatus != 124 -+ } -+ -+ func differentialReproducesInNonReplMode(_ program: Program, _ script: String, _ differentialFuzzingPositionDumpSeed: UInt32) -> Bool { -+ // write script to tmp file -+ let filename = "program_\(self.formatDate())_\(program.id)_\(differentialFuzzingPositionDumpSeed).js" -+ let fileURL = URL(fileURLWithPath: "/tmp").appendingPathComponent(filename) -+ -+ // also prepend a filter for benign differentials here -+ let prependedScript = prependJS + script -+ -+ do { -+ try prependedScript.write(to: fileURL, atomically: false, encoding: String.Encoding.utf8) -+ } catch { -+ logger.error("Failed to write file \(fileURL): \(error)") -+ return false -+ } -+ -+ let task = Process() -+ -+ let d8Path = self.runner.processArguments[0] -+ -+ task.executableURL = URL(fileURLWithPath: "/usr/bin/timeout") -+ task.arguments = ["15", self.config.relateToolPath!, "--validate", "--d8=\(d8Path)", "--poc=\(fileURL.path)"] -+ task.standardOutput = FileHandle.nullDevice -+ task.standardError = FileHandle.nullDevice -+ -+ do { -+ try task.run() -+ } catch let error { -+ logger.error(error.localizedDescription) -+ try? FileManager.default.removeItem(atPath: fileURL.path) -+ return false -+ } -+ -+ task.waitUntilExit() -+ -+ if task.isRunning { -+ task.terminate() -+ try? FileManager.default.removeItem(atPath: fileURL.path) -+ return false -+ } -+ try? FileManager.default.removeItem(atPath: fileURL.path) -+ return task.terminationStatus != 0 && task.terminationStatus != 124 -+ } -+ -+ private func readOutputDump(_ seed: UInt32) -> String? { -+ let filename = "/tmp/\(seed)_output_dump.txt" -+ let fileURL = URL(fileURLWithPath: filename) -+ -+ guard let data = try? Data(contentsOf: fileURL) else { -+ logger.warning("Failed to read output file at \(fileURL)") -+ return nil -+ } -+ -+ let asciiData = Data(data.filter { byte in byte <= 127 }) -+ -+ return String(data: asciiData, encoding: .utf8)! -+ } -+ - /// Executes a program. - /// - /// This will first lift the given FuzzIL program to the target language, then use the configured script runner to execute it. -@@ -457,17 +647,113 @@ public class Fuzzer { - /// - program: The FuzzIL program to execute. - /// - timeout: The timeout after which to abort execution. If nil, the default timeout of this fuzzer will be used. - /// - purpose: The purpose of this program execution. -+ /// - differentialExecute: force disabled or auto (default) - /// - Returns: An Execution structure representing the execution outcome. -- public func execute(_ program: Program, withTimeout timeout: UInt32? = nil, purpose: ExecutionPurpose) -> Execution { -+ public func execute(_ program: Program, withTimeout timeout: UInt32? = nil, -+ purpose: ExecutionPurpose, -+ differentialExecute: DifferentialExecute = .auto) -> Execution { - dispatchPrecondition(condition: .onQueue(queue)) - assert(runner.isInitialized) - -- let script = lifter.lift(program) -+ let startTime = Date(); -+ -+ var g = SystemRandomNumberGenerator() -+ -+ // use unique localId for the seed -+ let differentialFuzzingPositionDumpSeed = localId * 100000 + UInt32(Int.random(in: 1...99999, using: &g)); -+ -+ self.removeMetadataFilesIfExist(differentialFuzzingPositionDumpSeed) - -+ let script = lifter.lift(program) - dispatchEvent(events.PreExecute, data: (program, purpose)) -- let execution = runner.run(script, withTimeout: timeout ?? config.timeout) -+ -+ var execution = runner.run(script, withTimeout: timeout ?? config.timeout, -+ differentialFuzzingPositionDumpSeed: differentialFuzzingPositionDumpSeed) - dispatchEvent(events.PostExecute, data: execution) - -+ -+ if execution.execTime * 1000 >= Double((timeout ?? config.timeout) + 500) { -+ logger.warning("execution took longer than expected \(execution.execTime * 1000)") -+ } -+ -+ let isJitExecution = execution.compilersUsed.count >= 1 -+ -+ if isJitExecution && execution.outcome == .succeeded { -+ dispatchEvent(events.JITExecutingProgramFound, data: (program, execution.compilersUsed, true)) -+ } -+ -+ if execution.outcome == .succeeded && isJitExecution && self.jitMetadataFileExists(differentialFuzzingPositionDumpSeed) && -+ differentialExecute != .disabled && purpose != .runtimeAssistedMutation { -+ assert(referenceRunner.isInitialized) -+ -+ // read the file /tmp/{dumpling_seed}_output_dump.txt as a string -+ let opt_stdout = readOutputDump(differentialFuzzingPositionDumpSeed) -+ if opt_stdout == nil { -+ self.removeMetadataFilesIfExist(differentialFuzzingPositionDumpSeed) -+ return execution -+ } -+ -+ let diff = referenceRunner.run(script, withTimeout: (timeout ?? config.timeout) * 2, -+ differentialFuzzingPositionDumpSeed: differentialFuzzingPositionDumpSeed) -+ // sparkplug is enabled in unopt runs so log that usage as well -+ -+ if diff.compilersUsed.contains(.sparkplug) { -+ assert(!diff.compilersUsed.contains(.maglev) && !diff.compilersUsed.contains(.turbofan)) -+ dispatchEvent(events.JITExecutingProgramFound, data: (program, diff.compilersUsed, false)) -+ } -+ -+ dispatchEvent(events.PostExecute, data: diff) -+ -+ if (diff.outcome == .timedOut) { -+ // treat this as a timeout here so that we can -+ execution.outcome = .timedOut -+ } -+ if Double(diff.execTime * 1000) >= Double((timeout ?? config.timeout)*2 + 500) { -+ logger.warning("diff execution took longer than expected \(diff.execTime * 1000)") -+ } -+ -+ // execution.execTime += diff.execTime -+ if (diff.outcome == .succeeded) { -+ let unopt_stdout = readOutputDump(differentialFuzzingPositionDumpSeed) -+ if unopt_stdout == nil { -+ self.removeMetadataFilesIfExist(differentialFuzzingPositionDumpSeed) -+ return execution -+ } -+ execution.unOptStdout = unopt_stdout -+ execution.optStdout = opt_stdout -+ if !relate(opt_stdout!, with: unopt_stdout!) { -+ execution.outcome = .differential -+ execution.reproducesInNonReplMode = differentialReproducesInNonReplMode(program, script, differentialFuzzingPositionDumpSeed) -+ } -+ let now = Date() -+ execution.bugOracleTime = now.timeIntervalSince(startTime) -+ if (execution.bugOracleTime!) * 1000 >= 8000.0 { -+ logger.warning("bugOracle took longer than expected \(execution.bugOracleTime! * 1000)") -+ } -+ dispatchEvent(events.RelationPerformed, data: (program, execution)) -+ } else { -+ // return diff -+ } -+ -+ self.removeMetadataFilesIfExist(differentialFuzzingPositionDumpSeed) -+ } else { -+ self.removeMetadataFilesIfExist(differentialFuzzingPositionDumpSeed) -+ -+ if case .crashed(_) = execution.outcome { -+ var poisoned = false -+ for p in execPoison { -+ if execution.stderr.contains(p) { -+ execution.outcome = .failed(1) -+ poisoned = true -+ } -+ } -+ if !poisoned && !crashReproducesWithoutDumping(program, script) { -+ execution.outcome = .failed(2) -+ } -+ } -+ } -+ -+ // assert(!self.jitMetadataFileExists(differentialFuzzingPositionDumpSeed)) - return execution - } - -@@ -553,11 +839,10 @@ public class Fuzzer { - program.comments.add("TERMSIG: \(termsig)", at: .footer) - program.comments.add("STDERR:", at: .footer) - program.comments.add(stderr.trimmingCharacters(in: .newlines), at: .footer) -- program.comments.add("STDOUT:", at: .footer) -- program.comments.add(stdout.trimmingCharacters(in: .newlines), at: .footer) - program.comments.add("FUZZER ARGS: \(config.arguments.joined(separator: " "))", at: .footer) - program.comments.add("TARGET ARGS: \(runner.processArguments.joined(separator: " "))", at: .footer) - program.comments.add("CONTRIBUTORS: \(program.contributors.map({ $0.name }).joined(separator: ", "))", at: .footer) -+ program.comments.add("REFERENCE ARGS: \(referenceRunner.processArguments.joined(separator: " "))\n", at: .footer) - program.comments.add("EXECUTION TIME: \(Int(exectime * 1000))ms", at: .footer) - } - assert(program.comments.at(.footer)?.contains("CRASH INFO") ?? false) -@@ -583,6 +868,38 @@ public class Fuzzer { - } - } - -+ func processDifferential(_ program: Program, withStderr stderr: String, withStdout stdout: String, origin: ProgramOrigin, optStdout: String) { -+ func processCommon(_ program: Program) { -+ let hasDiffInfo = program.comments.at(.footer)?.contains("DIFFERENTIAL INFO") ?? false -+ if !hasDiffInfo { -+ program.comments.add("DIFFERENTIAL INFO\n==========\n", at: .footer) -+ -+ program.comments.add("STDERR:\n" + stderr, at: .footer) -+ program.comments.add("ARGS: \(runner.processArguments.joined(separator: " "))\n", at: .footer) -+ program.comments.add("REFERENCE ARGS: \(referenceRunner.processArguments.joined(separator: " "))\n", at: .footer) -+ } -+ assert(program.comments.at(.footer)?.contains("DIFFERENTIAL INFO") ?? false) -+ -+ // Check for uniqueness only after minimization -+ let execution = execute(program, withTimeout: self.config.timeout * 2, purpose: .other) -+ if case .differential = execution.outcome { -+ dispatchEvent(events.DifferentialFound, data: (program, .deterministic, origin, optStdout, stdout, execution.reproducesInNonReplMode!)) -+ } else { -+ dispatchEvent(events.DifferentialFound, data: (program, .flaky, origin, optStdout, stdout, false)) -+ } -+ } -+ -+ if !origin.requiresMinimization() { -+ return processCommon(program) -+ } -+ -+ fuzzGroup.enter() -+ minimizer.withMinimizedCopy(program, withAspects: ProgramAspects(outcome: .differential)) { minimizedProgram in -+ self.fuzzGroup.leave() -+ processCommon(minimizedProgram) -+ } -+ } -+ - /// Constructs a new ProgramBuilder using this fuzzing context. - public func makeBuilder(forMutating parent: Program? = nil, mode: ProgramBuilder.Mode = .aggressive) -> ProgramBuilder { - dispatchPrecondition(condition: .onQueue(queue)) -@@ -699,20 +1016,6 @@ public class Fuzzer { - return b.finalize() - } - -- // Verifies that the fuzzer is not creating a large number of core dumps -- public func checkCoreFileGeneration() { -- #if os(Linux) -- do { -- let corePattern = try String(contentsOfFile: "/proc/sys/kernel/core_pattern", encoding: String.Encoding.ascii) -- if !corePattern.hasPrefix("|/bin/false") { -- logger.fatal("Please run: sudo sysctl -w 'kernel.core_pattern=|/bin/false'") -- } -- } catch { -- logger.warning("Could not check core dump behaviour. Please ensure core_pattern is set to '|/bin/false'") -- } -- #endif -- } -- - /// Runs a number of startup tests to check whether everything is configured correctly. - public func runStartupTests() { - assert(isInitialized) -@@ -737,6 +1040,7 @@ public class Fuzzer { - let complexProgram = makeComplexProgram() - for _ in 0..<5 { - let execution = execute(complexProgram, purpose: .startup) -+ logger.info("Execution time: \(execution.execTime * 1000)ms") - maxExecutionTime = max(maxExecutionTime, execution.execTime) - } - -@@ -801,7 +1105,7 @@ public class Fuzzer { - - mutating func notifyImportOutcome(_ outcome: ExecutionOutcome) { - switch outcome { -- case .crashed: -+ case .crashed, .differential: - // This is unexpected so we don't track these - break - case .failed: -diff --git a/Sources/Fuzzilli/Modules/Statistics.swift b/Sources/Fuzzilli/Modules/Statistics.swift -index 225cea6..d81f1cf 100644 ---- a/Sources/Fuzzilli/Modules/Statistics.swift -+++ b/Sources/Fuzzilli/Modules/Statistics.swift -@@ -50,6 +50,14 @@ public class Statistics: Module { - /// This is only computed for successful executions, and so excludes e.g. samples that timed out. - private var executionTimeAvg = MovingAverage(n: 1000) - -+ /// Moving average over the cummulative time it takes in the fuzzer to get through a bug oracle run -+ /// This will consider both executions and the time spent in the python script -+ private var cumbugOracleTimeAvg = MovingAverage(n: 1000) -+ -+ private var optDumpSizeAvg = MovingAverage(n: 10000) -+ private var unOptDumpSizeAvg = MovingAverage(n: 10000) -+ -+ - /// Moving average of the number of valid programs in the last 1000 generated programs. - private var correctnessRate = MovingAverage(n: 1000) - -@@ -73,6 +81,9 @@ public class Statistics: Module { - ownData.avgProgramSize = programSizeAvg.currentValue - ownData.avgCorpusProgramSize = corpusProgramSizeAvg.currentValue - ownData.avgExecutionTime = executionTimeAvg.currentValue -+ ownData.avgBugOracleTime = cumbugOracleTimeAvg.currentValue -+ ownData.avgDumpSizeOpt = optDumpSizeAvg.currentValue -+ ownData.avgDumpSizeUnOpt = unOptDumpSizeAvg.currentValue - ownData.fuzzerOverhead = fuzzerOverheadAvg.currentValue - ownData.minimizationOverhead = minimizationOverheadAvg.currentValue - ownData.correctnessRate = correctnessRate.currentValue -@@ -88,6 +99,13 @@ public class Statistics: Module { - data.timedOutSamples += node.timedOutSamples - data.totalExecs += node.totalExecs - -+ data.relationsPerformed += node.relationsPerformed -+ data.sparkplugSamples += node.sparkplugSamples -+ data.maglevSamples += node.maglevSamples -+ data.turbofanSamples += node.turbofanSamples -+ data.jitSamples += node.jitSamples -+ -+ - if !inactiveNodes.contains(id) { - // Add fields that only have meaning for active nodes - -@@ -100,6 +118,9 @@ public class Statistics: Module { - data.avgProgramSize += node.avgProgramSize * numNodesRepresentedByData - data.avgCorpusProgramSize += node.avgCorpusProgramSize * numNodesRepresentedByData - data.avgExecutionTime += node.avgExecutionTime * numNodesRepresentedByData -+ data.avgBugOracleTime += node.avgBugOracleTime * numNodesRepresentedByData -+ data.avgDumpSizeOpt += node.avgDumpSizeOpt * numNodesRepresentedByData -+ data.avgDumpSizeUnOpt += node.avgDumpSizeUnOpt * numNodesRepresentedByData - data.execsPerSecond += node.execsPerSecond - data.fuzzerOverhead += node.fuzzerOverhead * numNodesRepresentedByData - data.minimizationOverhead += node.minimizationOverhead * numNodesRepresentedByData -@@ -116,6 +137,9 @@ public class Statistics: Module { - data.avgProgramSize /= totalNumberOfNodes - data.avgCorpusProgramSize /= totalNumberOfNodes - data.avgExecutionTime /= totalNumberOfNodes -+ data.avgBugOracleTime /= totalNumberOfNodes -+ data.avgDumpSizeOpt /= totalNumberOfNodes -+ data.avgDumpSizeUnOpt /= totalNumberOfNodes - data.fuzzerOverhead /= totalNumberOfNodes - data.minimizationOverhead /= totalNumberOfNodes - data.correctnessRate /= totalNumberOfNodes -@@ -127,6 +151,40 @@ public class Statistics: Module { - public func initialize(with fuzzer: Fuzzer) { - fuzzer.registerEventListener(for: fuzzer.events.CrashFound) { _ in - self.ownData.crashingSamples += 1 -+ -+ } -+ fuzzer.registerEventListener(for: fuzzer.events.JITExecutingProgramFound) { ev in -+ for cu in ev.compilers { -+ if (cu == .turbofan) { -+ self.ownData.turbofanSamples += 1 -+ } else if (cu == .maglev) { -+ self.ownData.maglevSamples += 1 -+ } else { -+ assert(cu == .sparkplug) -+ self.ownData.sparkplugSamples += 1 -+ } -+ } -+ if (ev.is_optimizing) { -+ assert(!ev.compilers.contains(.sparkplug)) -+ self.ownData.jitSamples += 1 -+ } -+ } -+ fuzzer.registerEventListener(for: fuzzer.events.RelationPerformed) { ev in -+ self.cumbugOracleTimeAvg.add(ev.execution.bugOracleTime!) -+ self.optDumpSizeAvg.add(ev.execution.optStdout!.count) -+ self.unOptDumpSizeAvg.add(ev.execution.unOptStdout!.count) -+ -+ self.ownData.relationsPerformed += 1 -+ } -+ fuzzer.registerEventListener(for: fuzzer.events.DifferentialFound) { ev in -+ if (ev.reproducesInNonReplMode) { -+ self.ownData.differentialSamples += 1 -+ } -+ /* -+ if self.ownData.differentialSamples > 200 { -+ fatalError("Found more than 200 differentials, stopping") -+ } -+ */ - } - fuzzer.registerEventListener(for: fuzzer.events.TimeOutFound) { _ in - self.ownData.timedOutSamples += 1 -@@ -164,6 +222,7 @@ public class Statistics: Module { - let totalTime = now.timeIntervalSince(self.lastExecDate) - self.lastExecDate = now - -+ - let overhead = 1.0 - (exec.execTime / totalTime) - self.fuzzerOverheadAvg.add(overhead) - } -diff --git a/Sources/Fuzzilli/Modules/Storage.swift b/Sources/Fuzzilli/Modules/Storage.swift -index 3600507..40581cd 100644 ---- a/Sources/Fuzzilli/Modules/Storage.swift -+++ b/Sources/Fuzzilli/Modules/Storage.swift -@@ -19,6 +19,7 @@ public class Storage: Module { - private let storageDir: String - private let crashesDir: String - private let duplicateCrashesDir: String -+ private let differentialsDir: String - private let corpusDir: String - private let statisticsDir: String - private let stateFile: String -@@ -35,6 +36,7 @@ public class Storage: Module { - self.storageDir = storageDir - self.crashesDir = storageDir + "/crashes" - self.duplicateCrashesDir = storageDir + "/crashes/duplicates" -+ self.differentialsDir = storageDir + "/differentials" - self.corpusDir = storageDir + "/corpus" - self.failedDir = storageDir + "/failed" - self.timeOutDir = storageDir + "/timeouts" -@@ -52,6 +54,7 @@ public class Storage: Module { - do { - try FileManager.default.createDirectory(atPath: crashesDir, withIntermediateDirectories: true) - try FileManager.default.createDirectory(atPath: duplicateCrashesDir, withIntermediateDirectories: true) -+ try FileManager.default.createDirectory(atPath: differentialsDir, withIntermediateDirectories: true) - try FileManager.default.createDirectory(atPath: corpusDir, withIntermediateDirectories: true) - try FileManager.default.createDirectory(atPath: statisticsDir, withIntermediateDirectories: true) - if fuzzer.config.enableDiagnostics { -@@ -95,6 +98,21 @@ public class Storage: Module { - } - } - -+ fuzzer.registerEventListener(for: fuzzer.events.DifferentialFound) { ev in -+ let filename = "program_\(self.formatDate())_\(ev.program.id)_\(ev.behaviour.rawValue)" -+ var storeDir: String; -+ if (ev.reproducesInNonReplMode) { -+ storeDir = self.differentialsDir -+ } else { -+ return -+ } -+ -+ self.storeProgram(ev.program, as: filename, in: storeDir) -+ if ev.opt_stdout != nil { -+ self.storeDifferentialDump(ev.opt_stdout!, and: ev.unopt_stdout, as: filename, in: storeDir) -+ } -+ } -+ - fuzzer.registerEventListener(for: fuzzer.events.InterestingProgramFound) { ev in - let filename = "program_\(self.formatDate())_\(ev.program.id)" - self.storeProgram(ev.program, as: filename, in: self.corpusDir) -@@ -103,7 +121,7 @@ public class Storage: Module { - if fuzzer.config.enableDiagnostics { - fuzzer.registerEventListener(for: fuzzer.events.DiagnosticsEvent) { ev in - let filename = "\(self.formatDate())_\(ev.name)_\(String(currentMillis()))" -- let url = URL(fileURLWithPath: self.diagnosticsDir + filename + ".diag") -+ let url = URL(fileURLWithPath: self.diagnosticsDir + "/" + filename + ".diag") - self.createFile(url, withContent: ev.content) - } - -@@ -144,6 +162,13 @@ public class Storage: Module { - } - } - -+ private func storeDifferentialDump(_ opt: String, and unopt: String, as filename: String, in directory: String) { -+ let opt_url = URL(fileURLWithPath: "\(directory)/\(filename)_opt.txt") -+ let unopt_url = URL(fileURLWithPath: "\(directory)/\(filename)_unopt.txt") -+ createFile(opt_url, withContent: opt) -+ createFile(unopt_url, withContent: unopt) -+ } -+ - private func storeProgram(_ program: Program, as filename: String, in directory: String) { - // Always include comments when writing programs to disk - let options = LiftingOptions.includeComments -diff --git a/Sources/Fuzzilli/Modules/Sync.swift b/Sources/Fuzzilli/Modules/Sync.swift -index 241b5f4..dbf074e 100644 ---- a/Sources/Fuzzilli/Modules/Sync.swift -+++ b/Sources/Fuzzilli/Modules/Sync.swift -@@ -83,6 +83,10 @@ enum MessageType: UInt32 { - - // Log messages are forwarded from child to parent nides. - case log = 6 -+ -+ // A program that caused a differential. -+ // Only sent from a children to their parent. -+ case differentialProgram = 7 - } - - /// Distributed fuzzing nodes can be configured to only share their corpus in one direction in the tree. -@@ -243,6 +247,15 @@ public class DistributedFuzzingParentNode: DistributedFuzzingNode, Module { - logger.warning("Received malformed program from child node: \(error)") - } - -+ case .differentialProgram: -+ do { -+ let proto = try Fuzzilli_Protobuf_Program(serializedData: data) -+ let program = try Program(from: proto) -+ fuzzer.importDifferential(program, origin: .child(id: child)) -+ } catch { -+ logger.warning("Received malformed program from child node: \(error)") -+ } -+ - case .interestingProgram: - guard shouldAcceptCorpusSamplesFromChildren() else { - logger.warning("Received corpus sample from child node but not configured to accept them (corpus synchronization mode is \(corpusSynchronizationMode)). Ignoring message.") -@@ -357,6 +370,10 @@ public class DistributedFuzzingChildNode: DistributedFuzzingNode, Module { - self.sendProgram(ev.program, as: .crashingProgram) - } - -+ fuzzer.registerEventListener(for: fuzzer.events.DifferentialFound) { ev in -+ self.sendProgram(ev.program, as: .differentialProgram) -+ } -+ - fuzzer.registerEventListener(for: fuzzer.events.Shutdown) { _ in - if !self.parentIsShuttingDown { - let shutdownGroup = DispatchGroup() -@@ -390,6 +407,26 @@ public class DistributedFuzzingChildNode: DistributedFuzzingNode, Module { - } - } - -+ // remove every *_output_dump.txt and *_position_dump.json that are older than 5mins -+ fuzzer.timers.scheduleTask(every: 10 * Minutes) { -+ let dumpFilesRegex = try! Regex("^\\d+_(output|position)_dump\\.(txt|json)$") -+ -+ let items = try! FileManager.default.contentsOfDirectory(atPath: "/tmp") -+ -+ for item in items { -+ if item.contains(dumpFilesRegex) { -+ do { -+ let itemPath = "/tmp/\(item)"; -+ let attr = try FileManager.default.attributesOfItem(atPath: itemPath) -+ let date = attr[FileAttributeKey.modificationDate] as? Date -+ if (-(date!.timeIntervalSinceNow) >= 5 * Minutes) { -+ try? FileManager.default.removeItem(atPath: itemPath) -+ } -+ } catch {} -+ } -+ } -+ } -+ - // Forward log events to our parent node. - fuzzer.registerEventListener(for: fuzzer.events.Log) { ev in - let msg = Fuzzilli_Protobuf_LogMessage.with { -@@ -461,6 +498,7 @@ public class DistributedFuzzingChildNode: DistributedFuzzingNode, Module { - } - - case .crashingProgram, -+ .differentialProgram, - .statistics, - .log: - logger.error("Received unexpected message: \(messageType)") -@@ -468,7 +506,7 @@ public class DistributedFuzzingChildNode: DistributedFuzzingNode, Module { - } - - private func sendProgram(_ program: Program, as type: MessageType) { -- assert(type == .interestingProgram || type == .crashingProgram) -+ assert(type == .interestingProgram || type == .crashingProgram || type == .differentialProgram) - let proto = program.asProtobuf() - guard let payload = try? proto.serializedData() else { - return logger.error("Failed to serialize program") -diff --git a/Sources/Fuzzilli/Mutators/RuntimeAssistedMutator.swift b/Sources/Fuzzilli/Mutators/RuntimeAssistedMutator.swift -index 67498ea..1926cf4 100644 ---- a/Sources/Fuzzilli/Mutators/RuntimeAssistedMutator.swift -+++ b/Sources/Fuzzilli/Mutators/RuntimeAssistedMutator.swift -@@ -89,7 +89,7 @@ public class RuntimeAssistedMutator: Mutator { - assert(instrumentedProgram.code.contains(where: { $0.op is JsInternalOperation })) - - // Execute the instrumented program (with a higher timeout) and collect the output. -- let execution = fuzzer.execute(instrumentedProgram, withTimeout: fuzzer.config.timeout * 4, purpose: .runtimeAssistedMutation) -+ let execution = fuzzer.execute(instrumentedProgram, withTimeout: fuzzer.config.timeout * 4, purpose: .runtimeAssistedMutation, differentialExecute: .disabled) - switch execution.outcome { - case .failed(_): - // We generally do not expect the instrumentation code itself to cause runtime exceptions. Even if it performs new actions those should be guarded with try-catch. -@@ -114,6 +114,8 @@ public class RuntimeAssistedMutator: Mutator { - case .succeeded: - // The expected case. - break -+ case .differential: -+ fatalError("Differential result impossible") - } - - // Process the output to build the mutated program. -diff --git a/Sources/Fuzzilli/Protobuf/operations.pb.swift b/Sources/Fuzzilli/Protobuf/operations.pb.swift -index f01037f..f40ff9b 100644 ---- a/Sources/Fuzzilli/Protobuf/operations.pb.swift -+++ b/Sources/Fuzzilli/Protobuf/operations.pb.swift -@@ -72,7 +72,7 @@ public enum Fuzzilli_Protobuf_PropertyType: SwiftProtobuf.Enum { - - extension Fuzzilli_Protobuf_PropertyType: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. -- public static let allCases: [Fuzzilli_Protobuf_PropertyType] = [ -+ public static var allCases: [Fuzzilli_Protobuf_PropertyType] = [ - .value, - .getter, - .setter, -@@ -132,7 +132,7 @@ public enum Fuzzilli_Protobuf_UnaryOperator: SwiftProtobuf.Enum { - - extension Fuzzilli_Protobuf_UnaryOperator: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. -- public static let allCases: [Fuzzilli_Protobuf_UnaryOperator] = [ -+ public static var allCases: [Fuzzilli_Protobuf_UnaryOperator] = [ - .preInc, - .preDec, - .postInc, -@@ -214,7 +214,7 @@ public enum Fuzzilli_Protobuf_BinaryOperator: SwiftProtobuf.Enum { - - extension Fuzzilli_Protobuf_BinaryOperator: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. -- public static let allCases: [Fuzzilli_Protobuf_BinaryOperator] = [ -+ public static var allCases: [Fuzzilli_Protobuf_BinaryOperator] = [ - .add, - .sub, - .mul, -@@ -284,7 +284,7 @@ public enum Fuzzilli_Protobuf_Comparator: SwiftProtobuf.Enum { - - extension Fuzzilli_Protobuf_Comparator: CaseIterable { - // The compiler won't synthesize support with the UNRECOGNIZED case. -- public static let allCases: [Fuzzilli_Protobuf_Comparator] = [ -+ public static var allCases: [Fuzzilli_Protobuf_Comparator] = [ - .equal, - .strictEqual, - .notEqual, -@@ -1561,6 +1561,16 @@ public struct Fuzzilli_Protobuf_Return { - public init() {} - } - -+public struct Fuzzilli_Protobuf_DifferentialHash { -+ // 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_Yield { - // SwiftProtobuf.Message conformance is added in an extension below. See the - // `Message` and `Message+*Additions` files in the SwiftProtobuf library for -@@ -2511,192 +2521,6 @@ public struct Fuzzilli_Protobuf_Print { - public init() {} - } - --#if swift(>=5.5) && canImport(_Concurrency) --extension Fuzzilli_Protobuf_PropertyType: @unchecked Sendable {} --extension Fuzzilli_Protobuf_UnaryOperator: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BinaryOperator: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Comparator: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Parameters: @unchecked Sendable {} --extension Fuzzilli_Protobuf_LoadInteger: @unchecked Sendable {} --extension Fuzzilli_Protobuf_LoadBigInt: @unchecked Sendable {} --extension Fuzzilli_Protobuf_LoadFloat: @unchecked Sendable {} --extension Fuzzilli_Protobuf_LoadString: @unchecked Sendable {} --extension Fuzzilli_Protobuf_LoadBoolean: @unchecked Sendable {} --extension Fuzzilli_Protobuf_LoadUndefined: @unchecked Sendable {} --extension Fuzzilli_Protobuf_LoadNull: @unchecked Sendable {} --extension Fuzzilli_Protobuf_LoadThis: @unchecked Sendable {} --extension Fuzzilli_Protobuf_LoadArguments: @unchecked Sendable {} --extension Fuzzilli_Protobuf_LoadRegExp: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginObjectLiteral: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ObjectLiteralAddProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ObjectLiteralAddElement: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ObjectLiteralAddComputedProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ObjectLiteralCopyProperties: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ObjectLiteralSetPrototype: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginObjectLiteralMethod: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndObjectLiteralMethod: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginObjectLiteralComputedMethod: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndObjectLiteralComputedMethod: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginObjectLiteralGetter: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndObjectLiteralGetter: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginObjectLiteralSetter: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndObjectLiteralSetter: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndObjectLiteral: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginClassDefinition: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginClassConstructor: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndClassConstructor: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ClassAddInstanceProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ClassAddInstanceElement: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ClassAddInstanceComputedProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginClassInstanceMethod: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndClassInstanceMethod: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginClassInstanceGetter: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndClassInstanceGetter: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginClassInstanceSetter: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndClassInstanceSetter: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ClassAddStaticProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ClassAddStaticElement: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ClassAddStaticComputedProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginClassStaticInitializer: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndClassStaticInitializer: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginClassStaticMethod: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndClassStaticMethod: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginClassStaticGetter: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndClassStaticGetter: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginClassStaticSetter: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndClassStaticSetter: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ClassAddPrivateInstanceProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginClassPrivateInstanceMethod: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndClassPrivateInstanceMethod: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ClassAddPrivateStaticProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginClassPrivateStaticMethod: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndClassPrivateStaticMethod: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndClassDefinition: @unchecked Sendable {} --extension Fuzzilli_Protobuf_CreateArray: @unchecked Sendable {} --extension Fuzzilli_Protobuf_CreateIntArray: @unchecked Sendable {} --extension Fuzzilli_Protobuf_CreateFloatArray: @unchecked Sendable {} --extension Fuzzilli_Protobuf_CreateTemplateString: @unchecked Sendable {} --extension Fuzzilli_Protobuf_CreateArrayWithSpread: @unchecked Sendable {} --extension Fuzzilli_Protobuf_LoadBuiltin: @unchecked Sendable {} --extension Fuzzilli_Protobuf_GetProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_SetProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_UpdateProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_DeleteProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ConfigureProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_GetElement: @unchecked Sendable {} --extension Fuzzilli_Protobuf_SetElement: @unchecked Sendable {} --extension Fuzzilli_Protobuf_UpdateElement: @unchecked Sendable {} --extension Fuzzilli_Protobuf_DeleteElement: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ConfigureElement: @unchecked Sendable {} --extension Fuzzilli_Protobuf_GetComputedProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_SetComputedProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_UpdateComputedProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_DeleteComputedProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ConfigureComputedProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_TypeOf: @unchecked Sendable {} --extension Fuzzilli_Protobuf_TestInstanceOf: @unchecked Sendable {} --extension Fuzzilli_Protobuf_TestIn: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginPlainFunction: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndPlainFunction: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginArrowFunction: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndArrowFunction: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginGeneratorFunction: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndGeneratorFunction: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginAsyncFunction: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndAsyncFunction: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginAsyncArrowFunction: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndAsyncArrowFunction: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginAsyncGeneratorFunction: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndAsyncGeneratorFunction: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginConstructor: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndConstructor: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Return: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Yield: @unchecked Sendable {} --extension Fuzzilli_Protobuf_YieldEach: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Await: @unchecked Sendable {} --extension Fuzzilli_Protobuf_CallFunction: @unchecked Sendable {} --extension Fuzzilli_Protobuf_CallFunctionWithSpread: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Construct: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ConstructWithSpread: @unchecked Sendable {} --extension Fuzzilli_Protobuf_CallMethod: @unchecked Sendable {} --extension Fuzzilli_Protobuf_CallMethodWithSpread: @unchecked Sendable {} --extension Fuzzilli_Protobuf_CallComputedMethod: @unchecked Sendable {} --extension Fuzzilli_Protobuf_CallComputedMethodWithSpread: @unchecked Sendable {} --extension Fuzzilli_Protobuf_UnaryOperation: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BinaryOperation: @unchecked Sendable {} --extension Fuzzilli_Protobuf_TernaryOperation: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Update: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Dup: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Reassign: @unchecked Sendable {} --extension Fuzzilli_Protobuf_DestructArray: @unchecked Sendable {} --extension Fuzzilli_Protobuf_DestructArrayAndReassign: @unchecked Sendable {} --extension Fuzzilli_Protobuf_DestructObject: @unchecked Sendable {} --extension Fuzzilli_Protobuf_DestructObjectAndReassign: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Compare: @unchecked Sendable {} --extension Fuzzilli_Protobuf_LoadNamedVariable: @unchecked Sendable {} --extension Fuzzilli_Protobuf_StoreNamedVariable: @unchecked Sendable {} --extension Fuzzilli_Protobuf_DefineNamedVariable: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Eval: @unchecked Sendable {} --extension Fuzzilli_Protobuf_CallSuperConstructor: @unchecked Sendable {} --extension Fuzzilli_Protobuf_CallSuperMethod: @unchecked Sendable {} --extension Fuzzilli_Protobuf_GetPrivateProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_SetPrivateProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_UpdatePrivateProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_CallPrivateMethod: @unchecked Sendable {} --extension Fuzzilli_Protobuf_GetSuperProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_SetSuperProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_GetComputedSuperProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_SetComputedSuperProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_UpdateSuperProperty: @unchecked Sendable {} --extension Fuzzilli_Protobuf_LoadNewTarget: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Explore: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Probe: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Fixup: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginWith: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndWith: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginIf: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginElse: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndIf: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginSwitch: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginSwitchCase: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginSwitchDefaultCase: @unchecked Sendable {} --extension Fuzzilli_Protobuf_SwitchBreak: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndSwitchCase: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndSwitch: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginWhileLoopHeader: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginWhileLoopBody: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndWhileLoop: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginDoWhileLoopBody: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginDoWhileLoopHeader: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndDoWhileLoop: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginForLoopInitializer: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginForLoopCondition: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginForLoopAfterthought: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginForLoopBody: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndForLoop: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginForInLoop: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndForInLoop: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginForOfLoop: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginForOfLoopWithDestruct: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndForOfLoop: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginRepeatLoop: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndRepeatLoop: @unchecked Sendable {} --extension Fuzzilli_Protobuf_LoopBreak: @unchecked Sendable {} --extension Fuzzilli_Protobuf_LoopContinue: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginTry: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginCatch: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginFinally: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndTryCatch: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndTryCatchFinally: @unchecked Sendable {} --extension Fuzzilli_Protobuf_ThrowException: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginCodeString: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndCodeString: @unchecked Sendable {} --extension Fuzzilli_Protobuf_BeginBlockStatement: @unchecked Sendable {} --extension Fuzzilli_Protobuf_EndBlockStatement: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Nop: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Print: @unchecked Sendable {} --#endif // swift(>=5.5) && canImport(_Concurrency) -- - // MARK: - Code below here is support for the SwiftProtobuf runtime. - - fileprivate let _protobuf_package = "fuzzilli.protobuf" -@@ -2764,12 +2588,9 @@ extension Fuzzilli_Protobuf_Parameters: SwiftProtobuf.Message, SwiftProtobuf._Me - - 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.decodeSingularUInt32Field(value: &self.count) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.hasRest_p) }() -+ case 1: try decoder.decodeSingularUInt32Field(value: &self.count) -+ case 2: try decoder.decodeSingularBoolField(value: &self.hasRest_p) - default: break - } - } -@@ -2801,11 +2622,8 @@ extension Fuzzilli_Protobuf_LoadInteger: SwiftProtobuf.Message, SwiftProtobuf._M - - 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.decodeSingularInt64Field(value: &self.value) }() -+ case 1: try decoder.decodeSingularInt64Field(value: &self.value) - default: break - } - } -@@ -2833,11 +2651,8 @@ extension Fuzzilli_Protobuf_LoadBigInt: SwiftProtobuf.Message, SwiftProtobuf._Me - - 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.decodeSingularInt64Field(value: &self.value) }() -+ case 1: try decoder.decodeSingularInt64Field(value: &self.value) - default: break - } - } -@@ -2865,11 +2680,8 @@ extension Fuzzilli_Protobuf_LoadFloat: SwiftProtobuf.Message, SwiftProtobuf._Mes - - 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.decodeSingularDoubleField(value: &self.value) }() -+ case 1: try decoder.decodeSingularDoubleField(value: &self.value) - default: break - } - } -@@ -2897,11 +2709,8 @@ extension Fuzzilli_Protobuf_LoadString: SwiftProtobuf.Message, SwiftProtobuf._Me - - 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.value) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.value) - default: break - } - } -@@ -2929,11 +2738,8 @@ extension Fuzzilli_Protobuf_LoadBoolean: SwiftProtobuf.Message, SwiftProtobuf._M - - 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.decodeSingularBoolField(value: &self.value) }() -+ case 1: try decoder.decodeSingularBoolField(value: &self.value) - default: break - } - } -@@ -3038,12 +2844,9 @@ extension Fuzzilli_Protobuf_LoadRegExp: SwiftProtobuf.Message, SwiftProtobuf._Me - - 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.pattern) }() -- case 2: try { try decoder.decodeSingularUInt32Field(value: &self.flags) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.pattern) -+ case 2: try decoder.decodeSingularUInt32Field(value: &self.flags) - default: break - } - } -@@ -3094,11 +2897,8 @@ extension Fuzzilli_Protobuf_ObjectLiteralAddProperty: SwiftProtobuf.Message, Swi - - 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.propertyName) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) - default: break - } - } -@@ -3126,11 +2926,8 @@ extension Fuzzilli_Protobuf_ObjectLiteralAddElement: SwiftProtobuf.Message, Swif - - 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.decodeSingularInt64Field(value: &self.index) }() -+ case 1: try decoder.decodeSingularInt64Field(value: &self.index) - default: break - } - } -@@ -3216,28 +3013,21 @@ extension Fuzzilli_Protobuf_BeginObjectLiteralMethod: SwiftProtobuf.Message, Swi - - 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.methodName) }() -- case 2: try { try decoder.decodeSingularMessageField(value: &self._parameters) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.methodName) -+ case 2: 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 - if !self.methodName.isEmpty { - try visitor.visitSingularStringField(value: self.methodName, fieldNumber: 1) - } -- try { if let v = self._parameters { -+ if let v = self._parameters { - try visitor.visitSingularMessageField(value: v, fieldNumber: 2) -- } }() -+ } - try unknownFields.traverse(visitor: &visitor) - } - -@@ -3276,24 +3066,17 @@ extension Fuzzilli_Protobuf_BeginObjectLiteralComputedMethod: SwiftProtobuf.Mess - - 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) }() -+ case 1: 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 { -+ if let v = self._parameters { - try visitor.visitSingularMessageField(value: v, fieldNumber: 1) -- } }() -+ } - try unknownFields.traverse(visitor: &visitor) - } - -@@ -3331,11 +3114,8 @@ extension Fuzzilli_Protobuf_BeginObjectLiteralGetter: SwiftProtobuf.Message, Swi - - 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.propertyName) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) - default: break - } - } -@@ -3382,11 +3162,8 @@ extension Fuzzilli_Protobuf_BeginObjectLiteralSetter: SwiftProtobuf.Message, Swi - - 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.propertyName) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) - default: break - } - } -@@ -3452,11 +3229,8 @@ extension Fuzzilli_Protobuf_BeginClassDefinition: SwiftProtobuf.Message, SwiftPr - - 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.decodeSingularBoolField(value: &self.hasSuperclass_p) }() -+ case 1: try decoder.decodeSingularBoolField(value: &self.hasSuperclass_p) - default: break - } - } -@@ -3484,24 +3258,17 @@ extension Fuzzilli_Protobuf_BeginClassConstructor: SwiftProtobuf.Message, SwiftP - - 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) }() -+ case 1: 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 { -+ if let v = self._parameters { - try visitor.visitSingularMessageField(value: v, fieldNumber: 1) -- } }() -+ } - try unknownFields.traverse(visitor: &visitor) - } - -@@ -3540,12 +3307,9 @@ extension Fuzzilli_Protobuf_ClassAddInstanceProperty: SwiftProtobuf.Message, Swi - - 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.propertyName) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.hasValue_p) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) -+ case 2: try decoder.decodeSingularBoolField(value: &self.hasValue_p) - default: break - } - } -@@ -3578,12 +3342,9 @@ extension Fuzzilli_Protobuf_ClassAddInstanceElement: SwiftProtobuf.Message, Swif - - 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.decodeSingularInt64Field(value: &self.index) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.hasValue_p) }() -+ case 1: try decoder.decodeSingularInt64Field(value: &self.index) -+ case 2: try decoder.decodeSingularBoolField(value: &self.hasValue_p) - default: break - } - } -@@ -3615,11 +3376,8 @@ extension Fuzzilli_Protobuf_ClassAddInstanceComputedProperty: SwiftProtobuf.Mess - - 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.decodeSingularBoolField(value: &self.hasValue_p) }() -+ case 1: try decoder.decodeSingularBoolField(value: &self.hasValue_p) - default: break - } - } -@@ -3648,28 +3406,21 @@ extension Fuzzilli_Protobuf_BeginClassInstanceMethod: SwiftProtobuf.Message, Swi - - 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.methodName) }() -- case 2: try { try decoder.decodeSingularMessageField(value: &self._parameters) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.methodName) -+ case 2: 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 - if !self.methodName.isEmpty { - try visitor.visitSingularStringField(value: self.methodName, fieldNumber: 1) - } -- try { if let v = self._parameters { -+ if let v = self._parameters { - try visitor.visitSingularMessageField(value: v, fieldNumber: 2) -- } }() -+ } - try unknownFields.traverse(visitor: &visitor) - } - -@@ -3708,11 +3459,8 @@ extension Fuzzilli_Protobuf_BeginClassInstanceGetter: SwiftProtobuf.Message, Swi - - 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.propertyName) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) - default: break - } - } -@@ -3759,11 +3507,8 @@ extension Fuzzilli_Protobuf_BeginClassInstanceSetter: SwiftProtobuf.Message, Swi - - 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.propertyName) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) - default: break - } - } -@@ -3811,12 +3556,9 @@ extension Fuzzilli_Protobuf_ClassAddStaticProperty: SwiftProtobuf.Message, Swift - - 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.propertyName) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.hasValue_p) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) -+ case 2: try decoder.decodeSingularBoolField(value: &self.hasValue_p) - default: break - } - } -@@ -3849,12 +3591,9 @@ extension Fuzzilli_Protobuf_ClassAddStaticElement: SwiftProtobuf.Message, SwiftP - - 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.decodeSingularInt64Field(value: &self.index) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.hasValue_p) }() -+ case 1: try decoder.decodeSingularInt64Field(value: &self.index) -+ case 2: try decoder.decodeSingularBoolField(value: &self.hasValue_p) - default: break - } - } -@@ -3886,11 +3625,8 @@ extension Fuzzilli_Protobuf_ClassAddStaticComputedProperty: SwiftProtobuf.Messag - - 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.decodeSingularBoolField(value: &self.hasValue_p) }() -+ case 1: try decoder.decodeSingularBoolField(value: &self.hasValue_p) - default: break - } - } -@@ -3957,28 +3693,21 @@ extension Fuzzilli_Protobuf_BeginClassStaticMethod: SwiftProtobuf.Message, Swift - - 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.methodName) }() -- case 2: try { try decoder.decodeSingularMessageField(value: &self._parameters) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.methodName) -+ case 2: 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 - if !self.methodName.isEmpty { - try visitor.visitSingularStringField(value: self.methodName, fieldNumber: 1) - } -- try { if let v = self._parameters { -+ if let v = self._parameters { - try visitor.visitSingularMessageField(value: v, fieldNumber: 2) -- } }() -+ } - try unknownFields.traverse(visitor: &visitor) - } - -@@ -4017,11 +3746,8 @@ extension Fuzzilli_Protobuf_BeginClassStaticGetter: SwiftProtobuf.Message, Swift - - 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.propertyName) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) - default: break - } - } -@@ -4068,11 +3794,8 @@ extension Fuzzilli_Protobuf_BeginClassStaticSetter: SwiftProtobuf.Message, Swift - - 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.propertyName) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) - default: break - } - } -@@ -4120,12 +3843,9 @@ extension Fuzzilli_Protobuf_ClassAddPrivateInstanceProperty: SwiftProtobuf.Messa - - 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.propertyName) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.hasValue_p) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) -+ case 2: try decoder.decodeSingularBoolField(value: &self.hasValue_p) - default: break - } - } -@@ -4158,28 +3878,21 @@ extension Fuzzilli_Protobuf_BeginClassPrivateInstanceMethod: SwiftProtobuf.Messa - - 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.methodName) }() -- case 2: try { try decoder.decodeSingularMessageField(value: &self._parameters) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.methodName) -+ case 2: 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 - if !self.methodName.isEmpty { - try visitor.visitSingularStringField(value: self.methodName, fieldNumber: 1) - } -- try { if let v = self._parameters { -+ if let v = self._parameters { - try visitor.visitSingularMessageField(value: v, fieldNumber: 2) -- } }() -+ } - try unknownFields.traverse(visitor: &visitor) - } - -@@ -4219,12 +3932,9 @@ extension Fuzzilli_Protobuf_ClassAddPrivateStaticProperty: SwiftProtobuf.Message - - 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.propertyName) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.hasValue_p) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) -+ case 2: try decoder.decodeSingularBoolField(value: &self.hasValue_p) - default: break - } - } -@@ -4257,28 +3967,21 @@ extension Fuzzilli_Protobuf_BeginClassPrivateStaticMethod: SwiftProtobuf.Message - - 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.methodName) }() -- case 2: try { try decoder.decodeSingularMessageField(value: &self._parameters) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.methodName) -+ case 2: 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 - if !self.methodName.isEmpty { - try visitor.visitSingularStringField(value: self.methodName, fieldNumber: 1) - } -- try { if let v = self._parameters { -+ if let v = self._parameters { - try visitor.visitSingularMessageField(value: v, fieldNumber: 2) -- } }() -+ } - try unknownFields.traverse(visitor: &visitor) - } - -@@ -4355,11 +4058,8 @@ extension Fuzzilli_Protobuf_CreateIntArray: SwiftProtobuf.Message, SwiftProtobuf - - 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.decodeRepeatedInt64Field(value: &self.values) }() -+ case 1: try decoder.decodeRepeatedInt64Field(value: &self.values) - default: break - } - } -@@ -4387,11 +4087,8 @@ extension Fuzzilli_Protobuf_CreateFloatArray: SwiftProtobuf.Message, SwiftProtob - - 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.decodeRepeatedDoubleField(value: &self.values) }() -+ case 1: try decoder.decodeRepeatedDoubleField(value: &self.values) - default: break - } - } -@@ -4419,11 +4116,8 @@ extension Fuzzilli_Protobuf_CreateTemplateString: SwiftProtobuf.Message, SwiftPr - - 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.decodeRepeatedStringField(value: &self.parts) }() -+ case 1: try decoder.decodeRepeatedStringField(value: &self.parts) - default: break - } - } -@@ -4451,11 +4145,8 @@ extension Fuzzilli_Protobuf_CreateArrayWithSpread: SwiftProtobuf.Message, SwiftP - - 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.decodeRepeatedBoolField(value: &self.spreads) }() -+ case 1: try decoder.decodeRepeatedBoolField(value: &self.spreads) - default: break - } - } -@@ -4483,11 +4174,8 @@ extension Fuzzilli_Protobuf_LoadBuiltin: SwiftProtobuf.Message, SwiftProtobuf._M - - 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.builtinName) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.builtinName) - default: break - } - } -@@ -4516,12 +4204,9 @@ extension Fuzzilli_Protobuf_GetProperty: SwiftProtobuf.Message, SwiftProtobuf._M - - 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.propertyName) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.isGuarded) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) -+ case 2: try decoder.decodeSingularBoolField(value: &self.isGuarded) - default: break - } - } -@@ -4553,11 +4238,8 @@ extension Fuzzilli_Protobuf_SetProperty: SwiftProtobuf.Message, SwiftProtobuf._M - - 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.propertyName) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) - default: break - } - } -@@ -4586,12 +4268,9 @@ extension Fuzzilli_Protobuf_UpdateProperty: SwiftProtobuf.Message, SwiftProtobuf - - 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.propertyName) }() -- case 2: try { try decoder.decodeSingularEnumField(value: &self.op) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) -+ case 2: try decoder.decodeSingularEnumField(value: &self.op) - default: break - } - } -@@ -4624,12 +4303,9 @@ extension Fuzzilli_Protobuf_DeleteProperty: SwiftProtobuf.Message, SwiftProtobuf - - 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.propertyName) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.isGuarded) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) -+ case 2: try decoder.decodeSingularBoolField(value: &self.isGuarded) - default: break - } - } -@@ -4665,15 +4341,12 @@ extension Fuzzilli_Protobuf_ConfigureProperty: SwiftProtobuf.Message, SwiftProto - - 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.propertyName) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.isWritable) }() -- case 3: try { try decoder.decodeSingularBoolField(value: &self.isConfigurable) }() -- case 4: try { try decoder.decodeSingularBoolField(value: &self.isEnumerable) }() -- case 5: try { try decoder.decodeSingularEnumField(value: &self.type) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) -+ case 2: try decoder.decodeSingularBoolField(value: &self.isWritable) -+ case 3: try decoder.decodeSingularBoolField(value: &self.isConfigurable) -+ case 4: try decoder.decodeSingularBoolField(value: &self.isEnumerable) -+ case 5: try decoder.decodeSingularEnumField(value: &self.type) - default: break - } - } -@@ -4718,12 +4391,9 @@ extension Fuzzilli_Protobuf_GetElement: SwiftProtobuf.Message, SwiftProtobuf._Me - - 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.decodeSingularInt64Field(value: &self.index) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.isGuarded) }() -+ case 1: try decoder.decodeSingularInt64Field(value: &self.index) -+ case 2: try decoder.decodeSingularBoolField(value: &self.isGuarded) - default: break - } - } -@@ -4755,11 +4425,8 @@ extension Fuzzilli_Protobuf_SetElement: SwiftProtobuf.Message, SwiftProtobuf._Me - - 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.decodeSingularInt64Field(value: &self.index) }() -+ case 1: try decoder.decodeSingularInt64Field(value: &self.index) - default: break - } - } -@@ -4788,12 +4455,9 @@ extension Fuzzilli_Protobuf_UpdateElement: SwiftProtobuf.Message, SwiftProtobuf. - - 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.decodeSingularInt64Field(value: &self.index) }() -- case 2: try { try decoder.decodeSingularEnumField(value: &self.op) }() -+ case 1: try decoder.decodeSingularInt64Field(value: &self.index) -+ case 2: try decoder.decodeSingularEnumField(value: &self.op) - default: break - } - } -@@ -4826,12 +4490,9 @@ extension Fuzzilli_Protobuf_DeleteElement: SwiftProtobuf.Message, SwiftProtobuf. - - 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.decodeSingularInt64Field(value: &self.index) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.isGuarded) }() -+ case 1: try decoder.decodeSingularInt64Field(value: &self.index) -+ case 2: try decoder.decodeSingularBoolField(value: &self.isGuarded) - default: break - } - } -@@ -4867,15 +4528,12 @@ extension Fuzzilli_Protobuf_ConfigureElement: SwiftProtobuf.Message, SwiftProtob - - 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.decodeSingularInt64Field(value: &self.index) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.isWritable) }() -- case 3: try { try decoder.decodeSingularBoolField(value: &self.isConfigurable) }() -- case 4: try { try decoder.decodeSingularBoolField(value: &self.isEnumerable) }() -- case 5: try { try decoder.decodeSingularEnumField(value: &self.type) }() -+ case 1: try decoder.decodeSingularInt64Field(value: &self.index) -+ case 2: try decoder.decodeSingularBoolField(value: &self.isWritable) -+ case 3: try decoder.decodeSingularBoolField(value: &self.isConfigurable) -+ case 4: try decoder.decodeSingularBoolField(value: &self.isEnumerable) -+ case 5: try decoder.decodeSingularEnumField(value: &self.type) - default: break - } - } -@@ -4919,11 +4577,8 @@ extension Fuzzilli_Protobuf_GetComputedProperty: SwiftProtobuf.Message, SwiftPro - - 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.decodeSingularBoolField(value: &self.isGuarded) }() -+ case 1: try decoder.decodeSingularBoolField(value: &self.isGuarded) - default: break - } - } -@@ -4970,11 +4625,8 @@ extension Fuzzilli_Protobuf_UpdateComputedProperty: SwiftProtobuf.Message, Swift - - 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.op) }() -+ case 1: try decoder.decodeSingularEnumField(value: &self.op) - default: break - } - } -@@ -5002,11 +4654,8 @@ extension Fuzzilli_Protobuf_DeleteComputedProperty: SwiftProtobuf.Message, Swift - - 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.decodeSingularBoolField(value: &self.isGuarded) }() -+ case 1: try decoder.decodeSingularBoolField(value: &self.isGuarded) - default: break - } - } -@@ -5037,14 +4686,11 @@ extension Fuzzilli_Protobuf_ConfigureComputedProperty: SwiftProtobuf.Message, Sw - - 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.decodeSingularBoolField(value: &self.isWritable) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.isConfigurable) }() -- case 3: try { try decoder.decodeSingularBoolField(value: &self.isEnumerable) }() -- case 4: try { try decoder.decodeSingularEnumField(value: &self.type) }() -+ case 1: try decoder.decodeSingularBoolField(value: &self.isWritable) -+ case 2: try decoder.decodeSingularBoolField(value: &self.isConfigurable) -+ case 3: try decoder.decodeSingularBoolField(value: &self.isEnumerable) -+ case 4: try decoder.decodeSingularEnumField(value: &self.type) - default: break - } - } -@@ -5142,25 +4788,18 @@ extension Fuzzilli_Protobuf_BeginPlainFunction: SwiftProtobuf.Message, SwiftProt - - 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) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.isStrict) }() -+ case 1: try decoder.decodeSingularMessageField(value: &self._parameters) -+ case 2: try decoder.decodeSingularBoolField(value: &self.isStrict) - 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 { -+ if let v = self._parameters { - try visitor.visitSingularMessageField(value: v, fieldNumber: 1) -- } }() -+ } - if self.isStrict != false { - try visitor.visitSingularBoolField(value: self.isStrict, fieldNumber: 2) - } -@@ -5203,25 +4842,18 @@ extension Fuzzilli_Protobuf_BeginArrowFunction: SwiftProtobuf.Message, SwiftProt - - 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) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.isStrict) }() -+ case 1: try decoder.decodeSingularMessageField(value: &self._parameters) -+ case 2: try decoder.decodeSingularBoolField(value: &self.isStrict) - 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 { -+ if let v = self._parameters { - try visitor.visitSingularMessageField(value: v, fieldNumber: 1) -- } }() -+ } - if self.isStrict != false { - try visitor.visitSingularBoolField(value: self.isStrict, fieldNumber: 2) - } -@@ -5264,25 +4896,18 @@ extension Fuzzilli_Protobuf_BeginGeneratorFunction: SwiftProtobuf.Message, Swift - - 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) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.isStrict) }() -+ case 1: try decoder.decodeSingularMessageField(value: &self._parameters) -+ case 2: try decoder.decodeSingularBoolField(value: &self.isStrict) - 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 { -+ if let v = self._parameters { - try visitor.visitSingularMessageField(value: v, fieldNumber: 1) -- } }() -+ } - if self.isStrict != false { - try visitor.visitSingularBoolField(value: self.isStrict, fieldNumber: 2) - } -@@ -5325,25 +4950,18 @@ extension Fuzzilli_Protobuf_BeginAsyncFunction: SwiftProtobuf.Message, SwiftProt - - 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) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.isStrict) }() -+ case 1: try decoder.decodeSingularMessageField(value: &self._parameters) -+ case 2: try decoder.decodeSingularBoolField(value: &self.isStrict) - 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 { -+ if let v = self._parameters { - try visitor.visitSingularMessageField(value: v, fieldNumber: 1) -- } }() -+ } - if self.isStrict != false { - try visitor.visitSingularBoolField(value: self.isStrict, fieldNumber: 2) - } -@@ -5386,25 +5004,18 @@ extension Fuzzilli_Protobuf_BeginAsyncArrowFunction: SwiftProtobuf.Message, Swif - - 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) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.isStrict) }() -+ case 1: try decoder.decodeSingularMessageField(value: &self._parameters) -+ case 2: try decoder.decodeSingularBoolField(value: &self.isStrict) - 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 { -+ if let v = self._parameters { - try visitor.visitSingularMessageField(value: v, fieldNumber: 1) -- } }() -+ } - if self.isStrict != false { - try visitor.visitSingularBoolField(value: self.isStrict, fieldNumber: 2) - } -@@ -5447,25 +5058,18 @@ extension Fuzzilli_Protobuf_BeginAsyncGeneratorFunction: SwiftProtobuf.Message, - - 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) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.isStrict) }() -+ case 1: try decoder.decodeSingularMessageField(value: &self._parameters) -+ case 2: try decoder.decodeSingularBoolField(value: &self.isStrict) - 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 { -+ if let v = self._parameters { - try visitor.visitSingularMessageField(value: v, fieldNumber: 1) -- } }() -+ } - if self.isStrict != false { - try visitor.visitSingularBoolField(value: self.isStrict, fieldNumber: 2) - } -@@ -5507,24 +5111,17 @@ extension Fuzzilli_Protobuf_BeginConstructor: SwiftProtobuf.Message, SwiftProtob - - 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) }() -+ case 1: 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 { -+ if let v = self._parameters { - try visitor.visitSingularMessageField(value: v, fieldNumber: 1) -- } }() -+ } - try unknownFields.traverse(visitor: &visitor) - } - -@@ -5573,6 +5170,25 @@ extension Fuzzilli_Protobuf_Return: SwiftProtobuf.Message, SwiftProtobuf._Messag - } - } - -+extension Fuzzilli_Protobuf_DifferentialHash: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { -+ public static let protoMessageName: String = _protobuf_package + ".DifferentialHash" -+ public static let _protobuf_nameMap = SwiftProtobuf._NameMap() -+ -+ public mutating func decodeMessage(decoder: inout D) throws { -+ while let _ = try decoder.nextFieldNumber() { -+ } -+ } -+ -+ public func traverse(visitor: inout V) throws { -+ try unknownFields.traverse(visitor: &visitor) -+ } -+ -+ public static func ==(lhs: Fuzzilli_Protobuf_DifferentialHash, rhs: Fuzzilli_Protobuf_DifferentialHash) -> Bool { -+ if lhs.unknownFields != rhs.unknownFields {return false} -+ return true -+ } -+} -+ - extension Fuzzilli_Protobuf_Yield: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - public static let protoMessageName: String = _protobuf_package + ".Yield" - public static let _protobuf_nameMap = SwiftProtobuf._NameMap() -@@ -5638,11 +5254,8 @@ extension Fuzzilli_Protobuf_CallFunction: SwiftProtobuf.Message, SwiftProtobuf._ - - 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.decodeSingularBoolField(value: &self.isGuarded) }() -+ case 1: try decoder.decodeSingularBoolField(value: &self.isGuarded) - default: break - } - } -@@ -5671,12 +5284,9 @@ extension Fuzzilli_Protobuf_CallFunctionWithSpread: SwiftProtobuf.Message, Swift - - 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.decodeRepeatedBoolField(value: &self.spreads) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.isGuarded) }() -+ case 1: try decoder.decodeRepeatedBoolField(value: &self.spreads) -+ case 2: try decoder.decodeSingularBoolField(value: &self.isGuarded) - default: break - } - } -@@ -5708,11 +5318,8 @@ extension Fuzzilli_Protobuf_Construct: SwiftProtobuf.Message, SwiftProtobuf._Mes - - 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.decodeSingularBoolField(value: &self.isGuarded) }() -+ case 1: try decoder.decodeSingularBoolField(value: &self.isGuarded) - default: break - } - } -@@ -5741,12 +5348,9 @@ extension Fuzzilli_Protobuf_ConstructWithSpread: SwiftProtobuf.Message, SwiftPro - - 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.decodeRepeatedBoolField(value: &self.spreads) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.isGuarded) }() -+ case 1: try decoder.decodeRepeatedBoolField(value: &self.spreads) -+ case 2: try decoder.decodeSingularBoolField(value: &self.isGuarded) - default: break - } - } -@@ -5779,12 +5383,9 @@ extension Fuzzilli_Protobuf_CallMethod: SwiftProtobuf.Message, SwiftProtobuf._Me - - 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.methodName) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.isGuarded) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.methodName) -+ case 2: try decoder.decodeSingularBoolField(value: &self.isGuarded) - default: break - } - } -@@ -5818,13 +5419,10 @@ extension Fuzzilli_Protobuf_CallMethodWithSpread: SwiftProtobuf.Message, SwiftPr - - 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.methodName) }() -- case 2: try { try decoder.decodeRepeatedBoolField(value: &self.spreads) }() -- case 3: try { try decoder.decodeSingularBoolField(value: &self.isGuarded) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.methodName) -+ case 2: try decoder.decodeRepeatedBoolField(value: &self.spreads) -+ case 3: try decoder.decodeSingularBoolField(value: &self.isGuarded) - default: break - } - } -@@ -5860,11 +5458,8 @@ extension Fuzzilli_Protobuf_CallComputedMethod: SwiftProtobuf.Message, SwiftProt - - 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.decodeSingularBoolField(value: &self.isGuarded) }() -+ case 1: try decoder.decodeSingularBoolField(value: &self.isGuarded) - default: break - } - } -@@ -5893,12 +5488,9 @@ extension Fuzzilli_Protobuf_CallComputedMethodWithSpread: SwiftProtobuf.Message, - - 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.decodeRepeatedBoolField(value: &self.spreads) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.isGuarded) }() -+ case 1: try decoder.decodeRepeatedBoolField(value: &self.spreads) -+ case 2: try decoder.decodeSingularBoolField(value: &self.isGuarded) - default: break - } - } -@@ -5930,11 +5522,8 @@ extension Fuzzilli_Protobuf_UnaryOperation: SwiftProtobuf.Message, SwiftProtobuf - - 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.op) }() -+ case 1: try decoder.decodeSingularEnumField(value: &self.op) - default: break - } - } -@@ -5962,11 +5551,8 @@ extension Fuzzilli_Protobuf_BinaryOperation: SwiftProtobuf.Message, SwiftProtobu - - 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.op) }() -+ case 1: try decoder.decodeSingularEnumField(value: &self.op) - default: break - } - } -@@ -6013,11 +5599,8 @@ extension Fuzzilli_Protobuf_Update: SwiftProtobuf.Message, SwiftProtobuf._Messag - - 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.op) }() -+ case 1: try decoder.decodeSingularEnumField(value: &self.op) - default: break - } - } -@@ -6084,12 +5667,9 @@ extension Fuzzilli_Protobuf_DestructArray: SwiftProtobuf.Message, SwiftProtobuf. - - 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.decodeRepeatedInt32Field(value: &self.indices) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.lastIsRest) }() -+ case 1: try decoder.decodeRepeatedInt32Field(value: &self.indices) -+ case 2: try decoder.decodeSingularBoolField(value: &self.lastIsRest) - default: break - } - } -@@ -6122,12 +5702,9 @@ extension Fuzzilli_Protobuf_DestructArrayAndReassign: SwiftProtobuf.Message, Swi - - 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.decodeRepeatedInt32Field(value: &self.indices) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.lastIsRest) }() -+ case 1: try decoder.decodeRepeatedInt32Field(value: &self.indices) -+ case 2: try decoder.decodeSingularBoolField(value: &self.lastIsRest) - default: break - } - } -@@ -6160,12 +5737,9 @@ extension Fuzzilli_Protobuf_DestructObject: SwiftProtobuf.Message, SwiftProtobuf - - 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.decodeRepeatedStringField(value: &self.properties) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.hasRestElement_p) }() -+ case 1: try decoder.decodeRepeatedStringField(value: &self.properties) -+ case 2: try decoder.decodeSingularBoolField(value: &self.hasRestElement_p) - default: break - } - } -@@ -6198,12 +5772,9 @@ extension Fuzzilli_Protobuf_DestructObjectAndReassign: SwiftProtobuf.Message, Sw - - 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.decodeRepeatedStringField(value: &self.properties) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.hasRestElement_p) }() -+ case 1: try decoder.decodeRepeatedStringField(value: &self.properties) -+ case 2: try decoder.decodeSingularBoolField(value: &self.hasRestElement_p) - default: break - } - } -@@ -6235,11 +5806,8 @@ extension Fuzzilli_Protobuf_Compare: SwiftProtobuf.Message, SwiftProtobuf._Messa - - 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.op) }() -+ case 1: try decoder.decodeSingularEnumField(value: &self.op) - default: break - } - } -@@ -6267,11 +5835,8 @@ extension Fuzzilli_Protobuf_LoadNamedVariable: SwiftProtobuf.Message, SwiftProto - - 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) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.variableName) - default: break - } - } -@@ -6299,11 +5864,8 @@ extension Fuzzilli_Protobuf_StoreNamedVariable: SwiftProtobuf.Message, SwiftProt - - 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) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.variableName) - default: break - } - } -@@ -6331,11 +5893,8 @@ extension Fuzzilli_Protobuf_DefineNamedVariable: SwiftProtobuf.Message, SwiftPro - - 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) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.variableName) - default: break - } - } -@@ -6364,12 +5923,9 @@ extension Fuzzilli_Protobuf_Eval: SwiftProtobuf.Message, SwiftProtobuf._MessageI - - 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.code) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.hasOutput_p) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.code) -+ case 2: try decoder.decodeSingularBoolField(value: &self.hasOutput_p) - default: break - } - } -@@ -6401,11 +5957,8 @@ extension Fuzzilli_Protobuf_CallSuperConstructor: SwiftProtobuf.Message, SwiftPr - - 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.decodeRepeatedBoolField(value: &self.spreads) }() -+ case 1: try decoder.decodeRepeatedBoolField(value: &self.spreads) - default: break - } - } -@@ -6433,11 +5986,8 @@ extension Fuzzilli_Protobuf_CallSuperMethod: SwiftProtobuf.Message, SwiftProtobu - - 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.methodName) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.methodName) - default: break - } - } -@@ -6465,11 +6015,8 @@ extension Fuzzilli_Protobuf_GetPrivateProperty: SwiftProtobuf.Message, SwiftProt - - 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.propertyName) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) - default: break - } - } -@@ -6497,11 +6044,8 @@ extension Fuzzilli_Protobuf_SetPrivateProperty: SwiftProtobuf.Message, SwiftProt - - 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.propertyName) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) - default: break - } - } -@@ -6530,12 +6074,9 @@ extension Fuzzilli_Protobuf_UpdatePrivateProperty: SwiftProtobuf.Message, SwiftP - - 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.propertyName) }() -- case 2: try { try decoder.decodeSingularEnumField(value: &self.op) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) -+ case 2: try decoder.decodeSingularEnumField(value: &self.op) - default: break - } - } -@@ -6567,11 +6108,8 @@ extension Fuzzilli_Protobuf_CallPrivateMethod: SwiftProtobuf.Message, SwiftProto - - 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.methodName) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.methodName) - default: break - } - } -@@ -6599,11 +6137,8 @@ extension Fuzzilli_Protobuf_GetSuperProperty: SwiftProtobuf.Message, SwiftProtob - - 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.propertyName) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) - default: break - } - } -@@ -6631,11 +6166,8 @@ extension Fuzzilli_Protobuf_SetSuperProperty: SwiftProtobuf.Message, SwiftProtob - - 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.propertyName) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) - default: break - } - } -@@ -6702,12 +6234,9 @@ extension Fuzzilli_Protobuf_UpdateSuperProperty: SwiftProtobuf.Message, SwiftPro - - 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.propertyName) }() -- case 2: try { try decoder.decodeSingularEnumField(value: &self.op) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.propertyName) -+ case 2: try decoder.decodeSingularEnumField(value: &self.op) - default: break - } - } -@@ -6759,12 +6288,9 @@ extension Fuzzilli_Protobuf_Explore: SwiftProtobuf.Message, SwiftProtobuf._Messa - - 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.id) }() -- case 2: try { try decoder.decodeSingularInt64Field(value: &self.rngSeed) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.id) -+ case 2: try decoder.decodeSingularInt64Field(value: &self.rngSeed) - default: break - } - } -@@ -6796,11 +6322,8 @@ extension Fuzzilli_Protobuf_Probe: SwiftProtobuf.Message, SwiftProtobuf._Message - - 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.id) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.id) - default: break - } - } -@@ -6831,14 +6354,11 @@ extension Fuzzilli_Protobuf_Fixup: SwiftProtobuf.Message, SwiftProtobuf._Message - - 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.id) }() -- case 2: try { try decoder.decodeSingularStringField(value: &self.action) }() -- case 3: try { try decoder.decodeSingularStringField(value: &self.originalOperation) }() -- case 4: try { try decoder.decodeSingularBoolField(value: &self.hasOutput_p) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.id) -+ case 2: try decoder.decodeSingularStringField(value: &self.action) -+ case 3: try decoder.decodeSingularStringField(value: &self.originalOperation) -+ case 4: try decoder.decodeSingularBoolField(value: &self.hasOutput_p) - default: break - } - } -@@ -6916,11 +6436,8 @@ extension Fuzzilli_Protobuf_BeginIf: SwiftProtobuf.Message, SwiftProtobuf._Messa - - 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.decodeSingularBoolField(value: &self.inverted) }() -+ case 1: try decoder.decodeSingularBoolField(value: &self.inverted) - default: break - } - } -@@ -7062,11 +6579,8 @@ extension Fuzzilli_Protobuf_EndSwitchCase: SwiftProtobuf.Message, SwiftProtobuf. - - 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.decodeSingularBoolField(value: &self.fallsThrough) }() -+ case 1: try decoder.decodeSingularBoolField(value: &self.fallsThrough) - default: break - } - } -@@ -7380,12 +6894,9 @@ extension Fuzzilli_Protobuf_BeginForOfLoopWithDestruct: SwiftProtobuf.Message, S - - 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.decodeRepeatedInt32Field(value: &self.indices) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.hasRestElement_p) }() -+ case 1: try decoder.decodeRepeatedInt32Field(value: &self.indices) -+ case 2: try decoder.decodeSingularBoolField(value: &self.hasRestElement_p) - default: break - } - } -@@ -7437,12 +6948,9 @@ extension Fuzzilli_Protobuf_BeginRepeatLoop: SwiftProtobuf.Message, SwiftProtobu - - 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.decodeSingularInt64Field(value: &self.iterations) }() -- case 2: try { try decoder.decodeSingularBoolField(value: &self.exposesLoopCounter) }() -+ case 1: try decoder.decodeSingularInt64Field(value: &self.iterations) -+ case 2: try decoder.decodeSingularBoolField(value: &self.exposesLoopCounter) - default: break - } - } -diff --git a/Sources/Fuzzilli/Protobuf/operations.proto b/Sources/Fuzzilli/Protobuf/operations.proto -index 524e45a..35069b8 100644 ---- a/Sources/Fuzzilli/Protobuf/operations.proto -+++ b/Sources/Fuzzilli/Protobuf/operations.proto -@@ -401,6 +401,9 @@ message EndConstructor { - message Return { - } - -+message DifferentialHash { -+} -+ - message Yield { - } - -diff --git a/Sources/Fuzzilli/Protobuf/program.pb.swift b/Sources/Fuzzilli/Protobuf/program.pb.swift -index 1d4af9a..bd3fdc0 100644 ---- a/Sources/Fuzzilli/Protobuf/program.pb.swift -+++ b/Sources/Fuzzilli/Protobuf/program.pb.swift -@@ -7,7 +7,7 @@ - // For information on using the generated types, please see the documentation: - // https://github.com/apple/swift-protobuf/ - --// Copyright 2023 Google LLC -+// Copyright 2024 Google LLC - // - // Licensed under the Apache License, Version 2.0 (the "License"); - // you may not use this file except in compliance with the License. -@@ -44,1432 +44,1438 @@ public struct Fuzzilli_Protobuf_Instruction { - /// The operation is either encoded as an index, referring to the nth operation - /// (so that shared operations are also only present once in the protobuf), or - /// as one of the many concrete Operation messages. -- public var inouts: [UInt32] = [] -+ public var inouts: [UInt32] { -+ get {return _storage._inouts} -+ set {_uniqueStorage()._inouts = newValue} -+ } - -- public var operation: Fuzzilli_Protobuf_Instruction.OneOf_Operation? = nil -+ public var operation: OneOf_Operation? { -+ get {return _storage._operation} -+ set {_uniqueStorage()._operation = newValue} -+ } - - public var opIdx: UInt32 { - get { -- if case .opIdx(let v)? = operation {return v} -+ if case .opIdx(let v)? = _storage._operation {return v} - return 0 - } -- set {operation = .opIdx(newValue)} -+ set {_uniqueStorage()._operation = .opIdx(newValue)} - } - - public var nop: Fuzzilli_Protobuf_Nop { - get { -- if case .nop(let v)? = operation {return v} -+ if case .nop(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_Nop() - } -- set {operation = .nop(newValue)} -+ set {_uniqueStorage()._operation = .nop(newValue)} - } - - public var loadInteger: Fuzzilli_Protobuf_LoadInteger { - get { -- if case .loadInteger(let v)? = operation {return v} -+ if case .loadInteger(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_LoadInteger() - } -- set {operation = .loadInteger(newValue)} -+ set {_uniqueStorage()._operation = .loadInteger(newValue)} - } - - public var loadBigInt: Fuzzilli_Protobuf_LoadBigInt { - get { -- if case .loadBigInt(let v)? = operation {return v} -+ if case .loadBigInt(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_LoadBigInt() - } -- set {operation = .loadBigInt(newValue)} -+ set {_uniqueStorage()._operation = .loadBigInt(newValue)} - } - - public var loadFloat: Fuzzilli_Protobuf_LoadFloat { - get { -- if case .loadFloat(let v)? = operation {return v} -+ if case .loadFloat(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_LoadFloat() - } -- set {operation = .loadFloat(newValue)} -+ set {_uniqueStorage()._operation = .loadFloat(newValue)} - } - - public var loadString: Fuzzilli_Protobuf_LoadString { - get { -- if case .loadString(let v)? = operation {return v} -+ if case .loadString(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_LoadString() - } -- set {operation = .loadString(newValue)} -+ set {_uniqueStorage()._operation = .loadString(newValue)} - } - - public var loadBoolean: Fuzzilli_Protobuf_LoadBoolean { - get { -- if case .loadBoolean(let v)? = operation {return v} -+ if case .loadBoolean(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_LoadBoolean() - } -- set {operation = .loadBoolean(newValue)} -+ set {_uniqueStorage()._operation = .loadBoolean(newValue)} - } - - public var loadUndefined: Fuzzilli_Protobuf_LoadUndefined { - get { -- if case .loadUndefined(let v)? = operation {return v} -+ if case .loadUndefined(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_LoadUndefined() - } -- set {operation = .loadUndefined(newValue)} -+ set {_uniqueStorage()._operation = .loadUndefined(newValue)} - } - - public var loadNull: Fuzzilli_Protobuf_LoadNull { - get { -- if case .loadNull(let v)? = operation {return v} -+ if case .loadNull(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_LoadNull() - } -- set {operation = .loadNull(newValue)} -+ set {_uniqueStorage()._operation = .loadNull(newValue)} - } - - public var loadThis: Fuzzilli_Protobuf_LoadThis { - get { -- if case .loadThis(let v)? = operation {return v} -+ if case .loadThis(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_LoadThis() - } -- set {operation = .loadThis(newValue)} -+ set {_uniqueStorage()._operation = .loadThis(newValue)} - } - - public var loadArguments: Fuzzilli_Protobuf_LoadArguments { - get { -- if case .loadArguments(let v)? = operation {return v} -+ if case .loadArguments(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_LoadArguments() - } -- set {operation = .loadArguments(newValue)} -+ set {_uniqueStorage()._operation = .loadArguments(newValue)} - } - - public var loadRegExp: Fuzzilli_Protobuf_LoadRegExp { - get { -- if case .loadRegExp(let v)? = operation {return v} -+ if case .loadRegExp(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_LoadRegExp() - } -- set {operation = .loadRegExp(newValue)} -+ set {_uniqueStorage()._operation = .loadRegExp(newValue)} - } - - public var beginObjectLiteral: Fuzzilli_Protobuf_BeginObjectLiteral { - get { -- if case .beginObjectLiteral(let v)? = operation {return v} -+ if case .beginObjectLiteral(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginObjectLiteral() - } -- set {operation = .beginObjectLiteral(newValue)} -+ set {_uniqueStorage()._operation = .beginObjectLiteral(newValue)} - } - - public var objectLiteralAddProperty: Fuzzilli_Protobuf_ObjectLiteralAddProperty { - get { -- if case .objectLiteralAddProperty(let v)? = operation {return v} -+ if case .objectLiteralAddProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ObjectLiteralAddProperty() - } -- set {operation = .objectLiteralAddProperty(newValue)} -+ set {_uniqueStorage()._operation = .objectLiteralAddProperty(newValue)} - } - - public var objectLiteralAddElement: Fuzzilli_Protobuf_ObjectLiteralAddElement { - get { -- if case .objectLiteralAddElement(let v)? = operation {return v} -+ if case .objectLiteralAddElement(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ObjectLiteralAddElement() - } -- set {operation = .objectLiteralAddElement(newValue)} -+ set {_uniqueStorage()._operation = .objectLiteralAddElement(newValue)} - } - - public var objectLiteralAddComputedProperty: Fuzzilli_Protobuf_ObjectLiteralAddComputedProperty { - get { -- if case .objectLiteralAddComputedProperty(let v)? = operation {return v} -+ if case .objectLiteralAddComputedProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ObjectLiteralAddComputedProperty() - } -- set {operation = .objectLiteralAddComputedProperty(newValue)} -+ set {_uniqueStorage()._operation = .objectLiteralAddComputedProperty(newValue)} - } - - public var objectLiteralCopyProperties: Fuzzilli_Protobuf_ObjectLiteralCopyProperties { - get { -- if case .objectLiteralCopyProperties(let v)? = operation {return v} -+ if case .objectLiteralCopyProperties(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ObjectLiteralCopyProperties() - } -- set {operation = .objectLiteralCopyProperties(newValue)} -+ set {_uniqueStorage()._operation = .objectLiteralCopyProperties(newValue)} - } - - public var objectLiteralSetPrototype: Fuzzilli_Protobuf_ObjectLiteralSetPrototype { - get { -- if case .objectLiteralSetPrototype(let v)? = operation {return v} -+ if case .objectLiteralSetPrototype(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ObjectLiteralSetPrototype() - } -- set {operation = .objectLiteralSetPrototype(newValue)} -+ set {_uniqueStorage()._operation = .objectLiteralSetPrototype(newValue)} - } - - public var beginObjectLiteralMethod: Fuzzilli_Protobuf_BeginObjectLiteralMethod { - get { -- if case .beginObjectLiteralMethod(let v)? = operation {return v} -+ if case .beginObjectLiteralMethod(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginObjectLiteralMethod() - } -- set {operation = .beginObjectLiteralMethod(newValue)} -+ set {_uniqueStorage()._operation = .beginObjectLiteralMethod(newValue)} - } - - public var endObjectLiteralMethod: Fuzzilli_Protobuf_EndObjectLiteralMethod { - get { -- if case .endObjectLiteralMethod(let v)? = operation {return v} -+ if case .endObjectLiteralMethod(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndObjectLiteralMethod() - } -- set {operation = .endObjectLiteralMethod(newValue)} -+ set {_uniqueStorage()._operation = .endObjectLiteralMethod(newValue)} - } - - public var beginObjectLiteralComputedMethod: Fuzzilli_Protobuf_BeginObjectLiteralComputedMethod { - get { -- if case .beginObjectLiteralComputedMethod(let v)? = operation {return v} -+ if case .beginObjectLiteralComputedMethod(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginObjectLiteralComputedMethod() - } -- set {operation = .beginObjectLiteralComputedMethod(newValue)} -+ set {_uniqueStorage()._operation = .beginObjectLiteralComputedMethod(newValue)} - } - - public var endObjectLiteralComputedMethod: Fuzzilli_Protobuf_EndObjectLiteralComputedMethod { - get { -- if case .endObjectLiteralComputedMethod(let v)? = operation {return v} -+ if case .endObjectLiteralComputedMethod(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndObjectLiteralComputedMethod() - } -- set {operation = .endObjectLiteralComputedMethod(newValue)} -+ set {_uniqueStorage()._operation = .endObjectLiteralComputedMethod(newValue)} - } - - public var beginObjectLiteralGetter: Fuzzilli_Protobuf_BeginObjectLiteralGetter { - get { -- if case .beginObjectLiteralGetter(let v)? = operation {return v} -+ if case .beginObjectLiteralGetter(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginObjectLiteralGetter() - } -- set {operation = .beginObjectLiteralGetter(newValue)} -+ set {_uniqueStorage()._operation = .beginObjectLiteralGetter(newValue)} - } - - public var endObjectLiteralGetter: Fuzzilli_Protobuf_EndObjectLiteralGetter { - get { -- if case .endObjectLiteralGetter(let v)? = operation {return v} -+ if case .endObjectLiteralGetter(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndObjectLiteralGetter() - } -- set {operation = .endObjectLiteralGetter(newValue)} -+ set {_uniqueStorage()._operation = .endObjectLiteralGetter(newValue)} - } - - public var beginObjectLiteralSetter: Fuzzilli_Protobuf_BeginObjectLiteralSetter { - get { -- if case .beginObjectLiteralSetter(let v)? = operation {return v} -+ if case .beginObjectLiteralSetter(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginObjectLiteralSetter() - } -- set {operation = .beginObjectLiteralSetter(newValue)} -+ set {_uniqueStorage()._operation = .beginObjectLiteralSetter(newValue)} - } - - public var endObjectLiteralSetter: Fuzzilli_Protobuf_EndObjectLiteralSetter { - get { -- if case .endObjectLiteralSetter(let v)? = operation {return v} -+ if case .endObjectLiteralSetter(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndObjectLiteralSetter() - } -- set {operation = .endObjectLiteralSetter(newValue)} -+ set {_uniqueStorage()._operation = .endObjectLiteralSetter(newValue)} - } - - public var endObjectLiteral: Fuzzilli_Protobuf_EndObjectLiteral { - get { -- if case .endObjectLiteral(let v)? = operation {return v} -+ if case .endObjectLiteral(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndObjectLiteral() - } -- set {operation = .endObjectLiteral(newValue)} -+ set {_uniqueStorage()._operation = .endObjectLiteral(newValue)} - } - - public var beginClassDefinition: Fuzzilli_Protobuf_BeginClassDefinition { - get { -- if case .beginClassDefinition(let v)? = operation {return v} -+ if case .beginClassDefinition(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginClassDefinition() - } -- set {operation = .beginClassDefinition(newValue)} -+ set {_uniqueStorage()._operation = .beginClassDefinition(newValue)} - } - - public var beginClassConstructor: Fuzzilli_Protobuf_BeginClassConstructor { - get { -- if case .beginClassConstructor(let v)? = operation {return v} -+ if case .beginClassConstructor(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginClassConstructor() - } -- set {operation = .beginClassConstructor(newValue)} -+ set {_uniqueStorage()._operation = .beginClassConstructor(newValue)} - } - - public var endClassConstructor: Fuzzilli_Protobuf_EndClassConstructor { - get { -- if case .endClassConstructor(let v)? = operation {return v} -+ if case .endClassConstructor(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndClassConstructor() - } -- set {operation = .endClassConstructor(newValue)} -+ set {_uniqueStorage()._operation = .endClassConstructor(newValue)} - } - - public var classAddInstanceProperty: Fuzzilli_Protobuf_ClassAddInstanceProperty { - get { -- if case .classAddInstanceProperty(let v)? = operation {return v} -+ if case .classAddInstanceProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ClassAddInstanceProperty() - } -- set {operation = .classAddInstanceProperty(newValue)} -+ set {_uniqueStorage()._operation = .classAddInstanceProperty(newValue)} - } - - public var classAddInstanceElement: Fuzzilli_Protobuf_ClassAddInstanceElement { - get { -- if case .classAddInstanceElement(let v)? = operation {return v} -+ if case .classAddInstanceElement(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ClassAddInstanceElement() - } -- set {operation = .classAddInstanceElement(newValue)} -+ set {_uniqueStorage()._operation = .classAddInstanceElement(newValue)} - } - - public var classAddInstanceComputedProperty: Fuzzilli_Protobuf_ClassAddInstanceComputedProperty { - get { -- if case .classAddInstanceComputedProperty(let v)? = operation {return v} -+ if case .classAddInstanceComputedProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ClassAddInstanceComputedProperty() - } -- set {operation = .classAddInstanceComputedProperty(newValue)} -+ set {_uniqueStorage()._operation = .classAddInstanceComputedProperty(newValue)} - } - - public var beginClassInstanceMethod: Fuzzilli_Protobuf_BeginClassInstanceMethod { - get { -- if case .beginClassInstanceMethod(let v)? = operation {return v} -+ if case .beginClassInstanceMethod(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginClassInstanceMethod() - } -- set {operation = .beginClassInstanceMethod(newValue)} -+ set {_uniqueStorage()._operation = .beginClassInstanceMethod(newValue)} - } - - public var endClassInstanceMethod: Fuzzilli_Protobuf_EndClassInstanceMethod { - get { -- if case .endClassInstanceMethod(let v)? = operation {return v} -+ if case .endClassInstanceMethod(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndClassInstanceMethod() - } -- set {operation = .endClassInstanceMethod(newValue)} -+ set {_uniqueStorage()._operation = .endClassInstanceMethod(newValue)} - } - - public var beginClassInstanceGetter: Fuzzilli_Protobuf_BeginClassInstanceGetter { - get { -- if case .beginClassInstanceGetter(let v)? = operation {return v} -+ if case .beginClassInstanceGetter(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginClassInstanceGetter() - } -- set {operation = .beginClassInstanceGetter(newValue)} -+ set {_uniqueStorage()._operation = .beginClassInstanceGetter(newValue)} - } - - public var endClassInstanceGetter: Fuzzilli_Protobuf_EndClassInstanceGetter { - get { -- if case .endClassInstanceGetter(let v)? = operation {return v} -+ if case .endClassInstanceGetter(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndClassInstanceGetter() - } -- set {operation = .endClassInstanceGetter(newValue)} -+ set {_uniqueStorage()._operation = .endClassInstanceGetter(newValue)} - } - - public var beginClassInstanceSetter: Fuzzilli_Protobuf_BeginClassInstanceSetter { - get { -- if case .beginClassInstanceSetter(let v)? = operation {return v} -+ if case .beginClassInstanceSetter(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginClassInstanceSetter() - } -- set {operation = .beginClassInstanceSetter(newValue)} -+ set {_uniqueStorage()._operation = .beginClassInstanceSetter(newValue)} - } - - public var endClassInstanceSetter: Fuzzilli_Protobuf_EndClassInstanceSetter { - get { -- if case .endClassInstanceSetter(let v)? = operation {return v} -+ if case .endClassInstanceSetter(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndClassInstanceSetter() - } -- set {operation = .endClassInstanceSetter(newValue)} -+ set {_uniqueStorage()._operation = .endClassInstanceSetter(newValue)} - } - - public var classAddStaticProperty: Fuzzilli_Protobuf_ClassAddStaticProperty { - get { -- if case .classAddStaticProperty(let v)? = operation {return v} -+ if case .classAddStaticProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ClassAddStaticProperty() - } -- set {operation = .classAddStaticProperty(newValue)} -+ set {_uniqueStorage()._operation = .classAddStaticProperty(newValue)} - } - - public var classAddStaticElement: Fuzzilli_Protobuf_ClassAddStaticElement { - get { -- if case .classAddStaticElement(let v)? = operation {return v} -+ if case .classAddStaticElement(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ClassAddStaticElement() - } -- set {operation = .classAddStaticElement(newValue)} -+ set {_uniqueStorage()._operation = .classAddStaticElement(newValue)} - } - - public var classAddStaticComputedProperty: Fuzzilli_Protobuf_ClassAddStaticComputedProperty { - get { -- if case .classAddStaticComputedProperty(let v)? = operation {return v} -+ if case .classAddStaticComputedProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ClassAddStaticComputedProperty() - } -- set {operation = .classAddStaticComputedProperty(newValue)} -+ set {_uniqueStorage()._operation = .classAddStaticComputedProperty(newValue)} - } - - public var beginClassStaticInitializer: Fuzzilli_Protobuf_BeginClassStaticInitializer { - get { -- if case .beginClassStaticInitializer(let v)? = operation {return v} -+ if case .beginClassStaticInitializer(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginClassStaticInitializer() - } -- set {operation = .beginClassStaticInitializer(newValue)} -+ set {_uniqueStorage()._operation = .beginClassStaticInitializer(newValue)} - } - - public var endClassStaticInitializer: Fuzzilli_Protobuf_EndClassStaticInitializer { - get { -- if case .endClassStaticInitializer(let v)? = operation {return v} -+ if case .endClassStaticInitializer(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndClassStaticInitializer() - } -- set {operation = .endClassStaticInitializer(newValue)} -+ set {_uniqueStorage()._operation = .endClassStaticInitializer(newValue)} - } - - public var beginClassStaticMethod: Fuzzilli_Protobuf_BeginClassStaticMethod { - get { -- if case .beginClassStaticMethod(let v)? = operation {return v} -+ if case .beginClassStaticMethod(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginClassStaticMethod() - } -- set {operation = .beginClassStaticMethod(newValue)} -+ set {_uniqueStorage()._operation = .beginClassStaticMethod(newValue)} - } - - public var endClassStaticMethod: Fuzzilli_Protobuf_EndClassStaticMethod { - get { -- if case .endClassStaticMethod(let v)? = operation {return v} -+ if case .endClassStaticMethod(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndClassStaticMethod() - } -- set {operation = .endClassStaticMethod(newValue)} -+ set {_uniqueStorage()._operation = .endClassStaticMethod(newValue)} - } - - public var beginClassStaticGetter: Fuzzilli_Protobuf_BeginClassStaticGetter { - get { -- if case .beginClassStaticGetter(let v)? = operation {return v} -+ if case .beginClassStaticGetter(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginClassStaticGetter() - } -- set {operation = .beginClassStaticGetter(newValue)} -+ set {_uniqueStorage()._operation = .beginClassStaticGetter(newValue)} - } - - public var endClassStaticGetter: Fuzzilli_Protobuf_EndClassStaticGetter { - get { -- if case .endClassStaticGetter(let v)? = operation {return v} -+ if case .endClassStaticGetter(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndClassStaticGetter() - } -- set {operation = .endClassStaticGetter(newValue)} -+ set {_uniqueStorage()._operation = .endClassStaticGetter(newValue)} - } - - public var beginClassStaticSetter: Fuzzilli_Protobuf_BeginClassStaticSetter { - get { -- if case .beginClassStaticSetter(let v)? = operation {return v} -+ if case .beginClassStaticSetter(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginClassStaticSetter() - } -- set {operation = .beginClassStaticSetter(newValue)} -+ set {_uniqueStorage()._operation = .beginClassStaticSetter(newValue)} - } - - public var endClassStaticSetter: Fuzzilli_Protobuf_EndClassStaticSetter { - get { -- if case .endClassStaticSetter(let v)? = operation {return v} -+ if case .endClassStaticSetter(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndClassStaticSetter() - } -- set {operation = .endClassStaticSetter(newValue)} -+ set {_uniqueStorage()._operation = .endClassStaticSetter(newValue)} - } - - public var classAddPrivateInstanceProperty: Fuzzilli_Protobuf_ClassAddPrivateInstanceProperty { - get { -- if case .classAddPrivateInstanceProperty(let v)? = operation {return v} -+ if case .classAddPrivateInstanceProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ClassAddPrivateInstanceProperty() - } -- set {operation = .classAddPrivateInstanceProperty(newValue)} -+ set {_uniqueStorage()._operation = .classAddPrivateInstanceProperty(newValue)} - } - - public var beginClassPrivateInstanceMethod: Fuzzilli_Protobuf_BeginClassPrivateInstanceMethod { - get { -- if case .beginClassPrivateInstanceMethod(let v)? = operation {return v} -+ if case .beginClassPrivateInstanceMethod(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginClassPrivateInstanceMethod() - } -- set {operation = .beginClassPrivateInstanceMethod(newValue)} -+ set {_uniqueStorage()._operation = .beginClassPrivateInstanceMethod(newValue)} - } - - public var endClassPrivateInstanceMethod: Fuzzilli_Protobuf_EndClassPrivateInstanceMethod { - get { -- if case .endClassPrivateInstanceMethod(let v)? = operation {return v} -+ if case .endClassPrivateInstanceMethod(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndClassPrivateInstanceMethod() - } -- set {operation = .endClassPrivateInstanceMethod(newValue)} -+ set {_uniqueStorage()._operation = .endClassPrivateInstanceMethod(newValue)} - } - - public var classAddPrivateStaticProperty: Fuzzilli_Protobuf_ClassAddPrivateStaticProperty { - get { -- if case .classAddPrivateStaticProperty(let v)? = operation {return v} -+ if case .classAddPrivateStaticProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ClassAddPrivateStaticProperty() - } -- set {operation = .classAddPrivateStaticProperty(newValue)} -+ set {_uniqueStorage()._operation = .classAddPrivateStaticProperty(newValue)} - } - - public var beginClassPrivateStaticMethod: Fuzzilli_Protobuf_BeginClassPrivateStaticMethod { - get { -- if case .beginClassPrivateStaticMethod(let v)? = operation {return v} -+ if case .beginClassPrivateStaticMethod(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginClassPrivateStaticMethod() - } -- set {operation = .beginClassPrivateStaticMethod(newValue)} -+ set {_uniqueStorage()._operation = .beginClassPrivateStaticMethod(newValue)} - } - - public var endClassPrivateStaticMethod: Fuzzilli_Protobuf_EndClassPrivateStaticMethod { - get { -- if case .endClassPrivateStaticMethod(let v)? = operation {return v} -+ if case .endClassPrivateStaticMethod(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndClassPrivateStaticMethod() - } -- set {operation = .endClassPrivateStaticMethod(newValue)} -+ set {_uniqueStorage()._operation = .endClassPrivateStaticMethod(newValue)} - } - - public var endClassDefinition: Fuzzilli_Protobuf_EndClassDefinition { - get { -- if case .endClassDefinition(let v)? = operation {return v} -+ if case .endClassDefinition(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndClassDefinition() - } -- set {operation = .endClassDefinition(newValue)} -+ set {_uniqueStorage()._operation = .endClassDefinition(newValue)} - } - - public var createArray: Fuzzilli_Protobuf_CreateArray { - get { -- if case .createArray(let v)? = operation {return v} -+ if case .createArray(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_CreateArray() - } -- set {operation = .createArray(newValue)} -+ set {_uniqueStorage()._operation = .createArray(newValue)} - } - - public var createIntArray: Fuzzilli_Protobuf_CreateIntArray { - get { -- if case .createIntArray(let v)? = operation {return v} -+ if case .createIntArray(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_CreateIntArray() - } -- set {operation = .createIntArray(newValue)} -+ set {_uniqueStorage()._operation = .createIntArray(newValue)} - } - - public var createFloatArray: Fuzzilli_Protobuf_CreateFloatArray { - get { -- if case .createFloatArray(let v)? = operation {return v} -+ if case .createFloatArray(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_CreateFloatArray() - } -- set {operation = .createFloatArray(newValue)} -+ set {_uniqueStorage()._operation = .createFloatArray(newValue)} - } - - public var createArrayWithSpread: Fuzzilli_Protobuf_CreateArrayWithSpread { - get { -- if case .createArrayWithSpread(let v)? = operation {return v} -+ if case .createArrayWithSpread(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_CreateArrayWithSpread() - } -- set {operation = .createArrayWithSpread(newValue)} -+ set {_uniqueStorage()._operation = .createArrayWithSpread(newValue)} - } - - public var createTemplateString: Fuzzilli_Protobuf_CreateTemplateString { - get { -- if case .createTemplateString(let v)? = operation {return v} -+ if case .createTemplateString(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_CreateTemplateString() - } -- set {operation = .createTemplateString(newValue)} -+ set {_uniqueStorage()._operation = .createTemplateString(newValue)} - } - - public var loadBuiltin: Fuzzilli_Protobuf_LoadBuiltin { - get { -- if case .loadBuiltin(let v)? = operation {return v} -+ if case .loadBuiltin(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_LoadBuiltin() - } -- set {operation = .loadBuiltin(newValue)} -+ set {_uniqueStorage()._operation = .loadBuiltin(newValue)} - } - - public var getProperty: Fuzzilli_Protobuf_GetProperty { - get { -- if case .getProperty(let v)? = operation {return v} -+ if case .getProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_GetProperty() - } -- set {operation = .getProperty(newValue)} -+ set {_uniqueStorage()._operation = .getProperty(newValue)} - } - - public var setProperty: Fuzzilli_Protobuf_SetProperty { - get { -- if case .setProperty(let v)? = operation {return v} -+ if case .setProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_SetProperty() - } -- set {operation = .setProperty(newValue)} -+ set {_uniqueStorage()._operation = .setProperty(newValue)} - } - - public var updateProperty: Fuzzilli_Protobuf_UpdateProperty { - get { -- if case .updateProperty(let v)? = operation {return v} -+ if case .updateProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_UpdateProperty() - } -- set {operation = .updateProperty(newValue)} -+ set {_uniqueStorage()._operation = .updateProperty(newValue)} - } - - public var deleteProperty: Fuzzilli_Protobuf_DeleteProperty { - get { -- if case .deleteProperty(let v)? = operation {return v} -+ if case .deleteProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_DeleteProperty() - } -- set {operation = .deleteProperty(newValue)} -+ set {_uniqueStorage()._operation = .deleteProperty(newValue)} - } - - public var configureProperty: Fuzzilli_Protobuf_ConfigureProperty { - get { -- if case .configureProperty(let v)? = operation {return v} -+ if case .configureProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ConfigureProperty() - } -- set {operation = .configureProperty(newValue)} -+ set {_uniqueStorage()._operation = .configureProperty(newValue)} - } - - public var getElement: Fuzzilli_Protobuf_GetElement { - get { -- if case .getElement(let v)? = operation {return v} -+ if case .getElement(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_GetElement() - } -- set {operation = .getElement(newValue)} -+ set {_uniqueStorage()._operation = .getElement(newValue)} - } - - public var setElement: Fuzzilli_Protobuf_SetElement { - get { -- if case .setElement(let v)? = operation {return v} -+ if case .setElement(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_SetElement() - } -- set {operation = .setElement(newValue)} -+ set {_uniqueStorage()._operation = .setElement(newValue)} - } - - public var updateElement: Fuzzilli_Protobuf_UpdateElement { - get { -- if case .updateElement(let v)? = operation {return v} -+ if case .updateElement(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_UpdateElement() - } -- set {operation = .updateElement(newValue)} -+ set {_uniqueStorage()._operation = .updateElement(newValue)} - } - - public var deleteElement: Fuzzilli_Protobuf_DeleteElement { - get { -- if case .deleteElement(let v)? = operation {return v} -+ if case .deleteElement(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_DeleteElement() - } -- set {operation = .deleteElement(newValue)} -+ set {_uniqueStorage()._operation = .deleteElement(newValue)} - } - - public var configureElement: Fuzzilli_Protobuf_ConfigureElement { - get { -- if case .configureElement(let v)? = operation {return v} -+ if case .configureElement(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ConfigureElement() - } -- set {operation = .configureElement(newValue)} -+ set {_uniqueStorage()._operation = .configureElement(newValue)} - } - - public var getComputedProperty: Fuzzilli_Protobuf_GetComputedProperty { - get { -- if case .getComputedProperty(let v)? = operation {return v} -+ if case .getComputedProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_GetComputedProperty() - } -- set {operation = .getComputedProperty(newValue)} -+ set {_uniqueStorage()._operation = .getComputedProperty(newValue)} - } - - public var setComputedProperty: Fuzzilli_Protobuf_SetComputedProperty { - get { -- if case .setComputedProperty(let v)? = operation {return v} -+ if case .setComputedProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_SetComputedProperty() - } -- set {operation = .setComputedProperty(newValue)} -+ set {_uniqueStorage()._operation = .setComputedProperty(newValue)} - } - - public var updateComputedProperty: Fuzzilli_Protobuf_UpdateComputedProperty { - get { -- if case .updateComputedProperty(let v)? = operation {return v} -+ if case .updateComputedProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_UpdateComputedProperty() - } -- set {operation = .updateComputedProperty(newValue)} -+ set {_uniqueStorage()._operation = .updateComputedProperty(newValue)} - } - - public var deleteComputedProperty: Fuzzilli_Protobuf_DeleteComputedProperty { - get { -- if case .deleteComputedProperty(let v)? = operation {return v} -+ if case .deleteComputedProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_DeleteComputedProperty() - } -- set {operation = .deleteComputedProperty(newValue)} -+ set {_uniqueStorage()._operation = .deleteComputedProperty(newValue)} - } - - public var configureComputedProperty: Fuzzilli_Protobuf_ConfigureComputedProperty { - get { -- if case .configureComputedProperty(let v)? = operation {return v} -+ if case .configureComputedProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ConfigureComputedProperty() - } -- set {operation = .configureComputedProperty(newValue)} -+ set {_uniqueStorage()._operation = .configureComputedProperty(newValue)} - } - - public var typeOf: Fuzzilli_Protobuf_TypeOf { - get { -- if case .typeOf(let v)? = operation {return v} -+ if case .typeOf(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_TypeOf() - } -- set {operation = .typeOf(newValue)} -+ set {_uniqueStorage()._operation = .typeOf(newValue)} - } - - public var testInstanceOf: Fuzzilli_Protobuf_TestInstanceOf { - get { -- if case .testInstanceOf(let v)? = operation {return v} -+ if case .testInstanceOf(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_TestInstanceOf() - } -- set {operation = .testInstanceOf(newValue)} -+ set {_uniqueStorage()._operation = .testInstanceOf(newValue)} - } - - public var testIn: Fuzzilli_Protobuf_TestIn { - get { -- if case .testIn(let v)? = operation {return v} -+ if case .testIn(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_TestIn() - } -- set {operation = .testIn(newValue)} -+ set {_uniqueStorage()._operation = .testIn(newValue)} - } - - public var beginPlainFunction: Fuzzilli_Protobuf_BeginPlainFunction { - get { -- if case .beginPlainFunction(let v)? = operation {return v} -+ if case .beginPlainFunction(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginPlainFunction() - } -- set {operation = .beginPlainFunction(newValue)} -+ set {_uniqueStorage()._operation = .beginPlainFunction(newValue)} - } - - public var endPlainFunction: Fuzzilli_Protobuf_EndPlainFunction { - get { -- if case .endPlainFunction(let v)? = operation {return v} -+ if case .endPlainFunction(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndPlainFunction() - } -- set {operation = .endPlainFunction(newValue)} -+ set {_uniqueStorage()._operation = .endPlainFunction(newValue)} - } - - public var beginArrowFunction: Fuzzilli_Protobuf_BeginArrowFunction { - get { -- if case .beginArrowFunction(let v)? = operation {return v} -+ if case .beginArrowFunction(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginArrowFunction() - } -- set {operation = .beginArrowFunction(newValue)} -+ set {_uniqueStorage()._operation = .beginArrowFunction(newValue)} - } - - public var endArrowFunction: Fuzzilli_Protobuf_EndArrowFunction { - get { -- if case .endArrowFunction(let v)? = operation {return v} -+ if case .endArrowFunction(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndArrowFunction() - } -- set {operation = .endArrowFunction(newValue)} -+ set {_uniqueStorage()._operation = .endArrowFunction(newValue)} - } - - public var beginGeneratorFunction: Fuzzilli_Protobuf_BeginGeneratorFunction { - get { -- if case .beginGeneratorFunction(let v)? = operation {return v} -+ if case .beginGeneratorFunction(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginGeneratorFunction() - } -- set {operation = .beginGeneratorFunction(newValue)} -+ set {_uniqueStorage()._operation = .beginGeneratorFunction(newValue)} - } - - public var endGeneratorFunction: Fuzzilli_Protobuf_EndGeneratorFunction { - get { -- if case .endGeneratorFunction(let v)? = operation {return v} -+ if case .endGeneratorFunction(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndGeneratorFunction() - } -- set {operation = .endGeneratorFunction(newValue)} -+ set {_uniqueStorage()._operation = .endGeneratorFunction(newValue)} - } - - public var beginAsyncFunction: Fuzzilli_Protobuf_BeginAsyncFunction { - get { -- if case .beginAsyncFunction(let v)? = operation {return v} -+ if case .beginAsyncFunction(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginAsyncFunction() - } -- set {operation = .beginAsyncFunction(newValue)} -+ set {_uniqueStorage()._operation = .beginAsyncFunction(newValue)} - } - - public var endAsyncFunction: Fuzzilli_Protobuf_EndAsyncFunction { - get { -- if case .endAsyncFunction(let v)? = operation {return v} -+ if case .endAsyncFunction(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndAsyncFunction() - } -- set {operation = .endAsyncFunction(newValue)} -+ set {_uniqueStorage()._operation = .endAsyncFunction(newValue)} - } - - public var beginAsyncArrowFunction: Fuzzilli_Protobuf_BeginAsyncArrowFunction { - get { -- if case .beginAsyncArrowFunction(let v)? = operation {return v} -+ if case .beginAsyncArrowFunction(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginAsyncArrowFunction() - } -- set {operation = .beginAsyncArrowFunction(newValue)} -+ set {_uniqueStorage()._operation = .beginAsyncArrowFunction(newValue)} - } - - public var endAsyncArrowFunction: Fuzzilli_Protobuf_EndAsyncArrowFunction { - get { -- if case .endAsyncArrowFunction(let v)? = operation {return v} -+ if case .endAsyncArrowFunction(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndAsyncArrowFunction() - } -- set {operation = .endAsyncArrowFunction(newValue)} -+ set {_uniqueStorage()._operation = .endAsyncArrowFunction(newValue)} - } - - public var beginAsyncGeneratorFunction: Fuzzilli_Protobuf_BeginAsyncGeneratorFunction { - get { -- if case .beginAsyncGeneratorFunction(let v)? = operation {return v} -+ if case .beginAsyncGeneratorFunction(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginAsyncGeneratorFunction() - } -- set {operation = .beginAsyncGeneratorFunction(newValue)} -+ set {_uniqueStorage()._operation = .beginAsyncGeneratorFunction(newValue)} - } - - public var endAsyncGeneratorFunction: Fuzzilli_Protobuf_EndAsyncGeneratorFunction { - get { -- if case .endAsyncGeneratorFunction(let v)? = operation {return v} -+ if case .endAsyncGeneratorFunction(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndAsyncGeneratorFunction() - } -- set {operation = .endAsyncGeneratorFunction(newValue)} -+ set {_uniqueStorage()._operation = .endAsyncGeneratorFunction(newValue)} - } - - public var beginConstructor: Fuzzilli_Protobuf_BeginConstructor { - get { -- if case .beginConstructor(let v)? = operation {return v} -+ if case .beginConstructor(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginConstructor() - } -- set {operation = .beginConstructor(newValue)} -+ set {_uniqueStorage()._operation = .beginConstructor(newValue)} - } - - public var endConstructor: Fuzzilli_Protobuf_EndConstructor { - get { -- if case .endConstructor(let v)? = operation {return v} -+ if case .endConstructor(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndConstructor() - } -- set {operation = .endConstructor(newValue)} -+ set {_uniqueStorage()._operation = .endConstructor(newValue)} - } - - public var `return`: Fuzzilli_Protobuf_Return { - get { -- if case .return(let v)? = operation {return v} -+ if case .return(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_Return() - } -- set {operation = .return(newValue)} -+ set {_uniqueStorage()._operation = .return(newValue)} - } - - public var yield: Fuzzilli_Protobuf_Yield { - get { -- if case .yield(let v)? = operation {return v} -+ if case .yield(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_Yield() - } -- set {operation = .yield(newValue)} -+ set {_uniqueStorage()._operation = .yield(newValue)} - } - - public var yieldEach: Fuzzilli_Protobuf_YieldEach { - get { -- if case .yieldEach(let v)? = operation {return v} -+ if case .yieldEach(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_YieldEach() - } -- set {operation = .yieldEach(newValue)} -+ set {_uniqueStorage()._operation = .yieldEach(newValue)} - } - - public var await: Fuzzilli_Protobuf_Await { - get { -- if case .await(let v)? = operation {return v} -+ if case .await(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_Await() - } -- set {operation = .await(newValue)} -+ set {_uniqueStorage()._operation = .await(newValue)} - } - - public var callFunction: Fuzzilli_Protobuf_CallFunction { - get { -- if case .callFunction(let v)? = operation {return v} -+ if case .callFunction(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_CallFunction() - } -- set {operation = .callFunction(newValue)} -+ set {_uniqueStorage()._operation = .callFunction(newValue)} - } - - public var callFunctionWithSpread: Fuzzilli_Protobuf_CallFunctionWithSpread { - get { -- if case .callFunctionWithSpread(let v)? = operation {return v} -+ if case .callFunctionWithSpread(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_CallFunctionWithSpread() - } -- set {operation = .callFunctionWithSpread(newValue)} -+ set {_uniqueStorage()._operation = .callFunctionWithSpread(newValue)} - } - - public var construct: Fuzzilli_Protobuf_Construct { - get { -- if case .construct(let v)? = operation {return v} -+ if case .construct(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_Construct() - } -- set {operation = .construct(newValue)} -+ set {_uniqueStorage()._operation = .construct(newValue)} - } - - public var constructWithSpread: Fuzzilli_Protobuf_ConstructWithSpread { - get { -- if case .constructWithSpread(let v)? = operation {return v} -+ if case .constructWithSpread(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ConstructWithSpread() - } -- set {operation = .constructWithSpread(newValue)} -+ set {_uniqueStorage()._operation = .constructWithSpread(newValue)} - } - - public var callMethod: Fuzzilli_Protobuf_CallMethod { - get { -- if case .callMethod(let v)? = operation {return v} -+ if case .callMethod(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_CallMethod() - } -- set {operation = .callMethod(newValue)} -+ set {_uniqueStorage()._operation = .callMethod(newValue)} - } - - public var callMethodWithSpread: Fuzzilli_Protobuf_CallMethodWithSpread { - get { -- if case .callMethodWithSpread(let v)? = operation {return v} -+ if case .callMethodWithSpread(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_CallMethodWithSpread() - } -- set {operation = .callMethodWithSpread(newValue)} -+ set {_uniqueStorage()._operation = .callMethodWithSpread(newValue)} - } - - public var callComputedMethod: Fuzzilli_Protobuf_CallComputedMethod { - get { -- if case .callComputedMethod(let v)? = operation {return v} -+ if case .callComputedMethod(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_CallComputedMethod() - } -- set {operation = .callComputedMethod(newValue)} -+ set {_uniqueStorage()._operation = .callComputedMethod(newValue)} - } - - public var callComputedMethodWithSpread: Fuzzilli_Protobuf_CallComputedMethodWithSpread { - get { -- if case .callComputedMethodWithSpread(let v)? = operation {return v} -+ if case .callComputedMethodWithSpread(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_CallComputedMethodWithSpread() - } -- set {operation = .callComputedMethodWithSpread(newValue)} -+ set {_uniqueStorage()._operation = .callComputedMethodWithSpread(newValue)} - } - - public var unaryOperation: Fuzzilli_Protobuf_UnaryOperation { - get { -- if case .unaryOperation(let v)? = operation {return v} -+ if case .unaryOperation(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_UnaryOperation() - } -- set {operation = .unaryOperation(newValue)} -+ set {_uniqueStorage()._operation = .unaryOperation(newValue)} - } - - public var binaryOperation: Fuzzilli_Protobuf_BinaryOperation { - get { -- if case .binaryOperation(let v)? = operation {return v} -+ if case .binaryOperation(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BinaryOperation() - } -- set {operation = .binaryOperation(newValue)} -+ set {_uniqueStorage()._operation = .binaryOperation(newValue)} - } - - public var ternaryOperation: Fuzzilli_Protobuf_TernaryOperation { - get { -- if case .ternaryOperation(let v)? = operation {return v} -+ if case .ternaryOperation(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_TernaryOperation() - } -- set {operation = .ternaryOperation(newValue)} -+ set {_uniqueStorage()._operation = .ternaryOperation(newValue)} - } - - public var update: Fuzzilli_Protobuf_Update { - get { -- if case .update(let v)? = operation {return v} -+ if case .update(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_Update() - } -- set {operation = .update(newValue)} -+ set {_uniqueStorage()._operation = .update(newValue)} - } - - public var dup: Fuzzilli_Protobuf_Dup { - get { -- if case .dup(let v)? = operation {return v} -+ if case .dup(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_Dup() - } -- set {operation = .dup(newValue)} -+ set {_uniqueStorage()._operation = .dup(newValue)} - } - - public var reassign: Fuzzilli_Protobuf_Reassign { - get { -- if case .reassign(let v)? = operation {return v} -+ if case .reassign(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_Reassign() - } -- set {operation = .reassign(newValue)} -+ set {_uniqueStorage()._operation = .reassign(newValue)} - } - - public var destructArray: Fuzzilli_Protobuf_DestructArray { - get { -- if case .destructArray(let v)? = operation {return v} -+ if case .destructArray(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_DestructArray() - } -- set {operation = .destructArray(newValue)} -+ set {_uniqueStorage()._operation = .destructArray(newValue)} - } - - public var destructArrayAndReassign: Fuzzilli_Protobuf_DestructArrayAndReassign { - get { -- if case .destructArrayAndReassign(let v)? = operation {return v} -+ if case .destructArrayAndReassign(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_DestructArrayAndReassign() - } -- set {operation = .destructArrayAndReassign(newValue)} -+ set {_uniqueStorage()._operation = .destructArrayAndReassign(newValue)} - } - - public var destructObject: Fuzzilli_Protobuf_DestructObject { - get { -- if case .destructObject(let v)? = operation {return v} -+ if case .destructObject(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_DestructObject() - } -- set {operation = .destructObject(newValue)} -+ set {_uniqueStorage()._operation = .destructObject(newValue)} - } - - public var destructObjectAndReassign: Fuzzilli_Protobuf_DestructObjectAndReassign { - get { -- if case .destructObjectAndReassign(let v)? = operation {return v} -+ if case .destructObjectAndReassign(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_DestructObjectAndReassign() - } -- set {operation = .destructObjectAndReassign(newValue)} -+ set {_uniqueStorage()._operation = .destructObjectAndReassign(newValue)} - } - - public var compare: Fuzzilli_Protobuf_Compare { - get { -- if case .compare(let v)? = operation {return v} -+ if case .compare(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_Compare() - } -- set {operation = .compare(newValue)} -+ set {_uniqueStorage()._operation = .compare(newValue)} - } - - public var loadNamedVariable: Fuzzilli_Protobuf_LoadNamedVariable { - get { -- if case .loadNamedVariable(let v)? = operation {return v} -+ if case .loadNamedVariable(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_LoadNamedVariable() - } -- set {operation = .loadNamedVariable(newValue)} -+ set {_uniqueStorage()._operation = .loadNamedVariable(newValue)} - } - - public var storeNamedVariable: Fuzzilli_Protobuf_StoreNamedVariable { - get { -- if case .storeNamedVariable(let v)? = operation {return v} -+ if case .storeNamedVariable(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_StoreNamedVariable() - } -- set {operation = .storeNamedVariable(newValue)} -+ set {_uniqueStorage()._operation = .storeNamedVariable(newValue)} - } - - public var defineNamedVariable: Fuzzilli_Protobuf_DefineNamedVariable { - get { -- if case .defineNamedVariable(let v)? = operation {return v} -+ if case .defineNamedVariable(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_DefineNamedVariable() - } -- set {operation = .defineNamedVariable(newValue)} -+ set {_uniqueStorage()._operation = .defineNamedVariable(newValue)} - } - - public var eval: Fuzzilli_Protobuf_Eval { - get { -- if case .eval(let v)? = operation {return v} -+ if case .eval(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_Eval() - } -- set {operation = .eval(newValue)} -+ set {_uniqueStorage()._operation = .eval(newValue)} - } - - public var beginWith: Fuzzilli_Protobuf_BeginWith { - get { -- if case .beginWith(let v)? = operation {return v} -+ if case .beginWith(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginWith() - } -- set {operation = .beginWith(newValue)} -+ set {_uniqueStorage()._operation = .beginWith(newValue)} - } - - public var endWith: Fuzzilli_Protobuf_EndWith { - get { -- if case .endWith(let v)? = operation {return v} -+ if case .endWith(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndWith() - } -- set {operation = .endWith(newValue)} -+ set {_uniqueStorage()._operation = .endWith(newValue)} - } - - public var callSuperConstructor: Fuzzilli_Protobuf_CallSuperConstructor { - get { -- if case .callSuperConstructor(let v)? = operation {return v} -+ if case .callSuperConstructor(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_CallSuperConstructor() - } -- set {operation = .callSuperConstructor(newValue)} -+ set {_uniqueStorage()._operation = .callSuperConstructor(newValue)} - } - - public var callSuperMethod: Fuzzilli_Protobuf_CallSuperMethod { - get { -- if case .callSuperMethod(let v)? = operation {return v} -+ if case .callSuperMethod(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_CallSuperMethod() - } -- set {operation = .callSuperMethod(newValue)} -+ set {_uniqueStorage()._operation = .callSuperMethod(newValue)} - } - - public var getPrivateProperty: Fuzzilli_Protobuf_GetPrivateProperty { - get { -- if case .getPrivateProperty(let v)? = operation {return v} -+ if case .getPrivateProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_GetPrivateProperty() - } -- set {operation = .getPrivateProperty(newValue)} -+ set {_uniqueStorage()._operation = .getPrivateProperty(newValue)} - } - - public var setPrivateProperty: Fuzzilli_Protobuf_SetPrivateProperty { - get { -- if case .setPrivateProperty(let v)? = operation {return v} -+ if case .setPrivateProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_SetPrivateProperty() - } -- set {operation = .setPrivateProperty(newValue)} -+ set {_uniqueStorage()._operation = .setPrivateProperty(newValue)} - } - - public var updatePrivateProperty: Fuzzilli_Protobuf_UpdatePrivateProperty { - get { -- if case .updatePrivateProperty(let v)? = operation {return v} -+ if case .updatePrivateProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_UpdatePrivateProperty() - } -- set {operation = .updatePrivateProperty(newValue)} -+ set {_uniqueStorage()._operation = .updatePrivateProperty(newValue)} - } - - public var callPrivateMethod: Fuzzilli_Protobuf_CallPrivateMethod { - get { -- if case .callPrivateMethod(let v)? = operation {return v} -+ if case .callPrivateMethod(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_CallPrivateMethod() - } -- set {operation = .callPrivateMethod(newValue)} -+ set {_uniqueStorage()._operation = .callPrivateMethod(newValue)} - } - - public var getSuperProperty: Fuzzilli_Protobuf_GetSuperProperty { - get { -- if case .getSuperProperty(let v)? = operation {return v} -+ if case .getSuperProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_GetSuperProperty() - } -- set {operation = .getSuperProperty(newValue)} -+ set {_uniqueStorage()._operation = .getSuperProperty(newValue)} - } - - public var setSuperProperty: Fuzzilli_Protobuf_SetSuperProperty { - get { -- if case .setSuperProperty(let v)? = operation {return v} -+ if case .setSuperProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_SetSuperProperty() - } -- set {operation = .setSuperProperty(newValue)} -+ set {_uniqueStorage()._operation = .setSuperProperty(newValue)} - } - - public var getComputedSuperProperty: Fuzzilli_Protobuf_GetComputedSuperProperty { - get { -- if case .getComputedSuperProperty(let v)? = operation {return v} -+ if case .getComputedSuperProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_GetComputedSuperProperty() - } -- set {operation = .getComputedSuperProperty(newValue)} -+ set {_uniqueStorage()._operation = .getComputedSuperProperty(newValue)} - } - - public var setComputedSuperProperty: Fuzzilli_Protobuf_SetComputedSuperProperty { - get { -- if case .setComputedSuperProperty(let v)? = operation {return v} -+ if case .setComputedSuperProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_SetComputedSuperProperty() - } -- set {operation = .setComputedSuperProperty(newValue)} -+ set {_uniqueStorage()._operation = .setComputedSuperProperty(newValue)} - } - - public var updateSuperProperty: Fuzzilli_Protobuf_UpdateSuperProperty { - get { -- if case .updateSuperProperty(let v)? = operation {return v} -+ if case .updateSuperProperty(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_UpdateSuperProperty() - } -- set {operation = .updateSuperProperty(newValue)} -+ set {_uniqueStorage()._operation = .updateSuperProperty(newValue)} - } - - public var beginIf: Fuzzilli_Protobuf_BeginIf { - get { -- if case .beginIf(let v)? = operation {return v} -+ if case .beginIf(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginIf() - } -- set {operation = .beginIf(newValue)} -+ set {_uniqueStorage()._operation = .beginIf(newValue)} - } - - public var beginElse: Fuzzilli_Protobuf_BeginElse { - get { -- if case .beginElse(let v)? = operation {return v} -+ if case .beginElse(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginElse() - } -- set {operation = .beginElse(newValue)} -+ set {_uniqueStorage()._operation = .beginElse(newValue)} - } - - public var endIf: Fuzzilli_Protobuf_EndIf { - get { -- if case .endIf(let v)? = operation {return v} -+ if case .endIf(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndIf() - } -- set {operation = .endIf(newValue)} -+ set {_uniqueStorage()._operation = .endIf(newValue)} - } - - public var beginWhileLoopHeader: Fuzzilli_Protobuf_BeginWhileLoopHeader { - get { -- if case .beginWhileLoopHeader(let v)? = operation {return v} -+ if case .beginWhileLoopHeader(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginWhileLoopHeader() - } -- set {operation = .beginWhileLoopHeader(newValue)} -+ set {_uniqueStorage()._operation = .beginWhileLoopHeader(newValue)} - } - - public var beginWhileLoopBody: Fuzzilli_Protobuf_BeginWhileLoopBody { - get { -- if case .beginWhileLoopBody(let v)? = operation {return v} -+ if case .beginWhileLoopBody(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginWhileLoopBody() - } -- set {operation = .beginWhileLoopBody(newValue)} -+ set {_uniqueStorage()._operation = .beginWhileLoopBody(newValue)} - } - - public var endWhileLoop: Fuzzilli_Protobuf_EndWhileLoop { - get { -- if case .endWhileLoop(let v)? = operation {return v} -+ if case .endWhileLoop(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndWhileLoop() - } -- set {operation = .endWhileLoop(newValue)} -+ set {_uniqueStorage()._operation = .endWhileLoop(newValue)} - } - - public var beginDoWhileLoopBody: Fuzzilli_Protobuf_BeginDoWhileLoopBody { - get { -- if case .beginDoWhileLoopBody(let v)? = operation {return v} -+ if case .beginDoWhileLoopBody(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginDoWhileLoopBody() - } -- set {operation = .beginDoWhileLoopBody(newValue)} -+ set {_uniqueStorage()._operation = .beginDoWhileLoopBody(newValue)} - } - - public var beginDoWhileLoopHeader: Fuzzilli_Protobuf_BeginDoWhileLoopHeader { - get { -- if case .beginDoWhileLoopHeader(let v)? = operation {return v} -+ if case .beginDoWhileLoopHeader(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginDoWhileLoopHeader() - } -- set {operation = .beginDoWhileLoopHeader(newValue)} -+ set {_uniqueStorage()._operation = .beginDoWhileLoopHeader(newValue)} - } - - public var endDoWhileLoop: Fuzzilli_Protobuf_EndDoWhileLoop { - get { -- if case .endDoWhileLoop(let v)? = operation {return v} -+ if case .endDoWhileLoop(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndDoWhileLoop() - } -- set {operation = .endDoWhileLoop(newValue)} -+ set {_uniqueStorage()._operation = .endDoWhileLoop(newValue)} - } - - public var beginForLoopInitializer: Fuzzilli_Protobuf_BeginForLoopInitializer { - get { -- if case .beginForLoopInitializer(let v)? = operation {return v} -+ if case .beginForLoopInitializer(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginForLoopInitializer() - } -- set {operation = .beginForLoopInitializer(newValue)} -+ set {_uniqueStorage()._operation = .beginForLoopInitializer(newValue)} - } - - public var beginForLoopCondition: Fuzzilli_Protobuf_BeginForLoopCondition { - get { -- if case .beginForLoopCondition(let v)? = operation {return v} -+ if case .beginForLoopCondition(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginForLoopCondition() - } -- set {operation = .beginForLoopCondition(newValue)} -+ set {_uniqueStorage()._operation = .beginForLoopCondition(newValue)} - } - - public var beginForLoopAfterthought: Fuzzilli_Protobuf_BeginForLoopAfterthought { - get { -- if case .beginForLoopAfterthought(let v)? = operation {return v} -+ if case .beginForLoopAfterthought(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginForLoopAfterthought() - } -- set {operation = .beginForLoopAfterthought(newValue)} -+ set {_uniqueStorage()._operation = .beginForLoopAfterthought(newValue)} - } - - public var beginForLoopBody: Fuzzilli_Protobuf_BeginForLoopBody { - get { -- if case .beginForLoopBody(let v)? = operation {return v} -+ if case .beginForLoopBody(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginForLoopBody() - } -- set {operation = .beginForLoopBody(newValue)} -+ set {_uniqueStorage()._operation = .beginForLoopBody(newValue)} - } - - public var endForLoop: Fuzzilli_Protobuf_EndForLoop { - get { -- if case .endForLoop(let v)? = operation {return v} -+ if case .endForLoop(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndForLoop() - } -- set {operation = .endForLoop(newValue)} -+ set {_uniqueStorage()._operation = .endForLoop(newValue)} - } - - public var beginForInLoop: Fuzzilli_Protobuf_BeginForInLoop { - get { -- if case .beginForInLoop(let v)? = operation {return v} -+ if case .beginForInLoop(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginForInLoop() - } -- set {operation = .beginForInLoop(newValue)} -+ set {_uniqueStorage()._operation = .beginForInLoop(newValue)} - } - - public var endForInLoop: Fuzzilli_Protobuf_EndForInLoop { - get { -- if case .endForInLoop(let v)? = operation {return v} -+ if case .endForInLoop(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndForInLoop() - } -- set {operation = .endForInLoop(newValue)} -+ set {_uniqueStorage()._operation = .endForInLoop(newValue)} - } - - public var beginForOfLoop: Fuzzilli_Protobuf_BeginForOfLoop { - get { -- if case .beginForOfLoop(let v)? = operation {return v} -+ if case .beginForOfLoop(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginForOfLoop() - } -- set {operation = .beginForOfLoop(newValue)} -+ set {_uniqueStorage()._operation = .beginForOfLoop(newValue)} - } - - public var beginForOfLoopWithDestruct: Fuzzilli_Protobuf_BeginForOfLoopWithDestruct { - get { -- if case .beginForOfLoopWithDestruct(let v)? = operation {return v} -+ if case .beginForOfLoopWithDestruct(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginForOfLoopWithDestruct() - } -- set {operation = .beginForOfLoopWithDestruct(newValue)} -+ set {_uniqueStorage()._operation = .beginForOfLoopWithDestruct(newValue)} - } - - public var endForOfLoop: Fuzzilli_Protobuf_EndForOfLoop { - get { -- if case .endForOfLoop(let v)? = operation {return v} -+ if case .endForOfLoop(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndForOfLoop() - } -- set {operation = .endForOfLoop(newValue)} -+ set {_uniqueStorage()._operation = .endForOfLoop(newValue)} - } - - public var beginRepeatLoop: Fuzzilli_Protobuf_BeginRepeatLoop { - get { -- if case .beginRepeatLoop(let v)? = operation {return v} -+ if case .beginRepeatLoop(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginRepeatLoop() - } -- set {operation = .beginRepeatLoop(newValue)} -+ set {_uniqueStorage()._operation = .beginRepeatLoop(newValue)} - } - - public var endRepeatLoop: Fuzzilli_Protobuf_EndRepeatLoop { - get { -- if case .endRepeatLoop(let v)? = operation {return v} -+ if case .endRepeatLoop(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndRepeatLoop() - } -- set {operation = .endRepeatLoop(newValue)} -+ set {_uniqueStorage()._operation = .endRepeatLoop(newValue)} - } - - public var loopBreak: Fuzzilli_Protobuf_LoopBreak { - get { -- if case .loopBreak(let v)? = operation {return v} -+ if case .loopBreak(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_LoopBreak() - } -- set {operation = .loopBreak(newValue)} -+ set {_uniqueStorage()._operation = .loopBreak(newValue)} - } - - public var loopContinue: Fuzzilli_Protobuf_LoopContinue { - get { -- if case .loopContinue(let v)? = operation {return v} -+ if case .loopContinue(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_LoopContinue() - } -- set {operation = .loopContinue(newValue)} -+ set {_uniqueStorage()._operation = .loopContinue(newValue)} - } - - public var beginTry: Fuzzilli_Protobuf_BeginTry { - get { -- if case .beginTry(let v)? = operation {return v} -+ if case .beginTry(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginTry() - } -- set {operation = .beginTry(newValue)} -+ set {_uniqueStorage()._operation = .beginTry(newValue)} - } - - public var beginCatch: Fuzzilli_Protobuf_BeginCatch { - get { -- if case .beginCatch(let v)? = operation {return v} -+ if case .beginCatch(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginCatch() - } -- set {operation = .beginCatch(newValue)} -+ set {_uniqueStorage()._operation = .beginCatch(newValue)} - } - - public var beginFinally: Fuzzilli_Protobuf_BeginFinally { - get { -- if case .beginFinally(let v)? = operation {return v} -+ if case .beginFinally(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginFinally() - } -- set {operation = .beginFinally(newValue)} -+ set {_uniqueStorage()._operation = .beginFinally(newValue)} - } - - public var endTryCatchFinally: Fuzzilli_Protobuf_EndTryCatchFinally { - get { -- if case .endTryCatchFinally(let v)? = operation {return v} -+ if case .endTryCatchFinally(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndTryCatchFinally() - } -- set {operation = .endTryCatchFinally(newValue)} -+ set {_uniqueStorage()._operation = .endTryCatchFinally(newValue)} - } - - public var throwException: Fuzzilli_Protobuf_ThrowException { - get { -- if case .throwException(let v)? = operation {return v} -+ if case .throwException(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_ThrowException() - } -- set {operation = .throwException(newValue)} -+ set {_uniqueStorage()._operation = .throwException(newValue)} - } - - public var beginCodeString: Fuzzilli_Protobuf_BeginCodeString { - get { -- if case .beginCodeString(let v)? = operation {return v} -+ if case .beginCodeString(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginCodeString() - } -- set {operation = .beginCodeString(newValue)} -+ set {_uniqueStorage()._operation = .beginCodeString(newValue)} - } - - public var endCodeString: Fuzzilli_Protobuf_EndCodeString { - get { -- if case .endCodeString(let v)? = operation {return v} -+ if case .endCodeString(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndCodeString() - } -- set {operation = .endCodeString(newValue)} -+ set {_uniqueStorage()._operation = .endCodeString(newValue)} - } - - public var beginBlockStatement: Fuzzilli_Protobuf_BeginBlockStatement { - get { -- if case .beginBlockStatement(let v)? = operation {return v} -+ if case .beginBlockStatement(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginBlockStatement() - } -- set {operation = .beginBlockStatement(newValue)} -+ set {_uniqueStorage()._operation = .beginBlockStatement(newValue)} - } - - public var endBlockStatement: Fuzzilli_Protobuf_EndBlockStatement { - get { -- if case .endBlockStatement(let v)? = operation {return v} -+ if case .endBlockStatement(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndBlockStatement() - } -- set {operation = .endBlockStatement(newValue)} -+ set {_uniqueStorage()._operation = .endBlockStatement(newValue)} - } - - public var beginSwitch: Fuzzilli_Protobuf_BeginSwitch { - get { -- if case .beginSwitch(let v)? = operation {return v} -+ if case .beginSwitch(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginSwitch() - } -- set {operation = .beginSwitch(newValue)} -+ set {_uniqueStorage()._operation = .beginSwitch(newValue)} - } - - public var beginSwitchCase: Fuzzilli_Protobuf_BeginSwitchCase { - get { -- if case .beginSwitchCase(let v)? = operation {return v} -+ if case .beginSwitchCase(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginSwitchCase() - } -- set {operation = .beginSwitchCase(newValue)} -+ set {_uniqueStorage()._operation = .beginSwitchCase(newValue)} - } - - public var beginSwitchDefaultCase: Fuzzilli_Protobuf_BeginSwitchDefaultCase { - get { -- if case .beginSwitchDefaultCase(let v)? = operation {return v} -+ if case .beginSwitchDefaultCase(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_BeginSwitchDefaultCase() - } -- set {operation = .beginSwitchDefaultCase(newValue)} -+ set {_uniqueStorage()._operation = .beginSwitchDefaultCase(newValue)} - } - - public var endSwitchCase: Fuzzilli_Protobuf_EndSwitchCase { - get { -- if case .endSwitchCase(let v)? = operation {return v} -+ if case .endSwitchCase(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndSwitchCase() - } -- set {operation = .endSwitchCase(newValue)} -+ set {_uniqueStorage()._operation = .endSwitchCase(newValue)} - } - - public var endSwitch: Fuzzilli_Protobuf_EndSwitch { - get { -- if case .endSwitch(let v)? = operation {return v} -+ if case .endSwitch(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_EndSwitch() - } -- set {operation = .endSwitch(newValue)} -+ set {_uniqueStorage()._operation = .endSwitch(newValue)} - } - - public var switchBreak: Fuzzilli_Protobuf_SwitchBreak { - get { -- if case .switchBreak(let v)? = operation {return v} -+ if case .switchBreak(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_SwitchBreak() - } -- set {operation = .switchBreak(newValue)} -+ set {_uniqueStorage()._operation = .switchBreak(newValue)} - } - - public var loadNewTarget: Fuzzilli_Protobuf_LoadNewTarget { - get { -- if case .loadNewTarget(let v)? = operation {return v} -+ if case .loadNewTarget(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_LoadNewTarget() - } -- set {operation = .loadNewTarget(newValue)} -+ set {_uniqueStorage()._operation = .loadNewTarget(newValue)} - } - - public var print: Fuzzilli_Protobuf_Print { - get { -- if case .print(let v)? = operation {return v} -+ if case .print(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_Print() - } -- set {operation = .print(newValue)} -+ set {_uniqueStorage()._operation = .print(newValue)} - } - - public var explore: Fuzzilli_Protobuf_Explore { - get { -- if case .explore(let v)? = operation {return v} -+ if case .explore(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_Explore() - } -- set {operation = .explore(newValue)} -+ set {_uniqueStorage()._operation = .explore(newValue)} - } - - public var probe: Fuzzilli_Protobuf_Probe { - get { -- if case .probe(let v)? = operation {return v} -+ if case .probe(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_Probe() - } -- set {operation = .probe(newValue)} -+ set {_uniqueStorage()._operation = .probe(newValue)} - } - - public var fixup: Fuzzilli_Protobuf_Fixup { - get { -- if case .fixup(let v)? = operation {return v} -+ if case .fixup(let v)? = _storage._operation {return v} - return Fuzzilli_Protobuf_Fixup() - } -- set {operation = .fixup(newValue)} -+ set {_uniqueStorage()._operation = .fixup(newValue)} - } - - public var unknownFields = SwiftProtobuf.UnknownStorage() -@@ -1656,722 +1662,185 @@ public struct Fuzzilli_Protobuf_Instruction { - - #if !swift(>=4.1) - public static func ==(lhs: Fuzzilli_Protobuf_Instruction.OneOf_Operation, rhs: Fuzzilli_Protobuf_Instruction.OneOf_Operation) -> Bool { -- // 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 (lhs, rhs) { -- case (.opIdx, .opIdx): return { -- guard case .opIdx(let l) = lhs, case .opIdx(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.nop, .nop): return { -- guard case .nop(let l) = lhs, case .nop(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.loadInteger, .loadInteger): return { -- guard case .loadInteger(let l) = lhs, case .loadInteger(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.loadBigInt, .loadBigInt): return { -- guard case .loadBigInt(let l) = lhs, case .loadBigInt(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.loadFloat, .loadFloat): return { -- guard case .loadFloat(let l) = lhs, case .loadFloat(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.loadString, .loadString): return { -- guard case .loadString(let l) = lhs, case .loadString(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.loadBoolean, .loadBoolean): return { -- guard case .loadBoolean(let l) = lhs, case .loadBoolean(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.loadUndefined, .loadUndefined): return { -- guard case .loadUndefined(let l) = lhs, case .loadUndefined(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.loadNull, .loadNull): return { -- guard case .loadNull(let l) = lhs, case .loadNull(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.loadThis, .loadThis): return { -- guard case .loadThis(let l) = lhs, case .loadThis(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.loadArguments, .loadArguments): return { -- guard case .loadArguments(let l) = lhs, case .loadArguments(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.loadRegExp, .loadRegExp): return { -- guard case .loadRegExp(let l) = lhs, case .loadRegExp(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginObjectLiteral, .beginObjectLiteral): return { -- guard case .beginObjectLiteral(let l) = lhs, case .beginObjectLiteral(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.objectLiteralAddProperty, .objectLiteralAddProperty): return { -- guard case .objectLiteralAddProperty(let l) = lhs, case .objectLiteralAddProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.objectLiteralAddElement, .objectLiteralAddElement): return { -- guard case .objectLiteralAddElement(let l) = lhs, case .objectLiteralAddElement(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.objectLiteralAddComputedProperty, .objectLiteralAddComputedProperty): return { -- guard case .objectLiteralAddComputedProperty(let l) = lhs, case .objectLiteralAddComputedProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.objectLiteralCopyProperties, .objectLiteralCopyProperties): return { -- guard case .objectLiteralCopyProperties(let l) = lhs, case .objectLiteralCopyProperties(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.objectLiteralSetPrototype, .objectLiteralSetPrototype): return { -- guard case .objectLiteralSetPrototype(let l) = lhs, case .objectLiteralSetPrototype(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginObjectLiteralMethod, .beginObjectLiteralMethod): return { -- guard case .beginObjectLiteralMethod(let l) = lhs, case .beginObjectLiteralMethod(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endObjectLiteralMethod, .endObjectLiteralMethod): return { -- guard case .endObjectLiteralMethod(let l) = lhs, case .endObjectLiteralMethod(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginObjectLiteralComputedMethod, .beginObjectLiteralComputedMethod): return { -- guard case .beginObjectLiteralComputedMethod(let l) = lhs, case .beginObjectLiteralComputedMethod(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endObjectLiteralComputedMethod, .endObjectLiteralComputedMethod): return { -- guard case .endObjectLiteralComputedMethod(let l) = lhs, case .endObjectLiteralComputedMethod(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginObjectLiteralGetter, .beginObjectLiteralGetter): return { -- guard case .beginObjectLiteralGetter(let l) = lhs, case .beginObjectLiteralGetter(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endObjectLiteralGetter, .endObjectLiteralGetter): return { -- guard case .endObjectLiteralGetter(let l) = lhs, case .endObjectLiteralGetter(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginObjectLiteralSetter, .beginObjectLiteralSetter): return { -- guard case .beginObjectLiteralSetter(let l) = lhs, case .beginObjectLiteralSetter(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endObjectLiteralSetter, .endObjectLiteralSetter): return { -- guard case .endObjectLiteralSetter(let l) = lhs, case .endObjectLiteralSetter(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endObjectLiteral, .endObjectLiteral): return { -- guard case .endObjectLiteral(let l) = lhs, case .endObjectLiteral(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginClassDefinition, .beginClassDefinition): return { -- guard case .beginClassDefinition(let l) = lhs, case .beginClassDefinition(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginClassConstructor, .beginClassConstructor): return { -- guard case .beginClassConstructor(let l) = lhs, case .beginClassConstructor(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endClassConstructor, .endClassConstructor): return { -- guard case .endClassConstructor(let l) = lhs, case .endClassConstructor(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.classAddInstanceProperty, .classAddInstanceProperty): return { -- guard case .classAddInstanceProperty(let l) = lhs, case .classAddInstanceProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.classAddInstanceElement, .classAddInstanceElement): return { -- guard case .classAddInstanceElement(let l) = lhs, case .classAddInstanceElement(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.classAddInstanceComputedProperty, .classAddInstanceComputedProperty): return { -- guard case .classAddInstanceComputedProperty(let l) = lhs, case .classAddInstanceComputedProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginClassInstanceMethod, .beginClassInstanceMethod): return { -- guard case .beginClassInstanceMethod(let l) = lhs, case .beginClassInstanceMethod(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endClassInstanceMethod, .endClassInstanceMethod): return { -- guard case .endClassInstanceMethod(let l) = lhs, case .endClassInstanceMethod(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginClassInstanceGetter, .beginClassInstanceGetter): return { -- guard case .beginClassInstanceGetter(let l) = lhs, case .beginClassInstanceGetter(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endClassInstanceGetter, .endClassInstanceGetter): return { -- guard case .endClassInstanceGetter(let l) = lhs, case .endClassInstanceGetter(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginClassInstanceSetter, .beginClassInstanceSetter): return { -- guard case .beginClassInstanceSetter(let l) = lhs, case .beginClassInstanceSetter(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endClassInstanceSetter, .endClassInstanceSetter): return { -- guard case .endClassInstanceSetter(let l) = lhs, case .endClassInstanceSetter(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.classAddStaticProperty, .classAddStaticProperty): return { -- guard case .classAddStaticProperty(let l) = lhs, case .classAddStaticProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.classAddStaticElement, .classAddStaticElement): return { -- guard case .classAddStaticElement(let l) = lhs, case .classAddStaticElement(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.classAddStaticComputedProperty, .classAddStaticComputedProperty): return { -- guard case .classAddStaticComputedProperty(let l) = lhs, case .classAddStaticComputedProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginClassStaticInitializer, .beginClassStaticInitializer): return { -- guard case .beginClassStaticInitializer(let l) = lhs, case .beginClassStaticInitializer(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endClassStaticInitializer, .endClassStaticInitializer): return { -- guard case .endClassStaticInitializer(let l) = lhs, case .endClassStaticInitializer(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginClassStaticMethod, .beginClassStaticMethod): return { -- guard case .beginClassStaticMethod(let l) = lhs, case .beginClassStaticMethod(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endClassStaticMethod, .endClassStaticMethod): return { -- guard case .endClassStaticMethod(let l) = lhs, case .endClassStaticMethod(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginClassStaticGetter, .beginClassStaticGetter): return { -- guard case .beginClassStaticGetter(let l) = lhs, case .beginClassStaticGetter(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endClassStaticGetter, .endClassStaticGetter): return { -- guard case .endClassStaticGetter(let l) = lhs, case .endClassStaticGetter(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginClassStaticSetter, .beginClassStaticSetter): return { -- guard case .beginClassStaticSetter(let l) = lhs, case .beginClassStaticSetter(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endClassStaticSetter, .endClassStaticSetter): return { -- guard case .endClassStaticSetter(let l) = lhs, case .endClassStaticSetter(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.classAddPrivateInstanceProperty, .classAddPrivateInstanceProperty): return { -- guard case .classAddPrivateInstanceProperty(let l) = lhs, case .classAddPrivateInstanceProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginClassPrivateInstanceMethod, .beginClassPrivateInstanceMethod): return { -- guard case .beginClassPrivateInstanceMethod(let l) = lhs, case .beginClassPrivateInstanceMethod(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endClassPrivateInstanceMethod, .endClassPrivateInstanceMethod): return { -- guard case .endClassPrivateInstanceMethod(let l) = lhs, case .endClassPrivateInstanceMethod(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.classAddPrivateStaticProperty, .classAddPrivateStaticProperty): return { -- guard case .classAddPrivateStaticProperty(let l) = lhs, case .classAddPrivateStaticProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginClassPrivateStaticMethod, .beginClassPrivateStaticMethod): return { -- guard case .beginClassPrivateStaticMethod(let l) = lhs, case .beginClassPrivateStaticMethod(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endClassPrivateStaticMethod, .endClassPrivateStaticMethod): return { -- guard case .endClassPrivateStaticMethod(let l) = lhs, case .endClassPrivateStaticMethod(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endClassDefinition, .endClassDefinition): return { -- guard case .endClassDefinition(let l) = lhs, case .endClassDefinition(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.createArray, .createArray): return { -- guard case .createArray(let l) = lhs, case .createArray(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.createIntArray, .createIntArray): return { -- guard case .createIntArray(let l) = lhs, case .createIntArray(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.createFloatArray, .createFloatArray): return { -- guard case .createFloatArray(let l) = lhs, case .createFloatArray(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.createArrayWithSpread, .createArrayWithSpread): return { -- guard case .createArrayWithSpread(let l) = lhs, case .createArrayWithSpread(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.createTemplateString, .createTemplateString): return { -- guard case .createTemplateString(let l) = lhs, case .createTemplateString(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.loadBuiltin, .loadBuiltin): return { -- guard case .loadBuiltin(let l) = lhs, case .loadBuiltin(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.getProperty, .getProperty): return { -- guard case .getProperty(let l) = lhs, case .getProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.setProperty, .setProperty): return { -- guard case .setProperty(let l) = lhs, case .setProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.updateProperty, .updateProperty): return { -- guard case .updateProperty(let l) = lhs, case .updateProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.deleteProperty, .deleteProperty): return { -- guard case .deleteProperty(let l) = lhs, case .deleteProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.configureProperty, .configureProperty): return { -- guard case .configureProperty(let l) = lhs, case .configureProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.getElement, .getElement): return { -- guard case .getElement(let l) = lhs, case .getElement(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.setElement, .setElement): return { -- guard case .setElement(let l) = lhs, case .setElement(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.updateElement, .updateElement): return { -- guard case .updateElement(let l) = lhs, case .updateElement(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.deleteElement, .deleteElement): return { -- guard case .deleteElement(let l) = lhs, case .deleteElement(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.configureElement, .configureElement): return { -- guard case .configureElement(let l) = lhs, case .configureElement(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.getComputedProperty, .getComputedProperty): return { -- guard case .getComputedProperty(let l) = lhs, case .getComputedProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.setComputedProperty, .setComputedProperty): return { -- guard case .setComputedProperty(let l) = lhs, case .setComputedProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.updateComputedProperty, .updateComputedProperty): return { -- guard case .updateComputedProperty(let l) = lhs, case .updateComputedProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.deleteComputedProperty, .deleteComputedProperty): return { -- guard case .deleteComputedProperty(let l) = lhs, case .deleteComputedProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.configureComputedProperty, .configureComputedProperty): return { -- guard case .configureComputedProperty(let l) = lhs, case .configureComputedProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.typeOf, .typeOf): return { -- guard case .typeOf(let l) = lhs, case .typeOf(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.testInstanceOf, .testInstanceOf): return { -- guard case .testInstanceOf(let l) = lhs, case .testInstanceOf(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.testIn, .testIn): return { -- guard case .testIn(let l) = lhs, case .testIn(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginPlainFunction, .beginPlainFunction): return { -- guard case .beginPlainFunction(let l) = lhs, case .beginPlainFunction(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endPlainFunction, .endPlainFunction): return { -- guard case .endPlainFunction(let l) = lhs, case .endPlainFunction(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginArrowFunction, .beginArrowFunction): return { -- guard case .beginArrowFunction(let l) = lhs, case .beginArrowFunction(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endArrowFunction, .endArrowFunction): return { -- guard case .endArrowFunction(let l) = lhs, case .endArrowFunction(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginGeneratorFunction, .beginGeneratorFunction): return { -- guard case .beginGeneratorFunction(let l) = lhs, case .beginGeneratorFunction(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endGeneratorFunction, .endGeneratorFunction): return { -- guard case .endGeneratorFunction(let l) = lhs, case .endGeneratorFunction(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginAsyncFunction, .beginAsyncFunction): return { -- guard case .beginAsyncFunction(let l) = lhs, case .beginAsyncFunction(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endAsyncFunction, .endAsyncFunction): return { -- guard case .endAsyncFunction(let l) = lhs, case .endAsyncFunction(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginAsyncArrowFunction, .beginAsyncArrowFunction): return { -- guard case .beginAsyncArrowFunction(let l) = lhs, case .beginAsyncArrowFunction(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endAsyncArrowFunction, .endAsyncArrowFunction): return { -- guard case .endAsyncArrowFunction(let l) = lhs, case .endAsyncArrowFunction(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginAsyncGeneratorFunction, .beginAsyncGeneratorFunction): return { -- guard case .beginAsyncGeneratorFunction(let l) = lhs, case .beginAsyncGeneratorFunction(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endAsyncGeneratorFunction, .endAsyncGeneratorFunction): return { -- guard case .endAsyncGeneratorFunction(let l) = lhs, case .endAsyncGeneratorFunction(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginConstructor, .beginConstructor): return { -- guard case .beginConstructor(let l) = lhs, case .beginConstructor(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endConstructor, .endConstructor): return { -- guard case .endConstructor(let l) = lhs, case .endConstructor(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.return, .return): return { -- guard case .return(let l) = lhs, case .return(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.yield, .yield): return { -- guard case .yield(let l) = lhs, case .yield(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.yieldEach, .yieldEach): return { -- guard case .yieldEach(let l) = lhs, case .yieldEach(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.await, .await): return { -- guard case .await(let l) = lhs, case .await(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.callFunction, .callFunction): return { -- guard case .callFunction(let l) = lhs, case .callFunction(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.callFunctionWithSpread, .callFunctionWithSpread): return { -- guard case .callFunctionWithSpread(let l) = lhs, case .callFunctionWithSpread(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.construct, .construct): return { -- guard case .construct(let l) = lhs, case .construct(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.constructWithSpread, .constructWithSpread): return { -- guard case .constructWithSpread(let l) = lhs, case .constructWithSpread(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.callMethod, .callMethod): return { -- guard case .callMethod(let l) = lhs, case .callMethod(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.callMethodWithSpread, .callMethodWithSpread): return { -- guard case .callMethodWithSpread(let l) = lhs, case .callMethodWithSpread(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.callComputedMethod, .callComputedMethod): return { -- guard case .callComputedMethod(let l) = lhs, case .callComputedMethod(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.callComputedMethodWithSpread, .callComputedMethodWithSpread): return { -- guard case .callComputedMethodWithSpread(let l) = lhs, case .callComputedMethodWithSpread(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.unaryOperation, .unaryOperation): return { -- guard case .unaryOperation(let l) = lhs, case .unaryOperation(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.binaryOperation, .binaryOperation): return { -- guard case .binaryOperation(let l) = lhs, case .binaryOperation(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.ternaryOperation, .ternaryOperation): return { -- guard case .ternaryOperation(let l) = lhs, case .ternaryOperation(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.update, .update): return { -- guard case .update(let l) = lhs, case .update(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.dup, .dup): return { -- guard case .dup(let l) = lhs, case .dup(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.reassign, .reassign): return { -- guard case .reassign(let l) = lhs, case .reassign(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.destructArray, .destructArray): return { -- guard case .destructArray(let l) = lhs, case .destructArray(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.destructArrayAndReassign, .destructArrayAndReassign): return { -- guard case .destructArrayAndReassign(let l) = lhs, case .destructArrayAndReassign(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.destructObject, .destructObject): return { -- guard case .destructObject(let l) = lhs, case .destructObject(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.destructObjectAndReassign, .destructObjectAndReassign): return { -- guard case .destructObjectAndReassign(let l) = lhs, case .destructObjectAndReassign(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.compare, .compare): return { -- guard case .compare(let l) = lhs, case .compare(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.loadNamedVariable, .loadNamedVariable): return { -- guard case .loadNamedVariable(let l) = lhs, case .loadNamedVariable(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.storeNamedVariable, .storeNamedVariable): return { -- guard case .storeNamedVariable(let l) = lhs, case .storeNamedVariable(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.defineNamedVariable, .defineNamedVariable): return { -- guard case .defineNamedVariable(let l) = lhs, case .defineNamedVariable(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.eval, .eval): return { -- guard case .eval(let l) = lhs, case .eval(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginWith, .beginWith): return { -- guard case .beginWith(let l) = lhs, case .beginWith(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endWith, .endWith): return { -- guard case .endWith(let l) = lhs, case .endWith(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.callSuperConstructor, .callSuperConstructor): return { -- guard case .callSuperConstructor(let l) = lhs, case .callSuperConstructor(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.callSuperMethod, .callSuperMethod): return { -- guard case .callSuperMethod(let l) = lhs, case .callSuperMethod(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.getPrivateProperty, .getPrivateProperty): return { -- guard case .getPrivateProperty(let l) = lhs, case .getPrivateProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.setPrivateProperty, .setPrivateProperty): return { -- guard case .setPrivateProperty(let l) = lhs, case .setPrivateProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.updatePrivateProperty, .updatePrivateProperty): return { -- guard case .updatePrivateProperty(let l) = lhs, case .updatePrivateProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.callPrivateMethod, .callPrivateMethod): return { -- guard case .callPrivateMethod(let l) = lhs, case .callPrivateMethod(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.getSuperProperty, .getSuperProperty): return { -- guard case .getSuperProperty(let l) = lhs, case .getSuperProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.setSuperProperty, .setSuperProperty): return { -- guard case .setSuperProperty(let l) = lhs, case .setSuperProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.getComputedSuperProperty, .getComputedSuperProperty): return { -- guard case .getComputedSuperProperty(let l) = lhs, case .getComputedSuperProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.setComputedSuperProperty, .setComputedSuperProperty): return { -- guard case .setComputedSuperProperty(let l) = lhs, case .setComputedSuperProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.updateSuperProperty, .updateSuperProperty): return { -- guard case .updateSuperProperty(let l) = lhs, case .updateSuperProperty(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginIf, .beginIf): return { -- guard case .beginIf(let l) = lhs, case .beginIf(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginElse, .beginElse): return { -- guard case .beginElse(let l) = lhs, case .beginElse(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endIf, .endIf): return { -- guard case .endIf(let l) = lhs, case .endIf(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginWhileLoopHeader, .beginWhileLoopHeader): return { -- guard case .beginWhileLoopHeader(let l) = lhs, case .beginWhileLoopHeader(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginWhileLoopBody, .beginWhileLoopBody): return { -- guard case .beginWhileLoopBody(let l) = lhs, case .beginWhileLoopBody(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endWhileLoop, .endWhileLoop): return { -- guard case .endWhileLoop(let l) = lhs, case .endWhileLoop(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginDoWhileLoopBody, .beginDoWhileLoopBody): return { -- guard case .beginDoWhileLoopBody(let l) = lhs, case .beginDoWhileLoopBody(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginDoWhileLoopHeader, .beginDoWhileLoopHeader): return { -- guard case .beginDoWhileLoopHeader(let l) = lhs, case .beginDoWhileLoopHeader(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endDoWhileLoop, .endDoWhileLoop): return { -- guard case .endDoWhileLoop(let l) = lhs, case .endDoWhileLoop(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginForLoopInitializer, .beginForLoopInitializer): return { -- guard case .beginForLoopInitializer(let l) = lhs, case .beginForLoopInitializer(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginForLoopCondition, .beginForLoopCondition): return { -- guard case .beginForLoopCondition(let l) = lhs, case .beginForLoopCondition(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginForLoopAfterthought, .beginForLoopAfterthought): return { -- guard case .beginForLoopAfterthought(let l) = lhs, case .beginForLoopAfterthought(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginForLoopBody, .beginForLoopBody): return { -- guard case .beginForLoopBody(let l) = lhs, case .beginForLoopBody(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endForLoop, .endForLoop): return { -- guard case .endForLoop(let l) = lhs, case .endForLoop(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginForInLoop, .beginForInLoop): return { -- guard case .beginForInLoop(let l) = lhs, case .beginForInLoop(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endForInLoop, .endForInLoop): return { -- guard case .endForInLoop(let l) = lhs, case .endForInLoop(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginForOfLoop, .beginForOfLoop): return { -- guard case .beginForOfLoop(let l) = lhs, case .beginForOfLoop(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginForOfLoopWithDestruct, .beginForOfLoopWithDestruct): return { -- guard case .beginForOfLoopWithDestruct(let l) = lhs, case .beginForOfLoopWithDestruct(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endForOfLoop, .endForOfLoop): return { -- guard case .endForOfLoop(let l) = lhs, case .endForOfLoop(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginRepeatLoop, .beginRepeatLoop): return { -- guard case .beginRepeatLoop(let l) = lhs, case .beginRepeatLoop(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endRepeatLoop, .endRepeatLoop): return { -- guard case .endRepeatLoop(let l) = lhs, case .endRepeatLoop(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.loopBreak, .loopBreak): return { -- guard case .loopBreak(let l) = lhs, case .loopBreak(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.loopContinue, .loopContinue): return { -- guard case .loopContinue(let l) = lhs, case .loopContinue(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginTry, .beginTry): return { -- guard case .beginTry(let l) = lhs, case .beginTry(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginCatch, .beginCatch): return { -- guard case .beginCatch(let l) = lhs, case .beginCatch(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginFinally, .beginFinally): return { -- guard case .beginFinally(let l) = lhs, case .beginFinally(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endTryCatchFinally, .endTryCatchFinally): return { -- guard case .endTryCatchFinally(let l) = lhs, case .endTryCatchFinally(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.throwException, .throwException): return { -- guard case .throwException(let l) = lhs, case .throwException(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginCodeString, .beginCodeString): return { -- guard case .beginCodeString(let l) = lhs, case .beginCodeString(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endCodeString, .endCodeString): return { -- guard case .endCodeString(let l) = lhs, case .endCodeString(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginBlockStatement, .beginBlockStatement): return { -- guard case .beginBlockStatement(let l) = lhs, case .beginBlockStatement(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endBlockStatement, .endBlockStatement): return { -- guard case .endBlockStatement(let l) = lhs, case .endBlockStatement(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginSwitch, .beginSwitch): return { -- guard case .beginSwitch(let l) = lhs, case .beginSwitch(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginSwitchCase, .beginSwitchCase): return { -- guard case .beginSwitchCase(let l) = lhs, case .beginSwitchCase(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.beginSwitchDefaultCase, .beginSwitchDefaultCase): return { -- guard case .beginSwitchDefaultCase(let l) = lhs, case .beginSwitchDefaultCase(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endSwitchCase, .endSwitchCase): return { -- guard case .endSwitchCase(let l) = lhs, case .endSwitchCase(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.endSwitch, .endSwitch): return { -- guard case .endSwitch(let l) = lhs, case .endSwitch(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.switchBreak, .switchBreak): return { -- guard case .switchBreak(let l) = lhs, case .switchBreak(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.loadNewTarget, .loadNewTarget): return { -- guard case .loadNewTarget(let l) = lhs, case .loadNewTarget(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.print, .print): return { -- guard case .print(let l) = lhs, case .print(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.explore, .explore): return { -- guard case .explore(let l) = lhs, case .explore(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.probe, .probe): return { -- guard case .probe(let l) = lhs, case .probe(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -- case (.fixup, .fixup): return { -- guard case .fixup(let l) = lhs, case .fixup(let r) = rhs else { preconditionFailure() } -- return l == r -- }() -+ case (.opIdx(let l), .opIdx(let r)): return l == r -+ case (.nop(let l), .nop(let r)): return l == r -+ case (.loadInteger(let l), .loadInteger(let r)): return l == r -+ case (.loadBigInt(let l), .loadBigInt(let r)): return l == r -+ case (.loadFloat(let l), .loadFloat(let r)): return l == r -+ case (.loadString(let l), .loadString(let r)): return l == r -+ case (.loadBoolean(let l), .loadBoolean(let r)): return l == r -+ case (.loadUndefined(let l), .loadUndefined(let r)): return l == r -+ case (.loadNull(let l), .loadNull(let r)): return l == r -+ case (.loadThis(let l), .loadThis(let r)): return l == r -+ case (.loadArguments(let l), .loadArguments(let r)): return l == r -+ case (.loadRegExp(let l), .loadRegExp(let r)): return l == r -+ case (.beginObjectLiteral(let l), .beginObjectLiteral(let r)): return l == r -+ case (.objectLiteralAddProperty(let l), .objectLiteralAddProperty(let r)): return l == r -+ case (.objectLiteralAddElement(let l), .objectLiteralAddElement(let r)): return l == r -+ case (.objectLiteralAddComputedProperty(let l), .objectLiteralAddComputedProperty(let r)): return l == r -+ case (.objectLiteralCopyProperties(let l), .objectLiteralCopyProperties(let r)): return l == r -+ case (.objectLiteralSetPrototype(let l), .objectLiteralSetPrototype(let r)): return l == r -+ case (.beginObjectLiteralMethod(let l), .beginObjectLiteralMethod(let r)): return l == r -+ case (.endObjectLiteralMethod(let l), .endObjectLiteralMethod(let r)): return l == r -+ case (.beginObjectLiteralComputedMethod(let l), .beginObjectLiteralComputedMethod(let r)): return l == r -+ case (.endObjectLiteralComputedMethod(let l), .endObjectLiteralComputedMethod(let r)): return l == r -+ case (.beginObjectLiteralGetter(let l), .beginObjectLiteralGetter(let r)): return l == r -+ case (.endObjectLiteralGetter(let l), .endObjectLiteralGetter(let r)): return l == r -+ case (.beginObjectLiteralSetter(let l), .beginObjectLiteralSetter(let r)): return l == r -+ case (.endObjectLiteralSetter(let l), .endObjectLiteralSetter(let r)): return l == r -+ case (.endObjectLiteral(let l), .endObjectLiteral(let r)): return l == r -+ case (.beginClassDefinition(let l), .beginClassDefinition(let r)): return l == r -+ case (.beginClassConstructor(let l), .beginClassConstructor(let r)): return l == r -+ case (.endClassConstructor(let l), .endClassConstructor(let r)): return l == r -+ case (.classAddInstanceProperty(let l), .classAddInstanceProperty(let r)): return l == r -+ case (.classAddInstanceElement(let l), .classAddInstanceElement(let r)): return l == r -+ case (.classAddInstanceComputedProperty(let l), .classAddInstanceComputedProperty(let r)): return l == r -+ case (.beginClassInstanceMethod(let l), .beginClassInstanceMethod(let r)): return l == r -+ case (.endClassInstanceMethod(let l), .endClassInstanceMethod(let r)): return l == r -+ case (.beginClassInstanceGetter(let l), .beginClassInstanceGetter(let r)): return l == r -+ case (.endClassInstanceGetter(let l), .endClassInstanceGetter(let r)): return l == r -+ case (.beginClassInstanceSetter(let l), .beginClassInstanceSetter(let r)): return l == r -+ case (.endClassInstanceSetter(let l), .endClassInstanceSetter(let r)): return l == r -+ case (.classAddStaticProperty(let l), .classAddStaticProperty(let r)): return l == r -+ case (.classAddStaticElement(let l), .classAddStaticElement(let r)): return l == r -+ case (.classAddStaticComputedProperty(let l), .classAddStaticComputedProperty(let r)): return l == r -+ case (.beginClassStaticInitializer(let l), .beginClassStaticInitializer(let r)): return l == r -+ case (.endClassStaticInitializer(let l), .endClassStaticInitializer(let r)): return l == r -+ case (.beginClassStaticMethod(let l), .beginClassStaticMethod(let r)): return l == r -+ case (.endClassStaticMethod(let l), .endClassStaticMethod(let r)): return l == r -+ case (.beginClassStaticGetter(let l), .beginClassStaticGetter(let r)): return l == r -+ case (.endClassStaticGetter(let l), .endClassStaticGetter(let r)): return l == r -+ case (.beginClassStaticSetter(let l), .beginClassStaticSetter(let r)): return l == r -+ case (.endClassStaticSetter(let l), .endClassStaticSetter(let r)): return l == r -+ case (.classAddPrivateInstanceProperty(let l), .classAddPrivateInstanceProperty(let r)): return l == r -+ case (.beginClassPrivateInstanceMethod(let l), .beginClassPrivateInstanceMethod(let r)): return l == r -+ case (.endClassPrivateInstanceMethod(let l), .endClassPrivateInstanceMethod(let r)): return l == r -+ case (.classAddPrivateStaticProperty(let l), .classAddPrivateStaticProperty(let r)): return l == r -+ case (.beginClassPrivateStaticMethod(let l), .beginClassPrivateStaticMethod(let r)): return l == r -+ case (.endClassPrivateStaticMethod(let l), .endClassPrivateStaticMethod(let r)): return l == r -+ case (.endClassDefinition(let l), .endClassDefinition(let r)): return l == r -+ case (.createArray(let l), .createArray(let r)): return l == r -+ case (.createIntArray(let l), .createIntArray(let r)): return l == r -+ case (.createFloatArray(let l), .createFloatArray(let r)): return l == r -+ case (.createArrayWithSpread(let l), .createArrayWithSpread(let r)): return l == r -+ case (.createTemplateString(let l), .createTemplateString(let r)): return l == r -+ case (.loadBuiltin(let l), .loadBuiltin(let r)): return l == r -+ case (.getProperty(let l), .getProperty(let r)): return l == r -+ case (.setProperty(let l), .setProperty(let r)): return l == r -+ case (.updateProperty(let l), .updateProperty(let r)): return l == r -+ case (.deleteProperty(let l), .deleteProperty(let r)): return l == r -+ case (.configureProperty(let l), .configureProperty(let r)): return l == r -+ case (.getElement(let l), .getElement(let r)): return l == r -+ case (.setElement(let l), .setElement(let r)): return l == r -+ case (.updateElement(let l), .updateElement(let r)): return l == r -+ case (.deleteElement(let l), .deleteElement(let r)): return l == r -+ case (.configureElement(let l), .configureElement(let r)): return l == r -+ case (.getComputedProperty(let l), .getComputedProperty(let r)): return l == r -+ case (.setComputedProperty(let l), .setComputedProperty(let r)): return l == r -+ case (.updateComputedProperty(let l), .updateComputedProperty(let r)): return l == r -+ case (.deleteComputedProperty(let l), .deleteComputedProperty(let r)): return l == r -+ case (.configureComputedProperty(let l), .configureComputedProperty(let r)): return l == r -+ case (.typeOf(let l), .typeOf(let r)): return l == r -+ case (.testInstanceOf(let l), .testInstanceOf(let r)): return l == r -+ case (.testIn(let l), .testIn(let r)): return l == r -+ case (.beginPlainFunction(let l), .beginPlainFunction(let r)): return l == r -+ case (.endPlainFunction(let l), .endPlainFunction(let r)): return l == r -+ case (.beginArrowFunction(let l), .beginArrowFunction(let r)): return l == r -+ case (.endArrowFunction(let l), .endArrowFunction(let r)): return l == r -+ case (.beginGeneratorFunction(let l), .beginGeneratorFunction(let r)): return l == r -+ case (.endGeneratorFunction(let l), .endGeneratorFunction(let r)): return l == r -+ case (.beginAsyncFunction(let l), .beginAsyncFunction(let r)): return l == r -+ case (.endAsyncFunction(let l), .endAsyncFunction(let r)): return l == r -+ case (.beginAsyncArrowFunction(let l), .beginAsyncArrowFunction(let r)): return l == r -+ case (.endAsyncArrowFunction(let l), .endAsyncArrowFunction(let r)): return l == r -+ case (.beginAsyncGeneratorFunction(let l), .beginAsyncGeneratorFunction(let r)): return l == r -+ case (.endAsyncGeneratorFunction(let l), .endAsyncGeneratorFunction(let r)): return l == r -+ case (.beginConstructor(let l), .beginConstructor(let r)): return l == r -+ case (.endConstructor(let l), .endConstructor(let r)): return l == r -+ case (.return(let l), .return(let r)): return l == r -+ case (.yield(let l), .yield(let r)): return l == r -+ case (.yieldEach(let l), .yieldEach(let r)): return l == r -+ case (.await(let l), .await(let r)): return l == r -+ case (.callFunction(let l), .callFunction(let r)): return l == r -+ case (.callFunctionWithSpread(let l), .callFunctionWithSpread(let r)): return l == r -+ case (.construct(let l), .construct(let r)): return l == r -+ case (.constructWithSpread(let l), .constructWithSpread(let r)): return l == r -+ case (.callMethod(let l), .callMethod(let r)): return l == r -+ case (.callMethodWithSpread(let l), .callMethodWithSpread(let r)): return l == r -+ case (.callComputedMethod(let l), .callComputedMethod(let r)): return l == r -+ case (.callComputedMethodWithSpread(let l), .callComputedMethodWithSpread(let r)): return l == r -+ case (.unaryOperation(let l), .unaryOperation(let r)): return l == r -+ case (.binaryOperation(let l), .binaryOperation(let r)): return l == r -+ case (.ternaryOperation(let l), .ternaryOperation(let r)): return l == r -+ case (.update(let l), .update(let r)): return l == r -+ case (.dup(let l), .dup(let r)): return l == r -+ case (.reassign(let l), .reassign(let r)): return l == r -+ case (.destructArray(let l), .destructArray(let r)): return l == r -+ case (.destructArrayAndReassign(let l), .destructArrayAndReassign(let r)): return l == r -+ case (.destructObject(let l), .destructObject(let r)): return l == r -+ case (.destructObjectAndReassign(let l), .destructObjectAndReassign(let r)): return l == r -+ case (.compare(let l), .compare(let r)): return l == r -+ case (.loadNamedVariable(let l), .loadNamedVariable(let r)): return l == r -+ case (.storeNamedVariable(let l), .storeNamedVariable(let r)): return l == r -+ case (.defineNamedVariable(let l), .defineNamedVariable(let r)): return l == r -+ case (.eval(let l), .eval(let r)): return l == r -+ case (.beginWith(let l), .beginWith(let r)): return l == r -+ case (.endWith(let l), .endWith(let r)): return l == r -+ case (.callSuperConstructor(let l), .callSuperConstructor(let r)): return l == r -+ case (.callSuperMethod(let l), .callSuperMethod(let r)): return l == r -+ case (.getPrivateProperty(let l), .getPrivateProperty(let r)): return l == r -+ case (.setPrivateProperty(let l), .setPrivateProperty(let r)): return l == r -+ case (.updatePrivateProperty(let l), .updatePrivateProperty(let r)): return l == r -+ case (.callPrivateMethod(let l), .callPrivateMethod(let r)): return l == r -+ case (.getSuperProperty(let l), .getSuperProperty(let r)): return l == r -+ case (.setSuperProperty(let l), .setSuperProperty(let r)): return l == r -+ case (.getComputedSuperProperty(let l), .getComputedSuperProperty(let r)): return l == r -+ case (.setComputedSuperProperty(let l), .setComputedSuperProperty(let r)): return l == r -+ case (.updateSuperProperty(let l), .updateSuperProperty(let r)): return l == r -+ case (.beginIf(let l), .beginIf(let r)): return l == r -+ case (.beginElse(let l), .beginElse(let r)): return l == r -+ case (.endIf(let l), .endIf(let r)): return l == r -+ case (.beginWhileLoopHeader(let l), .beginWhileLoopHeader(let r)): return l == r -+ case (.beginWhileLoopBody(let l), .beginWhileLoopBody(let r)): return l == r -+ case (.endWhileLoop(let l), .endWhileLoop(let r)): return l == r -+ case (.beginDoWhileLoopBody(let l), .beginDoWhileLoopBody(let r)): return l == r -+ case (.beginDoWhileLoopHeader(let l), .beginDoWhileLoopHeader(let r)): return l == r -+ case (.endDoWhileLoop(let l), .endDoWhileLoop(let r)): return l == r -+ case (.beginForLoopInitializer(let l), .beginForLoopInitializer(let r)): return l == r -+ case (.beginForLoopCondition(let l), .beginForLoopCondition(let r)): return l == r -+ case (.beginForLoopAfterthought(let l), .beginForLoopAfterthought(let r)): return l == r -+ case (.beginForLoopBody(let l), .beginForLoopBody(let r)): return l == r -+ case (.endForLoop(let l), .endForLoop(let r)): return l == r -+ case (.beginForInLoop(let l), .beginForInLoop(let r)): return l == r -+ case (.endForInLoop(let l), .endForInLoop(let r)): return l == r -+ case (.beginForOfLoop(let l), .beginForOfLoop(let r)): return l == r -+ case (.beginForOfLoopWithDestruct(let l), .beginForOfLoopWithDestruct(let r)): return l == r -+ case (.endForOfLoop(let l), .endForOfLoop(let r)): return l == r -+ case (.beginRepeatLoop(let l), .beginRepeatLoop(let r)): return l == r -+ case (.endRepeatLoop(let l), .endRepeatLoop(let r)): return l == r -+ case (.loopBreak(let l), .loopBreak(let r)): return l == r -+ case (.loopContinue(let l), .loopContinue(let r)): return l == r -+ case (.beginTry(let l), .beginTry(let r)): return l == r -+ case (.beginCatch(let l), .beginCatch(let r)): return l == r -+ case (.beginFinally(let l), .beginFinally(let r)): return l == r -+ case (.endTryCatchFinally(let l), .endTryCatchFinally(let r)): return l == r -+ case (.throwException(let l), .throwException(let r)): return l == r -+ case (.beginCodeString(let l), .beginCodeString(let r)): return l == r -+ case (.endCodeString(let l), .endCodeString(let r)): return l == r -+ case (.beginBlockStatement(let l), .beginBlockStatement(let r)): return l == r -+ case (.endBlockStatement(let l), .endBlockStatement(let r)): return l == r -+ case (.beginSwitch(let l), .beginSwitch(let r)): return l == r -+ case (.beginSwitchCase(let l), .beginSwitchCase(let r)): return l == r -+ case (.beginSwitchDefaultCase(let l), .beginSwitchDefaultCase(let r)): return l == r -+ case (.endSwitchCase(let l), .endSwitchCase(let r)): return l == r -+ case (.endSwitch(let l), .endSwitch(let r)): return l == r -+ case (.switchBreak(let l), .switchBreak(let r)): return l == r -+ case (.loadNewTarget(let l), .loadNewTarget(let r)): return l == r -+ case (.print(let l), .print(let r)): return l == r -+ case (.explore(let l), .explore(let r)): return l == r -+ case (.probe(let l), .probe(let r)): return l == r -+ case (.fixup(let l), .fixup(let r)): return l == r - default: return false - } - } -@@ -2379,6 +1848,8 @@ public struct Fuzzilli_Protobuf_Instruction { - } - - public init() {} -+ -+ fileprivate var _storage = _StorageClass.defaultInstance - } - - public struct Fuzzilli_Protobuf_Program { -@@ -2417,12 +1888,6 @@ public struct Fuzzilli_Protobuf_Program { - fileprivate var _storage = _StorageClass.defaultInstance - } - --#if swift(>=5.5) && canImport(_Concurrency) --extension Fuzzilli_Protobuf_Instruction: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Instruction.OneOf_Operation: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Program: @unchecked Sendable {} --#endif // swift(>=5.5) && canImport(_Concurrency) -- - // MARK: - Code below here is support for the SwiftProtobuf runtime. - - fileprivate let _protobuf_package = "fuzzilli.protobuf" -@@ -2611,3056 +2076,1839 @@ extension Fuzzilli_Protobuf_Instruction: SwiftProtobuf.Message, SwiftProtobuf._M - 179: .same(proto: "fixup"), - ] - -+ fileprivate class _StorageClass { -+ var _inouts: [UInt32] = [] -+ var _operation: Fuzzilli_Protobuf_Instruction.OneOf_Operation? -+ -+ static let defaultInstance = _StorageClass() -+ -+ private init() {} -+ -+ init(copying source: _StorageClass) { -+ _inouts = source._inouts -+ _operation = source._operation -+ } -+ } -+ -+ fileprivate mutating func _uniqueStorage() -> _StorageClass { -+ if !isKnownUniquelyReferenced(&_storage) { -+ _storage = _StorageClass(copying: _storage) -+ } -+ return _storage -+ } -+ - 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.decodeRepeatedUInt32Field(value: &self.inouts) }() -- case 2: try { -- var v: UInt32? -- try decoder.decodeSingularUInt32Field(value: &v) -- if let v = v { -- if self.operation != nil {try decoder.handleConflictingOneOf()} -- self.operation = .opIdx(v) -- } -- }() -- case 3: try { -- var v: Fuzzilli_Protobuf_Nop? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .nop(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .nop(v) -- } -- }() -- case 4: try { -- var v: Fuzzilli_Protobuf_LoadInteger? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .loadInteger(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .loadInteger(v) -- } -- }() -- case 5: try { -- var v: Fuzzilli_Protobuf_LoadBigInt? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .loadBigInt(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .loadBigInt(v) -- } -- }() -- case 6: try { -- var v: Fuzzilli_Protobuf_LoadFloat? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .loadFloat(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .loadFloat(v) -- } -- }() -- case 7: try { -- var v: Fuzzilli_Protobuf_LoadString? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .loadString(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .loadString(v) -- } -- }() -- case 8: try { -- var v: Fuzzilli_Protobuf_LoadBoolean? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .loadBoolean(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .loadBoolean(v) -- } -- }() -- case 9: try { -- var v: Fuzzilli_Protobuf_LoadUndefined? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .loadUndefined(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .loadUndefined(v) -- } -- }() -- case 10: try { -- var v: Fuzzilli_Protobuf_LoadNull? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .loadNull(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .loadNull(v) -- } -- }() -- case 11: try { -- var v: Fuzzilli_Protobuf_LoadThis? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .loadThis(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .loadThis(v) -- } -- }() -- case 12: try { -- var v: Fuzzilli_Protobuf_LoadArguments? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .loadArguments(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .loadArguments(v) -- } -- }() -- case 13: try { -- var v: Fuzzilli_Protobuf_LoadRegExp? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .loadRegExp(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .loadRegExp(v) -- } -- }() -- case 14: try { -- var v: Fuzzilli_Protobuf_BeginObjectLiteral? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginObjectLiteral(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginObjectLiteral(v) -- } -- }() -- case 15: try { -- var v: Fuzzilli_Protobuf_ObjectLiteralAddProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .objectLiteralAddProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .objectLiteralAddProperty(v) -- } -- }() -- case 16: try { -- var v: Fuzzilli_Protobuf_ObjectLiteralAddElement? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .objectLiteralAddElement(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .objectLiteralAddElement(v) -- } -- }() -- case 17: try { -- var v: Fuzzilli_Protobuf_ObjectLiteralAddComputedProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .objectLiteralAddComputedProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .objectLiteralAddComputedProperty(v) -- } -- }() -- case 18: try { -- var v: Fuzzilli_Protobuf_ObjectLiteralCopyProperties? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .objectLiteralCopyProperties(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .objectLiteralCopyProperties(v) -- } -- }() -- case 19: try { -- var v: Fuzzilli_Protobuf_ObjectLiteralSetPrototype? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .objectLiteralSetPrototype(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .objectLiteralSetPrototype(v) -- } -- }() -- case 20: try { -- var v: Fuzzilli_Protobuf_BeginObjectLiteralMethod? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginObjectLiteralMethod(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginObjectLiteralMethod(v) -- } -- }() -- case 21: try { -- var v: Fuzzilli_Protobuf_EndObjectLiteralMethod? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endObjectLiteralMethod(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endObjectLiteralMethod(v) -- } -- }() -- case 22: try { -- var v: Fuzzilli_Protobuf_BeginObjectLiteralComputedMethod? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginObjectLiteralComputedMethod(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginObjectLiteralComputedMethod(v) -- } -- }() -- case 23: try { -- var v: Fuzzilli_Protobuf_EndObjectLiteralComputedMethod? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endObjectLiteralComputedMethod(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endObjectLiteralComputedMethod(v) -- } -- }() -- case 24: try { -- var v: Fuzzilli_Protobuf_BeginObjectLiteralGetter? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginObjectLiteralGetter(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginObjectLiteralGetter(v) -- } -- }() -- case 25: try { -- var v: Fuzzilli_Protobuf_EndObjectLiteralGetter? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endObjectLiteralGetter(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endObjectLiteralGetter(v) -- } -- }() -- case 26: try { -- var v: Fuzzilli_Protobuf_BeginObjectLiteralSetter? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginObjectLiteralSetter(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginObjectLiteralSetter(v) -- } -- }() -- case 27: try { -- var v: Fuzzilli_Protobuf_EndObjectLiteralSetter? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endObjectLiteralSetter(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endObjectLiteralSetter(v) -- } -- }() -- case 28: try { -- var v: Fuzzilli_Protobuf_EndObjectLiteral? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endObjectLiteral(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endObjectLiteral(v) -- } -- }() -- case 29: try { -- var v: Fuzzilli_Protobuf_BeginClassDefinition? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginClassDefinition(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginClassDefinition(v) -- } -- }() -- case 30: try { -- var v: Fuzzilli_Protobuf_BeginClassConstructor? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginClassConstructor(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginClassConstructor(v) -- } -- }() -- case 31: try { -- var v: Fuzzilli_Protobuf_EndClassConstructor? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endClassConstructor(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endClassConstructor(v) -- } -- }() -- case 32: try { -- var v: Fuzzilli_Protobuf_ClassAddInstanceProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .classAddInstanceProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .classAddInstanceProperty(v) -- } -- }() -- case 33: try { -- var v: Fuzzilli_Protobuf_ClassAddInstanceElement? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .classAddInstanceElement(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .classAddInstanceElement(v) -- } -- }() -- case 34: try { -- var v: Fuzzilli_Protobuf_ClassAddInstanceComputedProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .classAddInstanceComputedProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .classAddInstanceComputedProperty(v) -- } -- }() -- case 35: try { -- var v: Fuzzilli_Protobuf_BeginClassInstanceMethod? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginClassInstanceMethod(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginClassInstanceMethod(v) -- } -- }() -- case 36: try { -- var v: Fuzzilli_Protobuf_EndClassInstanceMethod? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endClassInstanceMethod(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endClassInstanceMethod(v) -- } -- }() -- case 37: try { -- var v: Fuzzilli_Protobuf_BeginClassInstanceGetter? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginClassInstanceGetter(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginClassInstanceGetter(v) -- } -- }() -- case 38: try { -- var v: Fuzzilli_Protobuf_EndClassInstanceGetter? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endClassInstanceGetter(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endClassInstanceGetter(v) -- } -- }() -- case 39: try { -- var v: Fuzzilli_Protobuf_BeginClassInstanceSetter? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginClassInstanceSetter(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginClassInstanceSetter(v) -- } -- }() -- case 40: try { -- var v: Fuzzilli_Protobuf_EndClassInstanceSetter? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endClassInstanceSetter(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endClassInstanceSetter(v) -- } -- }() -- case 41: try { -- var v: Fuzzilli_Protobuf_ClassAddStaticProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .classAddStaticProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .classAddStaticProperty(v) -- } -- }() -- case 42: try { -- var v: Fuzzilli_Protobuf_ClassAddStaticElement? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .classAddStaticElement(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .classAddStaticElement(v) -- } -- }() -- case 43: try { -- var v: Fuzzilli_Protobuf_ClassAddStaticComputedProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .classAddStaticComputedProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .classAddStaticComputedProperty(v) -- } -- }() -- case 44: try { -- var v: Fuzzilli_Protobuf_BeginClassStaticInitializer? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginClassStaticInitializer(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginClassStaticInitializer(v) -- } -- }() -- case 45: try { -- var v: Fuzzilli_Protobuf_EndClassStaticInitializer? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endClassStaticInitializer(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endClassStaticInitializer(v) -- } -- }() -- case 46: try { -- var v: Fuzzilli_Protobuf_BeginClassStaticMethod? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginClassStaticMethod(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginClassStaticMethod(v) -- } -- }() -- case 47: try { -- var v: Fuzzilli_Protobuf_EndClassStaticMethod? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endClassStaticMethod(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endClassStaticMethod(v) -- } -- }() -- case 48: try { -- var v: Fuzzilli_Protobuf_BeginClassStaticGetter? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginClassStaticGetter(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginClassStaticGetter(v) -- } -- }() -- case 49: try { -- var v: Fuzzilli_Protobuf_EndClassStaticGetter? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endClassStaticGetter(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endClassStaticGetter(v) -- } -- }() -- case 50: try { -- var v: Fuzzilli_Protobuf_BeginClassStaticSetter? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginClassStaticSetter(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginClassStaticSetter(v) -- } -- }() -- case 51: try { -- var v: Fuzzilli_Protobuf_EndClassStaticSetter? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endClassStaticSetter(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endClassStaticSetter(v) -- } -- }() -- case 52: try { -- var v: Fuzzilli_Protobuf_ClassAddPrivateInstanceProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .classAddPrivateInstanceProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .classAddPrivateInstanceProperty(v) -- } -- }() -- case 53: try { -- var v: Fuzzilli_Protobuf_BeginClassPrivateInstanceMethod? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginClassPrivateInstanceMethod(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginClassPrivateInstanceMethod(v) -- } -- }() -- case 54: try { -- var v: Fuzzilli_Protobuf_EndClassPrivateInstanceMethod? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endClassPrivateInstanceMethod(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endClassPrivateInstanceMethod(v) -- } -- }() -- case 55: try { -- var v: Fuzzilli_Protobuf_ClassAddPrivateStaticProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .classAddPrivateStaticProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .classAddPrivateStaticProperty(v) -- } -- }() -- case 56: try { -- var v: Fuzzilli_Protobuf_BeginClassPrivateStaticMethod? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginClassPrivateStaticMethod(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginClassPrivateStaticMethod(v) -- } -- }() -- case 57: try { -- var v: Fuzzilli_Protobuf_EndClassPrivateStaticMethod? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endClassPrivateStaticMethod(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endClassPrivateStaticMethod(v) -- } -- }() -- case 58: try { -- var v: Fuzzilli_Protobuf_EndClassDefinition? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endClassDefinition(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endClassDefinition(v) -- } -- }() -- case 59: try { -- var v: Fuzzilli_Protobuf_CreateArray? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .createArray(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .createArray(v) -- } -- }() -- case 60: try { -- var v: Fuzzilli_Protobuf_CreateIntArray? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .createIntArray(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .createIntArray(v) -- } -- }() -- case 61: try { -- var v: Fuzzilli_Protobuf_CreateFloatArray? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .createFloatArray(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .createFloatArray(v) -- } -- }() -- case 62: try { -- var v: Fuzzilli_Protobuf_CreateArrayWithSpread? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .createArrayWithSpread(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .createArrayWithSpread(v) -- } -- }() -- case 63: try { -- var v: Fuzzilli_Protobuf_CreateTemplateString? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .createTemplateString(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .createTemplateString(v) -- } -- }() -- case 64: try { -- var v: Fuzzilli_Protobuf_LoadBuiltin? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .loadBuiltin(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .loadBuiltin(v) -- } -- }() -- case 65: try { -- var v: Fuzzilli_Protobuf_GetProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .getProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .getProperty(v) -- } -- }() -- case 66: try { -- var v: Fuzzilli_Protobuf_SetProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .setProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .setProperty(v) -- } -- }() -- case 67: try { -- var v: Fuzzilli_Protobuf_UpdateProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .updateProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .updateProperty(v) -- } -- }() -- case 68: try { -- var v: Fuzzilli_Protobuf_DeleteProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .deleteProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .deleteProperty(v) -- } -- }() -- case 69: try { -- var v: Fuzzilli_Protobuf_ConfigureProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .configureProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .configureProperty(v) -- } -- }() -- case 70: try { -- var v: Fuzzilli_Protobuf_GetElement? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .getElement(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .getElement(v) -- } -- }() -- case 71: try { -- var v: Fuzzilli_Protobuf_SetElement? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .setElement(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .setElement(v) -- } -- }() -- case 72: try { -- var v: Fuzzilli_Protobuf_UpdateElement? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .updateElement(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .updateElement(v) -- } -- }() -- case 73: try { -- var v: Fuzzilli_Protobuf_DeleteElement? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .deleteElement(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .deleteElement(v) -- } -- }() -- case 74: try { -- var v: Fuzzilli_Protobuf_ConfigureElement? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .configureElement(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .configureElement(v) -- } -- }() -- case 75: try { -- var v: Fuzzilli_Protobuf_GetComputedProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .getComputedProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .getComputedProperty(v) -- } -- }() -- case 76: try { -- var v: Fuzzilli_Protobuf_SetComputedProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .setComputedProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .setComputedProperty(v) -- } -- }() -- case 77: try { -- var v: Fuzzilli_Protobuf_UpdateComputedProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .updateComputedProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .updateComputedProperty(v) -- } -- }() -- case 78: try { -- var v: Fuzzilli_Protobuf_DeleteComputedProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .deleteComputedProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .deleteComputedProperty(v) -- } -- }() -- case 79: try { -- var v: Fuzzilli_Protobuf_ConfigureComputedProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .configureComputedProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .configureComputedProperty(v) -- } -- }() -- case 80: try { -- var v: Fuzzilli_Protobuf_TypeOf? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .typeOf(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .typeOf(v) -- } -- }() -- case 81: try { -- var v: Fuzzilli_Protobuf_TestInstanceOf? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .testInstanceOf(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .testInstanceOf(v) -- } -- }() -- case 82: try { -- var v: Fuzzilli_Protobuf_TestIn? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .testIn(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .testIn(v) -- } -- }() -- case 83: try { -- var v: Fuzzilli_Protobuf_BeginPlainFunction? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginPlainFunction(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginPlainFunction(v) -- } -- }() -- case 84: try { -- var v: Fuzzilli_Protobuf_EndPlainFunction? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endPlainFunction(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endPlainFunction(v) -- } -- }() -- case 85: try { -- var v: Fuzzilli_Protobuf_BeginArrowFunction? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginArrowFunction(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginArrowFunction(v) -- } -- }() -- case 86: try { -- var v: Fuzzilli_Protobuf_EndArrowFunction? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endArrowFunction(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endArrowFunction(v) -- } -- }() -- case 87: try { -- var v: Fuzzilli_Protobuf_BeginGeneratorFunction? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginGeneratorFunction(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginGeneratorFunction(v) -- } -- }() -- case 88: try { -- var v: Fuzzilli_Protobuf_EndGeneratorFunction? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endGeneratorFunction(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endGeneratorFunction(v) -- } -- }() -- case 89: try { -- var v: Fuzzilli_Protobuf_BeginAsyncFunction? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginAsyncFunction(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginAsyncFunction(v) -- } -- }() -- case 90: try { -- var v: Fuzzilli_Protobuf_EndAsyncFunction? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endAsyncFunction(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endAsyncFunction(v) -- } -- }() -- case 91: try { -- var v: Fuzzilli_Protobuf_BeginAsyncArrowFunction? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginAsyncArrowFunction(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginAsyncArrowFunction(v) -- } -- }() -- case 92: try { -- var v: Fuzzilli_Protobuf_EndAsyncArrowFunction? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endAsyncArrowFunction(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endAsyncArrowFunction(v) -- } -- }() -- case 93: try { -- var v: Fuzzilli_Protobuf_BeginAsyncGeneratorFunction? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginAsyncGeneratorFunction(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginAsyncGeneratorFunction(v) -- } -- }() -- case 94: try { -- var v: Fuzzilli_Protobuf_EndAsyncGeneratorFunction? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endAsyncGeneratorFunction(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endAsyncGeneratorFunction(v) -- } -- }() -- case 95: try { -- var v: Fuzzilli_Protobuf_BeginConstructor? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginConstructor(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginConstructor(v) -- } -- }() -- case 96: try { -- var v: Fuzzilli_Protobuf_EndConstructor? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endConstructor(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endConstructor(v) -- } -- }() -- case 97: try { -- var v: Fuzzilli_Protobuf_Return? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .return(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .return(v) -- } -- }() -- case 98: try { -- var v: Fuzzilli_Protobuf_Yield? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .yield(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .yield(v) -- } -- }() -- case 99: try { -- var v: Fuzzilli_Protobuf_YieldEach? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .yieldEach(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .yieldEach(v) -- } -- }() -- case 100: try { -- var v: Fuzzilli_Protobuf_Await? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .await(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .await(v) -- } -- }() -- case 101: try { -- var v: Fuzzilli_Protobuf_CallFunction? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .callFunction(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .callFunction(v) -- } -- }() -- case 102: try { -- var v: Fuzzilli_Protobuf_CallFunctionWithSpread? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .callFunctionWithSpread(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .callFunctionWithSpread(v) -- } -- }() -- case 103: try { -- var v: Fuzzilli_Protobuf_Construct? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .construct(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .construct(v) -- } -- }() -- case 104: try { -- var v: Fuzzilli_Protobuf_ConstructWithSpread? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .constructWithSpread(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .constructWithSpread(v) -- } -- }() -- case 105: try { -- var v: Fuzzilli_Protobuf_CallMethod? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .callMethod(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .callMethod(v) -- } -- }() -- case 106: try { -- var v: Fuzzilli_Protobuf_CallMethodWithSpread? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .callMethodWithSpread(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .callMethodWithSpread(v) -- } -- }() -- case 107: try { -- var v: Fuzzilli_Protobuf_CallComputedMethod? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .callComputedMethod(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .callComputedMethod(v) -- } -- }() -- case 108: try { -- var v: Fuzzilli_Protobuf_CallComputedMethodWithSpread? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .callComputedMethodWithSpread(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .callComputedMethodWithSpread(v) -- } -- }() -- case 109: try { -- var v: Fuzzilli_Protobuf_UnaryOperation? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .unaryOperation(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .unaryOperation(v) -- } -- }() -- case 110: try { -- var v: Fuzzilli_Protobuf_BinaryOperation? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .binaryOperation(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .binaryOperation(v) -- } -- }() -- case 111: try { -- var v: Fuzzilli_Protobuf_TernaryOperation? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .ternaryOperation(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .ternaryOperation(v) -- } -- }() -- case 112: try { -- var v: Fuzzilli_Protobuf_Update? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .update(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .update(v) -- } -- }() -- case 113: try { -- var v: Fuzzilli_Protobuf_Dup? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .dup(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .dup(v) -- } -- }() -- case 114: try { -- var v: Fuzzilli_Protobuf_Reassign? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .reassign(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .reassign(v) -- } -- }() -- case 115: try { -- var v: Fuzzilli_Protobuf_DestructArray? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .destructArray(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .destructArray(v) -- } -- }() -- case 116: try { -- var v: Fuzzilli_Protobuf_DestructArrayAndReassign? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .destructArrayAndReassign(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .destructArrayAndReassign(v) -- } -- }() -- case 117: try { -- var v: Fuzzilli_Protobuf_DestructObject? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .destructObject(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .destructObject(v) -- } -- }() -- case 118: try { -- var v: Fuzzilli_Protobuf_DestructObjectAndReassign? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .destructObjectAndReassign(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .destructObjectAndReassign(v) -- } -- }() -- case 119: try { -- var v: Fuzzilli_Protobuf_Compare? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .compare(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .compare(v) -- } -- }() -- case 120: try { -- var v: Fuzzilli_Protobuf_LoadNamedVariable? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .loadNamedVariable(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .loadNamedVariable(v) -- } -- }() -- case 121: try { -- var v: Fuzzilli_Protobuf_StoreNamedVariable? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .storeNamedVariable(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .storeNamedVariable(v) -- } -- }() -- case 122: try { -- var v: Fuzzilli_Protobuf_DefineNamedVariable? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .defineNamedVariable(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .defineNamedVariable(v) -- } -- }() -- case 123: try { -- var v: Fuzzilli_Protobuf_Eval? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .eval(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .eval(v) -- } -- }() -- case 124: try { -- var v: Fuzzilli_Protobuf_BeginWith? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginWith(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginWith(v) -- } -- }() -- case 125: try { -- var v: Fuzzilli_Protobuf_EndWith? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endWith(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endWith(v) -- } -- }() -- case 126: try { -- var v: Fuzzilli_Protobuf_CallSuperConstructor? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .callSuperConstructor(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .callSuperConstructor(v) -- } -- }() -- case 127: try { -- var v: Fuzzilli_Protobuf_CallSuperMethod? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .callSuperMethod(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .callSuperMethod(v) -- } -- }() -- case 128: try { -- var v: Fuzzilli_Protobuf_GetPrivateProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .getPrivateProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .getPrivateProperty(v) -- } -- }() -- case 129: try { -- var v: Fuzzilli_Protobuf_SetPrivateProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .setPrivateProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .setPrivateProperty(v) -- } -- }() -- case 130: try { -- var v: Fuzzilli_Protobuf_UpdatePrivateProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .updatePrivateProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .updatePrivateProperty(v) -- } -- }() -- case 131: try { -- var v: Fuzzilli_Protobuf_CallPrivateMethod? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .callPrivateMethod(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .callPrivateMethod(v) -- } -- }() -- case 132: try { -- var v: Fuzzilli_Protobuf_GetSuperProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .getSuperProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .getSuperProperty(v) -- } -- }() -- case 133: try { -- var v: Fuzzilli_Protobuf_SetSuperProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .setSuperProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .setSuperProperty(v) -- } -- }() -- case 134: try { -- var v: Fuzzilli_Protobuf_GetComputedSuperProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .getComputedSuperProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .getComputedSuperProperty(v) -- } -- }() -- case 135: try { -- var v: Fuzzilli_Protobuf_SetComputedSuperProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .setComputedSuperProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .setComputedSuperProperty(v) -- } -- }() -- case 136: try { -- var v: Fuzzilli_Protobuf_UpdateSuperProperty? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .updateSuperProperty(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .updateSuperProperty(v) -- } -- }() -- case 137: try { -- var v: Fuzzilli_Protobuf_BeginIf? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginIf(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginIf(v) -- } -- }() -- case 138: try { -- var v: Fuzzilli_Protobuf_BeginElse? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginElse(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginElse(v) -- } -- }() -- case 139: try { -- var v: Fuzzilli_Protobuf_EndIf? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endIf(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endIf(v) -- } -- }() -- case 140: try { -- var v: Fuzzilli_Protobuf_BeginWhileLoopHeader? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginWhileLoopHeader(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginWhileLoopHeader(v) -- } -- }() -- case 141: try { -- var v: Fuzzilli_Protobuf_BeginWhileLoopBody? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginWhileLoopBody(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginWhileLoopBody(v) -- } -- }() -- case 142: try { -- var v: Fuzzilli_Protobuf_EndWhileLoop? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endWhileLoop(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endWhileLoop(v) -- } -- }() -- case 143: try { -- var v: Fuzzilli_Protobuf_BeginDoWhileLoopBody? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginDoWhileLoopBody(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginDoWhileLoopBody(v) -- } -- }() -- case 144: try { -- var v: Fuzzilli_Protobuf_BeginDoWhileLoopHeader? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginDoWhileLoopHeader(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginDoWhileLoopHeader(v) -- } -- }() -- case 145: try { -- var v: Fuzzilli_Protobuf_EndDoWhileLoop? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endDoWhileLoop(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endDoWhileLoop(v) -- } -- }() -- case 146: try { -- var v: Fuzzilli_Protobuf_BeginForLoopInitializer? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginForLoopInitializer(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginForLoopInitializer(v) -- } -- }() -- case 147: try { -- var v: Fuzzilli_Protobuf_BeginForLoopCondition? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginForLoopCondition(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginForLoopCondition(v) -- } -- }() -- case 148: try { -- var v: Fuzzilli_Protobuf_BeginForLoopAfterthought? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginForLoopAfterthought(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginForLoopAfterthought(v) -- } -- }() -- case 149: try { -- var v: Fuzzilli_Protobuf_BeginForLoopBody? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginForLoopBody(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginForLoopBody(v) -- } -- }() -- case 150: try { -- var v: Fuzzilli_Protobuf_EndForLoop? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endForLoop(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endForLoop(v) -- } -- }() -- case 151: try { -- var v: Fuzzilli_Protobuf_BeginForInLoop? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginForInLoop(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginForInLoop(v) -- } -- }() -- case 152: try { -- var v: Fuzzilli_Protobuf_EndForInLoop? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endForInLoop(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endForInLoop(v) -- } -- }() -- case 153: try { -- var v: Fuzzilli_Protobuf_BeginForOfLoop? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginForOfLoop(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginForOfLoop(v) -- } -- }() -- case 154: try { -- var v: Fuzzilli_Protobuf_BeginForOfLoopWithDestruct? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginForOfLoopWithDestruct(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginForOfLoopWithDestruct(v) -- } -- }() -- case 155: try { -- var v: Fuzzilli_Protobuf_EndForOfLoop? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endForOfLoop(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endForOfLoop(v) -- } -- }() -- case 156: try { -- var v: Fuzzilli_Protobuf_BeginRepeatLoop? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginRepeatLoop(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginRepeatLoop(v) -- } -- }() -- case 157: try { -- var v: Fuzzilli_Protobuf_EndRepeatLoop? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endRepeatLoop(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endRepeatLoop(v) -- } -- }() -- case 158: try { -- var v: Fuzzilli_Protobuf_LoopBreak? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .loopBreak(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .loopBreak(v) -- } -- }() -- case 159: try { -- var v: Fuzzilli_Protobuf_LoopContinue? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .loopContinue(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .loopContinue(v) -- } -- }() -- case 160: try { -- var v: Fuzzilli_Protobuf_BeginTry? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginTry(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginTry(v) -- } -- }() -- case 161: try { -- var v: Fuzzilli_Protobuf_BeginCatch? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginCatch(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginCatch(v) -- } -- }() -- case 162: try { -- var v: Fuzzilli_Protobuf_BeginFinally? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginFinally(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginFinally(v) -- } -- }() -- case 163: try { -- var v: Fuzzilli_Protobuf_EndTryCatchFinally? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endTryCatchFinally(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endTryCatchFinally(v) -- } -- }() -- case 164: try { -- var v: Fuzzilli_Protobuf_ThrowException? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .throwException(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .throwException(v) -- } -- }() -- case 165: try { -- var v: Fuzzilli_Protobuf_BeginCodeString? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginCodeString(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginCodeString(v) -- } -- }() -- case 166: try { -- var v: Fuzzilli_Protobuf_EndCodeString? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endCodeString(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endCodeString(v) -- } -- }() -- case 167: try { -- var v: Fuzzilli_Protobuf_BeginBlockStatement? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginBlockStatement(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginBlockStatement(v) -- } -- }() -- case 168: try { -- var v: Fuzzilli_Protobuf_EndBlockStatement? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endBlockStatement(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endBlockStatement(v) -- } -- }() -- case 169: try { -- var v: Fuzzilli_Protobuf_BeginSwitch? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginSwitch(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginSwitch(v) -- } -- }() -- case 170: try { -- var v: Fuzzilli_Protobuf_BeginSwitchCase? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginSwitchCase(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginSwitchCase(v) -- } -- }() -- case 171: try { -- var v: Fuzzilli_Protobuf_BeginSwitchDefaultCase? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .beginSwitchDefaultCase(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .beginSwitchDefaultCase(v) -- } -- }() -- case 172: try { -- var v: Fuzzilli_Protobuf_EndSwitchCase? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endSwitchCase(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endSwitchCase(v) -- } -- }() -- case 173: try { -- var v: Fuzzilli_Protobuf_EndSwitch? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .endSwitch(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .endSwitch(v) -- } -- }() -- case 174: try { -- var v: Fuzzilli_Protobuf_SwitchBreak? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .switchBreak(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .switchBreak(v) -- } -- }() -- case 175: try { -- var v: Fuzzilli_Protobuf_LoadNewTarget? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .loadNewTarget(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .loadNewTarget(v) -- } -- }() -- case 176: try { -- var v: Fuzzilli_Protobuf_Print? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .print(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .print(v) -- } -- }() -- case 177: try { -- var v: Fuzzilli_Protobuf_Explore? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .explore(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .explore(v) -- } -- }() -- case 178: try { -- var v: Fuzzilli_Protobuf_Probe? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .probe(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .probe(v) -- } -- }() -- case 179: try { -- var v: Fuzzilli_Protobuf_Fixup? -- var hadOneofValue = false -- if let current = self.operation { -- hadOneofValue = true -- if case .fixup(let m) = current {v = m} -- } -- try decoder.decodeSingularMessageField(value: &v) -- if let v = v { -- if hadOneofValue {try decoder.handleConflictingOneOf()} -- self.operation = .fixup(v) -+ _ = _uniqueStorage() -+ try withExtendedLifetime(_storage) { (_storage: _StorageClass) in -+ while let fieldNumber = try decoder.nextFieldNumber() { -+ switch fieldNumber { -+ case 1: try decoder.decodeRepeatedUInt32Field(value: &_storage._inouts) -+ case 2: -+ if _storage._operation != nil {try decoder.handleConflictingOneOf()} -+ var v: UInt32? -+ try decoder.decodeSingularUInt32Field(value: &v) -+ if let v = v {_storage._operation = .opIdx(v)} -+ case 3: -+ var v: Fuzzilli_Protobuf_Nop? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .nop(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .nop(v)} -+ case 4: -+ var v: Fuzzilli_Protobuf_LoadInteger? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .loadInteger(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .loadInteger(v)} -+ case 5: -+ var v: Fuzzilli_Protobuf_LoadBigInt? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .loadBigInt(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .loadBigInt(v)} -+ case 6: -+ var v: Fuzzilli_Protobuf_LoadFloat? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .loadFloat(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .loadFloat(v)} -+ case 7: -+ var v: Fuzzilli_Protobuf_LoadString? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .loadString(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .loadString(v)} -+ case 8: -+ var v: Fuzzilli_Protobuf_LoadBoolean? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .loadBoolean(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .loadBoolean(v)} -+ case 9: -+ var v: Fuzzilli_Protobuf_LoadUndefined? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .loadUndefined(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .loadUndefined(v)} -+ case 10: -+ var v: Fuzzilli_Protobuf_LoadNull? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .loadNull(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .loadNull(v)} -+ case 11: -+ var v: Fuzzilli_Protobuf_LoadThis? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .loadThis(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .loadThis(v)} -+ case 12: -+ var v: Fuzzilli_Protobuf_LoadArguments? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .loadArguments(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .loadArguments(v)} -+ case 13: -+ var v: Fuzzilli_Protobuf_LoadRegExp? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .loadRegExp(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .loadRegExp(v)} -+ case 14: -+ var v: Fuzzilli_Protobuf_BeginObjectLiteral? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginObjectLiteral(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginObjectLiteral(v)} -+ case 15: -+ var v: Fuzzilli_Protobuf_ObjectLiteralAddProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .objectLiteralAddProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .objectLiteralAddProperty(v)} -+ case 16: -+ var v: Fuzzilli_Protobuf_ObjectLiteralAddElement? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .objectLiteralAddElement(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .objectLiteralAddElement(v)} -+ case 17: -+ var v: Fuzzilli_Protobuf_ObjectLiteralAddComputedProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .objectLiteralAddComputedProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .objectLiteralAddComputedProperty(v)} -+ case 18: -+ var v: Fuzzilli_Protobuf_ObjectLiteralCopyProperties? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .objectLiteralCopyProperties(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .objectLiteralCopyProperties(v)} -+ case 19: -+ var v: Fuzzilli_Protobuf_ObjectLiteralSetPrototype? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .objectLiteralSetPrototype(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .objectLiteralSetPrototype(v)} -+ case 20: -+ var v: Fuzzilli_Protobuf_BeginObjectLiteralMethod? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginObjectLiteralMethod(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginObjectLiteralMethod(v)} -+ case 21: -+ var v: Fuzzilli_Protobuf_EndObjectLiteralMethod? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endObjectLiteralMethod(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endObjectLiteralMethod(v)} -+ case 22: -+ var v: Fuzzilli_Protobuf_BeginObjectLiteralComputedMethod? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginObjectLiteralComputedMethod(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginObjectLiteralComputedMethod(v)} -+ case 23: -+ var v: Fuzzilli_Protobuf_EndObjectLiteralComputedMethod? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endObjectLiteralComputedMethod(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endObjectLiteralComputedMethod(v)} -+ case 24: -+ var v: Fuzzilli_Protobuf_BeginObjectLiteralGetter? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginObjectLiteralGetter(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginObjectLiteralGetter(v)} -+ case 25: -+ var v: Fuzzilli_Protobuf_EndObjectLiteralGetter? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endObjectLiteralGetter(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endObjectLiteralGetter(v)} -+ case 26: -+ var v: Fuzzilli_Protobuf_BeginObjectLiteralSetter? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginObjectLiteralSetter(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginObjectLiteralSetter(v)} -+ case 27: -+ var v: Fuzzilli_Protobuf_EndObjectLiteralSetter? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endObjectLiteralSetter(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endObjectLiteralSetter(v)} -+ case 28: -+ var v: Fuzzilli_Protobuf_EndObjectLiteral? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endObjectLiteral(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endObjectLiteral(v)} -+ case 29: -+ var v: Fuzzilli_Protobuf_BeginClassDefinition? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginClassDefinition(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginClassDefinition(v)} -+ case 30: -+ var v: Fuzzilli_Protobuf_BeginClassConstructor? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginClassConstructor(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginClassConstructor(v)} -+ case 31: -+ var v: Fuzzilli_Protobuf_EndClassConstructor? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endClassConstructor(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endClassConstructor(v)} -+ case 32: -+ var v: Fuzzilli_Protobuf_ClassAddInstanceProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .classAddInstanceProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .classAddInstanceProperty(v)} -+ case 33: -+ var v: Fuzzilli_Protobuf_ClassAddInstanceElement? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .classAddInstanceElement(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .classAddInstanceElement(v)} -+ case 34: -+ var v: Fuzzilli_Protobuf_ClassAddInstanceComputedProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .classAddInstanceComputedProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .classAddInstanceComputedProperty(v)} -+ case 35: -+ var v: Fuzzilli_Protobuf_BeginClassInstanceMethod? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginClassInstanceMethod(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginClassInstanceMethod(v)} -+ case 36: -+ var v: Fuzzilli_Protobuf_EndClassInstanceMethod? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endClassInstanceMethod(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endClassInstanceMethod(v)} -+ case 37: -+ var v: Fuzzilli_Protobuf_BeginClassInstanceGetter? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginClassInstanceGetter(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginClassInstanceGetter(v)} -+ case 38: -+ var v: Fuzzilli_Protobuf_EndClassInstanceGetter? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endClassInstanceGetter(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endClassInstanceGetter(v)} -+ case 39: -+ var v: Fuzzilli_Protobuf_BeginClassInstanceSetter? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginClassInstanceSetter(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginClassInstanceSetter(v)} -+ case 40: -+ var v: Fuzzilli_Protobuf_EndClassInstanceSetter? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endClassInstanceSetter(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endClassInstanceSetter(v)} -+ case 41: -+ var v: Fuzzilli_Protobuf_ClassAddStaticProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .classAddStaticProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .classAddStaticProperty(v)} -+ case 42: -+ var v: Fuzzilli_Protobuf_ClassAddStaticElement? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .classAddStaticElement(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .classAddStaticElement(v)} -+ case 43: -+ var v: Fuzzilli_Protobuf_ClassAddStaticComputedProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .classAddStaticComputedProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .classAddStaticComputedProperty(v)} -+ case 44: -+ var v: Fuzzilli_Protobuf_BeginClassStaticInitializer? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginClassStaticInitializer(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginClassStaticInitializer(v)} -+ case 45: -+ var v: Fuzzilli_Protobuf_EndClassStaticInitializer? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endClassStaticInitializer(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endClassStaticInitializer(v)} -+ case 46: -+ var v: Fuzzilli_Protobuf_BeginClassStaticMethod? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginClassStaticMethod(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginClassStaticMethod(v)} -+ case 47: -+ var v: Fuzzilli_Protobuf_EndClassStaticMethod? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endClassStaticMethod(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endClassStaticMethod(v)} -+ case 48: -+ var v: Fuzzilli_Protobuf_BeginClassStaticGetter? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginClassStaticGetter(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginClassStaticGetter(v)} -+ case 49: -+ var v: Fuzzilli_Protobuf_EndClassStaticGetter? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endClassStaticGetter(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endClassStaticGetter(v)} -+ case 50: -+ var v: Fuzzilli_Protobuf_BeginClassStaticSetter? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginClassStaticSetter(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginClassStaticSetter(v)} -+ case 51: -+ var v: Fuzzilli_Protobuf_EndClassStaticSetter? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endClassStaticSetter(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endClassStaticSetter(v)} -+ case 52: -+ var v: Fuzzilli_Protobuf_ClassAddPrivateInstanceProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .classAddPrivateInstanceProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .classAddPrivateInstanceProperty(v)} -+ case 53: -+ var v: Fuzzilli_Protobuf_BeginClassPrivateInstanceMethod? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginClassPrivateInstanceMethod(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginClassPrivateInstanceMethod(v)} -+ case 54: -+ var v: Fuzzilli_Protobuf_EndClassPrivateInstanceMethod? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endClassPrivateInstanceMethod(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endClassPrivateInstanceMethod(v)} -+ case 55: -+ var v: Fuzzilli_Protobuf_ClassAddPrivateStaticProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .classAddPrivateStaticProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .classAddPrivateStaticProperty(v)} -+ case 56: -+ var v: Fuzzilli_Protobuf_BeginClassPrivateStaticMethod? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginClassPrivateStaticMethod(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginClassPrivateStaticMethod(v)} -+ case 57: -+ var v: Fuzzilli_Protobuf_EndClassPrivateStaticMethod? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endClassPrivateStaticMethod(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endClassPrivateStaticMethod(v)} -+ case 58: -+ var v: Fuzzilli_Protobuf_EndClassDefinition? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endClassDefinition(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endClassDefinition(v)} -+ case 59: -+ var v: Fuzzilli_Protobuf_CreateArray? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .createArray(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .createArray(v)} -+ case 60: -+ var v: Fuzzilli_Protobuf_CreateIntArray? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .createIntArray(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .createIntArray(v)} -+ case 61: -+ var v: Fuzzilli_Protobuf_CreateFloatArray? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .createFloatArray(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .createFloatArray(v)} -+ case 62: -+ var v: Fuzzilli_Protobuf_CreateArrayWithSpread? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .createArrayWithSpread(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .createArrayWithSpread(v)} -+ case 63: -+ var v: Fuzzilli_Protobuf_CreateTemplateString? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .createTemplateString(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .createTemplateString(v)} -+ case 64: -+ var v: Fuzzilli_Protobuf_LoadBuiltin? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .loadBuiltin(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .loadBuiltin(v)} -+ case 65: -+ var v: Fuzzilli_Protobuf_GetProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .getProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .getProperty(v)} -+ case 66: -+ var v: Fuzzilli_Protobuf_SetProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .setProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .setProperty(v)} -+ case 67: -+ var v: Fuzzilli_Protobuf_UpdateProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .updateProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .updateProperty(v)} -+ case 68: -+ var v: Fuzzilli_Protobuf_DeleteProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .deleteProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .deleteProperty(v)} -+ case 69: -+ var v: Fuzzilli_Protobuf_ConfigureProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .configureProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .configureProperty(v)} -+ case 70: -+ var v: Fuzzilli_Protobuf_GetElement? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .getElement(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .getElement(v)} -+ case 71: -+ var v: Fuzzilli_Protobuf_SetElement? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .setElement(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .setElement(v)} -+ case 72: -+ var v: Fuzzilli_Protobuf_UpdateElement? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .updateElement(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .updateElement(v)} -+ case 73: -+ var v: Fuzzilli_Protobuf_DeleteElement? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .deleteElement(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .deleteElement(v)} -+ case 74: -+ var v: Fuzzilli_Protobuf_ConfigureElement? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .configureElement(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .configureElement(v)} -+ case 75: -+ var v: Fuzzilli_Protobuf_GetComputedProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .getComputedProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .getComputedProperty(v)} -+ case 76: -+ var v: Fuzzilli_Protobuf_SetComputedProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .setComputedProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .setComputedProperty(v)} -+ case 77: -+ var v: Fuzzilli_Protobuf_UpdateComputedProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .updateComputedProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .updateComputedProperty(v)} -+ case 78: -+ var v: Fuzzilli_Protobuf_DeleteComputedProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .deleteComputedProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .deleteComputedProperty(v)} -+ case 79: -+ var v: Fuzzilli_Protobuf_ConfigureComputedProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .configureComputedProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .configureComputedProperty(v)} -+ case 80: -+ var v: Fuzzilli_Protobuf_TypeOf? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .typeOf(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .typeOf(v)} -+ case 81: -+ var v: Fuzzilli_Protobuf_TestInstanceOf? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .testInstanceOf(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .testInstanceOf(v)} -+ case 82: -+ var v: Fuzzilli_Protobuf_TestIn? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .testIn(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .testIn(v)} -+ case 83: -+ var v: Fuzzilli_Protobuf_BeginPlainFunction? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginPlainFunction(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginPlainFunction(v)} -+ case 84: -+ var v: Fuzzilli_Protobuf_EndPlainFunction? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endPlainFunction(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endPlainFunction(v)} -+ case 85: -+ var v: Fuzzilli_Protobuf_BeginArrowFunction? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginArrowFunction(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginArrowFunction(v)} -+ case 86: -+ var v: Fuzzilli_Protobuf_EndArrowFunction? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endArrowFunction(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endArrowFunction(v)} -+ case 87: -+ var v: Fuzzilli_Protobuf_BeginGeneratorFunction? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginGeneratorFunction(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginGeneratorFunction(v)} -+ case 88: -+ var v: Fuzzilli_Protobuf_EndGeneratorFunction? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endGeneratorFunction(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endGeneratorFunction(v)} -+ case 89: -+ var v: Fuzzilli_Protobuf_BeginAsyncFunction? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginAsyncFunction(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginAsyncFunction(v)} -+ case 90: -+ var v: Fuzzilli_Protobuf_EndAsyncFunction? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endAsyncFunction(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endAsyncFunction(v)} -+ case 91: -+ var v: Fuzzilli_Protobuf_BeginAsyncArrowFunction? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginAsyncArrowFunction(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginAsyncArrowFunction(v)} -+ case 92: -+ var v: Fuzzilli_Protobuf_EndAsyncArrowFunction? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endAsyncArrowFunction(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endAsyncArrowFunction(v)} -+ case 93: -+ var v: Fuzzilli_Protobuf_BeginAsyncGeneratorFunction? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginAsyncGeneratorFunction(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginAsyncGeneratorFunction(v)} -+ case 94: -+ var v: Fuzzilli_Protobuf_EndAsyncGeneratorFunction? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endAsyncGeneratorFunction(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endAsyncGeneratorFunction(v)} -+ case 95: -+ var v: Fuzzilli_Protobuf_BeginConstructor? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginConstructor(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginConstructor(v)} -+ case 96: -+ var v: Fuzzilli_Protobuf_EndConstructor? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endConstructor(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endConstructor(v)} -+ case 97: -+ var v: Fuzzilli_Protobuf_Return? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .return(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .return(v)} -+ case 98: -+ var v: Fuzzilli_Protobuf_Yield? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .yield(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .yield(v)} -+ case 99: -+ var v: Fuzzilli_Protobuf_YieldEach? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .yieldEach(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .yieldEach(v)} -+ case 100: -+ var v: Fuzzilli_Protobuf_Await? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .await(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .await(v)} -+ case 101: -+ var v: Fuzzilli_Protobuf_CallFunction? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .callFunction(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .callFunction(v)} -+ case 102: -+ var v: Fuzzilli_Protobuf_CallFunctionWithSpread? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .callFunctionWithSpread(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .callFunctionWithSpread(v)} -+ case 103: -+ var v: Fuzzilli_Protobuf_Construct? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .construct(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .construct(v)} -+ case 104: -+ var v: Fuzzilli_Protobuf_ConstructWithSpread? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .constructWithSpread(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .constructWithSpread(v)} -+ case 105: -+ var v: Fuzzilli_Protobuf_CallMethod? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .callMethod(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .callMethod(v)} -+ case 106: -+ var v: Fuzzilli_Protobuf_CallMethodWithSpread? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .callMethodWithSpread(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .callMethodWithSpread(v)} -+ case 107: -+ var v: Fuzzilli_Protobuf_CallComputedMethod? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .callComputedMethod(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .callComputedMethod(v)} -+ case 108: -+ var v: Fuzzilli_Protobuf_CallComputedMethodWithSpread? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .callComputedMethodWithSpread(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .callComputedMethodWithSpread(v)} -+ case 109: -+ var v: Fuzzilli_Protobuf_UnaryOperation? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .unaryOperation(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .unaryOperation(v)} -+ case 110: -+ var v: Fuzzilli_Protobuf_BinaryOperation? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .binaryOperation(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .binaryOperation(v)} -+ case 111: -+ var v: Fuzzilli_Protobuf_TernaryOperation? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .ternaryOperation(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .ternaryOperation(v)} -+ case 112: -+ var v: Fuzzilli_Protobuf_Update? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .update(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .update(v)} -+ case 113: -+ var v: Fuzzilli_Protobuf_Dup? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .dup(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .dup(v)} -+ case 114: -+ var v: Fuzzilli_Protobuf_Reassign? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .reassign(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .reassign(v)} -+ case 115: -+ var v: Fuzzilli_Protobuf_DestructArray? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .destructArray(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .destructArray(v)} -+ case 116: -+ var v: Fuzzilli_Protobuf_DestructArrayAndReassign? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .destructArrayAndReassign(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .destructArrayAndReassign(v)} -+ case 117: -+ var v: Fuzzilli_Protobuf_DestructObject? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .destructObject(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .destructObject(v)} -+ case 118: -+ var v: Fuzzilli_Protobuf_DestructObjectAndReassign? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .destructObjectAndReassign(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .destructObjectAndReassign(v)} -+ case 119: -+ var v: Fuzzilli_Protobuf_Compare? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .compare(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .compare(v)} -+ case 120: -+ var v: Fuzzilli_Protobuf_LoadNamedVariable? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .loadNamedVariable(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .loadNamedVariable(v)} -+ case 121: -+ var v: Fuzzilli_Protobuf_StoreNamedVariable? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .storeNamedVariable(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .storeNamedVariable(v)} -+ case 122: -+ var v: Fuzzilli_Protobuf_DefineNamedVariable? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .defineNamedVariable(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .defineNamedVariable(v)} -+ case 123: -+ var v: Fuzzilli_Protobuf_Eval? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .eval(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .eval(v)} -+ case 124: -+ var v: Fuzzilli_Protobuf_BeginWith? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginWith(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginWith(v)} -+ case 125: -+ var v: Fuzzilli_Protobuf_EndWith? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endWith(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endWith(v)} -+ case 126: -+ var v: Fuzzilli_Protobuf_CallSuperConstructor? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .callSuperConstructor(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .callSuperConstructor(v)} -+ case 127: -+ var v: Fuzzilli_Protobuf_CallSuperMethod? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .callSuperMethod(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .callSuperMethod(v)} -+ case 128: -+ var v: Fuzzilli_Protobuf_GetPrivateProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .getPrivateProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .getPrivateProperty(v)} -+ case 129: -+ var v: Fuzzilli_Protobuf_SetPrivateProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .setPrivateProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .setPrivateProperty(v)} -+ case 130: -+ var v: Fuzzilli_Protobuf_UpdatePrivateProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .updatePrivateProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .updatePrivateProperty(v)} -+ case 131: -+ var v: Fuzzilli_Protobuf_CallPrivateMethod? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .callPrivateMethod(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .callPrivateMethod(v)} -+ case 132: -+ var v: Fuzzilli_Protobuf_GetSuperProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .getSuperProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .getSuperProperty(v)} -+ case 133: -+ var v: Fuzzilli_Protobuf_SetSuperProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .setSuperProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .setSuperProperty(v)} -+ case 134: -+ var v: Fuzzilli_Protobuf_GetComputedSuperProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .getComputedSuperProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .getComputedSuperProperty(v)} -+ case 135: -+ var v: Fuzzilli_Protobuf_SetComputedSuperProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .setComputedSuperProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .setComputedSuperProperty(v)} -+ case 136: -+ var v: Fuzzilli_Protobuf_UpdateSuperProperty? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .updateSuperProperty(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .updateSuperProperty(v)} -+ case 137: -+ var v: Fuzzilli_Protobuf_BeginIf? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginIf(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginIf(v)} -+ case 138: -+ var v: Fuzzilli_Protobuf_BeginElse? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginElse(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginElse(v)} -+ case 139: -+ var v: Fuzzilli_Protobuf_EndIf? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endIf(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endIf(v)} -+ case 140: -+ var v: Fuzzilli_Protobuf_BeginWhileLoopHeader? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginWhileLoopHeader(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginWhileLoopHeader(v)} -+ case 141: -+ var v: Fuzzilli_Protobuf_BeginWhileLoopBody? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginWhileLoopBody(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginWhileLoopBody(v)} -+ case 142: -+ var v: Fuzzilli_Protobuf_EndWhileLoop? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endWhileLoop(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endWhileLoop(v)} -+ case 143: -+ var v: Fuzzilli_Protobuf_BeginDoWhileLoopBody? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginDoWhileLoopBody(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginDoWhileLoopBody(v)} -+ case 144: -+ var v: Fuzzilli_Protobuf_BeginDoWhileLoopHeader? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginDoWhileLoopHeader(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginDoWhileLoopHeader(v)} -+ case 145: -+ var v: Fuzzilli_Protobuf_EndDoWhileLoop? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endDoWhileLoop(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endDoWhileLoop(v)} -+ case 146: -+ var v: Fuzzilli_Protobuf_BeginForLoopInitializer? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginForLoopInitializer(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginForLoopInitializer(v)} -+ case 147: -+ var v: Fuzzilli_Protobuf_BeginForLoopCondition? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginForLoopCondition(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginForLoopCondition(v)} -+ case 148: -+ var v: Fuzzilli_Protobuf_BeginForLoopAfterthought? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginForLoopAfterthought(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginForLoopAfterthought(v)} -+ case 149: -+ var v: Fuzzilli_Protobuf_BeginForLoopBody? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginForLoopBody(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginForLoopBody(v)} -+ case 150: -+ var v: Fuzzilli_Protobuf_EndForLoop? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endForLoop(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endForLoop(v)} -+ case 151: -+ var v: Fuzzilli_Protobuf_BeginForInLoop? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginForInLoop(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginForInLoop(v)} -+ case 152: -+ var v: Fuzzilli_Protobuf_EndForInLoop? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endForInLoop(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endForInLoop(v)} -+ case 153: -+ var v: Fuzzilli_Protobuf_BeginForOfLoop? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginForOfLoop(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginForOfLoop(v)} -+ case 154: -+ var v: Fuzzilli_Protobuf_BeginForOfLoopWithDestruct? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginForOfLoopWithDestruct(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginForOfLoopWithDestruct(v)} -+ case 155: -+ var v: Fuzzilli_Protobuf_EndForOfLoop? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endForOfLoop(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endForOfLoop(v)} -+ case 156: -+ var v: Fuzzilli_Protobuf_BeginRepeatLoop? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginRepeatLoop(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginRepeatLoop(v)} -+ case 157: -+ var v: Fuzzilli_Protobuf_EndRepeatLoop? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endRepeatLoop(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endRepeatLoop(v)} -+ case 158: -+ var v: Fuzzilli_Protobuf_LoopBreak? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .loopBreak(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .loopBreak(v)} -+ case 159: -+ var v: Fuzzilli_Protobuf_LoopContinue? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .loopContinue(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .loopContinue(v)} -+ case 160: -+ var v: Fuzzilli_Protobuf_BeginTry? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginTry(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginTry(v)} -+ case 161: -+ var v: Fuzzilli_Protobuf_BeginCatch? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginCatch(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginCatch(v)} -+ case 162: -+ var v: Fuzzilli_Protobuf_BeginFinally? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginFinally(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginFinally(v)} -+ case 163: -+ var v: Fuzzilli_Protobuf_EndTryCatchFinally? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endTryCatchFinally(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endTryCatchFinally(v)} -+ case 164: -+ var v: Fuzzilli_Protobuf_ThrowException? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .throwException(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .throwException(v)} -+ case 165: -+ var v: Fuzzilli_Protobuf_BeginCodeString? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginCodeString(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginCodeString(v)} -+ case 166: -+ var v: Fuzzilli_Protobuf_EndCodeString? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endCodeString(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endCodeString(v)} -+ case 167: -+ var v: Fuzzilli_Protobuf_BeginBlockStatement? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginBlockStatement(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginBlockStatement(v)} -+ case 168: -+ var v: Fuzzilli_Protobuf_EndBlockStatement? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endBlockStatement(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endBlockStatement(v)} -+ case 169: -+ var v: Fuzzilli_Protobuf_BeginSwitch? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginSwitch(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginSwitch(v)} -+ case 170: -+ var v: Fuzzilli_Protobuf_BeginSwitchCase? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginSwitchCase(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginSwitchCase(v)} -+ case 171: -+ var v: Fuzzilli_Protobuf_BeginSwitchDefaultCase? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .beginSwitchDefaultCase(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .beginSwitchDefaultCase(v)} -+ case 172: -+ var v: Fuzzilli_Protobuf_EndSwitchCase? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endSwitchCase(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endSwitchCase(v)} -+ case 173: -+ var v: Fuzzilli_Protobuf_EndSwitch? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .endSwitch(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .endSwitch(v)} -+ case 174: -+ var v: Fuzzilli_Protobuf_SwitchBreak? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .switchBreak(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .switchBreak(v)} -+ case 175: -+ var v: Fuzzilli_Protobuf_LoadNewTarget? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .loadNewTarget(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .loadNewTarget(v)} -+ case 176: -+ var v: Fuzzilli_Protobuf_Print? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .print(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .print(v)} -+ case 177: -+ var v: Fuzzilli_Protobuf_Explore? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .explore(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .explore(v)} -+ case 178: -+ var v: Fuzzilli_Protobuf_Probe? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .probe(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .probe(v)} -+ case 179: -+ var v: Fuzzilli_Protobuf_Fixup? -+ if let current = _storage._operation { -+ try decoder.handleConflictingOneOf() -+ if case .fixup(let m) = current {v = m} -+ } -+ try decoder.decodeSingularMessageField(value: &v) -+ if let v = v {_storage._operation = .fixup(v)} -+ default: break - } -- }() -- 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.inouts.isEmpty { -- try visitor.visitPackedUInt32Field(value: self.inouts, fieldNumber: 1) -- } -- switch self.operation { -- case .opIdx?: try { -- guard case .opIdx(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularUInt32Field(value: v, fieldNumber: 2) -- }() -- case .nop?: try { -- guard case .nop(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 3) -- }() -- case .loadInteger?: try { -- guard case .loadInteger(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 4) -- }() -- case .loadBigInt?: try { -- guard case .loadBigInt(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 5) -- }() -- case .loadFloat?: try { -- guard case .loadFloat(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 6) -- }() -- case .loadString?: try { -- guard case .loadString(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 7) -- }() -- case .loadBoolean?: try { -- guard case .loadBoolean(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 8) -- }() -- case .loadUndefined?: try { -- guard case .loadUndefined(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 9) -- }() -- case .loadNull?: try { -- guard case .loadNull(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 10) -- }() -- case .loadThis?: try { -- guard case .loadThis(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 11) -- }() -- case .loadArguments?: try { -- guard case .loadArguments(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 12) -- }() -- case .loadRegExp?: try { -- guard case .loadRegExp(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 13) -- }() -- case .beginObjectLiteral?: try { -- guard case .beginObjectLiteral(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 14) -- }() -- case .objectLiteralAddProperty?: try { -- guard case .objectLiteralAddProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 15) -- }() -- case .objectLiteralAddElement?: try { -- guard case .objectLiteralAddElement(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 16) -- }() -- case .objectLiteralAddComputedProperty?: try { -- guard case .objectLiteralAddComputedProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 17) -- }() -- case .objectLiteralCopyProperties?: try { -- guard case .objectLiteralCopyProperties(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 18) -- }() -- case .objectLiteralSetPrototype?: try { -- guard case .objectLiteralSetPrototype(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 19) -- }() -- case .beginObjectLiteralMethod?: try { -- guard case .beginObjectLiteralMethod(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 20) -- }() -- case .endObjectLiteralMethod?: try { -- guard case .endObjectLiteralMethod(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 21) -- }() -- case .beginObjectLiteralComputedMethod?: try { -- guard case .beginObjectLiteralComputedMethod(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 22) -- }() -- case .endObjectLiteralComputedMethod?: try { -- guard case .endObjectLiteralComputedMethod(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 23) -- }() -- case .beginObjectLiteralGetter?: try { -- guard case .beginObjectLiteralGetter(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 24) -- }() -- case .endObjectLiteralGetter?: try { -- guard case .endObjectLiteralGetter(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 25) -- }() -- case .beginObjectLiteralSetter?: try { -- guard case .beginObjectLiteralSetter(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 26) -- }() -- case .endObjectLiteralSetter?: try { -- guard case .endObjectLiteralSetter(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 27) -- }() -- case .endObjectLiteral?: try { -- guard case .endObjectLiteral(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 28) -- }() -- case .beginClassDefinition?: try { -- guard case .beginClassDefinition(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 29) -- }() -- case .beginClassConstructor?: try { -- guard case .beginClassConstructor(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 30) -- }() -- case .endClassConstructor?: try { -- guard case .endClassConstructor(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 31) -- }() -- case .classAddInstanceProperty?: try { -- guard case .classAddInstanceProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 32) -- }() -- case .classAddInstanceElement?: try { -- guard case .classAddInstanceElement(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 33) -- }() -- case .classAddInstanceComputedProperty?: try { -- guard case .classAddInstanceComputedProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 34) -- }() -- case .beginClassInstanceMethod?: try { -- guard case .beginClassInstanceMethod(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 35) -- }() -- case .endClassInstanceMethod?: try { -- guard case .endClassInstanceMethod(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 36) -- }() -- case .beginClassInstanceGetter?: try { -- guard case .beginClassInstanceGetter(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 37) -- }() -- case .endClassInstanceGetter?: try { -- guard case .endClassInstanceGetter(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 38) -- }() -- case .beginClassInstanceSetter?: try { -- guard case .beginClassInstanceSetter(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 39) -- }() -- case .endClassInstanceSetter?: try { -- guard case .endClassInstanceSetter(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 40) -- }() -- case .classAddStaticProperty?: try { -- guard case .classAddStaticProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 41) -- }() -- case .classAddStaticElement?: try { -- guard case .classAddStaticElement(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 42) -- }() -- case .classAddStaticComputedProperty?: try { -- guard case .classAddStaticComputedProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 43) -- }() -- case .beginClassStaticInitializer?: try { -- guard case .beginClassStaticInitializer(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 44) -- }() -- case .endClassStaticInitializer?: try { -- guard case .endClassStaticInitializer(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 45) -- }() -- case .beginClassStaticMethod?: try { -- guard case .beginClassStaticMethod(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 46) -- }() -- case .endClassStaticMethod?: try { -- guard case .endClassStaticMethod(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 47) -- }() -- case .beginClassStaticGetter?: try { -- guard case .beginClassStaticGetter(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 48) -- }() -- case .endClassStaticGetter?: try { -- guard case .endClassStaticGetter(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 49) -- }() -- case .beginClassStaticSetter?: try { -- guard case .beginClassStaticSetter(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 50) -- }() -- case .endClassStaticSetter?: try { -- guard case .endClassStaticSetter(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 51) -- }() -- case .classAddPrivateInstanceProperty?: try { -- guard case .classAddPrivateInstanceProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 52) -- }() -- case .beginClassPrivateInstanceMethod?: try { -- guard case .beginClassPrivateInstanceMethod(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 53) -- }() -- case .endClassPrivateInstanceMethod?: try { -- guard case .endClassPrivateInstanceMethod(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 54) -- }() -- case .classAddPrivateStaticProperty?: try { -- guard case .classAddPrivateStaticProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 55) -- }() -- case .beginClassPrivateStaticMethod?: try { -- guard case .beginClassPrivateStaticMethod(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 56) -- }() -- case .endClassPrivateStaticMethod?: try { -- guard case .endClassPrivateStaticMethod(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 57) -- }() -- case .endClassDefinition?: try { -- guard case .endClassDefinition(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 58) -- }() -- case .createArray?: try { -- guard case .createArray(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 59) -- }() -- case .createIntArray?: try { -- guard case .createIntArray(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 60) -- }() -- case .createFloatArray?: try { -- guard case .createFloatArray(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 61) -- }() -- case .createArrayWithSpread?: try { -- guard case .createArrayWithSpread(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 62) -- }() -- case .createTemplateString?: try { -- guard case .createTemplateString(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 63) -- }() -- case .loadBuiltin?: try { -- guard case .loadBuiltin(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 64) -- }() -- case .getProperty?: try { -- guard case .getProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 65) -- }() -- case .setProperty?: try { -- guard case .setProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 66) -- }() -- case .updateProperty?: try { -- guard case .updateProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 67) -- }() -- case .deleteProperty?: try { -- guard case .deleteProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 68) -- }() -- case .configureProperty?: try { -- guard case .configureProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 69) -- }() -- case .getElement?: try { -- guard case .getElement(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 70) -- }() -- case .setElement?: try { -- guard case .setElement(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 71) -- }() -- case .updateElement?: try { -- guard case .updateElement(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 72) -- }() -- case .deleteElement?: try { -- guard case .deleteElement(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 73) -- }() -- case .configureElement?: try { -- guard case .configureElement(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 74) -- }() -- case .getComputedProperty?: try { -- guard case .getComputedProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 75) -- }() -- case .setComputedProperty?: try { -- guard case .setComputedProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 76) -- }() -- case .updateComputedProperty?: try { -- guard case .updateComputedProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 77) -- }() -- case .deleteComputedProperty?: try { -- guard case .deleteComputedProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 78) -- }() -- case .configureComputedProperty?: try { -- guard case .configureComputedProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 79) -- }() -- case .typeOf?: try { -- guard case .typeOf(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 80) -- }() -- case .testInstanceOf?: try { -- guard case .testInstanceOf(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 81) -- }() -- case .testIn?: try { -- guard case .testIn(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 82) -- }() -- case .beginPlainFunction?: try { -- guard case .beginPlainFunction(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 83) -- }() -- case .endPlainFunction?: try { -- guard case .endPlainFunction(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 84) -- }() -- case .beginArrowFunction?: try { -- guard case .beginArrowFunction(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 85) -- }() -- case .endArrowFunction?: try { -- guard case .endArrowFunction(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 86) -- }() -- case .beginGeneratorFunction?: try { -- guard case .beginGeneratorFunction(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 87) -- }() -- case .endGeneratorFunction?: try { -- guard case .endGeneratorFunction(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 88) -- }() -- case .beginAsyncFunction?: try { -- guard case .beginAsyncFunction(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 89) -- }() -- case .endAsyncFunction?: try { -- guard case .endAsyncFunction(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 90) -- }() -- case .beginAsyncArrowFunction?: try { -- guard case .beginAsyncArrowFunction(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 91) -- }() -- case .endAsyncArrowFunction?: try { -- guard case .endAsyncArrowFunction(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 92) -- }() -- case .beginAsyncGeneratorFunction?: try { -- guard case .beginAsyncGeneratorFunction(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 93) -- }() -- case .endAsyncGeneratorFunction?: try { -- guard case .endAsyncGeneratorFunction(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 94) -- }() -- case .beginConstructor?: try { -- guard case .beginConstructor(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 95) -- }() -- case .endConstructor?: try { -- guard case .endConstructor(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 96) -- }() -- case .return?: try { -- guard case .return(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 97) -- }() -- case .yield?: try { -- guard case .yield(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 98) -- }() -- case .yieldEach?: try { -- guard case .yieldEach(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 99) -- }() -- case .await?: try { -- guard case .await(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 100) -- }() -- case .callFunction?: try { -- guard case .callFunction(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 101) -- }() -- case .callFunctionWithSpread?: try { -- guard case .callFunctionWithSpread(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 102) -- }() -- case .construct?: try { -- guard case .construct(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 103) -- }() -- case .constructWithSpread?: try { -- guard case .constructWithSpread(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 104) -- }() -- case .callMethod?: try { -- guard case .callMethod(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 105) -- }() -- case .callMethodWithSpread?: try { -- guard case .callMethodWithSpread(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 106) -- }() -- case .callComputedMethod?: try { -- guard case .callComputedMethod(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 107) -- }() -- case .callComputedMethodWithSpread?: try { -- guard case .callComputedMethodWithSpread(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 108) -- }() -- case .unaryOperation?: try { -- guard case .unaryOperation(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 109) -- }() -- case .binaryOperation?: try { -- guard case .binaryOperation(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 110) -- }() -- case .ternaryOperation?: try { -- guard case .ternaryOperation(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 111) -- }() -- case .update?: try { -- guard case .update(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 112) -- }() -- case .dup?: try { -- guard case .dup(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 113) -- }() -- case .reassign?: try { -- guard case .reassign(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 114) -- }() -- case .destructArray?: try { -- guard case .destructArray(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 115) -- }() -- case .destructArrayAndReassign?: try { -- guard case .destructArrayAndReassign(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 116) -- }() -- case .destructObject?: try { -- guard case .destructObject(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 117) -- }() -- case .destructObjectAndReassign?: try { -- guard case .destructObjectAndReassign(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 118) -- }() -- case .compare?: try { -- guard case .compare(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 119) -- }() -- case .loadNamedVariable?: try { -- guard case .loadNamedVariable(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 120) -- }() -- case .storeNamedVariable?: try { -- guard case .storeNamedVariable(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 121) -- }() -- case .defineNamedVariable?: try { -- guard case .defineNamedVariable(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 122) -- }() -- case .eval?: try { -- guard case .eval(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 123) -- }() -- case .beginWith?: try { -- guard case .beginWith(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 124) -- }() -- case .endWith?: try { -- guard case .endWith(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 125) -- }() -- case .callSuperConstructor?: try { -- guard case .callSuperConstructor(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 126) -- }() -- case .callSuperMethod?: try { -- guard case .callSuperMethod(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 127) -- }() -- case .getPrivateProperty?: try { -- guard case .getPrivateProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 128) -- }() -- case .setPrivateProperty?: try { -- guard case .setPrivateProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 129) -- }() -- case .updatePrivateProperty?: try { -- guard case .updatePrivateProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 130) -- }() -- case .callPrivateMethod?: try { -- guard case .callPrivateMethod(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 131) -- }() -- case .getSuperProperty?: try { -- guard case .getSuperProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 132) -- }() -- case .setSuperProperty?: try { -- guard case .setSuperProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 133) -- }() -- case .getComputedSuperProperty?: try { -- guard case .getComputedSuperProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 134) -- }() -- case .setComputedSuperProperty?: try { -- guard case .setComputedSuperProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 135) -- }() -- case .updateSuperProperty?: try { -- guard case .updateSuperProperty(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 136) -- }() -- case .beginIf?: try { -- guard case .beginIf(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 137) -- }() -- case .beginElse?: try { -- guard case .beginElse(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 138) -- }() -- case .endIf?: try { -- guard case .endIf(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 139) -- }() -- case .beginWhileLoopHeader?: try { -- guard case .beginWhileLoopHeader(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 140) -- }() -- case .beginWhileLoopBody?: try { -- guard case .beginWhileLoopBody(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 141) -- }() -- case .endWhileLoop?: try { -- guard case .endWhileLoop(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 142) -- }() -- case .beginDoWhileLoopBody?: try { -- guard case .beginDoWhileLoopBody(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 143) -- }() -- case .beginDoWhileLoopHeader?: try { -- guard case .beginDoWhileLoopHeader(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 144) -- }() -- case .endDoWhileLoop?: try { -- guard case .endDoWhileLoop(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 145) -- }() -- case .beginForLoopInitializer?: try { -- guard case .beginForLoopInitializer(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 146) -- }() -- case .beginForLoopCondition?: try { -- guard case .beginForLoopCondition(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 147) -- }() -- case .beginForLoopAfterthought?: try { -- guard case .beginForLoopAfterthought(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 148) -- }() -- case .beginForLoopBody?: try { -- guard case .beginForLoopBody(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 149) -- }() -- case .endForLoop?: try { -- guard case .endForLoop(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 150) -- }() -- case .beginForInLoop?: try { -- guard case .beginForInLoop(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 151) -- }() -- case .endForInLoop?: try { -- guard case .endForInLoop(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 152) -- }() -- case .beginForOfLoop?: try { -- guard case .beginForOfLoop(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 153) -- }() -- case .beginForOfLoopWithDestruct?: try { -- guard case .beginForOfLoopWithDestruct(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 154) -- }() -- case .endForOfLoop?: try { -- guard case .endForOfLoop(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 155) -- }() -- case .beginRepeatLoop?: try { -- guard case .beginRepeatLoop(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 156) -- }() -- case .endRepeatLoop?: try { -- guard case .endRepeatLoop(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 157) -- }() -- case .loopBreak?: try { -- guard case .loopBreak(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 158) -- }() -- case .loopContinue?: try { -- guard case .loopContinue(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 159) -- }() -- case .beginTry?: try { -- guard case .beginTry(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 160) -- }() -- case .beginCatch?: try { -- guard case .beginCatch(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 161) -- }() -- case .beginFinally?: try { -- guard case .beginFinally(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 162) -- }() -- case .endTryCatchFinally?: try { -- guard case .endTryCatchFinally(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 163) -- }() -- case .throwException?: try { -- guard case .throwException(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 164) -- }() -- case .beginCodeString?: try { -- guard case .beginCodeString(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 165) -- }() -- case .endCodeString?: try { -- guard case .endCodeString(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 166) -- }() -- case .beginBlockStatement?: try { -- guard case .beginBlockStatement(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 167) -- }() -- case .endBlockStatement?: try { -- guard case .endBlockStatement(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 168) -- }() -- case .beginSwitch?: try { -- guard case .beginSwitch(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 169) -- }() -- case .beginSwitchCase?: try { -- guard case .beginSwitchCase(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 170) -- }() -- case .beginSwitchDefaultCase?: try { -- guard case .beginSwitchDefaultCase(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 171) -- }() -- case .endSwitchCase?: try { -- guard case .endSwitchCase(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 172) -- }() -- case .endSwitch?: try { -- guard case .endSwitch(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 173) -- }() -- case .switchBreak?: try { -- guard case .switchBreak(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 174) -- }() -- case .loadNewTarget?: try { -- guard case .loadNewTarget(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 175) -- }() -- case .print?: try { -- guard case .print(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 176) -- }() -- case .explore?: try { -- guard case .explore(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 177) -- }() -- case .probe?: try { -- guard case .probe(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 178) -- }() -- case .fixup?: try { -- guard case .fixup(let v)? = self.operation else { preconditionFailure() } -- try visitor.visitSingularMessageField(value: v, fieldNumber: 179) -- }() -- case nil: break -+ try withExtendedLifetime(_storage) { (_storage: _StorageClass) in -+ if !_storage._inouts.isEmpty { -+ try visitor.visitPackedUInt32Field(value: _storage._inouts, fieldNumber: 1) -+ } -+ switch _storage._operation { -+ case .opIdx(let v)?: -+ try visitor.visitSingularUInt32Field(value: v, fieldNumber: 2) -+ case .nop(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 3) -+ case .loadInteger(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 4) -+ case .loadBigInt(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 5) -+ case .loadFloat(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 6) -+ case .loadString(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 7) -+ case .loadBoolean(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 8) -+ case .loadUndefined(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 9) -+ case .loadNull(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 10) -+ case .loadThis(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 11) -+ case .loadArguments(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 12) -+ case .loadRegExp(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 13) -+ case .beginObjectLiteral(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 14) -+ case .objectLiteralAddProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 15) -+ case .objectLiteralAddElement(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 16) -+ case .objectLiteralAddComputedProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 17) -+ case .objectLiteralCopyProperties(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 18) -+ case .objectLiteralSetPrototype(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 19) -+ case .beginObjectLiteralMethod(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 20) -+ case .endObjectLiteralMethod(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 21) -+ case .beginObjectLiteralComputedMethod(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 22) -+ case .endObjectLiteralComputedMethod(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 23) -+ case .beginObjectLiteralGetter(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 24) -+ case .endObjectLiteralGetter(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 25) -+ case .beginObjectLiteralSetter(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 26) -+ case .endObjectLiteralSetter(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 27) -+ case .endObjectLiteral(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 28) -+ case .beginClassDefinition(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 29) -+ case .beginClassConstructor(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 30) -+ case .endClassConstructor(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 31) -+ case .classAddInstanceProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 32) -+ case .classAddInstanceElement(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 33) -+ case .classAddInstanceComputedProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 34) -+ case .beginClassInstanceMethod(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 35) -+ case .endClassInstanceMethod(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 36) -+ case .beginClassInstanceGetter(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 37) -+ case .endClassInstanceGetter(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 38) -+ case .beginClassInstanceSetter(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 39) -+ case .endClassInstanceSetter(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 40) -+ case .classAddStaticProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 41) -+ case .classAddStaticElement(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 42) -+ case .classAddStaticComputedProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 43) -+ case .beginClassStaticInitializer(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 44) -+ case .endClassStaticInitializer(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 45) -+ case .beginClassStaticMethod(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 46) -+ case .endClassStaticMethod(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 47) -+ case .beginClassStaticGetter(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 48) -+ case .endClassStaticGetter(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 49) -+ case .beginClassStaticSetter(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 50) -+ case .endClassStaticSetter(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 51) -+ case .classAddPrivateInstanceProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 52) -+ case .beginClassPrivateInstanceMethod(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 53) -+ case .endClassPrivateInstanceMethod(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 54) -+ case .classAddPrivateStaticProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 55) -+ case .beginClassPrivateStaticMethod(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 56) -+ case .endClassPrivateStaticMethod(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 57) -+ case .endClassDefinition(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 58) -+ case .createArray(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 59) -+ case .createIntArray(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 60) -+ case .createFloatArray(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 61) -+ case .createArrayWithSpread(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 62) -+ case .createTemplateString(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 63) -+ case .loadBuiltin(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 64) -+ case .getProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 65) -+ case .setProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 66) -+ case .updateProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 67) -+ case .deleteProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 68) -+ case .configureProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 69) -+ case .getElement(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 70) -+ case .setElement(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 71) -+ case .updateElement(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 72) -+ case .deleteElement(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 73) -+ case .configureElement(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 74) -+ case .getComputedProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 75) -+ case .setComputedProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 76) -+ case .updateComputedProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 77) -+ case .deleteComputedProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 78) -+ case .configureComputedProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 79) -+ case .typeOf(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 80) -+ case .testInstanceOf(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 81) -+ case .testIn(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 82) -+ case .beginPlainFunction(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 83) -+ case .endPlainFunction(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 84) -+ case .beginArrowFunction(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 85) -+ case .endArrowFunction(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 86) -+ case .beginGeneratorFunction(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 87) -+ case .endGeneratorFunction(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 88) -+ case .beginAsyncFunction(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 89) -+ case .endAsyncFunction(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 90) -+ case .beginAsyncArrowFunction(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 91) -+ case .endAsyncArrowFunction(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 92) -+ case .beginAsyncGeneratorFunction(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 93) -+ case .endAsyncGeneratorFunction(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 94) -+ case .beginConstructor(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 95) -+ case .endConstructor(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 96) -+ case .return(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 97) -+ case .yield(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 98) -+ case .yieldEach(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 99) -+ case .await(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 100) -+ case .callFunction(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 101) -+ case .callFunctionWithSpread(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 102) -+ case .construct(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 103) -+ case .constructWithSpread(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 104) -+ case .callMethod(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 105) -+ case .callMethodWithSpread(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 106) -+ case .callComputedMethod(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 107) -+ case .callComputedMethodWithSpread(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 108) -+ case .unaryOperation(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 109) -+ case .binaryOperation(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 110) -+ case .ternaryOperation(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 111) -+ case .update(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 112) -+ case .dup(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 113) -+ case .reassign(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 114) -+ case .destructArray(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 115) -+ case .destructArrayAndReassign(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 116) -+ case .destructObject(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 117) -+ case .destructObjectAndReassign(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 118) -+ case .compare(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 119) -+ case .loadNamedVariable(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 120) -+ case .storeNamedVariable(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 121) -+ case .defineNamedVariable(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 122) -+ case .eval(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 123) -+ case .beginWith(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 124) -+ case .endWith(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 125) -+ case .callSuperConstructor(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 126) -+ case .callSuperMethod(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 127) -+ case .getPrivateProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 128) -+ case .setPrivateProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 129) -+ case .updatePrivateProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 130) -+ case .callPrivateMethod(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 131) -+ case .getSuperProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 132) -+ case .setSuperProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 133) -+ case .getComputedSuperProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 134) -+ case .setComputedSuperProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 135) -+ case .updateSuperProperty(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 136) -+ case .beginIf(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 137) -+ case .beginElse(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 138) -+ case .endIf(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 139) -+ case .beginWhileLoopHeader(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 140) -+ case .beginWhileLoopBody(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 141) -+ case .endWhileLoop(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 142) -+ case .beginDoWhileLoopBody(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 143) -+ case .beginDoWhileLoopHeader(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 144) -+ case .endDoWhileLoop(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 145) -+ case .beginForLoopInitializer(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 146) -+ case .beginForLoopCondition(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 147) -+ case .beginForLoopAfterthought(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 148) -+ case .beginForLoopBody(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 149) -+ case .endForLoop(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 150) -+ case .beginForInLoop(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 151) -+ case .endForInLoop(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 152) -+ case .beginForOfLoop(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 153) -+ case .beginForOfLoopWithDestruct(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 154) -+ case .endForOfLoop(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 155) -+ case .beginRepeatLoop(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 156) -+ case .endRepeatLoop(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 157) -+ case .loopBreak(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 158) -+ case .loopContinue(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 159) -+ case .beginTry(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 160) -+ case .beginCatch(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 161) -+ case .beginFinally(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 162) -+ case .endTryCatchFinally(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 163) -+ case .throwException(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 164) -+ case .beginCodeString(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 165) -+ case .endCodeString(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 166) -+ case .beginBlockStatement(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 167) -+ case .endBlockStatement(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 168) -+ case .beginSwitch(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 169) -+ case .beginSwitchCase(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 170) -+ case .beginSwitchDefaultCase(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 171) -+ case .endSwitchCase(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 172) -+ case .endSwitch(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 173) -+ case .switchBreak(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 174) -+ case .loadNewTarget(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 175) -+ case .print(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 176) -+ case .explore(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 177) -+ case .probe(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 178) -+ case .fixup(let v)?: -+ try visitor.visitSingularMessageField(value: v, fieldNumber: 179) -+ case nil: break -+ } - } - try unknownFields.traverse(visitor: &visitor) - } - - public static func ==(lhs: Fuzzilli_Protobuf_Instruction, rhs: Fuzzilli_Protobuf_Instruction) -> Bool { -- if lhs.inouts != rhs.inouts {return false} -- if lhs.operation != rhs.operation {return false} -+ if lhs._storage !== rhs._storage { -+ let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in -+ let _storage = _args.0 -+ let rhs_storage = _args.1 -+ if _storage._inouts != rhs_storage._inouts {return false} -+ if _storage._operation != rhs_storage._operation {return false} -+ return true -+ } -+ if !storagesAreEqual {return false} -+ } - if lhs.unknownFields != rhs.unknownFields {return false} - return true - } -@@ -5676,7 +3924,7 @@ extension Fuzzilli_Protobuf_Program: SwiftProtobuf.Message, SwiftProtobuf._Messa - ] - - fileprivate class _StorageClass { -- var _uuid: Data = Data() -+ var _uuid: Data = SwiftProtobuf.Internal.emptyData - var _code: [Fuzzilli_Protobuf_Instruction] = [] - var _comments: Dictionary = [:] - var _parent: Fuzzilli_Protobuf_Program? = nil -@@ -5704,14 +3952,11 @@ extension Fuzzilli_Protobuf_Program: SwiftProtobuf.Message, SwiftProtobuf._Messa - _ = _uniqueStorage() - try withExtendedLifetime(_storage) { (_storage: _StorageClass) in - 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.decodeSingularBytesField(value: &_storage._uuid) }() -- case 2: try { try decoder.decodeRepeatedMessageField(value: &_storage._code) }() -- case 3: try { try decoder.decodeMapField(fieldType: SwiftProtobuf._ProtobufMap.self, value: &_storage._comments) }() -- case 4: try { try decoder.decodeSingularMessageField(value: &_storage._parent) }() -+ case 1: try decoder.decodeSingularBytesField(value: &_storage._uuid) -+ case 2: try decoder.decodeRepeatedMessageField(value: &_storage._code) -+ case 3: try decoder.decodeMapField(fieldType: SwiftProtobuf._ProtobufMap.self, value: &_storage._comments) -+ case 4: try decoder.decodeSingularMessageField(value: &_storage._parent) - default: break - } - } -@@ -5720,10 +3965,6 @@ extension Fuzzilli_Protobuf_Program: SwiftProtobuf.Message, SwiftProtobuf._Messa - - public func traverse(visitor: inout V) throws { - try withExtendedLifetime(_storage) { (_storage: _StorageClass) in -- // 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 !_storage._uuid.isEmpty { - try visitor.visitSingularBytesField(value: _storage._uuid, fieldNumber: 1) - } -@@ -5733,9 +3974,9 @@ extension Fuzzilli_Protobuf_Program: SwiftProtobuf.Message, SwiftProtobuf._Messa - if !_storage._comments.isEmpty { - try visitor.visitMapField(fieldType: SwiftProtobuf._ProtobufMap.self, value: _storage._comments, fieldNumber: 3) - } -- try { if let v = _storage._parent { -+ if let v = _storage._parent { - try visitor.visitSingularMessageField(value: v, fieldNumber: 4) -- } }() -+ } - } - try unknownFields.traverse(visitor: &visitor) - } -diff --git a/Sources/Fuzzilli/Protobuf/program.proto b/Sources/Fuzzilli/Protobuf/program.proto -index 515dce6..c8da3a3 100644 ---- a/Sources/Fuzzilli/Protobuf/program.proto -+++ b/Sources/Fuzzilli/Protobuf/program.proto -@@ -1,4 +1,4 @@ --// Copyright 2023 Google LLC -+// Copyright 2024 Google LLC - // - // Licensed under the Apache License, Version 2.0 (the "License"); - // you may not use this file except in compliance with the License. -diff --git a/Sources/Fuzzilli/Protobuf/sync.pb.swift b/Sources/Fuzzilli/Protobuf/sync.pb.swift -index 909deec..92bf762 100644 ---- a/Sources/Fuzzilli/Protobuf/sync.pb.swift -+++ b/Sources/Fuzzilli/Protobuf/sync.pb.swift -@@ -58,9 +58,9 @@ public struct Fuzzilli_Protobuf_FuzzerState { - // `Message` and `Message+*Additions` files in the SwiftProtobuf library for - // methods supported on all messages. - -- public var corpus: Data = Data() -+ public var corpus: Data = SwiftProtobuf.Internal.emptyData - -- public var evaluatorState: Data = Data() -+ public var evaluatorState: Data = SwiftProtobuf.Internal.emptyData - - public var unknownFields = SwiftProtobuf.UnknownStorage() - -@@ -132,6 +132,11 @@ public struct Fuzzilli_Protobuf_Statistics { - set {_uniqueStorage()._avgExecutionTime = newValue} - } - -+ public var avgBugOracleTime: Double { -+ get {return _storage._avgBugOracleTime} -+ set {_uniqueStorage()._avgBugOracleTime = newValue} -+ } -+ - //// The current number of executions per second. - public var execsPerSecond: Double { - get {return _storage._execsPerSecond} -@@ -174,6 +179,46 @@ public struct Fuzzilli_Protobuf_Statistics { - set {_uniqueStorage()._timeoutRate = newValue} - } - -+ public var differentialSamples: UInt64 { -+ get {return _storage._differentialSamples} -+ set {_uniqueStorage()._differentialSamples = newValue} -+ } -+ -+ public var turbofanSamples: UInt64 { -+ get {return _storage._turbofanSamples} -+ set {_uniqueStorage()._turbofanSamples = newValue} -+ } -+ -+ public var maglevSamples: UInt64 { -+ get {return _storage._maglevSamples} -+ set {_uniqueStorage()._maglevSamples = newValue} -+ } -+ -+ public var sparkplugSamples: UInt64 { -+ get {return _storage._sparkplugSamples} -+ set {_uniqueStorage()._sparkplugSamples = newValue} -+ } -+ -+ public var relationsPerformed: UInt64 { -+ get {return _storage._relationsPerformed} -+ set {_uniqueStorage()._relationsPerformed = newValue} -+ } -+ -+ public var jitSamples: UInt64 { -+ get {return _storage._jitSamples} -+ set {_uniqueStorage()._jitSamples = newValue} -+ } -+ -+ public var avgDumpSizeOpt: Double { -+ get {return _storage._avgDumpSizeOpt} -+ set {_uniqueStorage()._avgDumpSizeOpt = newValue} -+ } -+ -+ public var avgDumpSizeUnOpt: Double { -+ get {return _storage._avgDumpSizeUnOpt} -+ set {_uniqueStorage()._avgDumpSizeUnOpt = newValue} -+ } -+ - public var unknownFields = SwiftProtobuf.UnknownStorage() - - public init() {} -@@ -181,12 +226,6 @@ public struct Fuzzilli_Protobuf_Statistics { - fileprivate var _storage = _StorageClass.defaultInstance - } - --#if swift(>=5.5) && canImport(_Concurrency) --extension Fuzzilli_Protobuf_LogMessage: @unchecked Sendable {} --extension Fuzzilli_Protobuf_FuzzerState: @unchecked Sendable {} --extension Fuzzilli_Protobuf_Statistics: @unchecked Sendable {} --#endif // swift(>=5.5) && canImport(_Concurrency) -- - // MARK: - Code below here is support for the SwiftProtobuf runtime. - - fileprivate let _protobuf_package = "fuzzilli.protobuf" -@@ -202,14 +241,11 @@ extension Fuzzilli_Protobuf_LogMessage: SwiftProtobuf.Message, SwiftProtobuf._Me - - 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.origin) }() -- case 2: try { try decoder.decodeSingularUInt32Field(value: &self.level) }() -- case 3: try { try decoder.decodeSingularStringField(value: &self.label) }() -- case 4: try { try decoder.decodeSingularStringField(value: &self.content) }() -+ case 1: try decoder.decodeSingularStringField(value: &self.origin) -+ case 2: try decoder.decodeSingularUInt32Field(value: &self.level) -+ case 3: try decoder.decodeSingularStringField(value: &self.label) -+ case 4: try decoder.decodeSingularStringField(value: &self.content) - default: break - } - } -@@ -250,12 +286,9 @@ extension Fuzzilli_Protobuf_FuzzerState: SwiftProtobuf.Message, SwiftProtobuf._M - - 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.decodeSingularBytesField(value: &self.corpus) }() -- case 2: try { try decoder.decodeSingularBytesField(value: &self.evaluatorState) }() -+ case 1: try decoder.decodeSingularBytesField(value: &self.corpus) -+ case 2: try decoder.decodeSingularBytesField(value: &self.evaluatorState) - default: break - } - } -@@ -299,6 +332,15 @@ extension Fuzzilli_Protobuf_Statistics: SwiftProtobuf.Message, SwiftProtobuf._Me - 15: .same(proto: "coverage"), - 16: .same(proto: "correctnessRate"), - 17: .same(proto: "timeoutRate"), -+ 18: .same(proto: "differentialSamples"), -+ 19: .same(proto: "turbofanSamples"), -+ 20: .same(proto: "maglevSamples"), -+ 21: .same(proto: "relationsPerformed"), -+ 22: .same(proto: "sparkplugSamples"), -+ 23: .same(proto: "avgBugOracleTime"), -+ 24: .same(proto: "jitSamples"), -+ 25: .same(proto: "avgDumpSizeOpt"), -+ 26: .same(proto: "avgDumpSizeUnOpt"), - ] - - fileprivate class _StorageClass { -@@ -319,6 +361,15 @@ extension Fuzzilli_Protobuf_Statistics: SwiftProtobuf.Message, SwiftProtobuf._Me - var _coverage: Double = 0 - var _correctnessRate: Double = 0 - var _timeoutRate: Double = 0 -+ var _differentialSamples: UInt64 = 0 -+ var _turbofanSamples: UInt64 = 0 -+ var _maglevSamples: UInt64 = 0 -+ var _relationsPerformed: UInt64 = 0 -+ var _sparkplugSamples: UInt64 = 0 -+ var _avgBugOracleTime: Double = 0 -+ var _jitSamples: UInt64 = 0 -+ var _avgDumpSizeOpt: Double = 0 -+ var _avgDumpSizeUnOpt: Double = 0 - - static let defaultInstance = _StorageClass() - -@@ -342,6 +393,15 @@ extension Fuzzilli_Protobuf_Statistics: SwiftProtobuf.Message, SwiftProtobuf._Me - _coverage = source._coverage - _correctnessRate = source._correctnessRate - _timeoutRate = source._timeoutRate -+ _differentialSamples = source._differentialSamples -+ _turbofanSamples = source._turbofanSamples -+ _maglevSamples = source._maglevSamples -+ _relationsPerformed = source._relationsPerformed -+ _sparkplugSamples = source._sparkplugSamples -+ _avgBugOracleTime = source._avgBugOracleTime -+ _jitSamples = source._jitSamples -+ _avgDumpSizeOpt = source._avgDumpSizeOpt -+ _avgDumpSizeUnOpt = source._avgDumpSizeUnOpt - } - } - -@@ -356,27 +416,33 @@ extension Fuzzilli_Protobuf_Statistics: SwiftProtobuf.Message, SwiftProtobuf._Me - _ = _uniqueStorage() - try withExtendedLifetime(_storage) { (_storage: _StorageClass) in - 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.decodeSingularUInt64Field(value: &_storage._totalSamples) }() -- case 2: try { try decoder.decodeSingularUInt64Field(value: &_storage._validSamples) }() -- case 3: try { try decoder.decodeSingularUInt64Field(value: &_storage._interestingSamples) }() -- case 4: try { try decoder.decodeSingularUInt64Field(value: &_storage._timedOutSamples) }() -- case 5: try { try decoder.decodeSingularUInt64Field(value: &_storage._crashingSamples) }() -- case 6: try { try decoder.decodeSingularUInt64Field(value: &_storage._totalExecs) }() -- case 7: try { try decoder.decodeSingularDoubleField(value: &_storage._avgCorpusSize) }() -- case 8: try { try decoder.decodeSingularDoubleField(value: &_storage._avgProgramSize) }() -- case 9: try { try decoder.decodeSingularDoubleField(value: &_storage._avgCorpusProgramSize) }() -- case 10: try { try decoder.decodeSingularDoubleField(value: &_storage._avgExecutionTime) }() -- case 11: try { try decoder.decodeSingularDoubleField(value: &_storage._execsPerSecond) }() -- case 12: try { try decoder.decodeSingularDoubleField(value: &_storage._fuzzerOverhead) }() -- case 13: try { try decoder.decodeSingularDoubleField(value: &_storage._minimizationOverhead) }() -- case 14: try { try decoder.decodeSingularUInt64Field(value: &_storage._numChildNodes) }() -- case 15: try { try decoder.decodeSingularDoubleField(value: &_storage._coverage) }() -- case 16: try { try decoder.decodeSingularDoubleField(value: &_storage._correctnessRate) }() -- case 17: try { try decoder.decodeSingularDoubleField(value: &_storage._timeoutRate) }() -+ case 1: try decoder.decodeSingularUInt64Field(value: &_storage._totalSamples) -+ case 2: try decoder.decodeSingularUInt64Field(value: &_storage._validSamples) -+ case 3: try decoder.decodeSingularUInt64Field(value: &_storage._interestingSamples) -+ case 4: try decoder.decodeSingularUInt64Field(value: &_storage._timedOutSamples) -+ case 5: try decoder.decodeSingularUInt64Field(value: &_storage._crashingSamples) -+ case 6: try decoder.decodeSingularUInt64Field(value: &_storage._totalExecs) -+ case 7: try decoder.decodeSingularDoubleField(value: &_storage._avgCorpusSize) -+ case 8: try decoder.decodeSingularDoubleField(value: &_storage._avgProgramSize) -+ case 9: try decoder.decodeSingularDoubleField(value: &_storage._avgCorpusProgramSize) -+ case 10: try decoder.decodeSingularDoubleField(value: &_storage._avgExecutionTime) -+ case 11: try decoder.decodeSingularDoubleField(value: &_storage._execsPerSecond) -+ case 12: try decoder.decodeSingularDoubleField(value: &_storage._fuzzerOverhead) -+ case 13: try decoder.decodeSingularDoubleField(value: &_storage._minimizationOverhead) -+ case 14: try decoder.decodeSingularUInt64Field(value: &_storage._numChildNodes) -+ case 15: try decoder.decodeSingularDoubleField(value: &_storage._coverage) -+ case 16: try decoder.decodeSingularDoubleField(value: &_storage._correctnessRate) -+ case 17: try decoder.decodeSingularDoubleField(value: &_storage._timeoutRate) -+ case 18: try decoder.decodeSingularUInt64Field(value: &_storage._differentialSamples) -+ case 19: try decoder.decodeSingularUInt64Field(value: &_storage._turbofanSamples) -+ case 20: try decoder.decodeSingularUInt64Field(value: &_storage._maglevSamples) -+ case 21: try decoder.decodeSingularUInt64Field(value: &_storage._relationsPerformed) -+ case 22: try decoder.decodeSingularUInt64Field(value: &_storage._sparkplugSamples) -+ case 23: try decoder.decodeSingularDoubleField(value: &_storage._avgBugOracleTime) -+ case 24: try decoder.decodeSingularUInt64Field(value: &_storage._jitSamples) -+ case 25: try decoder.decodeSingularDoubleField(value: &_storage._avgDumpSizeOpt) -+ case 26: try decoder.decodeSingularDoubleField(value: &_storage._avgDumpSizeUnOpt) - default: break - } - } -@@ -436,6 +502,33 @@ extension Fuzzilli_Protobuf_Statistics: SwiftProtobuf.Message, SwiftProtobuf._Me - if _storage._timeoutRate != 0 { - try visitor.visitSingularDoubleField(value: _storage._timeoutRate, fieldNumber: 17) - } -+ if _storage._differentialSamples != 0 { -+ try visitor.visitSingularUInt64Field(value: _storage._differentialSamples, fieldNumber: 18) -+ } -+ if _storage._turbofanSamples != 0 { -+ try visitor.visitSingularUInt64Field(value: _storage._turbofanSamples, fieldNumber: 19) -+ } -+ if _storage._maglevSamples != 0 { -+ try visitor.visitSingularUInt64Field(value: _storage._maglevSamples, fieldNumber: 20) -+ } -+ if _storage._relationsPerformed != 0 { -+ try visitor.visitSingularUInt64Field(value: _storage._relationsPerformed, fieldNumber: 21) -+ } -+ if _storage._sparkplugSamples != 0 { -+ try visitor.visitSingularUInt64Field(value: _storage._sparkplugSamples, fieldNumber: 22) -+ } -+ if _storage._avgBugOracleTime != 0 { -+ try visitor.visitSingularDoubleField(value: _storage._avgBugOracleTime, fieldNumber: 23) -+ } -+ if _storage._jitSamples != 0 { -+ try visitor.visitSingularUInt64Field(value: _storage._jitSamples, fieldNumber: 24) -+ } -+ if _storage._avgDumpSizeOpt != 0 { -+ try visitor.visitSingularDoubleField(value: _storage._avgDumpSizeOpt, fieldNumber: 25) -+ } -+ if _storage._avgDumpSizeUnOpt != 0 { -+ try visitor.visitSingularDoubleField(value: _storage._avgDumpSizeUnOpt, fieldNumber: 26) -+ } - } - try unknownFields.traverse(visitor: &visitor) - } -@@ -462,6 +555,15 @@ extension Fuzzilli_Protobuf_Statistics: SwiftProtobuf.Message, SwiftProtobuf._Me - if _storage._coverage != rhs_storage._coverage {return false} - if _storage._correctnessRate != rhs_storage._correctnessRate {return false} - if _storage._timeoutRate != rhs_storage._timeoutRate {return false} -+ if _storage._differentialSamples != rhs_storage._differentialSamples {return false} -+ if _storage._turbofanSamples != rhs_storage._turbofanSamples {return false} -+ if _storage._maglevSamples != rhs_storage._maglevSamples {return false} -+ if _storage._relationsPerformed != rhs_storage._relationsPerformed {return false} -+ if _storage._sparkplugSamples != rhs_storage._sparkplugSamples {return false} -+ if _storage._avgBugOracleTime != rhs_storage._avgBugOracleTime {return false} -+ if _storage._jitSamples != rhs_storage._jitSamples {return false} -+ if _storage._avgDumpSizeOpt != rhs_storage._avgDumpSizeOpt {return false} -+ if _storage._avgDumpSizeUnOpt != rhs_storage._avgDumpSizeUnOpt {return false} - return true - } - if !storagesAreEqual {return false} -diff --git a/Sources/Fuzzilli/Protobuf/sync.proto b/Sources/Fuzzilli/Protobuf/sync.proto -index f237d82..bb1942b 100644 ---- a/Sources/Fuzzilli/Protobuf/sync.proto -+++ b/Sources/Fuzzilli/Protobuf/sync.proto -@@ -81,4 +81,22 @@ message Statistics { - - /// The timeout rate of recently generated programs (number of timeouts divided by number of generated programs). - double timeoutRate = 17; -+ -+ uint64 differentialSamples = 18; -+ -+ uint64 turbofanSamples = 19; -+ -+ uint64 maglevSamples = 20; -+ -+ uint64 relationsPerformed = 21; -+ -+ uint64 sparkplugSamples = 22; -+ -+ double avgBugOracleTime = 23; -+ -+ uint64 jitSamples = 24; -+ -+ double avgDumpSizeOpt = 25; -+ -+ double avgDumpSizeUnOpt = 26; - } -diff --git a/Sources/Fuzzilli/Util/MockFuzzer.swift b/Sources/Fuzzilli/Util/MockFuzzer.swift -index be921c9..665b8e9 100644 ---- a/Sources/Fuzzilli/Util/MockFuzzer.swift -+++ b/Sources/Fuzzilli/Util/MockFuzzer.swift -@@ -17,22 +17,31 @@ import Foundation - // Mock implementations of fuzzer components for testing. - - struct MockExecution: Execution { -- let outcome: ExecutionOutcome -+ var outcome: ExecutionOutcome - let stdout: String - let stderr: String - let fuzzout: String -- let execTime: TimeInterval -+ var execTime: TimeInterval -+ let executionHash: Int -+ var compilersUsed: [JITType] -+ var unOptStdout: String?=nil -+ var optStdout: String?=nil -+ var reproducesInNonReplMode: Bool?=false -+ var bugOracleTime: TimeInterval?=nil - } - - class MockScriptRunner: ScriptRunner { - var processArguments: [String] = [] - -- func run(_ script: String, withTimeout timeout: UInt32) -> Execution { -+ func run(_ script: String, withTimeout timeout: UInt32, -+ differentialFuzzingPositionDumpSeed: UInt32) -> Execution { - return MockExecution(outcome: .succeeded, - stdout: "", - stderr: "", - fuzzout: "", -- execTime: TimeInterval(0.1)) -+ execTime: TimeInterval(0.1), -+ executionHash: 0, -+ compilersUsed: []) - } - - func setEnvironmentVariable(_ key: String, to value: String) {} -@@ -162,6 +171,7 @@ public func makeMockFuzzer(config maybeConfiguration: Configuration? = nil, engi - - // A script runner to execute JavaScript code in an instrumented JS engine. - let runner = maybeRunner ?? MockScriptRunner() -+ let referenceRunner = maybeRunner ?? MockScriptRunner() - - // the mutators to use for this fuzzing engine. - let mutators = WeightedList([ -@@ -197,6 +207,7 @@ public func makeMockFuzzer(config maybeConfiguration: Configuration? = nil, engi - // Construct the fuzzer instance. - let fuzzer = Fuzzer(configuration: configuration, - scriptRunner: runner, -+ referenceRunner: referenceRunner, - engine: engine, - mutators: mutators, - codeGenerators: codeGenerators, -@@ -206,6 +217,7 @@ public func makeMockFuzzer(config maybeConfiguration: Configuration? = nil, engi - lifter: lifter, - corpus: corpus, - minimizer: minimizer, -+ localId: 0, - queue: DispatchQueue.main) - - fuzzer.registerEventListener(for: fuzzer.events.Log) { ev in -diff --git a/Sources/FuzzilliCli/Profiles/DuktapeProfile.swift b/Sources/FuzzilliCli/Profiles/DuktapeProfile.swift -index 8d38b9b..bcba056 100644 ---- a/Sources/FuzzilliCli/Profiles/DuktapeProfile.swift -+++ b/Sources/FuzzilliCli/Profiles/DuktapeProfile.swift -@@ -19,6 +19,8 @@ let duktapeProfile = Profile( - ["--reprl"] - }, - -+ processArgsReference: ["--reprl"], -+ - processEnv: ["UBSAN_OPTIONS": "handle_segv=0"], - - maxExecsBeforeRespawn: 1000, -@@ -26,6 +28,7 @@ let duktapeProfile = Profile( - timeout: 250, - - codePrefix: """ -+ const fhash = () => null; - """, - - codeSuffix: """ -@@ -35,6 +38,10 @@ let duktapeProfile = Profile( - - crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", "fuzzilli('FUZZILLI_CRASH', 1)"], - -+ differentialTests: [], -+ -+ differentialTestsInvariant: [], -+ - additionalCodeGenerators: [], - - additionalProgramTemplates: WeightedList([]), -diff --git a/Sources/FuzzilliCli/Profiles/JSCProfile.swift b/Sources/FuzzilliCli/Profiles/JSCProfile.swift -index ee6a2f3..11c29b1 100644 ---- a/Sources/FuzzilliCli/Profiles/JSCProfile.swift -+++ b/Sources/FuzzilliCli/Profiles/JSCProfile.swift -@@ -36,10 +36,14 @@ fileprivate let GcGenerator = CodeGenerator("GcGenerator") { b in - b.callFunction(b.loadBuiltin("gc")) - } - -+fileprivate let commonArgs = [ -+ "--validateOptions=true", -+ "--reprl", -+] -+ - let jscProfile = Profile( - processArgs: { randomize in -- var args = [ -- "--validateOptions=true", -+ var args = commonArgs + [ - // No need to call functions thousands of times before they are JIT compiled - "--thresholdForJITSoon=10", - "--thresholdForJITAfterWarmUp=10", -@@ -49,8 +53,7 @@ let jscProfile = Profile( - "--thresholdForFTLOptimizeAfterWarmUp=1000", - "--thresholdForFTLOptimizeSoon=1000", - // Enable bounds check elimination validation -- "--validateBCE=true", -- "--reprl"] -+ "--validateBCE=true"] - - guard randomize else { return args } - -@@ -69,6 +72,13 @@ let jscProfile = Profile( - return args - }, - -+ processArgsReference: commonArgs + [ -+ "--useConcurrentGC=false", -+ // Disable JIT -+ "--useFTLJIT=false", -+ "--useBaselineJIT=false", -+ "--useDFGJIT=false"], -+ - processEnv: ["UBSAN_OPTIONS":"handle_segv=0"], - - maxExecsBeforeRespawn: 1000, -@@ -76,6 +86,7 @@ let jscProfile = Profile( - timeout: 250, - - codePrefix: """ -+ const fhash = fuzzilli_hash; - """, - - codeSuffix: """ -@@ -84,7 +95,14 @@ let jscProfile = Profile( - - ecmaVersion: ECMAScriptVersion.es6, - -- crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", "fuzzilli('FUZZILLI_CRASH', 1)", "fuzzilli('FUZZILLI_CRASH', 2)"], -+ crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", -+ "fuzzilli('FUZZILLI_CRASH', 1)", -+ "fuzzilli('FUZZILLI_CRASH', 2)"], -+ -+ differentialTests: ["fuzzilli_hash(fuzzilli('FUZZILLI_RANDOM'))",], -+ -+ differentialTestsInvariant: ["fuzzilli_hash(Math.random())", -+ "fuzzilli_hash(Date.now())",], - - additionalCodeGenerators: [ - (ForceDFGCompilationGenerator, 5), -diff --git a/Sources/FuzzilliCli/Profiles/JerryscriptProfile.swift b/Sources/FuzzilliCli/Profiles/JerryscriptProfile.swift -index 3a41c64..cc069b1 100644 ---- a/Sources/FuzzilliCli/Profiles/JerryscriptProfile.swift -+++ b/Sources/FuzzilliCli/Profiles/JerryscriptProfile.swift -@@ -19,6 +19,8 @@ let jerryscriptProfile = Profile( - ["--reprl-fuzzilli"] - }, - -+ processArgsReference: ["--reprl-fuzzilli"], -+ - processEnv: ["UBSAN_OPTIONS":"handle_segv=0"], - - maxExecsBeforeRespawn: 1000, -@@ -26,6 +28,7 @@ let jerryscriptProfile = Profile( - timeout: 250, - - codePrefix: """ -+ const fhash = () => null; - """, - - codeSuffix: """ -@@ -35,6 +38,10 @@ let jerryscriptProfile = Profile( - - crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", "fuzzilli('FUZZILLI_CRASH', 1)"], - -+ differentialTests: [], -+ -+ differentialTestsInvariant: [], -+ - additionalCodeGenerators: [], - - additionalProgramTemplates: WeightedList([]), -diff --git a/Sources/FuzzilliCli/Profiles/Profile.swift b/Sources/FuzzilliCli/Profiles/Profile.swift -index 1a02a6d..dcd37d2 100644 ---- a/Sources/FuzzilliCli/Profiles/Profile.swift -+++ b/Sources/FuzzilliCli/Profiles/Profile.swift -@@ -14,8 +14,9 @@ - - import Fuzzilli - --struct Profile { -+public struct Profile { - let processArgs: (_ randomize: Bool) -> [String] -+ let processArgsReference: [String] - let processEnv: [String : String] - let maxExecsBeforeRespawn: Int - // Timeout is in milliseconds. -@@ -28,6 +29,9 @@ struct Profile { - // Used to verify that crashes can be detected. - let crashTests: [String] - -+ let differentialTests: [String] -+ let differentialTestsInvariant: [String] -+ - let additionalCodeGenerators: [(CodeGenerator, Int)] - let additionalProgramTemplates: WeightedList - -@@ -40,7 +44,7 @@ struct Profile { - let optionalPostProcessor: FuzzingPostProcessor? - } - --let profiles = [ -+public let profiles = [ - "qtjs": qtjsProfile, - "qjs": qjsProfile, - "jsc": jscProfile, -@@ -49,5 +53,4 @@ let profiles = [ - "duktape": duktapeProfile, - "jerryscript": jerryscriptProfile, - "xs": xsProfile, -- "v8holefuzzing": v8HoleFuzzingProfile, - ] -diff --git a/Sources/FuzzilliCli/Profiles/QjsProfile.swift b/Sources/FuzzilliCli/Profiles/QjsProfile.swift -index 90bcbf3..68f4ebd 100644 ---- a/Sources/FuzzilliCli/Profiles/QjsProfile.swift -+++ b/Sources/FuzzilliCli/Profiles/QjsProfile.swift -@@ -19,6 +19,8 @@ let qjsProfile = Profile( - ["--reprl"] - }, - -+ processArgsReference: ["--reprl"], -+ - processEnv: ["UBSAN_OPTIONS": "handle_segv=0"], - - maxExecsBeforeRespawn: 1000, -@@ -26,6 +28,7 @@ let qjsProfile = Profile( - timeout: 250, - - codePrefix: """ -+ const fhash = () => null; - """, - - codeSuffix: """ -@@ -35,6 +38,11 @@ let qjsProfile = Profile( - - crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", "fuzzilli('FUZZILLI_CRASH', 1)", "fuzzilli('FUZZILLI_CRASH', 2)"], - -+ differentialTests: [], -+ -+ differentialTestsInvariant: [], -+ -+ - additionalCodeGenerators: [], - - additionalProgramTemplates: WeightedList([]), -diff --git a/Sources/FuzzilliCli/Profiles/QtjsProfile.swift b/Sources/FuzzilliCli/Profiles/QtjsProfile.swift -index 7595516..3ba7fc4 100644 ---- a/Sources/FuzzilliCli/Profiles/QtjsProfile.swift -+++ b/Sources/FuzzilliCli/Profiles/QtjsProfile.swift -@@ -27,6 +27,8 @@ let qtjsProfile = Profile( - ["-reprl"] - }, - -+ processArgsReference: ["-reprl"], -+ - processEnv: ["UBSAN_OPTIONS":"handle_segv=0"], - - maxExecsBeforeRespawn: 1000, -@@ -34,6 +36,7 @@ let qtjsProfile = Profile( - timeout: 250, - - codePrefix: """ -+ const fhash = () => null; - """, - - codeSuffix: """ -@@ -45,6 +48,10 @@ let qtjsProfile = Profile( - // Used to verify that crashes can be detected. - crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)"], - -+ differentialTests: [], -+ -+ differentialTestsInvariant: [], -+ - additionalCodeGenerators: [ - (ForceQV4JITGenerator, 20), - ], -@@ -57,6 +64,7 @@ let qtjsProfile = Profile( - - additionalBuiltins: [ - "gc" : .function([] => .undefined), -+ "placeholder" : .function([] => .undefined), - ], - - optionalPostProcessor: nil -diff --git a/Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift b/Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift -index 081cbd3..4d7aced 100644 ---- a/Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift -+++ b/Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift -@@ -27,16 +27,25 @@ fileprivate let GcGenerator = CodeGenerator("GcGenerator") { b in - b.callFunction(b.loadBuiltin("gc")) - } - -+fileprivate let commonArgs = [ -+ "--baseline-warmup-threshold=10", -+ "--fuzzing-safe", -+ "--disable-oom-functions", -+ "--reprl" -+] -+ -+fileprivate let differentialArgs = [ -+ "--differential-testing" -+] -+ - let spidermonkeyProfile = Profile( - processArgs: { randomize in -- var args = [ -- "--baseline-warmup-threshold=10", -+ var args = commonArgs + [ - "--ion-warmup-threshold=100", - "--ion-check-range-analysis", - "--ion-extra-checks", -- "--fuzzing-safe", -- "--disable-oom-functions", -- "--reprl"] -+ ] -+ - - guard randomize else { return args } - -@@ -70,6 +79,11 @@ let spidermonkeyProfile = Profile( - return args - }, - -+ processArgsReference: commonArgs + differentialArgs + [ -+ "--no-threads", -+ "--no-ion", -+ ], -+ - processEnv: ["UBSAN_OPTIONS": "handle_segv=0"], - - maxExecsBeforeRespawn: 1000, -@@ -77,6 +91,7 @@ let spidermonkeyProfile = Profile( - timeout: 250, - - codePrefix: """ -+ const fhash = fuzzilli_hash; - """, - - codeSuffix: """ -@@ -85,7 +100,14 @@ let spidermonkeyProfile = Profile( - - ecmaVersion: ECMAScriptVersion.es6, - -- crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", "fuzzilli('FUZZILLI_CRASH', 1)", "fuzzilli('FUZZILLI_CRASH', 2)"], -+ crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", -+ "fuzzilli('FUZZILLI_CRASH', 1)", -+ "fuzzilli('FUZZILLI_CRASH', 2)"], -+ -+ differentialTests: ["for(let i=0; i<200; i++) {fuzzilli_hash(fuzzilli('FUZZILLI_RANDOM'))}",], -+ -+ differentialTestsInvariant: ["for(let i=0; i < 200; i++) {fuzzilli_hash(Math.random())}", -+ "for(let i=0; i < 200; i++) {fuzzilli_hash(Date.now())}",], - - additionalCodeGenerators: [ - (ForceSpidermonkeyIonGenerator, 10), -diff --git a/Sources/FuzzilliCli/Profiles/V8HoleFuzzingProfile.swift b/Sources/FuzzilliCli/Profiles/V8HoleFuzzingProfile.swift -deleted file mode 100644 -index 1fcf1e3..0000000 ---- a/Sources/FuzzilliCli/Profiles/V8HoleFuzzingProfile.swift -+++ /dev/null -@@ -1,106 +0,0 @@ --// Copyright 2023 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 Fuzzilli -- --// TODO: move common parts (e.g. generators) into a V8CommonProfile.swift. --fileprivate let ForceJITCompilationThroughLoopGenerator = CodeGenerator("ForceJITCompilationThroughLoopGenerator", inputs: .required(.function())) { b, f in -- assert(b.type(of: f).Is(.function())) -- let arguments = b.randomArguments(forCalling: f) -- b.buildRepeatLoop(n: 100) { _ in -- b.callFunction(f, withArgs: arguments) -- } --} --fileprivate let ForceTurboFanCompilationGenerator = CodeGenerator("ForceTurboFanCompilationGenerator", inputs: .required(.function())) { b, f in -- assert(b.type(of: f).Is(.function())) -- let arguments = b.randomArguments(forCalling: f) -- b.callFunction(f, withArgs: arguments) -- b.eval("%PrepareFunctionForOptimization(%@)", with: [f]); -- b.callFunction(f, withArgs: arguments) -- b.callFunction(f, withArgs: arguments) -- b.eval("%OptimizeFunctionOnNextCall(%@)", with: [f]); -- b.callFunction(f, withArgs: arguments) --} --fileprivate let ForceMaglevCompilationGenerator = CodeGenerator("ForceMaglevCompilationGenerator", inputs: .required(.function())) { b, f in -- assert(b.type(of: f).Is(.function())) -- let arguments = b.randomArguments(forCalling: f) -- b.callFunction(f, withArgs: arguments) -- b.eval("%PrepareFunctionForOptimization(%@)", with: [f]); -- b.callFunction(f, withArgs: arguments) -- b.callFunction(f, withArgs: arguments) -- b.eval("%OptimizeMaglevOnNextCall(%@)", with: [f]); -- b.callFunction(f, withArgs: arguments) --} --// Insert random GC calls throughout our code. --fileprivate let GcGenerator = CodeGenerator("GcGenerator") { b in -- let gc = b.loadBuiltin("gc") -- // Do minor GCs more frequently. -- let type = b.loadString(probability(0.25) ? "major" : "minor") -- // If the execution type is 'async', gc() returns a Promise, we currently -- // do not really handle other than typing the return of gc to .undefined | -- // .jsPromise. One could either chain a .then or create two wrapper -- // functions that are differently typed such that fuzzilli always knows -- // what the type of the return value is. -- let execution = b.loadString(probability(0.5) ? "sync" : "async") -- b.callFunction(gc, withArgs: [b.createObject(with: ["type": type, "execution": execution])]) --} -- --// 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 -- b.eval("%LeakHole()", hasOutput: true) --} -- --let v8HoleFuzzingProfile = Profile( -- processArgs: { randomize in -- var args = [ -- "--expose-gc", -- "--omit-quit", -- "--allow-natives-syntax", -- "--fuzzing", -- "--hole-fuzzing", -- "--jit-fuzzing", -- "--future", -- "--harmony", -- ] -- return args -- }, -- processEnv: [:], -- maxExecsBeforeRespawn: 1000, -- timeout: 250, -- codePrefix: """ -- """, -- codeSuffix: """ -- """, -- ecmaVersion: ECMAScriptVersion.es6, -- crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", "fuzzilli('FUZZILLI_CRASH', 7)"], -- additionalCodeGenerators: [ -- (ForceJITCompilationThroughLoopGenerator, 5), -- (ForceTurboFanCompilationGenerator, 5), -- (ForceMaglevCompilationGenerator, 5), -- (GcGenerator, 10), -- (HoleLeakGenerator, 25), -- ], -- additionalProgramTemplates: WeightedList([ -- ]), -- disabledCodeGenerators: [], -- disabledMutators: [], -- additionalBuiltins: [ -- "gc" : .function([] => (.undefined | .jsPromise)), -- "d8" : .object(), -- "Worker" : .constructor([.anything, .object()] => .object(withMethods: ["postMessage","getMessage"])), -- ], -- optionalPostProcessor: nil --) -diff --git a/Sources/FuzzilliCli/Profiles/V8Profile.swift b/Sources/FuzzilliCli/Profiles/V8Profile.swift -index 6325b44..ae32f8e 100644 ---- a/Sources/FuzzilliCli/Profiles/V8Profile.swift -+++ b/Sources/FuzzilliCli/Profiles/V8Profile.swift -@@ -411,131 +411,16 @@ fileprivate let RegExpFuzzer = ProgramTemplate("RegExpFuzzer") { b in - b.build(n: 15) - } - --let v8Profile = Profile( -- processArgs: { randomize in -- var args = [ -- "--expose-gc", -- "--omit-quit", -- "--allow-natives-syntax", -- "--fuzzing", -- "--jit-fuzzing", -- "--future", -- "--harmony", -- "--js-staging" -- ] -- -- guard randomize else { return args } -- -- // -- // Existing features that should sometimes be disabled. -- // -- if probability(0.1) { -- args.append("--no-turbofan") -- } -- -- if probability(0.1) { -- args.append("--no-turboshaft") -- } -- -- if probability(0.1) { -- args.append("--no-maglev") -- } -- -- if probability(0.1) { -- args.append("--no-sparkplug") -- } -- -- if probability(0.1) { -- args.append("--no-short-builtin-calls") -- } -- -- // -- // Future features that should sometimes be enabled. -- // -- if probability(0.25) { -- args.append("--minor-ms") -- } -- -- if probability(0.25) { -- args.append("--shared-string-table") -- } -- -- if probability(0.25) && !args.contains("--no-maglev") { -- args.append("--maglev-future") -- } -- -- if probability(0.25) && !args.contains("--no-turboshaft") { -- args.append("--turboshaft-future") -- } -+fileprivate let config = V8DifferentialConfig() - -- if probability(0.1) && !args.contains("--no-turboshaft") { -- args.append("--turboshaft-typed-optimizations") -- } -- -- if probability(0.1) { -- args.append("--harmony-struct") -- } -- -- // -- // Sometimes enable additional verification/stressing logic (which may be fairly expensive). -- // -- if probability(0.1) { -- args.append("--verify-heap") -- } -- if probability(0.1) { -- args.append("--turbo-verify") -- } -- if probability(0.1) { -- args.append("--turbo-verify-allocation") -- } -- if probability(0.1) { -- args.append("--assert-types") -- } -- if probability(0.1) { -- args.append("--turboshaft-assert-types") -- } -- if probability(0.1) { -- args.append("--deopt-every-n-times=\(chooseUniform(from: [100, 250, 500, 1000, 2500, 5000, 10000]))") -- } -- -- // -- // More exotic configuration changes. -- // -- if probability(0.05) { -- if probability(0.5) { args.append("--stress-gc-during-compilation") } -- if probability(0.5) { args.append("--lazy-new-space-shrinking") } -- -- args.append(probability(0.5) ? "--always-sparkplug" : "--no-always-sparkplug") -- args.append(probability(0.5) ? "--always-osr" : "--no-always-osr") -- args.append(probability(0.5) ? "--concurrent-osr" : "--no-concurrent-osr") -- args.append(probability(0.5) ? "--force-slow-path" : "--no-force-slow-path") -- -- // Maglev related flags -- args.append(probability(0.5) ? "--maglev-inline-api-calls" : "--no-maglev-inline-api-calls") -- -- // Compiler related flags -- args.append(probability(0.5) ? "--always-turbofan" : "--no-always-turbofan") -- args.append(probability(0.5) ? "--turbo-move-optimization" : "--no-turbo-move-optimization") -- args.append(probability(0.5) ? "--turbo-jt" : "--no-turbo-jt") -- args.append(probability(0.5) ? "--turbo-loop-peeling" : "--no-turbo-loop-peeling") -- args.append(probability(0.5) ? "--turbo-loop-variable" : "--no-turbo-loop-variable") -- args.append(probability(0.5) ? "--turbo-loop-rotation" : "--no-turbo-loop-rotation") -- args.append(probability(0.5) ? "--turbo-cf-optimization" : "--no-turbo-cf-optimization") -- args.append(probability(0.5) ? "--turbo-escape" : "--no-turbo-escape") -- args.append(probability(0.5) ? "--turbo-allocation-folding" : "--no-turbo-allocation-folding") -- args.append(probability(0.5) ? "--turbo-instruction-scheduling" : "--no-turbo-instruction-scheduling") -- args.append(probability(0.5) ? "--turbo-stress-instruction-scheduling" : "--no-turbo-stress-instruction-scheduling") -- args.append(probability(0.5) ? "--turbo-store-elimination" : "--no-turbo-store-elimination") -- args.append(probability(0.5) ? "--turbo-rewrite-far-jumps" : "--no-turbo-rewrite-far-jumps") -- args.append(probability(0.5) ? "--turbo-optimize-apply" : "--no-turbo-optimize-apply") -- args.append(chooseUniform(from: ["--no-enable-sse3", "--no-enable-ssse3", "--no-enable-sse4-1", "--no-enable-sse4-2", "--no-enable-avx", "--no-enable-avx2"])) -- args.append(probability(0.5) ? "--turbo-load-elimination" : "--no-turbo-load-elimination") -- args.append(probability(0.5) ? "--turbo-inlining" : "--no-turbo-inlining") -- args.append(probability(0.5) ? "--turbo-splitting" : "--no-turbo-splitting") -- } -+let v8Profile = Profile( -+ processArgs: { _ in -+ var args = config.commonArgs -+ args.append(contentsOf: config.differentialArgs) -+ return args -+ }, - -- return args -- }, -+ processArgsReference: config.commonArgs + config.referenceArgs, - - processEnv: [:], - -@@ -543,8 +428,9 @@ let v8Profile = Profile( - - timeout: 250, - -- codePrefix: """ -- """, -+ codePrefix: """ -+%EnableFrameDumping(); -+""", - - codeSuffix: """ - """, -@@ -553,6 +439,10 @@ let v8Profile = Profile( - - crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", "fuzzilli('FUZZILLI_CRASH', 1)", "fuzzilli('FUZZILLI_CRASH', 2)", "fuzzilli('FUZZILLI_CRASH', 3)"], - -+ differentialTests: [], -+ -+ differentialTestsInvariant: [], -+ - additionalCodeGenerators: [ - (ForceJITCompilationThroughLoopGenerator, 5), - (ForceTurboFanCompilationGenerator, 5), -diff --git a/Sources/FuzzilliCli/Profiles/XSProfile.swift b/Sources/FuzzilliCli/Profiles/XSProfile.swift -index 422ecc4..55a6239 100644 ---- a/Sources/FuzzilliCli/Profiles/XSProfile.swift -+++ b/Sources/FuzzilliCli/Profiles/XSProfile.swift -@@ -19,6 +19,8 @@ let xsProfile = Profile( - ["-f"] - }, - -+ processArgsReference: ["-f"], -+ - processEnv: ["UBSAN_OPTIONS":"handle_segv=0"], - - maxExecsBeforeRespawn: 1000, -@@ -26,6 +28,7 @@ let xsProfile = Profile( - timeout: 250, - - codePrefix: """ -+ const fhash = () => null; - """, - - codeSuffix: """ -@@ -36,6 +39,11 @@ let xsProfile = Profile( - - crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", "fuzzilli('FUZZILLI_CRASH', 1)", "fuzzilli('FUZZILLI_CRASH', 2)"], - -+ differentialTests: ["fuzzilli_hash(fuzzilli('FUZZILLI_RANDOM'))",], -+ -+ differentialTestsInvariant: ["fuzzilli_hash(Math.random())", -+ "fuzzilli_hash(Date.now())",], -+ - additionalCodeGenerators: [], - - additionalProgramTemplates: WeightedList([]), -diff --git a/Sources/FuzzilliCli/TerminalUI.swift b/Sources/FuzzilliCli/TerminalUI.swift -index 1bb5f1c..0d3eed9 100644 ---- a/Sources/FuzzilliCli/TerminalUI.swift -+++ b/Sources/FuzzilliCli/TerminalUI.swift -@@ -38,6 +38,40 @@ class TerminalUI { - } - } - -+ private func getProcessIDsForD8(_ d8Path: String) -> [pid_t] { -+ var pids: [pid_t] = [] -+ -+ let task = Process() -+ task.executableURL = URL(fileURLWithPath: "/bin/ps") -+ task.arguments = ["-e", "-o", "pid,command"] -+ -+ let pipe = Pipe() -+ task.standardOutput = pipe -+ -+ do { -+ try task.run() -+ } catch { -+ return [] -+ } -+ -+ let data = pipe.fileHandleForReading.readDataToEndOfFile() -+ if let output = String(data: data, encoding: .utf8) { -+ let lines = output.components(separatedBy: "\n") -+ for line in lines { -+ if line.contains(d8Path) && !line.contains("ps -e -o pid,command") && !line.contains("FuzzilliCli") { -+ let components = line.trimmingCharacters(in: .whitespacesAndNewlines).components(separatedBy: " ") -+ if let pid = pid_t(components[0]) { -+ pids.append(pid) -+ } -+ } -+ } -+ } -+ -+ task.waitUntilExit() -+ -+ return pids -+ } -+ - func initOnFuzzerQueue(_ fuzzer: Fuzzer) { - // Register log event listener now to be able to print log messages - // generated during fuzzer initialization -@@ -52,6 +86,13 @@ class TerminalUI { - } - } - -+ fuzzer.registerEventListener(for: fuzzer.events.DifferentialFound) { diff in -+ if diff.reproducesInNonReplMode { -+ print("########## Unique Differential Found ##########") -+ print(fuzzer.lifter.lift(diff.program, withOptions: .includeComments)) -+ } -+ } -+ - fuzzer.registerEventListener(for: fuzzer.events.CrashFound) { crash in - if crash.isUnique { - print("########## Unique Crash Found ##########") -@@ -90,12 +131,41 @@ class TerminalUI { - print() - } - -+ // Nuke stray d8 processes. Pretty terrible but it is what it is -+ // With default timeout 250ms this will nuke a d8 process if it survives longer than ~30 minutes -+ fuzzer.timers.scheduleTask(every: (15 * Minutes + Double.random(in: (1 * Minutes)...(10 * Minutes)))) { -+ let nukeAfterSeconds = ((Double(fuzzer.config.timeout) / 1000.0) * 8.0) * Double((fuzzer.runner as! REPRL).maxExecsBeforeRespawn) -+ let d8Path = fuzzer.runner.processArguments[0] -+ let pids = self.getProcessIDsForD8(d8Path) -+ for p in pids { -+ let statFilePath = "/proc/\(p)/stat" -+ -+ var fileStats = stat() -+ -+ if stat(statFilePath, &fileStats) == 0 { -+ let accessTime = Double(fileStats.st_atim.tv_sec) -+ -+ let currentTime = Date().timeIntervalSince1970 -+ -+ let secondsElapsed = currentTime - accessTime -+ if (secondsElapsed >= nukeAfterSeconds) { -+ let result = kill(p, SIGKILL) -+ if result == 0 { -+ print("Nuked stray d8 process \(p)") -+ } -+ } -+ } -+ } -+ } -+ - // Randomly sample generated and interesting programs and print them. - // The goal of this is to give users a better "feeling" for what the fuzzer is currently doing. -+ /* - fuzzer.timers.scheduleTask(every: 5 * Minutes) { - self.printNextInterestingProgram = true - self.printNextGeneratedProgram = true - } -+ */ - } - } - } -@@ -125,28 +195,49 @@ class TerminalUI { - } else { - print("Fuzzer Statistics") - } -+ -+ let bugOracleUsageRate = (Float(stats.relationsPerformed) / Float(stats.totalExecs)) * 100 -+ let jitUsageRate = (Float(stats.jitSamples) / Float(stats.totalExecs)) * 100 -+ -+ let cestDateFormatter = DateFormatter() -+ cestDateFormatter.dateFormat = "HH:mm:ss - dd.MM.yyyy" -+ -+ cestDateFormatter.timeZone = TimeZone(abbreviation: "CEST") -+ let date = Date() -+ - print(""" - ----------------- -- Fuzzer state: \(state) -- Uptime: \(formatTimeInterval(fuzzer.uptime())) -- Total Samples: \(stats.totalSamples) -- Interesting Samples Found: \(stats.interestingSamples) -- Last Interesting Sample: \(formatTimeInterval(timeSinceLastInterestingProgram)) -- Valid Samples Found: \(stats.validSamples) -- Corpus Size: \(fuzzer.corpus.size)\(maybeAvgCorpusSize) -- Correctness Rate: \(String(format: "%.2f%%", stats.correctnessRate * 100)) (overall: \(String(format: "%.2f%%", stats.overallCorrectnessRate * 100))) -- Timeout Rate: \(String(format: "%.2f%%", stats.timeoutRate * 100)) (overall: \(String(format: "%.2f%%", stats.overallTimeoutRate * 100))) -- Crashes Found: \(stats.crashingSamples) -- Timeouts Hit: \(stats.timedOutSamples) -- Coverage: \(String(format: "%.2f%%", stats.coverage * 100)) -- Avg. program size: \(String(format: "%.2f", stats.avgProgramSize)) -- Avg. corpus program size: \(String(format: "%.2f", stats.avgCorpusProgramSize)) -- Avg. program execution time: \(Int(stats.avgExecutionTime * 1000))ms -- Connected nodes: \(stats.numChildNodes) -- Execs / Second: \(String(format: "%.2f", stats.execsPerSecond)) -- Fuzzer Overhead: \(String(format: "%.2f", stats.fuzzerOverhead * 100))% -- Minimization Overhead: \(String(format: "%.2f", stats.minimizationOverhead * 100))% -- Total Execs: \(stats.totalExecs) -+ Fuzzer state: \(state) -+ Uptime: \(formatTimeInterval(fuzzer.uptime())) -+ Current Time: \(cestDateFormatter.string(from: date)) -+ Total Samples: \(stats.totalSamples) -+ Interesting Samples Found: \(stats.interestingSamples) -+ Last Interesting Sample: \(formatTimeInterval(timeSinceLastInterestingProgram)) -+ Valid Samples Found: \(stats.validSamples) -+ Corpus Size: \(fuzzer.corpus.size)\(maybeAvgCorpusSize) -+ Correctness Rate: \(String(format: "%.2f%%", stats.correctnessRate * 100)) (overall: \(String(format: "%.2f%%", stats.overallCorrectnessRate * 100))) -+ Timeout Rate: \(String(format: "%.2f%%", stats.timeoutRate * 100)) (overall: \(String(format: "%.2f%%", stats.overallTimeoutRate * 100))) -+ Crashes Found: \(stats.crashingSamples) -+ Differentials Found: \(stats.differentialSamples) -+ Sparkplug Executions: \(stats.sparkplugSamples) -+ Maglev Executions: \(stats.maglevSamples) -+ TurboFan Executions: \(stats.turbofanSamples) -+ Relations Performed: \(stats.relationsPerformed) -+ Bug Oracle Usage: \(String(format: "%.2f%%", bugOracleUsageRate)) -+ JIT Usage Rate: \(String(format: "%.2f%%", jitUsageRate)) -+ Timeouts Hit: \(stats.timedOutSamples) -+ Coverage: \(String(format: "%.2f%%", stats.coverage * 100)) -+ Avg. program size: \(String(format: "%.2f", stats.avgProgramSize)) -+ Avg. corpus program size: \(String(format: "%.2f", stats.avgCorpusProgramSize)) -+ Avg. program execution time: \(Int(stats.avgExecutionTime * 1000))ms -+ Avg. bug oracle execution time: \(Int(stats.avgBugOracleTime * 1000))ms -+ Avg. dump size (opt): \(String(format: "%.2f", stats.avgDumpSizeOpt / 1000.0))KB -+ Avg. dump size (unopt): \(String(format: "%.2f", stats.avgDumpSizeUnOpt / 1000.0))KB -+ Connected nodes: \(stats.numChildNodes) -+ Execs / Second: \(String(format: "%.2f", stats.execsPerSecond)) -+ Fuzzer Overhead: \(String(format: "%.2f", stats.fuzzerOverhead * 100))% -+ Minimization Overhead: \(String(format: "%.2f", stats.minimizationOverhead * 100))% -+ Total Execs: \(stats.totalExecs) - """) - } - -diff --git a/Sources/FuzzilliCli/main.swift b/Sources/FuzzilliCli/main.swift -index 98a9787..736e7de 100644 ---- a/Sources/FuzzilliCli/main.swift -+++ b/Sources/FuzzilliCli/main.swift -@@ -97,6 +97,9 @@ Options: - --additionalArguments=args : Pass additional arguments to the JS engine. If multiple arguments are passed, they should be separated by a comma. - --tag=tag : Optional string tag associated with this instance which will be stored in the settings.json file as well as in crashing samples. - This can for example be used to remember the target revision that is being fuzzed. -+ --dumpling-depth : Depth used during dumping to traverse objects (default: 3) -+ --dumpling-prop-count : Amount of properties/array elements to consider during dumping (default: 5) -+ - """) - exit(0) - } -@@ -152,6 +155,9 @@ let swarmTesting = args.has("--swarmTesting") - let argumentRandomization = args.has("--argumentRandomization") - let additionalArguments = args["--additionalArguments"] ?? "" - let tag = args["--tag"] -+let dumplingDepth = UInt32(args.int(for: "--dumpling-depth") ?? 3) -+let dumplingPropCount = UInt32(args.int(for: "--dumpling-prop-count") ?? 5) -+ - - guard numJobs >= 1 else { - configError("Must have at least 1 job") -@@ -363,12 +369,16 @@ func loadCorpus(from dirPath: String) -> [Program] { - // When using multiple jobs, all Fuzzilli instances should use the same arguments for the JS shell, even if - // argument randomization is enabled. This way, their corpora are "compatible" and crashes that require - // (a subset of) the randomly chosen flags can be reproduced on the main instance. --let jsShellArguments = profile.processArgs(argumentRandomization) + additionalArguments.split(separator: ",").map(String.init) -+let jsShellArguments = profile.processArgs(argumentRandomization) + additionalArguments.split(separator: ",").map(String.init) + ["--dumpling-depth=\(dumplingDepth)", "--dumpling-prop-count=\(dumplingPropCount)"] - logger.info("Using the following arguments for the target engine: \(jsShellArguments)") -+let jsShellRefArguments = profile.processArgsReference + ["--dumpling-depth=\(dumplingDepth)", "--dumpling-prop-count=\(dumplingPropCount)"] -+ -+func makeFuzzer(with configuration: Configuration, localId: UInt32) -> Fuzzer { -+ assert(localId >= 1) - --func makeFuzzer(with configuration: Configuration) -> Fuzzer { - // A script runner to execute JavaScript code in an instrumented JS engine. - let runner = REPRL(executable: jsShellPath, processArguments: jsShellArguments, processEnvironment: profile.processEnv, maxExecsBeforeRespawn: profile.maxExecsBeforeRespawn) -+ let referenceRunner = REPRL(executable: jsShellPath, processArguments: jsShellRefArguments, processEnvironment: profile.processEnv, maxExecsBeforeRespawn: profile.maxExecsBeforeRespawn) - - /// The mutation fuzzer responsible for mutating programs from the corpus and evaluating the outcome. - let disabledMutators = Set(profile.disabledMutators) -@@ -461,10 +471,11 @@ func makeFuzzer(with configuration: Configuration) -> Fuzzer { - - // Minimizer to minimize crashes and interesting programs. - let minimizer = Minimizer() -- -+ - // Construct the fuzzer instance. - return Fuzzer(configuration: configuration, - scriptRunner: runner, -+ referenceRunner: referenceRunner, - engine: engine, - mutators: mutators, - codeGenerators: codeGenerators, -@@ -473,21 +484,45 @@ func makeFuzzer(with configuration: Configuration) -> Fuzzer { - environment: environment, - lifter: lifter, - corpus: corpus, -- minimizer: minimizer) -+ minimizer: minimizer, -+ localId: localId) - } - -+let cwd = FileManager.default.currentDirectoryPath -+#if DEBUG -+ public let relateToolPath = "\(cwd)/.build/debug/RelateTool" -+#else -+ public let relateToolPath = "\(cwd)/.build/release/RelateTool" -+#endif -+if !FileManager.default.fileExists(atPath: relateToolPath) { -+ logger.fatal("RelateTool not found in cwd (\(relateToolPath))") -+} -+if !FileManager.default.fileExists(atPath: "/usr/bin/timeout") { -+ logger.fatal("/usr/bin/timeout not found") -+} -+if !FileManager.default.fileExists(atPath: "/bin/ps") { -+ logger.fatal("/bin/ps not found") -+} -+ -+ - // The configuration of the main fuzzer instance. - let mainConfig = Configuration(arguments: CommandLine.arguments, - timeout: UInt32(timeout), - logLevel: logLevel, - crashTests: profile.crashTests, -+ differentialTests: profile.differentialTests, -+ differentialTestsInvariant: profile.differentialTestsInvariant, - minimizationLimit: minimizationLimit, - enableDiagnostics: diagnostics, - enableInspection: inspect, - staticCorpus: staticCorpus, -- tag: tag) -+ tag: tag, -+ relateToolPath: relateToolPath, -+ dumplingDepth: dumplingDepth, -+ dumplingPropCount: dumplingPropCount, -+ storagePath: storagePath) - --let fuzzer = makeFuzzer(with: mainConfig) -+let fuzzer = makeFuzzer(with: mainConfig, localId: 1) - - // Create a "UI". We do this now, before fuzzer initialization, so - // we are able to print log messages generated during initialization. -@@ -514,9 +549,6 @@ fuzzer.sync { - // Always want some statistics. - fuzzer.addModule(Statistics()) - -- // Check core file generation on linux, prior to moving corpus file directories -- fuzzer.checkCoreFileGeneration() -- - // Exit this process when the main fuzzer stops. - fuzzer.registerEventListener(for: fuzzer.events.ShutdownComplete) { reason in - if resume, let path = storagePath { -@@ -617,20 +649,27 @@ let workerConfig = Configuration(arguments: CommandLine.arguments, - timeout: UInt32(timeout), - logLevel: .warning, - crashTests: profile.crashTests, -+ differentialTests: profile.differentialTests, -+ differentialTestsInvariant: profile.differentialTestsInvariant, - minimizationLimit: minimizationLimit, - enableDiagnostics: false, - enableInspection: inspect, - staticCorpus: staticCorpus, -- tag: tag) -- --for _ in 1.. (status: Int32, exec_time: UInt64) { - var exec_time: UInt64 = 0 - var status: Int32 = 0 -+ var encodedJitState: UInt8 = 0 - code.withCString { -- status = reprl_execute(ctx, $0, UInt64(code.count), 1_000_000, &exec_time, 0) -+ // differentialFuzzingPositionDumpSeed == 0 for tests -+ status = reprl_execute(ctx, $0, UInt64(code.count), 1_000_000, &exec_time, 0, 0, &encodedJitState) - } - return (status, exec_time) - } -diff --git a/Sources/RelateTool/main.swift b/Sources/RelateTool/main.swift -new file mode 100644 -index 0000000..0007d8b ---- /dev/null -+++ b/Sources/RelateTool/main.swift -@@ -0,0 +1,114 @@ -+import Foundation -+import Fuzzilli -+ -+ -+func printRunningCmd(_ process: Process) { -+ print("Running command: \(process.executableURL!.path) \(process.arguments!.joined(separator: " "))") -+} -+ -+// -+// Process commandline arguments. -+// -+let args = Arguments.parse(from: CommandLine.arguments) -+ -+let jsShellPath = args["--d8"] ?? "/root/localTools/v8/out/fuzzbuild/d8" -+var poc = args["--poc"]! -+ -+let logLevel: OracleLogLevel -+if args["--logLevel"] == nil || args["--logLevel"] == "info" { -+ logLevel = .info -+} else if args["--logLevel"] == "debug" { -+ logLevel = .debug -+} else { -+ logLevel = .none -+} -+ -+let validate = args["--validate"] -+ -+let usePrepend = args["--prepend"] -+ -+let progOutput = args["--progOutput"] -+ -+if usePrepend != nil { -+ var prependPath = URL(fileURLWithPath: #file) -+ for _ in 0...2 { -+ prependPath.deleteLastPathComponent() -+ } -+ prependPath = prependPath.appendingPathComponent("prepend.js") -+ print(prependPath.path) -+ if !FileManager.default.fileExists(atPath: prependPath.path) { -+ print("prepend.js not found!") -+ exit(-1) -+ } -+ -+ let prependJS = try! String(contentsOfFile: prependPath.path) -+ -+ let filename = "prepend_" + URL(fileURLWithPath: poc).lastPathComponent -+ let fileURL = URL(fileURLWithPath: "/tmp").appendingPathComponent(filename) -+ -+ let prependedScript = prependJS + (try! String(contentsOfFile: poc)) -+ -+ do { -+ try prependedScript.write(to: fileURL, atomically: false, encoding: String.Encoding.utf8) -+ } catch { -+ print("Failed to write file \(fileURL): \(error)") -+ exit(-1) -+ } -+ -+ poc = fileURL.path -+} -+ -+let optRun = Process() -+ -+let optOutput = Pipe() -+optRun.standardOutput = optOutput -+optRun.standardError = optOutput -+ -+ -+let config = V8DifferentialConfig() -+ -+var g = SystemRandomNumberGenerator() -+let differentialFuzzingDumpSeed = UInt32(Int.random(in: 1...9999, using: &g)); -+ -+optRun.executableURL = URL(fileURLWithPath: jsShellPath) -+optRun.arguments = config.commonArgs + config.differentialArgs + ["--dumping-seed=\(differentialFuzzingDumpSeed)", poc] -+ -+printRunningCmd(optRun) -+try! optRun.run() -+optRun.waitUntilExit() -+if progOutput != nil { -+ print(String(data: optOutput.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)!) -+} -+let optDumps = try? String(contentsOfFile: "/tmp/\(differentialFuzzingDumpSeed)_output_dump.txt", encoding: .utf8) -+if optDumps == nil { -+ if validate == nil { -+ print("Produced no opt Dumps") -+ } else { -+ exit(1) -+ } -+} else { -+ let unOptRun = Process() -+ let unOptOutput = Pipe() -+ unOptRun.standardOutput = unOptOutput -+ unOptRun.standardError = unOptOutput -+ unOptRun.executableURL = URL(fileURLWithPath: jsShellPath) -+ unOptRun.arguments = config.commonArgs + config.referenceArgs + ["--dumping-seed=\(differentialFuzzingDumpSeed)", poc] -+ printRunningCmd(unOptRun) -+ try! unOptRun.run() -+ unOptRun.waitUntilExit() -+ if progOutput != nil { -+ print(String(data: unOptOutput.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)!) -+ } -+ let unOptDumps = try! String(contentsOfFile: "/tmp/\(differentialFuzzingDumpSeed)_output_dump.txt", encoding: .utf8) -+ let result = relate(optDumps!, with: unOptDumps, logLevel) -+ -+ if (usePrepend != nil) { -+ try? FileManager.default.removeItem(atPath: poc) -+ } -+ -+ if validate == nil { -+ print(result) -+ } else if !result { -+ exit(1) -+ } -+} -diff --git a/Sources/libreprl/include/libreprl.h b/Sources/libreprl/include/libreprl.h -index 1bfd693..777288b 100644 ---- a/Sources/libreprl/include/libreprl.h -+++ b/Sources/libreprl/include/libreprl.h -@@ -53,8 +53,9 @@ void reprl_destroy_context(struct reprl_context* ctx); - /// @param timeout The maximum allowed execution time in microseconds - /// @param execution_time A pointer to which, if execution succeeds, the execution time in microseconds is written to - /// @param fresh_instance if true, forces the creation of a new instance of the target -+/// @param execHash A pointer to which the differential testing execution hash is written to - /// @return A REPRL exit status (see below) or a negative number in case of an error --int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script_length, uint64_t timeout, uint64_t* execution_time, int fresh_instance); -+int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script_length, uint64_t timeout, uint64_t* execution_time, int fresh_instance, uint32_t source_pos_dump_seed, uint8_t* jit_state); - - /// Returns true if the execution terminated due to a signal. - /// -diff --git a/Sources/libreprl/libreprl-posix.c b/Sources/libreprl/libreprl-posix.c -index 43c4e48..94cc7f0 100644 ---- a/Sources/libreprl/libreprl-posix.c -+++ b/Sources/libreprl/libreprl-posix.c -@@ -30,12 +30,17 @@ - #include - #include - #include -+#include - #include - #include - #include - #include - #include - -+// The REPRL protocol version. Must be identical between server and client. -+// Bump this up whenever there are non-backwards-compatible changes to the protocol -+#define REPRL_PROTOCOL_VERSION 2 -+ - // Well-known file descriptor numbers for reprl <-> child communication, child process side - #define REPRL_CHILD_CTRL_IN 100 - #define REPRL_CHILD_CTRL_OUT 101 -@@ -141,7 +146,7 @@ static struct data_channel* reprl_create_data_channel(struct reprl_context* ctx) - reprl_error(ctx, "Failed to mmap data channel file: %s", strerror(errno)); - return NULL; - } -- -+ - struct data_channel* channel = malloc(sizeof(struct data_channel)); - channel->fd = fd; - channel->mapping = mapping; -@@ -214,6 +219,19 @@ static int reprl_spawn_child(struct reprl_context* ctx) - _exit(-1); - } - -+ #ifdef __linux__ -+ // Set RLIMIT_CORE to 0, such that we don't produce core dumps. The -+ // added benefit of doing this here, in the child process, is that we -+ // can still get core dumps when Fuzzilli crashes. -+ struct rlimit core_limit; -+ core_limit.rlim_cur = 0; -+ core_limit.rlim_max = 0; -+ if (setrlimit(RLIMIT_CORE, &core_limit) < 0) { -+ fprintf(stderr, "setrlimit failed in the child: %s\n", strerror(errno)); -+ _exit(-1); -+ }; -+ #endif -+ - // Unblock any blocked signals. It seems that libdispatch sometimes blocks delivery of certain signals. - sigset_t newset; - sigemptyset(&newset); -@@ -259,21 +277,42 @@ static int reprl_spawn_child(struct reprl_context* ctx) - } - ctx->pid = pid; - -- char helo[5] = { 0 }; -- if (read(ctx->ctrl_in, helo, 4) != 4) { -+ char handshake[5] = { 0 }; -+ if (read(ctx->ctrl_in, handshake, 4) != 4) { - reprl_terminate_child(ctx); -- return reprl_error(ctx, "Did not receive HELO message from child: %s", strerror(errno)); -+ return reprl_error(ctx, "Did not receive handshake message from child: %s", strerror(errno)); - } -- -- if (strncmp(helo, "HELO", 4) != 0) { -+ -+ if (strncmp(handshake, "HELO", 4) == 0) { -+ // Client is using protocol version 1. - reprl_terminate_child(ctx); -- return reprl_error(ctx, "Received invalid HELO message from child: %s", helo); -+ return reprl_error(ctx, "The client uses an old version of the REPRL protocol, please update it to the latest version"); - } -- -- if (write(ctx->ctrl_out, helo, 4) != 4) { -+ -+ if (strncmp(handshake, "XDXD", 4) != 0) { -+ reprl_terminate_child(ctx); -+ return reprl_error(ctx, "Received invalid handshake message from child: %s", handshake); -+ } -+ -+ // The server side just sends back the handshake message, but not the protocol version. -+ if (write(ctx->ctrl_out, handshake, 4) != 4) { -+ reprl_terminate_child(ctx); -+ return reprl_error(ctx, "Failed to send handshake reply message to child: %s", strerror(errno)); -+ } -+ -+ #ifdef __linux__ -+ struct rlimit core_limit = {}; -+ if (prlimit(pid, RLIMIT_CORE, NULL, &core_limit) < 0) { - reprl_terminate_child(ctx); -- return reprl_error(ctx, "Failed to send HELO reply message to child: %s", strerror(errno)); -+ return reprl_error(ctx, "prlimit failed: %s\n", strerror(errno)); - } -+ if (core_limit.rlim_cur != 0 || core_limit.rlim_max != 0) { -+ reprl_terminate_child(ctx); -+ return reprl_error(ctx, "Detected non-zero RLIMIT_CORE. Check that the child does not set RLIMIT_CORE manually.\n"); -+ } -+ #endif -+ -+ - - return 0; - } -@@ -326,20 +365,20 @@ int reprl_initialize_context(struct reprl_context* ctx, const char** argv, const - void reprl_destroy_context(struct reprl_context* ctx) - { - reprl_terminate_child(ctx); -- -+ - free_string_array(ctx->argv); - free_string_array(ctx->envp); -- -+ - reprl_destroy_data_channel(ctx->data_in); - reprl_destroy_data_channel(ctx->data_out); - reprl_destroy_data_channel(ctx->child_stdout); - reprl_destroy_data_channel(ctx->child_stderr); -- -+ - free(ctx->last_error); - free(ctx); - } - --int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script_length, uint64_t timeout, uint64_t* execution_time, int fresh_instance) -+int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script_length, uint64_t timeout, uint64_t* execution_time, int fresh_instance, uint32_t source_pos_dump_seed, uint8_t* jit_state) - { - if (!ctx->initialized) { - return reprl_error(ctx, "REPRL context is not initialized"); -@@ -363,11 +402,14 @@ int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script - lseek(ctx->data_in->fd, 0, SEEK_SET); - if (ctx->child_stdout) { - lseek(ctx->child_stdout->fd, 0, SEEK_SET); -+ -+ memset(ctx->child_stdout->mapping, 0, REPRL_MAX_DATA_SIZE); - } -+ - if (ctx->child_stderr) { - lseek(ctx->child_stderr->fd, 0, SEEK_SET); - } -- -+ - // Spawn a new instance if necessary. - if (!ctx->pid) { - int r = reprl_spawn_child(ctx); -@@ -379,6 +421,7 @@ int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script - - // Tell child to execute the script. - if (write(ctx->ctrl_out, "exec", 4) != 4 || -+ write(ctx->ctrl_out, &source_pos_dump_seed, sizeof(source_pos_dump_seed)) != sizeof(source_pos_dump_seed) || - write(ctx->ctrl_out, &script_length, 8) != 8) { - // These can fail if the child unexpectedly terminated between executions. - // Check for that here to be able to provide a better error message. -@@ -408,13 +451,26 @@ int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script - // We expect all signal handlers to be installed with SA_RESTART, so receiving EINTR here is unexpected and thus also an error. - return reprl_error(ctx, "Failed to poll: %s", strerror(errno)); - } -- -+ - // Poll succeeded, so there must be something to read now (either the status or EOF). -+ #pragma pack(1) -+ struct { -+ int status; -+ uint8_t jit_state; -+ } s; -+ // we only want to read 5 bytes and not care about padding -+ // just to be sure for the future -+ const size_t ssize = 5; -+ -+ -+ ssize_t rv = read(ctx->ctrl_in, &s, ssize); - int status; -- ssize_t rv = read(ctx->ctrl_in, &status, 4); -+ -+ // printf("---STDOUT---\n%s\n", reprl_fetch_stdout(ctx)); -+ // printf("---STDERR---\n%s\n", reprl_fetch_stderr(ctx)); - if (rv < 0) { - return reprl_error(ctx, "Failed to read from control pipe: %s", strerror(errno)); -- } else if (rv != 4) { -+ } else if (rv != ssize) { - // Most likely, the child process crashed and closed the write end of the control pipe. - // Unfortunately, there probably is nothing that guarantees that waitpid() will immediately succeed now, - // and we also don't want to block here. So just retry waitpid() a few times... -@@ -423,7 +479,6 @@ int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script - success = waitpid(ctx->pid, &status, WNOHANG) == ctx->pid; - if (!success) usleep(10); - } while (!success && current_usecs() - start_time < timeout); -- - if (!success) { - // Wait failed, so something weird must have happened. Maybe somehow the control pipe was closed without the child exiting? - // Probably the best we can do is kill the child and return an error. -@@ -443,7 +498,11 @@ int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script - return reprl_error(ctx, "Waitpid returned unexpected child state %i", status); - } - } -- -+ else { -+ status = s.status; -+ *jit_state = s.jit_state; -+ } -+ - // The status must be a positive number, see the status encoding format below. - // We also don't allow the child process to indicate a timeout. If we wanted, - // we could treat it as an error if the upper bits are set. -diff --git a/prepend.js b/prepend.js -new file mode 100644 -index 0000000..104c9c2 ---- /dev/null -+++ b/prepend.js -@@ -0,0 +1,282 @@ -+const prettyPrinted = function prettyPrinted(msg) { return console.log(msg); }; -+// Mock Math.random. -+(function() { -+ let index = 1; -+ Math.random = function() { -+ const x = Math.sin(index++) * 10000; -+ return x - Math.floor(x); -+ } -+})(); -+// Mock Math.pow. Work around an optimization for -0.5. -+(function() { -+ const origMathPow = Math.pow; -+ Math.pow = function(a, b) { -+ if (b === -0.5) { -+ return 0; -+ } else { -+ return origMathPow(a, b); -+ } -+ } -+})(); -+// Mock Date. -+(function() { -+ let index = 0; -+ let mockDate = 1477662728696; -+ const mockDateNow = function() { -+ index = (index + 1) % 10; -+ mockDate = mockDate + index + 1; -+ return mockDate; -+ } -+ const origDate = Date; -+ const construct = Reflect.construct; -+ const constructDate = function(args) { -+ let result; -+ if (args.length) { -+ result = construct(origDate, args); -+ } else { -+ result = new origDate(mockDateNow()); -+ } -+ result.constructor = function(...args) { return constructDate(args); } -+ Object.defineProperty( -+ result, "constructor", { configurable: false, writable: false }); -+ return result; -+ } -+ origDate.prototype.constructor = function(...args) { -+ return constructDate(args); -+ }; -+ var handler = { -+ apply: function(target, thisArg, args) { -+ return constructDate(args); -+ }, -+ construct: function(target, args, newTarget) { -+ return constructDate(args); -+ }, -+ get: function(target, property, receiver) { -+ if (property == "now") { -+ return mockDateNow; -+ } -+ if (property == "prototype") { -+ return origDate.prototype; -+ } -+ }, -+ } -+ Date = new Proxy(Date, handler); -+})(); -+// Mock this.performance. -+(function() { -+ function mockPerformanceMark(name) { -+ return {entryType: "mark", name: name, startTime: 0.0, duration: 0} -+ } -+ function mockPerformanceMeasure(name) { -+ return {entryType: "measure", name: name, startTime: 0.0, duration: 0} -+ } -+ -+ var handler = { -+ get: function(target, property, receiver) { -+ if (property == "now") { -+ return 1.1; -+ } -+ if (property == "prototype") { -+ return undefined; -+ } -+ if (property == "mark") { -+ return mockPerformanceMark; -+ } -+ if (property == "measure") { -+ return mockPerformanceMeasure; -+ } -+ if (property == "measureMemory") { -+ return []; -+ } -+ }, -+ } -+ this.performance = new Proxy(this.performance, handler); -+})(); -+// Mock readline so that test cases don't hang. -+readline = function() { return "foo"; }; -+// Mock stack traces. -+Error.prepareStackTrace = function(error, structuredStackTrace) { -+ return ""; -+}; -+Object.defineProperty( -+ Error, 'prepareStackTrace', { configurable: false, writable: false }); -+// Mock buffer access in float typed arrays because of varying NaN patterns. -+(function() { -+ const origArrayFrom = Array.from; -+ const origArrayIsArray = Array.isArray; -+ const origFunctionPrototype = Function.prototype; -+ const origIsNaN = isNaN; -+ const origIterator = Symbol.iterator; -+ const deNaNify = function(value) { return origIsNaN(value) ? 1 : value; }; -+ const mock = function(type) { -+ // Remove NaN values from parameters to "set" function. -+ const set = type.prototype.set; -+ type.prototype.set = function(array, offset) { -+ if (Array.isArray(array)) { -+ array = array.map(deNaNify); -+ } -+ set.apply(this, [array, offset]); -+ }; -+ const handler = { -+ // Remove NaN values from parameters to constructor. -+ construct: function(target, args) { -+ for (let i = 0; i < args.length; i++) { -+ if (args[i] != null && -+ typeof args[i][origIterator] === 'function') { -+ // Consume iterators. -+ args[i] = origArrayFrom(args[i]); -+ } -+ if (origArrayIsArray(args[i])) { -+ args[i] = args[i].map(deNaNify); -+ } -+ } -+ const obj = new ( -+ origFunctionPrototype.bind.call(type, null, ...args)); -+ return new Proxy(obj, { -+ get: function(x, prop) { -+ if (typeof x[prop] == "function") -+ return x[prop].bind(obj); -+ return x[prop]; -+ }, -+ // Remove NaN values that get assigned. -+ set: function(target, prop, value, receiver) { -+ target[prop] = deNaNify(value); -+ return value; -+ } -+ }); -+ }, -+ }; -+ return new Proxy(type, handler); -+ } -+ Float32Array = mock(Float32Array); -+ Float64Array = mock(Float64Array); -+})(); -+// Mock buffer access via DataViews because of varying NaN patterns. -+(function() { -+ const origIsNaN = isNaN; -+ const deNaNify = function(value) { return origIsNaN(value) ? 1 : value; }; -+ const origSetFloat32 = DataView.prototype.setFloat32; -+ DataView.prototype.setFloat32 = function(offset, value, ...rest) { -+ origSetFloat32.call(this, offset, deNaNify(value), ...rest); -+ }; -+ const origSetFloat64 = DataView.prototype.setFloat64; -+ DataView.prototype.setFloat64 = function(offset, value, ...rest) { -+ origSetFloat64.call(this, offset, deNaNify(value), ...rest); -+ }; -+})(); -+// Mock maximum typed-array buffer and limit to 1MiB. Otherwise we might -+// get range errors. We ignore those by crashing, but that reduces coverage, -+// hence, let's reduce the range-error rate. -+(function() { -+ // Math.min might be manipulated in test cases. -+ const min = Math.min; -+ const maxBytes = 1048576; -+ const mock = function(type) { -+ const maxLength = maxBytes / (type.BYTES_PER_ELEMENT || 1); -+ const handler = { -+ construct: function(target, args) { -+ if (args[0] && typeof args[0] != "object") { -+ // Length used as first argument. -+ args[0] = min(maxLength, Number(args[0])); -+ } else if (args[0] instanceof ArrayBuffer && args.length > 1) { -+ // Buffer used as first argument. -+ const buffer = args[0]; -+ args[1] = Number(args[1]); -+ // Ensure offset is multiple of bytes per element. -+ args[1] = args[1] - (args[1] % type.BYTES_PER_ELEMENT); -+ // Limit offset to length of buffer. -+ args[1] = min(args[1], buffer.byteLength || 0); -+ if (args.length > 2) { -+ // If also length is given, limit it to the maximum that's possible -+ // given buffer and offset. Avoid NaN offset turning the length -+ // NaN, too. -+ const maxBytesLeft = buffer.byteLength - (args[1] || 0); -+ const maxLengthLeft = maxBytesLeft / type.BYTES_PER_ELEMENT; -+ args[2] = min(Number(args[2]), maxLengthLeft); -+ } -+ } -+ return new (Function.prototype.bind.apply(type, [null].concat(args))); -+ }, -+ }; -+ return new Proxy(type, handler); -+ } -+ ArrayBuffer = mock(ArrayBuffer); -+ SharedArrayBuffer = mock(SharedArrayBuffer); -+ Int8Array = mock(Int8Array); -+ Uint8Array = mock(Uint8Array); -+ Uint8ClampedArray = mock(Uint8ClampedArray); -+ Int16Array = mock(Int16Array); -+ Uint16Array = mock(Uint16Array); -+ Int32Array = mock(Int32Array); -+ Uint32Array = mock(Uint32Array); -+ BigInt64Array = mock(BigInt64Array); -+ BigUint64Array = mock(BigUint64Array); -+ Float32Array = mock(Float32Array); -+ Float64Array = mock(Float64Array); -+})(); -+// Mock typed array set function and cap offset to not throw a range error. -+(function() { -+ // Math.min might be manipulated in test cases. -+ const min = Math.min; -+ const types = [ -+ Int8Array, -+ Uint8Array, -+ Uint8ClampedArray, -+ Int16Array, -+ Uint16Array, -+ Int32Array, -+ Uint32Array, -+ BigInt64Array, -+ BigUint64Array, -+ Float32Array, -+ Float64Array, -+ ]; -+ for (const type of types) { -+ const set = type.prototype.set; -+ type.prototype.set = function(array, offset) { -+ if (Array.isArray(array)) { -+ offset = Number(offset); -+ offset = min(offset, this.length - array.length); -+ } -+ set.call(this, array, offset); -+ }; -+ } -+})(); -+// Mock Worker. -+(function() { -+ let index = 0; -+ const workerMessages = [ -+ undefined, 0, -1, "", "foo", 42, [], {}, [0], {"x": 0} -+ ]; -+ Worker = function(code){ -+ try { -+ print(prettyPrinted(eval(code))); -+ } catch(e) { -+ print(prettyPrinted(e)); -+ } -+ this.getMessage = function(){ -+ index = (index + 1) % 10; -+ return workerMessages[index]; -+ } -+ this.postMessage = function(msg){ -+ print(prettyPrinted(msg)); -+ } -+ }; -+})(); -+// Mock Realm. -+Realm.eval = function(realm, code) { return eval(code) }; -+// Mock the nondeterministic parts of WeakRef and FinalizationRegistry. -+WeakRef.prototype.deref = function() { }; -+FinalizationRegistry = function(callback) { }; -+FinalizationRegistry.prototype.register = function(target, holdings) { }; -+FinalizationRegistry.prototype.unregister = function(unregisterToken) { }; -+FinalizationRegistry.prototype.cleanupSome = function() { }; -+FinalizationRegistry.prototype[Symbol.toStringTag] = "FinalizationRegistry"; -+// Mock the nondeterministic Atomics.waitAsync. -+Atomics.waitAsync = function() { -+ // Return a mock "Promise" whose "then" function will call the callback -+ // immediately. -+ return {'value': {'then': function (f) { f(); }}}; -+} -+// Mock serializer API with no-ops. -+d8.serializer = {'serialize': (x) => x, 'deserialize': (x) => x}; From 7e668c9db862eab62b85fb59e792f2e650f2a983 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Mon, 26 Jan 2026 14:38:14 +0100 Subject: [PATCH 86/89] [github] Also test the Compiler/Parser functionality Change-Id: I947059c23b71448a97b58a3f36f79f8fef0b8ff7 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8956180 Commit-Queue: Danylo Mocherniuk Auto-Submit: Matthias Liedtke Reviewed-by: Danylo Mocherniuk --- .github/workflows/swift.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 19f5536db..951fa3872 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -35,6 +35,9 @@ jobs: # Install protoc so the presubmit can also validate the generated *.pb.swift files. if: ${{ matrix.os == 'ubuntu-latest' && matrix.kind == 'debug' }} run: sudo apt install -y protobuf-compiler + - name: Install Node.js dependencies for Compiler/Parser tests + working-directory: ./Sources/Fuzzilli/Compiler/Parser + run: npm install - name: Run presubmit checks if: ${{ matrix.os == 'ubuntu-latest' && matrix.kind == 'debug' }} run: python3 Tools/presubmit.py From debc47a3f6808a18aebe17d01c8e7cb29cd42fe0 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Mon, 26 Jan 2026 17:11:34 +0100 Subject: [PATCH 87/89] Fix assertion in InliningReducer for explicit resource management This fixes https://github.com/googleprojectzero/fuzzilli/issues/545 (by just doing the same for the other kinds of disposable variables that we already do for `loadDisposableVariable`) Change-Id: I11ddb6323124deb7f99dbf110fee214be62b33a9 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8956877 Commit-Queue: Matthias Liedtke Auto-Submit: Matthias Liedtke Reviewed-by: Michael Achenbach --- Sources/Fuzzilli/Minimization/InliningReducer.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Sources/Fuzzilli/Minimization/InliningReducer.swift b/Sources/Fuzzilli/Minimization/InliningReducer.swift index c9555538d..9704f81d9 100644 --- a/Sources/Fuzzilli/Minimization/InliningReducer.swift +++ b/Sources/Fuzzilli/Minimization/InliningReducer.swift @@ -118,7 +118,10 @@ struct InliningReducer: Reducer { // Can't inline functions that are passed as arguments to other functions. deleteCandidates(instr.inputs.dropFirst()) - case .loadDisposableVariable: + case .loadDisposableVariable, + .createNamedDisposableVariable, + .loadAsyncDisposableVariable, + .createNamedAsyncDisposableVariable: // Can't inline functions that are also used as a disposable variable. deleteCandidates(instr.inputs) fallthrough From 948c97b9e4d420c51f100a861cff87a6ff23461c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Fl=C3=BCckiger?= Date: Mon, 26 Jan 2026 15:09:58 +0100 Subject: [PATCH 88/89] Add immutable ArrayBuffer support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: 450237486 Change-Id: I30de85f87ca170a998fc17a72e15c4579db37774 Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8901996 Auto-Submit: Olivier Flückiger Reviewed-by: Matthias Liedtke Commit-Queue: Matthias Liedtke --- .../Fuzzilli/Environment/JavaScriptEnvironment.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift index 921e07878..595e5f985 100644 --- a/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift +++ b/Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift @@ -1063,7 +1063,7 @@ public extension ILType { static let jsFinalizationRegistry = ILType.object(ofGroup: "FinalizationRegistry", withMethods: ["register", "unregister"]) /// Type of a JavaScript ArrayBuffer object. - static let jsArrayBuffer = ILType.object(ofGroup: "ArrayBuffer", withProperties: ["byteLength", "maxByteLength", "resizable"], withMethods: ["resize", "slice", "transfer"]) + static let jsArrayBuffer = ILType.object(ofGroup: "ArrayBuffer", withProperties: ["byteLength", "maxByteLength", "resizable"], withMethods: ["resize", "slice", "transfer", "transferToFixedLength", "transferToImmutable"]) /// Type of a JavaScript SharedArrayBuffer object. static let jsSharedArrayBuffer = ILType.object(ofGroup: "SharedArrayBuffer", withProperties: ["byteLength", "maxByteLength", "growable"], withMethods: ["grow", "slice"]) @@ -1665,9 +1665,11 @@ public extension ObjectGroup { "resizable" : .boolean, ], methods: [ - "resize" : [.integer] => .undefined, - "slice" : [.integer, .opt(.integer)] => .jsArrayBuffer, - "transfer" : [.opt(.integer)] => .jsArrayBuffer, + "resize" : [.integer] => .undefined, + "slice" : [.integer, .opt(.integer)] => .jsArrayBuffer, + "transfer" : [.opt(.integer)] => .jsArrayBuffer, + "transferToFixedLength" : [.opt(.integer)] => .jsArrayBuffer, + "transferToImmutable" : [] => .jsArrayBuffer, ] ) From 4ac378c3c06376b9c7e798fdddc228620a03c1b0 Mon Sep 17 00:00:00 2001 From: Matthias Liedtke Date: Mon, 26 Jan 2026 14:19:40 +0100 Subject: [PATCH 89/89] [parser] Support rest parameters on transpiling JS -> FuzzIL This fixes https://github.com/googleprojectzero/fuzzilli/issues/546 Change-Id: I8331dd909c05a51bfe73749e8677b18501e261bd Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8956179 Reviewed-by: Michael Achenbach Commit-Queue: Matthias Liedtke Auto-Submit: Matthias Liedtke --- Sources/Fuzzilli/Compiler/Compiler.swift | 24 +-- Sources/Fuzzilli/Compiler/Parser/parser.js | 17 +- Sources/Fuzzilli/Protobuf/ast.pb.swift | 183 ++++++++++++++---- Sources/Fuzzilli/Protobuf/ast.proto | 17 +- Tests/FuzzilliTests/CompilerTests.swift | 4 +- .../CompilerTests/rest_parameter.js | 28 +++ 6 files changed, 213 insertions(+), 60 deletions(-) create mode 100644 Tests/FuzzilliTests/CompilerTests/rest_parameter.js diff --git a/Sources/Fuzzilli/Compiler/Compiler.swift b/Sources/Fuzzilli/Compiler/Compiler.swift index 21264dc68..09999ce35 100644 --- a/Sources/Fuzzilli/Compiler/Compiler.swift +++ b/Sources/Fuzzilli/Compiler/Compiler.swift @@ -238,7 +238,8 @@ public class JavaScriptCompiler { try enterNewScope { var parameters = head.innerOutputs map("this", to: parameters.removeFirst()) - mapParameters([setter.parameter], to: parameters) + map(setter.parameter.name, to: parameters.removeFirst()) + assert(parameters.isEmpty) for statement in setter.body { try compileStatement(statement) } @@ -511,7 +512,7 @@ public class JavaScriptCompiler { emit(EndForOfLoop()) case .breakStatement: - // If we're in both .loop and .switch context, then the loop must be the most recent context + // If we're in both .loop and .switch context, then the loop must be the most recent context // (switch blocks don't propagate an outer .loop context) so we just need to check for .loop here if contextAnalyzer.context.contains(.loop){ emit(LoopBreak()) @@ -565,14 +566,14 @@ public class JavaScriptCompiler { emit(EndWith()) case .switchStatement(let switchStatement): // TODO Replace the precomputation of tests with compilation of the test expressions in the cases. - // To do this, we would need to redesign Switch statements in FuzzIL to (for example) have a BeginSwitchCaseHead, BeginSwitchCaseBody, and EndSwitchCase. + // To do this, we would need to redesign Switch statements in FuzzIL to (for example) have a BeginSwitchCaseHead, BeginSwitchCaseBody, and EndSwitchCase. // Then the expression would go inside the header. var precomputedTests = [Variable]() for caseStatement in switchStatement.cases { if caseStatement.hasTest { let test = try compileExpression(caseStatement.test) precomputedTests.append(test) - } + } } let discriminant = try compileExpression(switchStatement.discriminant) emit(BeginSwitch(), withInputs: [discriminant]) @@ -589,7 +590,7 @@ public class JavaScriptCompiler { } // We could also do an optimization here where we check if the last statement in the case is a break, and if so, we drop the last instruction // and set the fallsThrough flag to false. - emit(EndSwitchCase(fallsThrough: true)) + emit(EndSwitchCase(fallsThrough: true)) } emit(EndSwitch()) } @@ -896,7 +897,8 @@ public class JavaScriptCompiler { try enterNewScope { var parameters = instr.innerOutputs map("this", to: parameters.removeFirst()) - mapParameters([setter.parameter], to: parameters) + map(setter.parameter.name, to: parameters.removeFirst()) + assert(parameters.isEmpty) for statement in setter.body { try compileStatement(statement) } @@ -1251,15 +1253,15 @@ public class JavaScriptCompiler { scopes.top[identifier] = v } - private func mapParameters(_ parameters: [Compiler_Protobuf_Parameter], to variables: ArraySlice) { - assert(parameters.count == variables.count) - for (param, v) in zip(parameters, variables) { + private func mapParameters(_ parameters: Compiler_Protobuf_Parameters, to variables: ArraySlice) { + assert(parameters.parameters.count == variables.count) + for (param, v) in zip(parameters.parameters, variables) { map(param.name, to: v) } } - private func convertParameters(_ parameters: [Compiler_Protobuf_Parameter]) -> Parameters { - return Parameters(count: parameters.count) + private func convertParameters(_ parameters: Compiler_Protobuf_Parameters) -> Parameters { + return Parameters(count: parameters.parameters.count, hasRestParameter: parameters.hasRestElement_p) } /// Convenience accessor for the currently active scope. diff --git a/Sources/Fuzzilli/Compiler/Parser/parser.js b/Sources/Fuzzilli/Compiler/Parser/parser.js index 9d31b61a1..372af3fbc 100644 --- a/Sources/Fuzzilli/Compiler/Parser/parser.js +++ b/Sources/Fuzzilli/Compiler/Parser/parser.js @@ -84,13 +84,22 @@ function parse(script, proto) { } function visitParameter(param) { - assert(param.type == 'Identifier', "Expected parameter type to have type 'Identifier', found " + param.type); - return make('Parameter', { name: param.name }); + switch (param.type) { + case 'Identifier': + return make('Parameter', { name: param.name }); + case 'RestElement': + return make('Parameter', { name: param.argument.name }); + default: + assert(false, "Unknown parameter type: " + param.type); + } } function visitParameters(params) { - return params.map(visitParameter) - } + return make('Parameters', { + parameters: params.map(visitParameter), + hasRestElement: params.some(param => param.type === 'RestElement'), + }); + }; // Processes the body of a block statement node and returns a list of statements. function visitBody(node) { diff --git a/Sources/Fuzzilli/Protobuf/ast.pb.swift b/Sources/Fuzzilli/Protobuf/ast.pb.swift index da0503c8f..92ba974ad 100644 --- a/Sources/Fuzzilli/Protobuf/ast.pb.swift +++ b/Sources/Fuzzilli/Protobuf/ast.pb.swift @@ -252,6 +252,20 @@ public struct Compiler_Protobuf_DisposableVariableDeclaration: Sendable { public init() {} } +public struct Compiler_Protobuf_Parameters: 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: [Compiler_Protobuf_Parameter] = [] + + public var hasRestElement_p: Bool = false + + 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 @@ -261,13 +275,22 @@ public struct Compiler_Protobuf_FunctionDeclaration: Sendable { public var type: Compiler_Protobuf_FunctionType = .plain - public var parameters: [Compiler_Protobuf_Parameter] = [] + public var parameters: Compiler_Protobuf_Parameters { + get {return _parameters ?? Compiler_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 body: [Compiler_Protobuf_Statement] = [] public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} + + fileprivate var _parameters: Compiler_Protobuf_Parameters? = nil } public struct Compiler_Protobuf_PropertyKey: Sendable { @@ -358,13 +381,22 @@ public struct Compiler_Protobuf_ClassConstructor: Sendable { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - public var parameters: [Compiler_Protobuf_Parameter] = [] + public var parameters: Compiler_Protobuf_Parameters { + get {return _parameters ?? Compiler_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 body: [Compiler_Protobuf_Statement] = [] public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} + + fileprivate var _parameters: Compiler_Protobuf_Parameters? = nil } public struct Compiler_Protobuf_ClassMethod: Sendable { @@ -383,7 +415,14 @@ public struct Compiler_Protobuf_ClassMethod: Sendable { public var isStatic: Bool = false - public var parameters: [Compiler_Protobuf_Parameter] = [] + public var parameters: Compiler_Protobuf_Parameters { + get {return _parameters ?? Compiler_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 body: [Compiler_Protobuf_Statement] = [] @@ -392,6 +431,7 @@ public struct Compiler_Protobuf_ClassMethod: Sendable { public init() {} fileprivate var _key: Compiler_Protobuf_PropertyKey? = nil + fileprivate var _parameters: Compiler_Protobuf_Parameters? = nil } public struct Compiler_Protobuf_ClassGetter: Sendable { @@ -1516,7 +1556,14 @@ public struct Compiler_Protobuf_ObjectMethod: Sendable { public var type: Compiler_Protobuf_FunctionType = .plain - public var parameters: [Compiler_Protobuf_Parameter] = [] + public var parameters: Compiler_Protobuf_Parameters { + get {return _parameters ?? Compiler_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 body: [Compiler_Protobuf_Statement] = [] @@ -1529,6 +1576,8 @@ public struct Compiler_Protobuf_ObjectMethod: Sendable { } public init() {} + + fileprivate var _parameters: Compiler_Protobuf_Parameters? = nil } public struct Compiler_Protobuf_ObjectGetter: Sendable { @@ -1701,13 +1750,22 @@ public struct Compiler_Protobuf_FunctionExpression: Sendable { public var type: Compiler_Protobuf_FunctionType = .plain - public var parameters: [Compiler_Protobuf_Parameter] = [] + public var parameters: Compiler_Protobuf_Parameters { + get {return _parameters ?? Compiler_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 body: [Compiler_Protobuf_Statement] = [] public var unknownFields = SwiftProtobuf.UnknownStorage() public init() {} + + fileprivate var _parameters: Compiler_Protobuf_Parameters? = nil } public struct Compiler_Protobuf_ArrowFunctionExpression: @unchecked Sendable { @@ -1720,10 +1778,14 @@ public struct Compiler_Protobuf_ArrowFunctionExpression: @unchecked Sendable { set {_uniqueStorage()._type = newValue} } - public var parameters: [Compiler_Protobuf_Parameter] { - get {return _storage._parameters} + public var parameters: Compiler_Protobuf_Parameters { + get {return _storage._parameters ?? Compiler_Protobuf_Parameters()} set {_uniqueStorage()._parameters = newValue} } + /// Returns true if `parameters` has been explicitly set. + public var hasParameters: Bool {return _storage._parameters != nil} + /// Clears the value of `parameters`. Subsequent reads from it will return its default value. + public mutating func clearParameters() {_uniqueStorage()._parameters = nil} /// The body can either be an expression or a block statement. public var body: OneOf_Body? { @@ -2716,6 +2778,41 @@ extension Compiler_Protobuf_DisposableVariableDeclaration: SwiftProtobuf.Message } } +extension Compiler_Protobuf_Parameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".Parameters" + public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}parameters\0\u{1}hasRestElement\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.parameters) }() + case 2: try { try decoder.decodeSingularBoolField(value: &self.hasRestElement_p) }() + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.parameters.isEmpty { + try visitor.visitRepeatedMessageField(value: self.parameters, fieldNumber: 1) + } + if self.hasRestElement_p != false { + try visitor.visitSingularBoolField(value: self.hasRestElement_p, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Compiler_Protobuf_Parameters, rhs: Compiler_Protobuf_Parameters) -> Bool { + if lhs.parameters != rhs.parameters {return false} + if lhs.hasRestElement_p != rhs.hasRestElement_p {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") @@ -2728,7 +2825,7 @@ extension Compiler_Protobuf_FunctionDeclaration: SwiftProtobuf.Message, SwiftPro switch fieldNumber { case 1: try { try decoder.decodeSingularStringField(value: &self.name) }() case 2: try { try decoder.decodeSingularEnumField(value: &self.type) }() - case 3: try { try decoder.decodeRepeatedMessageField(value: &self.parameters) }() + case 3: try { try decoder.decodeSingularMessageField(value: &self._parameters) }() case 4: try { try decoder.decodeRepeatedMessageField(value: &self.body) }() default: break } @@ -2736,15 +2833,19 @@ extension Compiler_Protobuf_FunctionDeclaration: SwiftProtobuf.Message, SwiftPro } 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.name.isEmpty { try visitor.visitSingularStringField(value: self.name, fieldNumber: 1) } if self.type != .plain { try visitor.visitSingularEnumField(value: self.type, fieldNumber: 2) } - if !self.parameters.isEmpty { - try visitor.visitRepeatedMessageField(value: self.parameters, fieldNumber: 3) - } + try { if let v = self._parameters { + try visitor.visitSingularMessageField(value: v, fieldNumber: 3) + } }() if !self.body.isEmpty { try visitor.visitRepeatedMessageField(value: self.body, fieldNumber: 4) } @@ -2754,7 +2855,7 @@ extension Compiler_Protobuf_FunctionDeclaration: SwiftProtobuf.Message, SwiftPro public static func ==(lhs: Compiler_Protobuf_FunctionDeclaration, rhs: Compiler_Protobuf_FunctionDeclaration) -> Bool { if lhs.name != rhs.name {return false} if lhs.type != rhs.type {return false} - if lhs.parameters != rhs.parameters {return false} + if lhs._parameters != rhs._parameters {return false} if lhs.body != rhs.body {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true @@ -2889,7 +2990,7 @@ extension Compiler_Protobuf_ClassConstructor: SwiftProtobuf.Message, SwiftProtob // 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.parameters) }() + case 1: try { try decoder.decodeSingularMessageField(value: &self._parameters) }() case 2: try { try decoder.decodeRepeatedMessageField(value: &self.body) }() default: break } @@ -2897,9 +2998,13 @@ extension Compiler_Protobuf_ClassConstructor: SwiftProtobuf.Message, SwiftProtob } public func traverse(visitor: inout V) throws { - if !self.parameters.isEmpty { - try visitor.visitRepeatedMessageField(value: self.parameters, 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._parameters { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() if !self.body.isEmpty { try visitor.visitRepeatedMessageField(value: self.body, fieldNumber: 2) } @@ -2907,7 +3012,7 @@ extension Compiler_Protobuf_ClassConstructor: SwiftProtobuf.Message, SwiftProtob } public static func ==(lhs: Compiler_Protobuf_ClassConstructor, rhs: Compiler_Protobuf_ClassConstructor) -> Bool { - if lhs.parameters != rhs.parameters {return false} + if lhs._parameters != rhs._parameters {return false} if lhs.body != rhs.body {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true @@ -2926,7 +3031,7 @@ extension Compiler_Protobuf_ClassMethod: SwiftProtobuf.Message, SwiftProtobuf._M 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.decodeRepeatedMessageField(value: &self.parameters) }() + case 3: try { try decoder.decodeSingularMessageField(value: &self._parameters) }() case 4: try { try decoder.decodeRepeatedMessageField(value: &self.body) }() default: break } @@ -2944,9 +3049,9 @@ extension Compiler_Protobuf_ClassMethod: SwiftProtobuf.Message, SwiftProtobuf._M if self.isStatic != false { try visitor.visitSingularBoolField(value: self.isStatic, fieldNumber: 2) } - if !self.parameters.isEmpty { - try visitor.visitRepeatedMessageField(value: self.parameters, fieldNumber: 3) - } + try { if let v = self._parameters { + try visitor.visitSingularMessageField(value: v, fieldNumber: 3) + } }() if !self.body.isEmpty { try visitor.visitRepeatedMessageField(value: self.body, fieldNumber: 4) } @@ -2956,7 +3061,7 @@ extension Compiler_Protobuf_ClassMethod: SwiftProtobuf.Message, SwiftProtobuf._M public static func ==(lhs: Compiler_Protobuf_ClassMethod, rhs: Compiler_Protobuf_ClassMethod) -> Bool { if lhs._key != rhs._key {return false} if lhs.isStatic != rhs.isStatic {return false} - if lhs.parameters != rhs.parameters {return false} + if lhs._parameters != rhs._parameters {return false} if lhs.body != rhs.body {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true @@ -5378,7 +5483,7 @@ extension Compiler_Protobuf_ObjectMethod: SwiftProtobuf.Message, SwiftProtobuf._ } }() case 3: try { try decoder.decodeSingularEnumField(value: &self.type) }() - case 4: try { try decoder.decodeRepeatedMessageField(value: &self.parameters) }() + case 4: try { try decoder.decodeSingularMessageField(value: &self._parameters) }() case 5: try { try decoder.decodeRepeatedMessageField(value: &self.body) }() default: break } @@ -5404,9 +5509,9 @@ extension Compiler_Protobuf_ObjectMethod: SwiftProtobuf.Message, SwiftProtobuf._ if self.type != .plain { try visitor.visitSingularEnumField(value: self.type, fieldNumber: 3) } - if !self.parameters.isEmpty { - try visitor.visitRepeatedMessageField(value: self.parameters, fieldNumber: 4) - } + try { if let v = self._parameters { + try visitor.visitSingularMessageField(value: v, fieldNumber: 4) + } }() if !self.body.isEmpty { try visitor.visitRepeatedMessageField(value: self.body, fieldNumber: 5) } @@ -5416,7 +5521,7 @@ extension Compiler_Protobuf_ObjectMethod: SwiftProtobuf.Message, SwiftProtobuf._ public static func ==(lhs: Compiler_Protobuf_ObjectMethod, rhs: Compiler_Protobuf_ObjectMethod) -> Bool { if lhs.key != rhs.key {return false} if lhs.type != rhs.type {return false} - if lhs.parameters != rhs.parameters {return false} + if lhs._parameters != rhs._parameters {return false} if lhs.body != rhs.body {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true @@ -5735,7 +5840,7 @@ extension Compiler_Protobuf_FunctionExpression: SwiftProtobuf.Message, SwiftProt switch fieldNumber { case 1: try { try decoder.decodeSingularStringField(value: &self.name) }() case 2: try { try decoder.decodeSingularEnumField(value: &self.type) }() - case 3: try { try decoder.decodeRepeatedMessageField(value: &self.parameters) }() + case 3: try { try decoder.decodeSingularMessageField(value: &self._parameters) }() case 4: try { try decoder.decodeRepeatedMessageField(value: &self.body) }() default: break } @@ -5743,15 +5848,19 @@ extension Compiler_Protobuf_FunctionExpression: SwiftProtobuf.Message, SwiftProt } 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.name.isEmpty { try visitor.visitSingularStringField(value: self.name, fieldNumber: 1) } if self.type != .plain { try visitor.visitSingularEnumField(value: self.type, fieldNumber: 2) } - if !self.parameters.isEmpty { - try visitor.visitRepeatedMessageField(value: self.parameters, fieldNumber: 3) - } + try { if let v = self._parameters { + try visitor.visitSingularMessageField(value: v, fieldNumber: 3) + } }() if !self.body.isEmpty { try visitor.visitRepeatedMessageField(value: self.body, fieldNumber: 4) } @@ -5761,7 +5870,7 @@ extension Compiler_Protobuf_FunctionExpression: SwiftProtobuf.Message, SwiftProt public static func ==(lhs: Compiler_Protobuf_FunctionExpression, rhs: Compiler_Protobuf_FunctionExpression) -> Bool { if lhs.name != rhs.name {return false} if lhs.type != rhs.type {return false} - if lhs.parameters != rhs.parameters {return false} + if lhs._parameters != rhs._parameters {return false} if lhs.body != rhs.body {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true @@ -5774,7 +5883,7 @@ extension Compiler_Protobuf_ArrowFunctionExpression: SwiftProtobuf.Message, Swif fileprivate class _StorageClass { var _type: Compiler_Protobuf_FunctionType = .plain - var _parameters: [Compiler_Protobuf_Parameter] = [] + var _parameters: Compiler_Protobuf_Parameters? = nil var _body: Compiler_Protobuf_ArrowFunctionExpression.OneOf_Body? // This property is used as the initial default value for new instances of the type. @@ -5808,7 +5917,7 @@ extension Compiler_Protobuf_ArrowFunctionExpression: SwiftProtobuf.Message, Swif // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch fieldNumber { case 1: try { try decoder.decodeSingularEnumField(value: &_storage._type) }() - case 2: try { try decoder.decodeRepeatedMessageField(value: &_storage._parameters) }() + case 2: try { try decoder.decodeSingularMessageField(value: &_storage._parameters) }() case 3: try { var v: Compiler_Protobuf_Statement? var hadOneofValue = false @@ -5850,9 +5959,9 @@ extension Compiler_Protobuf_ArrowFunctionExpression: SwiftProtobuf.Message, Swif if _storage._type != .plain { try visitor.visitSingularEnumField(value: _storage._type, fieldNumber: 1) } - if !_storage._parameters.isEmpty { - try visitor.visitRepeatedMessageField(value: _storage._parameters, fieldNumber: 2) - } + try { if let v = _storage._parameters { + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + } }() switch _storage._body { case .block?: try { guard case .block(let v)? = _storage._body else { preconditionFailure() } diff --git a/Sources/Fuzzilli/Protobuf/ast.proto b/Sources/Fuzzilli/Protobuf/ast.proto index 3a4545cde..a3e0bffed 100644 --- a/Sources/Fuzzilli/Protobuf/ast.proto +++ b/Sources/Fuzzilli/Protobuf/ast.proto @@ -66,10 +66,15 @@ enum FunctionType { ASYNC_GENERATOR = 3; } +message Parameters { + repeated Parameter parameters = 1; + bool hasRestElement = 2; +} + message FunctionDeclaration { string name = 1; FunctionType type = 2; - repeated Parameter parameters = 3; + Parameters parameters = 3; repeated Statement body = 4; } @@ -92,14 +97,14 @@ message ClassProperty { } message ClassConstructor { - repeated Parameter parameters = 1; + Parameters parameters = 1; repeated Statement body = 2; } message ClassMethod { PropertyKey key = 1; bool isStatic = 2; - repeated Parameter parameters = 3; + Parameters parameters = 3; repeated Statement body = 4; } @@ -330,7 +335,7 @@ message ObjectMethod { Expression expression = 2; } FunctionType type = 3; - repeated Parameter parameters = 4; + Parameters parameters = 4; repeated Statement body = 5; } @@ -373,13 +378,13 @@ message FunctionExpression { // The name is optional for function expressions string name = 1; FunctionType type = 2; - repeated Parameter parameters = 3; + Parameters parameters = 3; repeated Statement body = 4; } message ArrowFunctionExpression { FunctionType type = 1; - repeated Parameter parameters = 2; + Parameters parameters = 2; // The body can either be an expression or a block statement. oneof body { Statement block = 3; diff --git a/Tests/FuzzilliTests/CompilerTests.swift b/Tests/FuzzilliTests/CompilerTests.swift index b8f017ad9..a45e52bd3 100644 --- a/Tests/FuzzilliTests/CompilerTests.swift +++ b/Tests/FuzzilliTests/CompilerTests.swift @@ -47,7 +47,7 @@ class CompilerTests: XCTestCase { // Execute the original code and record the output. let result1 = try nodejs.executeScript(at: URL(fileURLWithPath: testcasePath)) guard result1.isSuccess else { - XCTFail("Tescase \(testName) failed to execute. Output:\n\(result1.output)") + XCTFail("TestCase \(testName) failed to execute. Output:\n\(result1.output)") continue } @@ -65,7 +65,7 @@ class CompilerTests: XCTestCase { let script = lifter.lift(program) let result2 = try nodejs.executeScript(script) guard result2.isSuccess else { - XCTFail("Tescase \(testName) failed to execute after compiling and lifting. Output:\n\(result2.output)") + XCTFail("TestCase \(testName) failed to execute after compiling and lifting. Output:\n\(result2.output)\nScript:\n\(script)") continue } diff --git a/Tests/FuzzilliTests/CompilerTests/rest_parameter.js b/Tests/FuzzilliTests/CompilerTests/rest_parameter.js new file mode 100644 index 000000000..fddea0c0c --- /dev/null +++ b/Tests/FuzzilliTests/CompilerTests/rest_parameter.js @@ -0,0 +1,28 @@ +function f(x, ...rest) { + return x * rest.length; +} +console.log(f(7, 2, 3, 4)); + +// A function expression is parsed differently than a function declaration. +console.log((function funcExpression(...rest) { return rest[2]; })(null, null, 75, null)); + +let arrow = (...values) => values.length; +console.log(arrow(1, 1, 1, 1, 1, 1, 1)); +console.log(((x, y, ...rest) => rest.length)(0, 0, 1, 2, 3, 2, 1)); + +class C { + constructor(...rest) { + this.args = rest; + } + + method(...args) { + return this.args.length * args.length; + } + + static staticMethod(...rest) { + return rest.length; + } +} + +console.log(new C(1, 1, 1, 1, 1, 1).method(2, 2, 2)); +console.log(C.staticMethod(1, 1));