fuzzilli icon indicating copy to clipboard operation
fuzzilli copied to clipboard

Nicer `FunctionSignature` literals

Open saelo opened this issue 3 years ago • 4 comments

In https://github.com/googleprojectzero/fuzzilli/commit/c1a3848b0cdc713b40a326bb76f791066128837e we had to change FunctionSignature literals to look like this:

let sig = [.plain(.integer), .opt(.string), .rest(.anything)] => .integer

since they now contain a Parameter struct instead of just a Type.

However, it would be nice if we found some way (that isn't a terrible hack) to be able to write FunctionSignature literals like this again (or something similar):

let sig = [.integer, .opt(.string), .anything...] => .integer

This is just a "nice to have" though.

saelo avatar Feb 15 '22 16:02 saelo

Well, I've looked into this, but the only feasible way to do this is with classes. If you're ok with that then I'll have this implemented asap

0x41c avatar Apr 05 '22 22:04 0x41c

Oh cool, do you have a short code snippet to explain what you mean (i.e. what would have to become a class, etc.)? Thanks!

saelo avatar Apr 07 '22 12:04 saelo

I actually found a way to do it using structs. I think I tried this before and was caught up with an issue that could have been solved by making my own  init(rawValue:) initializer.


struct BaseType: OptionSet, Hashable {
    fileprivate var _parameterKind: ParameterKind
    var rawValue: UInt32 = 0
    
    
    static let integer     = BaseType(rawValue: 1 << 1)
    static let bigint      = BaseType(rawValue: 1 << 9)
    static let anything    = BaseType([.integer, .bigint])
    
    init(rawValue: UInt32) {
        self._parameterKind = .plain
        self.rawValue = rawValue
    }
}

extension BaseType {
    
    enum ParameterKind {
        case plain
        case optional
        case variadic
    }
    
    var parameterKind: ParameterKind { _parameterKind }
    
    init(rawValue: UInt32, parameterKind: ParameterKind) {
        self.init(rawValue: rawValue)
        self._parameterKind = parameterKind
    }
    
    static func opt(_ _type: BaseType) -> BaseType {
        var _type = _type
        _type._parameterKind = .optional
        return _type
    }
    
    static func variadic(_ _type: BaseType) -> BaseType {
        var _type = _type
        _type._parameterKind = .variadic
        return _type
    }
}

typealias Parameter = BaseType

struct FunctionSignature {
    var parameters: [Parameter]
    var retVal: BaseType
}

infix operator =>: AdditionPrecedence
func => (paramTypes: [Parameter], returnType: BaseType) -> FunctionSignature {
    return FunctionSignature(parameters: paramTypes, retVal: returnType)
}

postfix operator ...
postfix func ...(_type: BaseType) -> BaseType {
    var _type = _type
    _type._parameterKind = .variadic
    return _type
}

let signature: FunctionSignature = [.integer, .opt(.bigint), .anything...] => .bigint

With that out of the way, my initial concern of needing to make the BaseType struct a class has been alleviated.

Having this set up as-is would be great, but the printing functions on the ParameterType struct should be moved to the BaseType within an extension to make everything come full circle as there's really no need to have a separate struct/enum representing it.

Edit: I've updated it to include all the good postfix/infix operator goodies in there ;)

0x41c avatar Apr 08 '22 17:04 0x41c

As a suggestion as well, maybe instead of wrapping an optional parameter in opt(_:), maybe we could define an "extend to optional" postfix operator -?. Ex:

postfix operator -?
postfix func -?(_type: BaseType) -> BaseType {
    var _type = _type
    _type._parameterKind = .optional
    return _type
}

let signature: FunctionSignature = [.integer, .bigint-?, .anything...] => .bigint

It was an idea that popped into my head when adding the operators into my snippet, and I think it would be good for unifying the style as being declarative instead of functional.

0x41c avatar Apr 08 '22 18:04 0x41c

This is now fixed as a drive-by in https://github.com/googleprojectzero/fuzzilli/commit/7f2c432232c620fd5f0bdf5ea243b72ad49a1db6

This way of doing it now has some redundancy with the duplicated constructors, but this also makes it possible to forbid certain parameter types completely, e.g. .unknown, which is nice. It also still allows using union types as parameters, which is required in the JavaScriptEnvironment.swift. So probably this is good enough... It'd be nice to just have ? to express optional parameters, but I think that's impossible, and probably .opt is intuitive enough.

saelo avatar Oct 14 '22 09:10 saelo