-
Notifications
You must be signed in to change notification settings - Fork 447
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: replaced AnyObject with Any #725
fix: replaced AnyObject with Any #725
Conversation
The workflow has been deliberately cancelled as tests will surely fail. |
case let hashable as EIP712Hashable: | ||
result = try hashable.hash() | ||
default: | ||
/// Cast to `AnyObject` is required. Otherwise, `nil` value will fail this condition. | ||
if (field as AnyObject) is NSNull { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the only case found so far where AnyObject
is required.
field
value can be nil
even though the type it has is Any
(interesting edge case).
So checking for field == nil
will return false
if field
is actually nil
.
@@ -121,7 +121,7 @@ public protocol ContractProtocol { | |||
/// - Returns: Encoded data for a given parameters, which is should be assigned to ``CodableTransaction.data`` property | |||
func deploy(bytecode: Data, | |||
constructor: ABI.Element.Constructor?, | |||
parameters: [AnyObject]?, | |||
parameters: [Any]?, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that in some places you drop a type erasure totally, while in others you've left it, but change the target type to Any
? Within my review I assume that you do this in following logic: in case if type erasure it was removed, in case its required target type has ben set to Any
. Tell me if i'm wrong with that, resolve this otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please provide one or more examples where I did the following:
in some places you drop a type erasure totally
I'll start to self-review this PR only now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To remove any misunderstanding: the goal I had in mind is to eliminate the use AnyObject
as much as possible. I simply performed the replacement of AnyObject
with Any
, ran the tests, fixed almost all issues that occurred (except ENS tests) and opened this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AnyObject
uses bridged types and basically changes the type of the object when it can.
For example:
String
->NSString
;Int
->NSNumber
;Array
->NSArray
;struct
->__SwiftValue
;- etc.
While using Any
we preserve this information.
And this is where it gets important!
When we have Int8
, Int16
, Int..
and UInt[8-64]
these all get represented as NSNumber
if we cast them to AnyObject
. And here is an example of what will happen:
We do not want to represent Int8
as Int
or the other way around.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I've finally got the point. AnyObject is about bridging swift types to an Obj-C one, but with type erasure. Guess it's some internal apple demand at its most.
…ing protocol to classes)
# Conflicts: # Sources/Web3Core/RLP/RLP.swift # Sources/Web3Core/Transaction/Envelope/EIP1559Envelope.swift # Sources/Web3Core/Transaction/Envelope/EIP2930Envelope.swift # Sources/Web3Core/Transaction/Envelope/LegacyEnvelope.swift # Sources/web3swift/Tokens/ERC1155/Web3+ERC1155.swift # Sources/web3swift/Tokens/ERC1376/Web3+ERC1376.swift # Sources/web3swift/Tokens/ERC1400/Web3+ERC1400.swift # Sources/web3swift/Tokens/ERC1410/Web3+ERC1410.swift # Sources/web3swift/Tokens/ERC1594/Web3+ERC1594.swift # Sources/web3swift/Tokens/ERC1633/Web3+ERC1633.swift # Sources/web3swift/Tokens/ERC1643/Web3+ERC1643.swift # Sources/web3swift/Tokens/ERC1644/Web3+ERC1644.swift # Sources/web3swift/Tokens/ERC20/Web3+ERC20.swift # Sources/web3swift/Tokens/ERC721/Web3+ERC721.swift # Sources/web3swift/Tokens/ERC721x/Web3+ERC721x.swift # Sources/web3swift/Tokens/ERC777/Web3+ERC777.swift # Sources/web3swift/Tokens/ERC888/Web3+ERC888.swift # Sources/web3swift/Tokens/ST20/Web3+ST20.swift # Sources/web3swift/Tokens/ST20/Web3+SecurityToken.swift # Sources/web3swift/Utils/ENS/ENSBaseRegistrar.swift # Sources/web3swift/Utils/ENS/ENSRegistry.swift # Sources/web3swift/Utils/ENS/ENSReverseRegistrar.swift # Sources/web3swift/Utils/ENS/ETHRegistrarController.swift
…innet instead of 0
@@ -112,14 +112,13 @@ public struct RLP { | |||
return encoded.bytes[0] | |||
} | |||
|
|||
// FIXME: Make encode generic to avoid casting it's argument to [AnyObject] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed this comment as I do not see a flexible enough solution for us to implement proper generics when we operate on an array that will hold not just one generic type but multiple different types at once.
This is where Any
really shines so to speak.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the type Any
as the last resort but if we are careful with it everything should go smoothly.
@@ -77,7 +77,7 @@ public enum Networks { | |||
|
|||
static let allValues = [Mainnet, Ropsten, Kovan, Rinkeby] | |||
|
|||
public static func fromInt(_ networkID: UInt) -> Networks? { | |||
public static func fromInt(_ networkID: UInt) -> Networks { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We never really returned nil
from here or at least we do not return any nil
s now.
var inputNumber: Int = 0 | ||
var inputs = [ABI.Element.InOut]() | ||
for comp in queryItems { | ||
if let inputType = try? ABITypeParser.parseTypeString(comp.name) { | ||
guard let rawValue = comp.value, | ||
let functionArgument = await parseFunctionArgument(inputType, | ||
rawValue.trimmingCharacters(in: .whitespacesAndNewlines), | ||
chainID: code.chainID ?? 0, | ||
chainID: code.chainID, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bug was introduced here by setting default value to 0.
Now, if the chainID
is nil
the value of Networks.Mainnnet.chainID
is used in the parseFunctionArgument
function.
Note, that the issue is related only to parsing ENS addresses, and to be specifically the host part, e.g. ethereum:someaddress.eth@8834
. If we do not have the chainID
, that's the 8834
value in the example we pick 0 as default. Then verification of the someaddress.eth
fails because of that chain ID value.
I do not think this is the final correct solution.
Instead what must be thought through is the following: what is the default behaviour when chain ID is not provided? Is the Mainnet chain ID a correct default value?
I'll look into this once again to figure out if we need to change this part again.
@yaroslavyaroslav @janndriessen I'm done with this PR. Except the issue mentioned in this comment: #725 (comment) I'll push the fix for that (if there will be a need for any fixes) in a different PR to make it easier to review later. |
I guess I'm unable to review this pr because of ci/cd failure. @JeneaVranceanu |
It fixes the issue when trying to call func element() with [Any].
Haha, seem like it was too early to celebrate 😄 The issue was actually an interesting one: in To fix that once and for all we now have |
… requested/decoded - we do not assume that client is using Ethereum Mainnet! According to EIP-681: chain_id is optional and contains the decimal chain ID, such that transactions on various test- and private networks can be requested. If no chain_id is present, the client's current network setting remains effective. In our case if no chainID is present we cannot validate ENS address and will not attempt to guess the network.
About the ENS parsing issue: I've pushed an update to this PR nonetheless and below are my final conclusions. According to EIP-681: chain_id is optional and contains the decimal chain ID, such that transactions on various test- and private networks can be requested. If no chain_id is present, the client's current network setting remains effective. |
I assume that the
My personal high priority goal for 4.0 would be to move all type erased bits of code to generic one. I'd made an issue for that soonish. |
No, no, I meant exactly that. The Food for thought about generics.Generics are good but at the moment I doubt that we will be able to implement them in "the standard way" generics are used. Here is a thought exercise so to speak (and maybe while I'll be writing it down it will all result in me realising I was wrong, who knows): We have quite a lot of different data types to handle. Let's use Ha ha, I was wrong. We can use the type constraint feature of generics to account for all supported data types. 🤦♂️ Okay, so the solution roughly would be: internal static func encode<T: Web3DataType>(_ elements: [T?]) -> Data? {
...
} Where
But we still must account for nested arrays, like in the case of The solution for the nested arrays issue could be: |
Yep, this is a tough one, i mean any mixed types arrays are a pain in the ass in swift. But it's solvable in case of encoding if we are known the all possible types that we should handle, e.g. if the API are stable, which is our case here. Decoding case is even more simpler than this, again if we know what type we're expecting at the end. Nothing to say that in particular both of this cases has been solved in new network implementation which was belongs at [Any] before and belongs at generic for now. I just have not enough effort back then to refactor contract ABI in the same way, but I'd bet it would fit there as well. Upd1: also worth to say that swift 5.7 provides a solution for exact that cases (scroll to the "Unlock existentials for all protocols"), but I'd suggest to not belongs on it yet, since it's not having backward capability. But in 5.7 the following code are legal: let tvShow: [any Equatable] = ["Brooklyn", 99] Upd2: I've got a post right about that https://yaroslavyaroslav.github.io/posts/generic-protocol-workaround/ based on the solution that I've implemented whithin 3.0.0 release. Upd3: I've read it just yet once more and there's a lot of grammatical errors, sorry for that 🤷🏻♂️. |
Ah, and about to generic nested arrays, we're handling them already actually. You can look at This was one of the first serious challenge of mine within this project. Also it's funny how even here in this open-source project we're loosing our grip of knowledge of the whole codebase, in favor of focusing on some logical piece of it in. I've previously saw it in some large and well decoupled enterprise codebases, where it's certainly expected though. |
@yaroslavyaroslav An example.
In the case of RLP, the way we have it implemented expects an array that can hold at the same time single values and arrays. It's not The issue is - how would you declare such a function with generics? |
@yaroslavyaroslav I'll fix conflicts and this PR should be ready for review. |
Conflicts fixed. |
For exact this given case I suppose we have to rely on 5.7 lang features only. And there we could make a protocol with the associated value that would be in its turn provides a methods to handle a given type. But that's a tough one, there definitely would be some caveats on this path. |
Definitely worth trying. I'll open an issue for that and link our discussion there with some parts copy-pasted and modified to describe the issue and suggested potential solution. |
@yaroslavyaroslav @janndriessen PR updated, conflicts fixed. Please, when have time review this PR. |
@janndriessen Please, approve if you have no more comments. |
Summary of Changes
Dropping the use of
AnyObject
in favour ofAny
.This change was performed by simply replacing
as AnyObject
andas [AnyObject]
with empty string ``. One particular case whereAnyObject
was still required is the `EIP712Hashable` in the implementation of a `func hash()`. Due to the use of reflection, all values are bridged to Objective-C representation (that is a guess) and to check a value for nullability we have to convert `Any` to `AnyObject` and check if it `is NSNull`.WARNING: All tests pass except the ones related to ENS. Ignore that, I'll fix and post an update on this PR when I find the reason for these failures.
Test Data or Screenshots
By submitting this pull request, you are confirming the following:
develop
branch.