From fde28126155d58c0e27dd32864462a9598165cbe Mon Sep 17 00:00:00 2001 From: Tobias Wienand Date: Fri, 26 Jul 2024 23:28:00 +0200 Subject: [PATCH 1/4] Implements the step JS->FuzzIL for the With-Statement --- Sources/Fuzzilli/Compiler/Compiler.swift | 10 ++ Sources/Fuzzilli/Compiler/Parser/parser.js | 6 + Sources/Fuzzilli/Protobuf/ast.pb.swift | 141 +++++++++++++++++++++ Sources/Fuzzilli/Protobuf/ast.proto | 6 + 4 files changed, 163 insertions(+) diff --git a/Sources/Fuzzilli/Compiler/Compiler.swift b/Sources/Fuzzilli/Compiler/Compiler.swift index 6f29085e0..4b9507b37 100644 --- a/Sources/Fuzzilli/Compiler/Compiler.swift +++ b/Sources/Fuzzilli/Compiler/Compiler.swift @@ -479,6 +479,16 @@ public class JavaScriptCompiler { let value = try compileExpression(throwStatement.argument) emit(ThrowException(), withInputs: [value]) + case .withStatement(let withStatement): + let object = try compileExpression(withStatement.object) + let beginWith = BeginWith() + emit(beginWith, withInputs: [object]) + try enterNewScope { + for statement in withStatement.body.blockStatement.body { + try compileStatement(statement) + } + } + emit(EndWith()) } } diff --git a/Sources/Fuzzilli/Compiler/Parser/parser.js b/Sources/Fuzzilli/Compiler/Parser/parser.js index 7c91d42c8..2029c6a57 100644 --- a/Sources/Fuzzilli/Compiler/Parser/parser.js +++ b/Sources/Fuzzilli/Compiler/Parser/parser.js @@ -320,6 +320,12 @@ function parse(script, proto) { case 'ThrowStatement': { return makeStatement('ThrowStatement', { argument: visitExpression(node.argument) }); } + case 'WithStatement': { + let withStatement = {}; + withStatement.object = visitExpression(node.object); + withStatement.body = visitStatement(node.body); + return makeStatement('WithStatement', withStatement); + } default: { throw "Unhandled node type " + node.type; } diff --git a/Sources/Fuzzilli/Protobuf/ast.pb.swift b/Sources/Fuzzilli/Protobuf/ast.pb.swift index 704c320e9..c402adfd5 100644 --- a/Sources/Fuzzilli/Protobuf/ast.pb.swift +++ b/Sources/Fuzzilli/Protobuf/ast.pb.swift @@ -875,6 +875,36 @@ public struct Compiler_Protobuf_ThrowStatement: @unchecked Sendable { fileprivate var _storage = _StorageClass.defaultInstance } +public struct Compiler_Protobuf_WithStatement: @unchecked 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 object: Compiler_Protobuf_Expression { + get {return _storage._object ?? Compiler_Protobuf_Expression()} + set {_uniqueStorage()._object = newValue} + } + /// Returns true if `object` has been explicitly set. + public var hasObject: Bool {return _storage._object != nil} + /// Clears the value of `object`. Subsequent reads from it will return its default value. + public mutating func clearObject() {_uniqueStorage()._object = nil} + + public var body: Compiler_Protobuf_Statement { + get {return _storage._body ?? Compiler_Protobuf_Statement()} + set {_uniqueStorage()._body = newValue} + } + /// Returns true if `body` has been explicitly set. + public var hasBody: Bool {return _storage._body != nil} + /// Clears the value of `body`. Subsequent reads from it will return its default value. + public mutating func clearBody() {_uniqueStorage()._body = nil} + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _storage = _StorageClass.defaultInstance +} + public struct Compiler_Protobuf_Statement: @unchecked Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -1021,6 +1051,14 @@ public struct Compiler_Protobuf_Statement: @unchecked Sendable { set {_uniqueStorage()._statement = .throwStatement(newValue)} } + public var withStatement: Compiler_Protobuf_WithStatement { + get { + if case .withStatement(let v)? = _storage._statement {return v} + return Compiler_Protobuf_WithStatement() + } + set {_uniqueStorage()._statement = .withStatement(newValue)} + } + public var unknownFields = SwiftProtobuf.UnknownStorage() public enum OneOf_Statement: Equatable, Sendable { @@ -1041,6 +1079,7 @@ public struct Compiler_Protobuf_Statement: @unchecked Sendable { case continueStatement(Compiler_Protobuf_ContinueStatement) case tryStatement(Compiler_Protobuf_TryStatement) case throwStatement(Compiler_Protobuf_ThrowStatement) + case withStatement(Compiler_Protobuf_WithStatement) } @@ -3896,6 +3935,90 @@ extension Compiler_Protobuf_ThrowStatement: SwiftProtobuf.Message, SwiftProtobuf } } +extension Compiler_Protobuf_WithStatement: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".WithStatement" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "object"), + 2: .same(proto: "body"), + ] + + fileprivate class _StorageClass { + var _object: Compiler_Protobuf_Expression? = nil + var _body: Compiler_Protobuf_Statement? = nil + + #if swift(>=5.10) + // This property is used as the initial default value for new instances of the type. + // The type itself is protecting the reference to its storage via CoW semantics. + // This will force a copy to be made of this reference when the first mutation occurs; + // hence, it is safe to mark this as `nonisolated(unsafe)`. + static nonisolated(unsafe) let defaultInstance = _StorageClass() + #else + static let defaultInstance = _StorageClass() + #endif + + private init() {} + + init(copying source: _StorageClass) { + _object = source._object + _body = source._body + } + } + + fileprivate mutating func _uniqueStorage() -> _StorageClass { + if !isKnownUniquelyReferenced(&_storage) { + _storage = _StorageClass(copying: _storage) + } + return _storage + } + + public mutating func decodeMessage(decoder: inout D) throws { + _ = _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.decodeSingularMessageField(value: &_storage._object) }() + case 2: try { try decoder.decodeSingularMessageField(value: &_storage._body) }() + default: break + } + } + } + } + + 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 + try { if let v = _storage._object { + try visitor.visitSingularMessageField(value: v, fieldNumber: 1) + } }() + try { if let v = _storage._body { + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + } }() + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Compiler_Protobuf_WithStatement, rhs: Compiler_Protobuf_WithStatement) -> Bool { + 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._object != rhs_storage._object {return false} + if _storage._body != rhs_storage._body {return false} + return true + } + if !storagesAreEqual {return false} + } + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension Compiler_Protobuf_Statement: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { public static let protoMessageName: String = _protobuf_package + ".Statement" public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ @@ -3916,6 +4039,7 @@ extension Compiler_Protobuf_Statement: SwiftProtobuf.Message, SwiftProtobuf._Mes 15: .same(proto: "continueStatement"), 16: .same(proto: "tryStatement"), 17: .same(proto: "throwStatement"), + 18: .same(proto: "withStatement"), ] fileprivate class _StorageClass { @@ -4174,6 +4298,19 @@ extension Compiler_Protobuf_Statement: SwiftProtobuf.Message, SwiftProtobuf._Mes _storage._statement = .throwStatement(v) } }() + case 18: try { + var v: Compiler_Protobuf_WithStatement? + var hadOneofValue = false + if let current = _storage._statement { + hadOneofValue = true + if case .withStatement(let m) = current {v = m} + } + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + if hadOneofValue {try decoder.handleConflictingOneOf()} + _storage._statement = .withStatement(v) + } + }() default: break } } @@ -4255,6 +4392,10 @@ extension Compiler_Protobuf_Statement: SwiftProtobuf.Message, SwiftProtobuf._Mes guard case .throwStatement(let v)? = _storage._statement else { preconditionFailure() } try visitor.visitSingularMessageField(value: v, fieldNumber: 17) }() + case .withStatement?: try { + guard case .withStatement(let v)? = _storage._statement else { preconditionFailure() } + try visitor.visitSingularMessageField(value: v, fieldNumber: 18) + }() case nil: break } } diff --git a/Sources/Fuzzilli/Protobuf/ast.proto b/Sources/Fuzzilli/Protobuf/ast.proto index 72870baca..3754b020e 100644 --- a/Sources/Fuzzilli/Protobuf/ast.proto +++ b/Sources/Fuzzilli/Protobuf/ast.proto @@ -202,6 +202,11 @@ message ThrowStatement { Expression argument = 1; } +message WithStatement { + Expression object = 1; + Statement body = 2; +} + message Statement { oneof statement { EmptyStatement emptyStatement = 1; @@ -221,6 +226,7 @@ message Statement { ContinueStatement continueStatement = 15; TryStatement tryStatement = 16; ThrowStatement throwStatement = 17; + WithStatement withStatement = 18; } } From 7ac6ae5fc777ed469413682640f33a3f7d9a40a3 Mon Sep 17 00:00:00 2001 From: Tobias Wienand Date: Sat, 27 Jul 2024 00:02:15 +0200 Subject: [PATCH 2/4] Implements two unit tests for the With-Statement --- Tests/FuzzilliTests/CompilerTests/basic_object_access.js | 8 ++++++++ Tests/FuzzilliTests/CompilerTests/basic_scoping.js | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/Tests/FuzzilliTests/CompilerTests/basic_object_access.js b/Tests/FuzzilliTests/CompilerTests/basic_object_access.js index 6a5720e73..1f300d6c7 100644 --- a/Tests/FuzzilliTests/CompilerTests/basic_object_access.js +++ b/Tests/FuzzilliTests/CompilerTests/basic_object_access.js @@ -12,3 +12,11 @@ o[2] = o[1] + o[0]; o[3] = o[2] + o[1]; output(JSON.stringify(o)); + +with(o) { + a = 3; + b = 4; + c = a + b; + } + +output(JSON.stringify(o)); \ No newline at end of file diff --git a/Tests/FuzzilliTests/CompilerTests/basic_scoping.js b/Tests/FuzzilliTests/CompilerTests/basic_scoping.js index 1c67d427c..e774dc724 100644 --- a/Tests/FuzzilliTests/CompilerTests/basic_scoping.js +++ b/Tests/FuzzilliTests/CompilerTests/basic_scoping.js @@ -16,5 +16,12 @@ function foo(x) { } output(x); output(y); + let obj = { x: 45, y: 9001 }; + with (obj) { + output(x); + output(y); + } + output(x); + output(y); } foo(44); From cf554445ff50a4690c02da366060a6e3b7fe3190 Mon Sep 17 00:00:00 2001 From: Tobias Wienand Date: Mon, 29 Jul 2024 18:09:27 +0200 Subject: [PATCH 3/4] Fixes style inconsistency in compiler switch --- Sources/Fuzzilli/Compiler/Compiler.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/Fuzzilli/Compiler/Compiler.swift b/Sources/Fuzzilli/Compiler/Compiler.swift index 4b9507b37..b464c28be 100644 --- a/Sources/Fuzzilli/Compiler/Compiler.swift +++ b/Sources/Fuzzilli/Compiler/Compiler.swift @@ -481,8 +481,7 @@ public class JavaScriptCompiler { case .withStatement(let withStatement): let object = try compileExpression(withStatement.object) - let beginWith = BeginWith() - emit(beginWith, withInputs: [object]) + emit(BeginWith(), withInputs: [object]) try enterNewScope { for statement in withStatement.body.blockStatement.body { try compileStatement(statement) From 578c61fb4e4342515cff52cb16e4ea310bcfaf92 Mon Sep 17 00:00:00 2001 From: Tobias Wienand Date: Tue, 30 Jul 2024 15:35:56 +0200 Subject: [PATCH 4/4] Changes compileStatement loop to compileBody in with-compilation --- Sources/Fuzzilli/Compiler/Compiler.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Sources/Fuzzilli/Compiler/Compiler.swift b/Sources/Fuzzilli/Compiler/Compiler.swift index b464c28be..9accca042 100644 --- a/Sources/Fuzzilli/Compiler/Compiler.swift +++ b/Sources/Fuzzilli/Compiler/Compiler.swift @@ -483,9 +483,7 @@ public class JavaScriptCompiler { let object = try compileExpression(withStatement.object) emit(BeginWith(), withInputs: [object]) try enterNewScope { - for statement in withStatement.body.blockStatement.body { - try compileStatement(statement) - } + try compileBody(withStatement.body) } emit(EndWith()) }