Skip to content
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

Distinguish between converters that are safe to call on default values and those that would fail during deserialization on default-valued fields #205

Merged
merged 26 commits into from
Nov 25, 2023

Conversation

andrewparmet
Copy link
Collaborator

@andrewparmet andrewparmet commented Oct 21, 2023

Prevents addition of fields that are marked as wrapped from breaking compiled code forwards compatibility when the wrapper type is not safe to use on an absent field. The three out-of-the-box converters that have this property are those for UUID, LocalDate (from String), and InetAddress.

Protokt now has a property that Converters use to indicate their ability to deserialize protobuf default values. Protokt attempts to instantiate each wrapper type with the default value if the wrapped field is not nullable and not marked with the nonnull annotation; i.e., non-optional scalar fields like bytes, string, int32. If that call fails, protokt will insist that the Converter declares itself unable to deserialize the default type and then marks the field as nullable and so it does not attempt to wrap the "zero" instance if the field doesn't appear on the wire.

The non_null option is now able to be applied to non-optional scalar fields in the case where they are wrapped by a converter that does not work on default values. It's a dangerous thing to do, but it's sufficiently hard to do this by accident and I could imagine a real use case for it.

This PR also inverts the type arguments for Converter since I think it's confusing for the Kotlin type to be first with the Protobuf type second when I think of it more as "wrapping from Protobuf type to Kotlin type." Open to undoing this though - it's easy to flip back and forth. Maybe renaming the type parameters is sufficient.


@AutoService(Converter::class)
@Deprecated("use LocalDateStringConverter or upgrade protokt")
object LocalDateConverter : Converter<LocalDate, String> {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed in V1; only needed for ABI backwards compatibility.

Comment on lines -72 to +80
} ?: interceptValueAccess(f, ctx, accessSize)
} ?: accessSize
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this wasn't doing anything

Comment on lines 85 to 94
fieldAccess: CodeBlock,
ctx: Context
) =
f.withWrapper(ctx) { wrapper, wrapped ->
val converter = converter(wrapper, wrapped, ctx)
if (converter.optimizedSizeof) {
CodeBlock.of("%T.sizeOf(%L)", converter.className, accessSize)
f.withWrapper(ctx) {
if (it.optimizedSizeof) {
callConverterMethod(OptimizedSizeOfConverter<Any, Any>::sizeOf, it, fieldAccess)
} else {
f.sizeOf(accessSize)
accessSize
}
} ?: f.sizeOf(accessSize)
} ?: accessSize
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tidy up this call to encapsulate sizeOf in MessageSizeGenerator

@andrewparmet andrewparmet requested a review from ogolberg October 21, 2023 18:28
@andrewparmet andrewparmet merged commit 0554c61 into open-toast:main Nov 25, 2023
@andrewparmet andrewparmet deleted the fix-wrapper-types branch November 25, 2023 15:08
ogolberg pushed a commit to ogolberg/protokt that referenced this pull request Jan 2, 2024
…s and those that would fail during deserialization on default-valued fields (open-toast#205)

Prevents addition of fields that are marked as wrapped from breaking compiled code forwards compatibility when the wrapper type is not safe to use on an absent field. The three out-of-the-box converters that have this property are those for UUID, LocalDate (from String), and InetAddress.

Protokt now has a property that Converters use to indicate their ability to deserialize protobuf default values. Protokt attempts to instantiate each wrapper type with the default value if the wrapped field is not nullable and not marked with the nonnull annotation; i.e., non-optional scalar fields like bytes, string, int32. If that call fails, protokt will insist that the Converter declares itself unable to deserialize the default type and then marks the field as nullable and so it does not attempt to wrap the "zero" instance if the field doesn't appear on the wire.

The non_null option is now able to be applied to non-optional scalar fields in the case where they are wrapped by a converter that does not work on default values. It's a dangerous thing to do, but it's sufficiently hard to do this by accident and I could imagine a real use case for it.

This PR also inverts the type arguments for Converter since I think it's confusing for the Kotlin type to be first with the Protobuf type second when I think of it more as "wrapping from Protobuf type to Kotlin type." Open to undoing this though - it's easy to flip back and forth. Maybe renaming the type parameters is sufficient.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants