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

Implementation by delegation serializes only the child proto if the interface also implements KtMessage #288

Open
andrewmellen-toast opened this issue Oct 23, 2024 · 0 comments

Comments

@andrewmellen-toast
Copy link

Description

Because of how the Kotlin compiler ends up building the output code for implementation by delegation, calling .serialize() on a proto class that implements an interface by delegation that also implements KtMessage ends up calling the wrong .serialize() method and produces an unexpected result.

Reproduction steps

syntax = "proto3"

package mypkg.v1;

import "protokt/protokt.proto";

option java_package = "com.mypkg.v1";

message ImplementerProto {
      option (protokt.class).implements = "com.mypkg.MyInterface";

      String value = 1;
}

message DelegationProto {
     option (protokt.class).implements = "com.mypkg.MyInterface by metadata";

     ImplementerProto metadata = 1;
     String more_data = 2;
}
interface MyInterface : KtMessage {
    val value: String
}

fun test() {
    val delegationProto = DelegationProto {
        metadata = ImplementerProto {
            value = "some value"
        }
        moreData = "some other value"
    }

    // Bug here: This is actually calling ImplementerProto's serialize() method, not DelegationProto's because
    // Kotlin's compiler overrides serialize() when doing the implementation by delegation
    val serializedProto = delegationProto.serialize()

    // this throws an exception
    DelegationProto.deserialize(serializedProto)

    // this succeeds
    ImplementerProto.deserialize(serializedProto)

    // doing this gets the correct value, because we're calling into serialize(KtSerializer),
    // which each generated proto class overrides explicitly
    val s = ByteArray(serializedProto.messageSize).apply {
            serializedProto.serialize(serializer(CodedOutputStream.newInstance(this)))
     }
}
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

No branches or pull requests

1 participant