diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_class_generator.cc b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_class_generator.cc index 376c7aa5eec..b0052f6a53e 100644 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_class_generator.cc +++ b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_class_generator.cc @@ -12,6 +12,8 @@ namespace protobuf { namespace compiler { namespace kotlin { +class FieldGenerator; // declared in "kotlin_file_generator.h" + void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const { generateHeader(printer, isBuilder); printer->Indent(); @@ -22,7 +24,7 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const { * Also note that fields should be declared before init section. */ for (FieldGenerator *gen: properties) { - gen->generateCode(printer, isBuilder, simpleName); + gen->generateCode(printer, isBuilder); printer->Print("\n"); } @@ -54,13 +56,16 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const { generateBuilder(printer); printer->Print("\n"); - generateMergeFrom(printer); + generateMergeMethods(printer); } // build() and setters are only for builders if (isBuilder) { printer->Print("\n"); generateBuildMethod(printer); + + printer->Print("\n"); + generateParseMethods(printer); } printer->Outdent(); @@ -70,11 +75,12 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const { ClassGenerator::ClassGenerator(Descriptor const *descriptor) : descriptor(descriptor) { simpleName = descriptor->name(); + builderName = "Builder" + simpleName; int field_count = descriptor->field_count(); for (int i = 0; i < field_count; ++i) { FieldDescriptor const * fieldDescriptor = descriptor->field(i); - properties.push_back(new FieldGenerator(fieldDescriptor)); + properties.push_back(new FieldGenerator(fieldDescriptor, /* enclosingClass = */ this)); } int nested_types_count = descriptor->nested_type_count(); @@ -111,24 +117,58 @@ void ClassGenerator::generateBuilder(io::Printer * printer) const { generateCode(printer, /* isBuilder = */ true); } -void ClassGenerator::generateMergeFrom(io::Printer * printer) const { - //TODO: Looks pretty dirty. Should reconsider process of generating readFrom, mergeFrom and writeTo. +void ClassGenerator::generateMergeMethods(io::Printer *printer) const { map vars; - printer->Print(vars, "fun mergeFrom (input: CodedInputStream) {\n"); + + // mergeWith(other: Message) + printer->Print("\n"); + vars["className"] = simpleName; + printer->Print(vars, "fun mergeWith (other: $className$) {\n"); printer->Indent(); for (int i = 0; i < properties.size(); ++i) { - properties[i]->generateSerializationCode(printer, /* isRead = */ true); + vars["fieldName"] = properties[i]->simpleName; + + // concatenate repeated fields + if (properties[i]->modifier == FieldDescriptor::LABEL_REPEATED) { + printer->Print(vars, "$fieldName$.addAll(other.$fieldName$)\n"); + } + + // Bytes type is handled separately + else if (properties[i]->protoType == FieldDescriptor::TYPE_BYTES) { + vars["initValue"] = properties[i]->getInitValue(); + printer->Print(vars, "$fieldName$?.plus(other.$fieldName$ ?: $initValue$)\n"); + } + + // for all other cases just take other's field + else { + printer->Print(vars, "$fieldName$ = other.$fieldName$\n"); + } } printer->Outdent(); printer->Print("}\n"); + + + // mergeFrom(input: CodedInputStream) + printer->Print("\n"); + printer->Print(vars, "fun mergeFrom (input: CodedInputStream) {\n"); + printer->Indent(); + + vars["builderName"] = builderName; + printer->Print(vars, "val builder = $builderName$()\n"); + printer->Print("mergeWith(builder.parseFrom(input).build())"); + + printer->Outdent(); + printer->Print("}\n"); } void ClassGenerator::generateSerializers(io::Printer * printer, bool isRead) const { + // readFrom(input: CodedInputStream) OR + // writeTo(output: CodedOutputStream) map vars; vars["funName"]= isRead ? "readFrom" : "writeTo"; - vars["returnType"] = isRead ? "Builder" + simpleName : "Unit"; + vars["returnType"] = isRead ? builderName : "Unit"; vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream"; vars["arg"] = isRead ? "input" : "output"; vars["maybeSeparator"] = isRead ? "" : ", "; @@ -151,7 +191,7 @@ void ClassGenerator::generateSerializersNoTag(io::Printer *printer, bool isRead) vars["funName"]= isRead ? "readFromNoTag" : "writeToNoTag"; vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream"; vars["arg"] = isRead ? "input" : "output"; - vars["returnType"] = isRead ? "Builder" + simpleName : "Unit"; + vars["returnType"] = isRead ? builderName : "Unit"; vars["maybeReturn"] = isRead ? "return this\n" : ""; // generate function header @@ -176,14 +216,14 @@ void ClassGenerator::generateHeader(io::Printer * printer, bool isBuilder) const // build list of arguments like 'field1: Type1, field2: Type2, ... ' string argumentList = ""; for (int i = 0; i < properties.size(); ++i) { - argumentList += properties[i]->simpleName + ": " + properties[i]->fullType + " = " + properties[i]->initValue; + argumentList += properties[i]->simpleName + ": " + properties[i]->fullType + " = " + properties[i]->getInitValue(); if (i + 1 != properties.size()) { argumentList += ", "; } } map vars; - vars["name"] = (isBuilder? "Builder" : "") + simpleName; + vars["name"] = isBuilder? builderName : simpleName; vars["argumentList"] = argumentList; vars["maybePrivate"] = isBuilder? "" : " private"; printer->Print(vars, @@ -230,6 +270,69 @@ void ClassGenerator::generateInitSection(io::Printer * printer) const { printer->Print("}\n"); } +void ClassGenerator::generateParseMethods(io::Printer *printer) const { + // parseFieldFrom(input: CodedInputStream): Boolean + map vars; + vars["builderName"] = builderName; + + printer->Print("fun parseFieldFrom(input: CodedInputStream): Boolean {\n"); + printer->Indent(); + + // messages are not required to end with 0-tag, therefore parsing method should check for EOF + printer->Print("if (input.isAtEnd()) { return false }\n"); + + // read tag and check if some field will follow (0-tag inidcates end of message) + printer->Print("val tag = input.readInt32NoTag()\n"); + printer->Print("if (tag == 0) { return false } \n"); + + // parse tag into field number and wire type + printer->Print("val fieldNumber = WireFormat.getTagFieldNumber(tag)\n"); + printer->Print("val wireType = WireFormat.getTagWireType(tag)\n"); + + // 'when' to map fieldNumber into fieldName + printer->Print("when(fieldNumber) {\n"); + printer->Indent(); + + for (int i = 0; i < properties.size(); ++i) { + vars["fieldNumber"] = std::to_string(properties[i]->fieldNumber); + vars["kotlinFunSuffix"] = properties[i]->getKotlinFunctionSuffix(); + printer->Print(vars, "$fieldNumber$ -> "); + + // code for serialization arrays and messages consists of more than one line and needs enclosing brackets + if (properties[i]->modifier == FieldDescriptor::LABEL_REPEATED + || properties[i]->protoType == FieldDescriptor::TYPE_MESSAGE) { + printer->Print("{\n"); + printer->Indent(); + } + + properties[i]->generateSerializationCode(printer, /* isRead = */ true, /* noTag = */ true); + + if (properties[i]->modifier == FieldDescriptor::LABEL_REPEATED + || properties[i]->protoType == FieldDescriptor::TYPE_MESSAGE) { + printer->Outdent(); + printer->Print("}\n"); + } + } + + printer->Outdent(); + printer->Print("}\n"); // when-clause + + printer->Print("return true"); + printer->Outdent(); + printer->Print("}\n"); // parseFieldFrom body + + // parseFrom(input: CodedInputStream) + printer->Print(vars, + "fun parseFrom(input: CodedInputStream): $builderName$ {\n"); + printer->Indent(); + printer->Print("while(parseFieldFrom(input)) {}\n"); + printer->Print("return this\n"); + + printer->Outdent(); + printer->Print("}\n"); +} + + const string ClassModifier::getName() const { string result = ""; switch (type) { diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_class_generator.h b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_class_generator.h index 27eeee7409c..44a6212d5e8 100644 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_class_generator.h +++ b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_class_generator.h @@ -16,6 +16,8 @@ namespace protobuf { namespace compiler { namespace kotlin { +class FieldGenerator; // declared in "kotlin_file_generator.h" + // wrapper for enum CLASS/INTERFACE with convenience method of getting name class ClassModifier { public: @@ -36,7 +38,7 @@ public: vector properties; vector classesDeclarations; vector enumsDeclaraions; - + string builderName; ClassGenerator (Descriptor const * descriptor); ~ClassGenerator (); @@ -62,7 +64,8 @@ private: */ void generateSerializersNoTag(io::Printer *printer, bool isRead = false) const; void generateSerializers(io::Printer * printer, bool isRead = false) const; - void generateMergeFrom(io::Printer * printer) const; + void generateMergeMethods(io::Printer *printer) const; + void generateParseMethods(io::Printer * printer) const; }; } // namespace kotlin diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_field_generator.cc b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_field_generator.cc index 7f7f13d78f2..be6ee75f6e2 100644 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_field_generator.cc +++ b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_field_generator.cc @@ -45,6 +45,7 @@ string FieldGenerator::protobufToKotlinField() const { /** * Simply maps protobuf field type to corresponding Kotlin. */ +// TODO: refactor in separate static namespace string FieldGenerator::protobufToKotlinType() const { FieldDescriptor::Type type = descriptor->type(); switch(type) { @@ -87,14 +88,8 @@ string FieldGenerator::protobufToKotlinType() const { } } -string FieldGenerator::getInitValue() const { - if (descriptor->is_repeated()) - #ifndef KOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW - return "mutableListOf()"; - #else - return "arrayOf()"; - #endif - +// TODO: refactor in separate static +string FieldGenerator::protobufTypeToInitValue(FieldDescriptor const * descriptor) const { FieldDescriptor::Type type = descriptor->type(); switch(type) { case FieldDescriptor::TYPE_BOOL: @@ -135,8 +130,18 @@ string FieldGenerator::getInitValue() const { return "0L"; // see notes for TYPE_FIXED64 } } +string FieldGenerator::getInitValue() const { + if (descriptor->is_repeated()) + #ifndef KOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW + return "mutableListOf()"; + #else + return "arrayOf()"; + #endif -void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder, string className) const { + return protobufTypeToInitValue(descriptor); +} + +void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder) const { map vars; vars["name"] = simpleName; vars["field"] = protobufToKotlinField(); @@ -149,30 +154,35 @@ void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder, string c // generate setter for builder if (isBuilder) { - generateSetter(printer, /* builderName = */ "Builder" + className); + generateSetter(printer); } // generate additional methods for repeated fields if (modifier == FieldDescriptor::LABEL_REPEATED) { - generateRepeatedMethods(printer, isBuilder, /* builderName = */ "Builder" + className); + generateRepeatedMethods(printer, isBuilder); } } -FieldGenerator::FieldGenerator(FieldDescriptor const * descriptor) +FieldGenerator::FieldGenerator(FieldDescriptor const * descriptor, ClassGenerator const * enclosingClass) : descriptor(descriptor) , modifier(descriptor->label()) + , enclosingClass(enclosingClass) , simpleName(descriptor->name()) , underlyingType(protobufToKotlinType()) , fullType(protobufToKotlinField()) - , initValue(getInitValue()) + , protoType(descriptor->type()) + , fieldNumber(descriptor->number()) { } +// TODO: long, complicated and messy method. Refactor it ASAP void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead, bool noTag) const { map vars; - vars["type"] = protobufTypeToKotlinFunctionSuffix(descriptor->type()); - vars["fieldNumber"] = std::to_string(descriptor->number()); + vars["type"] = protobufTypeToKotlinFunctionSuffix(descriptor->type()) + (noTag ? "NoTag" : ""); + vars["fieldNumber"] = std::to_string(fieldNumber); + vars["maybeFieldNumber"] = noTag ? "" : std::to_string(fieldNumber); vars["fieldName"] = simpleName; vars["arg"] = isRead ? "input" : "output"; + vars["maybeComma"] = ", "; /** * First of all, try to generate syntax for repeated fields because it's separate case. @@ -180,56 +190,74 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead * - Check if size of array is > 0, because empty repeated fields shouldn't appear in message * - Write tag explicitly * - Write length as int32 (note that tag shouldn't be added) - * - Write all repeated elements via recursive call + * - Write all repeated elements via recursive call (again, without tags) */ if (modifier == FieldDescriptor::LABEL_REPEATED) { - printer->Print(vars, "if ($fieldName$.size > 0) {\n"); - printer->Indent(); - // tag if (isRead) { - //TODO: dirty stub here! Normally, reading from input should be delegated to Parsers, with proper error handling and etc. - //Currently tag is ignored, and work of the library relies heavily on the field order guarantees. - //Thus, backward-compability and extensions are not supported. - printer->Print(vars, "val tag = input.readTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n"); + if (!noTag) { + printer->Print(vars, "val tag = input.readTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n"); + } printer->Print(vars, "val listSize = input.readInt32NoTag()\n"); printer->Print(vars, "for (i in 1..listSize) {\n"); printer->Indent(); - printer->Print(vars, "$fieldName$[i - 1].mergeFrom(input)\n"); + + /* hack: copy current FieldGenerator and change label to OPTIONAL. Also change name to + name of iterator in for-loop. + This will allow to re-use this function for generating serialization code for elements of array. + More importantly, this will care about nested types too. + Efficiently, it inlines serialization code for all underlying types. + This hack isn't necessary from the architectural point of view and could be safely + removed as soon as target code will support inheritance and interfaces. + (then writing CodedOutputStream.writeMessage will be possible). + */ + FieldGenerator singleFieldGen = FieldGenerator(descriptor, enclosingClass); + + /* Another dirty hack here: create tmp variable of a given type and read it from input stream + then add that tmp var into list. + This is made because simple recursive call will generate code, that tries to array[i].mergeFrom() + but this is incorrect, because array has old size, while 'i' iterates over new size, and of course + they are not necessary the same. + */ + // TODO: stub here, resolve name properly! + vars["fieldType"] = underlyingType + ".Builder" + underlyingType; + vars["initValue"] = getUnderlyingTypeInitValue(); + printer->Print(vars, "val tmp: $fieldType$ = $fieldType$()\n"); + singleFieldGen.simpleName = "tmp"; + singleFieldGen.modifier = FieldDescriptor::LABEL_OPTIONAL; + singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ true); + + printer->Print(vars, "$fieldName$.add(tmp.build())\n"); + printer->Outdent(); printer->Print("}\n"); } else { + printer->Print(vars, "if ($fieldName$.size > 0) {\n"); + printer->Indent(); + // tag printer->Print(vars, "output.writeTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n"); // length printer->Print(vars, "output.writeInt32NoTag($fieldName$.size)\n"); - printer->Print(vars, "output.writeInt32NoTag($fieldName$.size)\n"); // all elements printer->Print(vars, "for (item in $fieldName$) {\n"); printer->Indent(); - /* hack: copy current FieldGenerator and change label to OPTIONAL. This will allow - to re-use this function for generating serialization code for elements of array. - More importantly, this will care about nested types too. - Efficiently, it inlines serialization code for all underlying types. - This hack isn't necessary from the architectural point of view and could be safely5 - removed as soon as target code will support inheritance and interfaces. - (then writing CodedOutputStream.writeMessage will be possible). - */ - FieldGenerator singleFieldGen = FieldGenerator(descriptor); + // hack: see above + FieldGenerator singleFieldGen = FieldGenerator(descriptor, enclosingClass); + singleFieldGen.simpleName = "item"; singleFieldGen.modifier = FieldDescriptor::LABEL_OPTIONAL; singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ true); printer->Outdent(); // for-loop printer->Print("}\n"); + + printer->Outdent(); // if-clause + printer->Print("}\n"); } - - printer->Outdent(); // if-clause - printer->Print("}\n"); - return; } @@ -244,10 +272,10 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead if (descriptor->type() == FieldDescriptor::TYPE_ENUM) { vars["converter"] = underlyingType + ".fromIntTo" + underlyingType; if (isRead) { - printer->Print(vars, "$fieldName$ = $converter$(input.read$type$($fieldNumber$))\n"); + printer->Print(vars, "$fieldName$ = $converter$(input.read$type$($maybeFieldNumber$))\n"); } else { - printer->Print(vars, "output.write$type$ ($fieldNumber$, $fieldName$?.ord)\n"); + printer->Print(vars, "output.write$type$ ($maybeFieldNumber$$maybeComma$$fieldName$?.ord)\n"); } return; } @@ -257,17 +285,13 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead that message */ if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) { - vars["fieldName"] = noTag ? "item" : simpleName; - vars["maybeNoTag"] = noTag ? "NoTag" : ""; - vars["fieldNumber"] = std::to_string(descriptor->number()); + vars["maybeNoTag"] = "NoTag"; if (isRead) { printer->Print(vars, - "val tag = input.readTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n" "$fieldName$.readFrom$maybeNoTag$(input)\n"); } else { printer->Print(vars, - "output.writeTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n" "$fieldName$.writeTo$maybeNoTag$(output)\n"); } return; @@ -275,13 +299,14 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead /* Finally, serialize trivial cases */ if (isRead) { - printer->Print(vars, "$fieldName$ = input.read$type$($fieldNumber$)\n"); + printer->Print(vars, "$fieldName$ = input.read$type$($maybeFieldNumber$)\n"); } else { - printer->Print(vars, "output.write$type$ ($fieldNumber$, $fieldName$)\n"); + printer->Print(vars, "output.write$type$ ($maybeFieldNumber$$maybeComma$$fieldName$)\n"); } } +// TODO: refactor in separate static class string FieldGenerator::protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type type) const { switch (type) { case FieldDescriptor::TYPE_DOUBLE: @@ -321,11 +346,11 @@ string FieldGenerator::protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type } } -void FieldGenerator::generateSetter(io::Printer *printer, string builderName) const { +void FieldGenerator::generateSetter(io::Printer *printer) const { map vars; vars["camelCaseName"] = name_resolving::makeFirstLetterUpper(simpleName); vars["fieldName"] = simpleName; - vars["builderName"] = builderName; + vars["builderName"] = enclosingClass->builderName; vars["type"] = fullType; printer->Print(vars, "fun set$camelCaseName$(value: $type$): $builderName$ {\n"); @@ -337,12 +362,12 @@ void FieldGenerator::generateSetter(io::Printer *printer, string builderName) co printer->Print("}\n"); } -void FieldGenerator::generateRepeatedMethods(io::Printer * printer, bool isBuilder, string builderName) const { +void FieldGenerator::generateRepeatedMethods(io::Printer * printer, bool isBuilder) const { map vars; vars["elementType"] = underlyingType; vars["arg"] = "value"; vars["fieldName"] = simpleName; - vars["builderName"] = builderName; + vars["builderName"] = enclosingClass->builderName; // generate indexed setter for builders if (isBuilder) { @@ -382,6 +407,13 @@ void FieldGenerator::generateRepeatedMethods(io::Printer * printer, bool isBuild #endif } +string FieldGenerator::getKotlinFunctionSuffix() const { + return protobufTypeToKotlinFunctionSuffix(descriptor->type()); +} + +string FieldGenerator::getUnderlyingTypeInitValue() const { + return protobufTypeToInitValue(descriptor); +} } // namespace kotlin } // namespace compiler diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_field_generator.h b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_field_generator.h index 3e61f4beea4..f8381924dff 100644 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_field_generator.h +++ b/proto/compiler/google/src/google/protobuf/compiler/kotlin/src/kotlin_field_generator.h @@ -8,19 +8,22 @@ #include #include #include +#include "kotlin_class_generator.h" namespace google { namespace protobuf { namespace compiler { namespace kotlin { +class ClassGenerator; // declared in kotlin_class_generator.h + class FieldGenerator { private: FieldDescriptor const * descriptor; string protobufToKotlinField() const; string protobufToKotlinType () const; - string getInitValue() const; + // TODO: refactor from field generator to some static utility namespace /** * Converts one of protobuf wire types to corresponding Kotlin type with proper * naming, so it could be used as suffix after read/write, resulting in function @@ -30,10 +33,13 @@ private: * CodedInputStream.readSFixed32(fieldNumber: Int) */ string protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type type) const; - void generateSetter(io::Printer * printer, string builderName) const; - void generateRepeatedMethods(io::Printer * printer, bool isBuilder, string builderName) const; + string protobufTypeToInitValue(FieldDescriptor const * descriptor) const; + + void generateSetter(io::Printer * printer) const; + void generateRepeatedMethods(io::Printer * printer, bool isBuilder) const; public: FieldDescriptor::Label modifier; + ClassGenerator const * enclosingClass; // class, in which that field is defined string simpleName; string underlyingType; // unwrapped type. @@ -44,11 +50,15 @@ public: * fullType = underlyingType for all other cases */ string fullType; + string getInitValue() const; + string getUnderlyingTypeInitValue() const; + FieldDescriptor::Type protoType; + int fieldNumber; - string initValue; - void generateCode(io::Printer * printer, bool isBuilder, string className) const; + void generateCode(io::Printer * printer, bool isBuilder) const; void generateSerializationCode(io::Printer * printer, bool isRead = false, bool noTag = false) const; - FieldGenerator(FieldDescriptor const * descriptor); + FieldGenerator(FieldDescriptor const * descriptor, ClassGenerator const * enclosingClass); + string getKotlinFunctionSuffix() const; }; diff --git a/proto/compiler/google/src/google/protobuf/compiler/kotlin/test/addressbook.kt b/proto/compiler/google/src/google/protobuf/compiler/kotlin/test/addressbook.kt index 8f90ec5b6f0..2ef32e3b811 100644 --- a/proto/compiler/google/src/google/protobuf/compiler/kotlin/test/addressbook.kt +++ b/proto/compiler/google/src/google/protobuf/compiler/kotlin/test/addressbook.kt @@ -94,12 +94,33 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email fun build(): PhoneNumber { return PhoneNumber(number, type) } + + fun parseFieldFrom(input: CodedInputStream): Boolean { + if (input.isAtEnd()) { return false } + val tag = input.readInt32NoTag() + if (tag == 0) { return false } + val fieldNumber = WireFormat.getTagFieldNumber(tag) + val wireType = WireFormat.getTagWireType(tag) + when(fieldNumber) { + 1 -> number = input.readStringNoTag() + 2 -> type = PhoneType.fromIntToPhoneType(input.readEnumNoTag()) + } + return true} + fun parseFrom(input: CodedInputStream): BuilderPhoneNumber { + while(parseFieldFrom(input)) {} + return this + } + } + + + fun mergeWith (other: PhoneNumber) { + number = other.number + type = other.type } fun mergeFrom (input: CodedInputStream) { - number = input.readString(1) - type = PhoneType.fromIntToPhoneType(input.readEnum(2)) - } + val builder = BuilderPhoneNumber() + mergeWith(builder.parseFrom(input).build())} } @@ -114,9 +135,7 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email if (phones.size > 0) { output.writeTag(4, WireType.LENGTH_DELIMITED) output.writeInt32NoTag(phones.size) - output.writeInt32NoTag(phones.size) for (item in phones) { - output.writeTag(4, WireType.LENGTH_DELIMITED) item.writeToNoTag(output) } } @@ -190,12 +209,12 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email name = input.readString(1) id = input.readInt32(2) email = input.readString(3) - if (phones.size > 0) { - val tag = input.readTag(4, WireType.LENGTH_DELIMITED) - val listSize = input.readInt32NoTag() - for (i in 1..listSize) { - phones[i - 1].mergeFrom(input) - } + val tag = input.readTag(4, WireType.LENGTH_DELIMITED) + val listSize = input.readInt32NoTag() + for (i in 1..listSize) { + val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber() + tmp.readFromNoTag(input) + phones.add(tmp.build()) } someBytes = input.readBytes(5) return this @@ -204,21 +223,46 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email fun build(): Person { return Person(name, id, email, phones, someBytes) } + + fun parseFieldFrom(input: CodedInputStream): Boolean { + if (input.isAtEnd()) { return false } + val tag = input.readInt32NoTag() + if (tag == 0) { return false } + val fieldNumber = WireFormat.getTagFieldNumber(tag) + val wireType = WireFormat.getTagWireType(tag) + when(fieldNumber) { + 1 -> name = input.readStringNoTag() + 2 -> id = input.readInt32NoTag() + 3 -> email = input.readStringNoTag() + 4 -> { + val listSize = input.readInt32NoTag() + for (i in 1..listSize) { + val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber() + tmp.readFromNoTag(input) + phones.add(tmp.build()) + } + } + 5 -> someBytes = input.readBytesNoTag() + } + return true} + fun parseFrom(input: CodedInputStream): BuilderPerson { + while(parseFieldFrom(input)) {} + return this + } + } + + + fun mergeWith (other: Person) { + name = other.name + id = other.id + email = other.email + phones.addAll(other.phones) + someBytes?.plus(other.someBytes ?: ByteArray(0)) } fun mergeFrom (input: CodedInputStream) { - name = input.readString(1) - id = input.readInt32(2) - email = input.readString(3) - if (phones.size > 0) { - val tag = input.readTag(4, WireType.LENGTH_DELIMITED) - val listSize = input.readInt32NoTag() - for (i in 1..listSize) { - phones[i - 1].mergeFrom(input) - } - } - someBytes = input.readBytes(5) - } + val builder = BuilderPerson() + mergeWith(builder.parseFrom(input).build())} } @@ -239,9 +283,7 @@ class AddressBook private constructor (people: MutableList = mutableLi if (people.size > 0) { output.writeTag(1, WireType.LENGTH_DELIMITED) output.writeInt32NoTag(people.size) - output.writeInt32NoTag(people.size) for (item in people) { - output.writeTag(1, WireType.LENGTH_DELIMITED) item.writeToNoTag(output) } } @@ -279,12 +321,12 @@ class AddressBook private constructor (people: MutableList = mutableLi } fun readFromNoTag (input: CodedInputStream): BuilderAddressBook { - if (people.size > 0) { - val tag = input.readTag(1, WireType.LENGTH_DELIMITED) - val listSize = input.readInt32NoTag() - for (i in 1..listSize) { - people[i - 1].mergeFrom(input) - } + val tag = input.readTag(1, WireType.LENGTH_DELIMITED) + val listSize = input.readInt32NoTag() + for (i in 1..listSize) { + val tmp: Person.BuilderPerson = Person.BuilderPerson() + tmp.readFromNoTag(input) + people.add(tmp.build()) } return this } @@ -292,17 +334,38 @@ class AddressBook private constructor (people: MutableList = mutableLi fun build(): AddressBook { return AddressBook(people) } + + fun parseFieldFrom(input: CodedInputStream): Boolean { + if (input.isAtEnd()) { return false } + val tag = input.readInt32NoTag() + if (tag == 0) { return false } + val fieldNumber = WireFormat.getTagFieldNumber(tag) + val wireType = WireFormat.getTagWireType(tag) + when(fieldNumber) { + 1 -> { + val listSize = input.readInt32NoTag() + for (i in 1..listSize) { + val tmp: Person.BuilderPerson = Person.BuilderPerson() + tmp.readFromNoTag(input) + people.add(tmp.build()) + } + } + } + return true} + fun parseFrom(input: CodedInputStream): BuilderAddressBook { + while(parseFieldFrom(input)) {} + return this + } + } + + + fun mergeWith (other: AddressBook) { + people.addAll(other.people) } fun mergeFrom (input: CodedInputStream) { - if (people.size > 0) { - val tag = input.readTag(1, WireType.LENGTH_DELIMITED) - val listSize = input.readInt32NoTag() - for (i in 1..listSize) { - people[i - 1].mergeFrom(input) - } - } - } + val builder = BuilderAddressBook() + mergeWith(builder.parseFrom(input).build())} } diff --git a/proto/compiler/src/CodedInputStream.kt b/proto/compiler/src/CodedInputStream.kt index d60c3ddc290..b565df64a9c 100644 --- a/proto/compiler/src/CodedInputStream.kt +++ b/proto/compiler/src/CodedInputStream.kt @@ -234,7 +234,7 @@ class CodedInputStream(input: java.io.InputStream) { val actualFieldNumber = WireFormat.getTagFieldNumber(tag) val actualWireType = WireFormat.getTagWireType(tag) - checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.LENGTH_DELIMITED, actualWireType) + checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, expectedWireType, actualWireType) return tag } diff --git a/proto/compiler/src/PersonMessage.kt b/proto/compiler/src/PersonMessage.kt index cde8860f6a1..d582f0bf9f7 100644 --- a/proto/compiler/src/PersonMessage.kt +++ b/proto/compiler/src/PersonMessage.kt @@ -94,12 +94,33 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email fun build(): PhoneNumber { return PhoneNumber(number, type) } + + fun parseFieldFrom(input: CodedInputStream): Boolean { + if (input.isAtEnd()) { return false } + val tag = input.readInt32NoTag() + if (tag == 0) { return false } + val fieldNumber = WireFormat.getTagFieldNumber(tag) + val wireType = WireFormat.getTagWireType(tag) + when(fieldNumber) { + 1 -> number = input.readStringNoTag() + 2 -> type = PhoneType.fromIntToPhoneType(input.readEnumNoTag()) + } + return true} + fun parseFrom(input: CodedInputStream): BuilderPhoneNumber { + while(parseFieldFrom(input)) {} + return this + } + } + + + fun mergeWith (other: PhoneNumber) { + number = other.number + type = other.type } fun mergeFrom (input: CodedInputStream) { - number = input.readString(1) - type = PhoneType.fromIntToPhoneType(input.readEnum(2)) - } + val builder = BuilderPhoneNumber() + mergeWith(builder.parseFrom(input).build())} } @@ -114,9 +135,7 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email if (phones.size > 0) { output.writeTag(4, WireType.LENGTH_DELIMITED) output.writeInt32NoTag(phones.size) - output.writeInt32NoTag(phones.size) for (item in phones) { - output.writeTag(4, WireType.LENGTH_DELIMITED) item.writeToNoTag(output) } } @@ -190,12 +209,12 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email name = input.readString(1) id = input.readInt32(2) email = input.readString(3) - if (phones.size > 0) { - val tag = input.readTag(4, WireType.LENGTH_DELIMITED) - val listSize = input.readInt32NoTag() - for (i in 1..listSize) { - phones[i - 1].mergeFrom(input) - } + val tag = input.readTag(4, WireType.LENGTH_DELIMITED) + val listSize = input.readInt32NoTag() + for (i in 1..listSize) { + val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber() + tmp.readFromNoTag(input) + phones.add(tmp.build()) } someBytes = input.readBytes(5) return this @@ -204,21 +223,46 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email fun build(): Person { return Person(name, id, email, phones, someBytes) } + + fun parseFieldFrom(input: CodedInputStream): Boolean { + if (input.isAtEnd()) { return false } + val tag = input.readInt32NoTag() + if (tag == 0) { return false } + val fieldNumber = WireFormat.getTagFieldNumber(tag) + val wireType = WireFormat.getTagWireType(tag) + when(fieldNumber) { + 1 -> name = input.readStringNoTag() + 2 -> id = input.readInt32NoTag() + 3 -> email = input.readStringNoTag() + 4 -> { + val listSize = input.readInt32NoTag() + for (i in 1..listSize) { + val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber() + tmp.readFromNoTag(input) + phones.add(tmp.build()) + } + } + 5 -> someBytes = input.readBytesNoTag() + } + return true} + fun parseFrom(input: CodedInputStream): BuilderPerson { + while(parseFieldFrom(input)) {} + return this + } + } + + + fun mergeWith (other: Person) { + name = other.name + id = other.id + email = other.email + phones.addAll(other.phones) + someBytes?.plus(other.someBytes ?: ByteArray(0)) } fun mergeFrom (input: CodedInputStream) { - name = input.readString(1) - id = input.readInt32(2) - email = input.readString(3) - if (phones.size > 0) { - val tag = input.readTag(4, WireType.LENGTH_DELIMITED) - val listSize = input.readInt32NoTag() - for (i in 1..listSize) { - phones[i - 1].mergeFrom(input) - } - } - someBytes = input.readBytes(5) - } + val builder = BuilderPerson() + mergeWith(builder.parseFrom(input).build())} } @@ -239,9 +283,7 @@ class AddressBook private constructor (people: MutableList = mutableLi if (people.size > 0) { output.writeTag(1, WireType.LENGTH_DELIMITED) output.writeInt32NoTag(people.size) - output.writeInt32NoTag(people.size) for (item in people) { - output.writeTag(1, WireType.LENGTH_DELIMITED) item.writeToNoTag(output) } } @@ -279,12 +321,12 @@ class AddressBook private constructor (people: MutableList = mutableLi } fun readFromNoTag (input: CodedInputStream): BuilderAddressBook { - if (people.size > 0) { - val tag = input.readTag(1, WireType.LENGTH_DELIMITED) - val listSize = input.readInt32NoTag() - for (i in 1..listSize) { - people[i - 1].mergeFrom(input) - } + val tag = input.readTag(1, WireType.LENGTH_DELIMITED) + val listSize = input.readInt32NoTag() + for (i in 1..listSize) { + val tmp: Person.BuilderPerson = Person.BuilderPerson() + tmp.readFromNoTag(input) + people.add(tmp.build()) } return this } @@ -292,17 +334,38 @@ class AddressBook private constructor (people: MutableList = mutableLi fun build(): AddressBook { return AddressBook(people) } + + fun parseFieldFrom(input: CodedInputStream): Boolean { + if (input.isAtEnd()) { return false } + val tag = input.readInt32NoTag() + if (tag == 0) { return false } + val fieldNumber = WireFormat.getTagFieldNumber(tag) + val wireType = WireFormat.getTagWireType(tag) + when(fieldNumber) { + 1 -> { + val listSize = input.readInt32NoTag() + for (i in 1..listSize) { + val tmp: Person.BuilderPerson = Person.BuilderPerson() + tmp.readFromNoTag(input) + people.add(tmp.build()) + } + } + } + return true} + fun parseFrom(input: CodedInputStream): BuilderAddressBook { + while(parseFieldFrom(input)) {} + return this + } + } + + + fun mergeWith (other: AddressBook) { + people.addAll(other.people) } fun mergeFrom (input: CodedInputStream) { - if (people.size > 0) { - val tag = input.readTag(1, WireType.LENGTH_DELIMITED) - val listSize = input.readInt32NoTag() - for (i in 1..listSize) { - people[i - 1].mergeFrom(input) - } - } - } + val builder = BuilderAddressBook() + mergeWith(builder.parseFrom(input).build())} } diff --git a/proto/compiler/src/TestMessages.kt b/proto/compiler/src/TestMessages.kt index c2d25d0f9af..34c152bc394 100644 --- a/proto/compiler/src/TestMessages.kt +++ b/proto/compiler/src/TestMessages.kt @@ -16,14 +16,20 @@ fun testMessageSerialization() { .setEmail("wtf@dasda.com") // all setters return this builder, so you could chain modifiers in LINQ-style .setId(42) .setName("John Doe") - .setPhones(arrayOf( // repeated fields stored as Array<>, so use arrayOf() for creating repeated fields + .addPhoneNumber( // repeated fields stored as Array<>, so use arrayOf() for creating repeated fields Person.PhoneNumber.BuilderPhoneNumber() .setNumber("342143-23423-42") .setType(Person.PhoneType.HOME) .build() - )) + ) + .addPhoneNumber( + Person.PhoneNumber.BuilderPhoneNumber() + .setNumber("8-800-555-35-35") + .setType(Person.PhoneType.WORK) + .build() + ) .build() // don't forget to call build() to produce message - msg.writeTo(outs) + msg.writeToNoTag(outs) // Now let's use output stream as input to read our message from it! var ins = CodedInputStream(ByteArrayInputStream(s.toByteArray()))