Protobuf: refactored FieldGenerator interface heavily. Now it offers much more clearier interface for names getting (separating simple names from full-qualified names)

This commit is contained in:
dsavvinov
2016-07-20 15:33:11 +03:00
parent 5d58ddd8bb
commit bee4c839d2
12 changed files with 322 additions and 233 deletions
@@ -23,9 +23,14 @@ $(BINDIR):
mkdir -p $(BINDIR)
clean:
rm -rf $(BINDIR) $(EXE)
rm -rf $(BINDIR)
generate:
./protoc --kotlin_out=$$HOME/Downloads/carkot/proto/compiler/src ./test/addressbook.proto
./protoc --kotlin_out=$$HOME/Downloads/carkot/proto/compiler/src ./test/nested-msg.proto
.PHONY: clean all generate
debug:
gdb --args ./protoc --kotlin_out=$$HOME/Downloads/carkot/proto/compiler/src ./test/addressbook.proto
gdb --args ./protoc --kotlin_out=$$HOME/Downloads/carkot/proto/compiler/src ./test/nested-msg.proto
.PHONY: clean all generate debug
Binary file not shown.
@@ -0,0 +1,19 @@
//
// Created by user on 7/20/16.
//
#include "UnreachableStateException.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace kotlin {
UnreachableStateException::UnreachableStateException(std::string const & what)
: std::logic_error(what)
{ }
} // namespace kotlin
} // namespace compiler
} // namespace protobuf
} // namespace google
@@ -0,0 +1,28 @@
//
// Created by user on 7/20/16.
//
#ifndef GOOGLE_UNREACHABLESTATEEXCEPTION_H
#define GOOGLE_UNREACHABLESTATEEXCEPTION_H
#include <exception>
#include <stdexcept>
namespace google {
namespace protobuf {
namespace compiler {
namespace kotlin {
class UnreachableStateException : public std::logic_error {
public:
UnreachableStateException(std::string const & what);
};
} // namespace kotlin
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif //GOOGLE_UNREACHABLESTATEEXCEPTION_H
@@ -80,8 +80,6 @@ ClassGenerator::ClassGenerator(Descriptor const *descriptor, NameResolver * name
: descriptor(descriptor)
, nameResolver(nameResolver)
{
simpleName = descriptor->name();
int field_count = descriptor->field_count();
for (int i = 0; i < field_count; ++i) {
FieldDescriptor const * fieldDescriptor = descriptor->field(i);
@@ -91,14 +89,14 @@ ClassGenerator::ClassGenerator(Descriptor const *descriptor, NameResolver * name
int nested_types_count = descriptor->nested_type_count();
for (int i = 0; i < nested_types_count; ++i) {
Descriptor const * nestedClassDescriptor = descriptor->nested_type(i);
nameResolver->addClass(nestedClassDescriptor->name(), nameResolver->getClassName(simpleName));
nameResolver->addClass(nestedClassDescriptor->name(), getFullType());
classesDeclarations.push_back(new ClassGenerator(nestedClassDescriptor, nameResolver));
}
int enums_declarations_count = descriptor->enum_type_count();
for (int i = 0; i < enums_declarations_count; ++i) {
EnumDescriptor const * nestedEnumDescriptor = descriptor->enum_type(i);
nameResolver->addClass(nestedEnumDescriptor->name(), nameResolver->getClassName(simpleName));
nameResolver->addClass(nestedEnumDescriptor->name(), getFullType());
enumsDeclaraions.push_back(new EnumGenerator(nestedEnumDescriptor));
}
@@ -109,7 +107,7 @@ ClassGenerator::ClassGenerator(Descriptor const *descriptor, NameResolver * name
*/
std::sort(properties.begin(), properties.end(),
[](FieldGenerator const * first, FieldGenerator const * second) {
return first->fieldNumber < second->fieldNumber;
return first->getFieldNumber() < second->getFieldNumber();
});
}
@@ -137,7 +135,7 @@ void ClassGenerator::generateMergeMethods(io::Printer *printer) const {
// mergeWith(other: Message)
printer->Print("\n");
vars["className"] = simpleName;
vars["className"] = getFullType();
printer->Print(vars, "fun mergeWith (other: $className$) {\n");
printer->Indent();
@@ -145,12 +143,12 @@ void ClassGenerator::generateMergeMethods(io::Printer *printer) const {
vars["fieldName"] = properties[i]->simpleName;
// concatenate repeated fields
if (properties[i]->modifier == FieldDescriptor::LABEL_REPEATED) {
if (properties[i]->getProtoLabel() == FieldDescriptor::LABEL_REPEATED) {
printer->Print(vars, "$fieldName$.addAll(other.$fieldName$)\n");
}
// Bytes type is handled separately
else if (properties[i]->protoType == FieldDescriptor::TYPE_BYTES) {
else if (properties[i]->getProtoType() == FieldDescriptor::TYPE_BYTES) {
vars["initValue"] = properties[i]->getInitValue();
printer->Print(vars, "$fieldName$.plus(other.$fieldName$)\n");
}
@@ -170,7 +168,7 @@ void ClassGenerator::generateMergeMethods(io::Printer *printer) const {
printer->Print(vars, "fun mergeFrom (input: CodedInputStream) {\n");
printer->Indent();
vars["builderName"] = nameResolver->getBuilderName(simpleName);
vars["builderName"] = getBuilderFullType();
printer->Print(vars, "val builder = $builderName$()\n");
printer->Print("mergeWith(builder.parseFrom(input).build())");
@@ -184,7 +182,7 @@ void ClassGenerator::generateSerializers(io::Printer *printer, bool isRead) cons
vars["funName"]= isRead ? "readFrom" : "writeTo";
vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream";
vars["arg"] = isRead ? "input" : "output";
vars["returnType"] = isRead ? nameResolver->getBuilderName(simpleName) : "Unit";
vars["returnType"] = isRead ? getBuilderFullType() : "Unit";
vars["maybeReturn"] = isRead ? "return this\n" : "";
// generate function header
@@ -209,14 +207,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]->getInitValue();
argumentList += properties[i]->simpleName + ": " + properties[i]->getFullType() + " = " + properties[i]->getInitValue();
if (i + 1 != properties.size()) {
argumentList += ", ";
}
}
map<string, string> vars;
vars["name"] = isBuilder? "Builder" + simpleName : simpleName;
vars["name"] = isBuilder? getBuidlerSimpleType() : getSimpleType();
vars["argumentList"] = argumentList;
vars["maybePrivate"] = isBuilder? "" : " private";
printer->Print(vars,
@@ -227,7 +225,7 @@ void ClassGenerator::generateHeader(io::Printer * printer, bool isBuilder) const
void ClassGenerator::generateBuildMethod(io::Printer * printer) const {
map <string, string> vars;
vars["returnType"] = simpleName;
vars["returnType"] = getFullType();
printer->Print(vars,
"fun build(): $returnType$ {\n");
printer->Indent();
@@ -266,7 +264,7 @@ void ClassGenerator::generateInitSection(io::Printer * printer) const {
void ClassGenerator::generateParseMethods(io::Printer *printer) const {
// parseFieldFrom(input: CodedInputStream): Boolean
map <string, string> vars;
vars["builderName"] = nameResolver->getBuilderName(simpleName);
vars["builderName"] = getBuilderFullType();
printer->Print("fun parseFieldFrom(input: CodedInputStream): Boolean {\n");
printer->Indent();
@@ -287,21 +285,21 @@ void ClassGenerator::generateParseMethods(io::Printer *printer) const {
printer->Indent();
for (int i = 0; i < properties.size(); ++i) {
vars["fieldNumber"] = std::to_string(properties[i]->fieldNumber);
vars["fieldNumber"] = std::to_string(properties[i]->getFieldNumber());
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) {
if (properties[i]->getProtoLabel() == FieldDescriptor::LABEL_REPEATED
|| properties[i]->getProtoType() == 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) {
if (properties[i]->getProtoLabel() == FieldDescriptor::LABEL_REPEATED
|| properties[i]->getProtoType() == FieldDescriptor::TYPE_MESSAGE) {
printer->Outdent();
printer->Print("}\n");
}
@@ -339,36 +337,20 @@ void ClassGenerator::generateGetSizeMethod(io::Printer *printer) const {
printer->Print("}\n");
}
//int ClassGenerator::getSizeWithoutHeader() {
// int size = 0;
// for (int i = 0; i < properties.size(); ++i) {
// size += properties[i].getSizeWithHeader();
// }
// return 0;
//}
const string ClassModifier::getName() const {
string result = "";
switch (type) {
case CLASS:
result = "class";
break;
case INTERFACE:
result = "interface";
break;
}
return result;
string ClassGenerator::getSimpleType() const {
return descriptor->name();
}
ClassModifier::ClassModifier(ClassModifier::Type type)
: type(type)
{ }
string ClassGenerator::getFullType() const {
return nameResolver->getClassName(getSimpleType());
}
ClassModifier::ClassModifier() {
type = CLASS;
string ClassGenerator::getBuilderFullType() const {
return nameResolver->getBuilderName(getSimpleType());
}
string ClassGenerator::getBuidlerSimpleType() const {
return "Builder" + getSimpleType();
}
@@ -20,24 +20,13 @@ namespace kotlin {
class FieldGenerator; // declared in "kotlin_file_generator.h"
class NameResolver; // declared in "kotlin_name_resolver.h"
// wrapper for enum CLASS/INTERFACE with convenience method of getting name
class ClassModifier {
public:
enum Type {
CLASS,
INTERFACE
};
ClassModifier();
ClassModifier(Type type);
Type type;
string const getName() const;
};
class ClassGenerator {
public:
string simpleName;
string fullName;
string getSimpleType() const;
string getFullType() const;
string getBuidlerSimpleType() const;
string getBuilderFullType() const;
vector <FieldGenerator *> properties;
vector <ClassGenerator *> classesDeclarations;
vector <EnumGenerator *> enumsDeclaraions;
@@ -7,6 +7,7 @@
#include <google/protobuf/io/printer.h>
#include <google/protobuf/descriptor.h>
#include "kotlin_name_resolver.h"
#include "UnreachableStateException.h"
#include <iostream>
namespace google {
@@ -15,13 +16,22 @@ namespace compiler {
namespace kotlin {
string FieldGenerator::getInitValue() const {
return name_resolving::protobufTypeToInitValue(this);
if (getProtoLabel() == FieldDescriptor::LABEL_REPEATED) {
return "mutableListOf()";
}
if (getProtoType() == FieldDescriptor::TYPE_MESSAGE) {
return getBuilderFullType() + "().build()";
}
if (getProtoType() == FieldDescriptor::TYPE_ENUM) {
return getEnumFromIntConverter() + "(0)";
}
return name_resolving::protobufTypeToInitValue(getProtoType());
}
void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
map<string, string> vars;
vars["name"] = simpleName;
vars["field"] = name_resolving::protobufToKotlinField(descriptor);
vars["field"] = getFullType();
printer->Print(vars, "var $name$ : $field$\n");
// make setter private
@@ -35,29 +45,25 @@ void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
}
// generate additional methods for repeated fields
if (modifier == FieldDescriptor::LABEL_REPEATED) {
if (getProtoLabel() == FieldDescriptor::LABEL_REPEATED) {
generateRepeatedMethods(printer, isBuilder);
}
}
FieldGenerator::FieldGenerator(FieldDescriptor const * descriptor, ClassGenerator const * enclosingClass, NameResolver * nameResolver)
: descriptor(descriptor)
, modifier(descriptor->label())
, enclosingClass(enclosingClass)
, simpleName(descriptor->name())
, underlyingType(name_resolving::protobufToKotlinType(descriptor))
, fullType(name_resolving::protobufToKotlinField(descriptor))
, protoType(descriptor->type())
, fieldNumber(descriptor->number())
, nameResolver(nameResolver)
, simpleName(descriptor->name())
, protoLabel(descriptor->label())
{ }
// TODO: long, complicated and messy method. Refactor it ASAP
void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead, bool noTag) const {
map <string, string> vars;
vars["type"] = name_resolving::protobufTypeToKotlinFunctionSuffix(descriptor->type()) + (noTag ? "NoTag" : "");
vars["fieldNumber"] = std::to_string(fieldNumber);
vars["maybeFieldNumber"] = noTag ? "" : std::to_string(fieldNumber);
vars["type"] = getKotlinFunctionSuffix() + (noTag ? "NoTag" : "");
vars["fieldNumber"] = std::to_string(getFieldNumber());
vars["maybeFieldNumber"] = noTag ? "" : std::to_string(getFieldNumber());
vars["fieldName"] = simpleName;
vars["arg"] = isRead ? "input" : "output";
vars["maybeComma"] = ", ";
@@ -70,7 +76,7 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
* - Write length as int32 (note that tag shouldn't be added)
* - Write all repeated elements via recursive call (again, without tags)
*/
if (modifier == FieldDescriptor::LABEL_REPEATED) {
if (getProtoLabel() == FieldDescriptor::LABEL_REPEATED) {
// tag
if (isRead) {
if (!noTag) {
@@ -90,7 +96,7 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
removed as soon as target code will support inheritance and interfaces.
(then writing CodedOutputStream.writeMessage will be possible).
*/
FieldGenerator singleFieldGen = FieldGenerator(descriptor, enclosingClass, nameResolver);
FieldGenerator singleFieldGen = getUnderlyingTypeGenerator();
/* Another dirty hack here: create tmp variable of a given type and read it from input stream
then add that tmp var into list.
@@ -99,11 +105,11 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
to ArrayOutOfIndex errors.
*/
// TODO: stub here, resolve name properly!
vars["fieldType"] = underlyingType + ".Builder" + underlyingType;
vars["initValue"] = underlyingType + ".Builder" + underlyingType + "()";
printer->Print(vars, "val tmp: $fieldType$ = $initValue$\n");
vars["builderType"] = getUnderlyingTypeGenerator().getFullType();
vars["initValue"] = getUnderlyingTypeGenerator().getInitValue();
printer->Print(vars, "val tmp: $builderType$ = $initValue$\n");
singleFieldGen.simpleName = "tmp";
singleFieldGen.modifier = FieldDescriptor::LABEL_OPTIONAL;
singleFieldGen.protoLabel = FieldDescriptor::LABEL_OPTIONAL;
// Note that primitive types are packed by default in proto3, i.e. they are should be written without tag
bool isPrimitive = descriptor->type() != FieldDescriptor::TYPE_BYTES &&
@@ -112,9 +118,9 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
descriptor->type() != FieldDescriptor::TYPE_ENUM;
singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ isPrimitive);
singleFieldGen.generateSizeEstimationCode(printer, "readSize"); // add size of current element to total size
singleFieldGen.generateSizeEstimationCode(printer, /* varName = */ "readSize"); // add size of current element to total size
printer->Print(vars, "$fieldName$.add(tmp.build())\n");
printer->Print(vars, "$fieldName$.add(tmp)\n");
printer->Outdent();
printer->Print("}\n");
@@ -138,7 +144,7 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
// hack: see above
FieldGenerator singleFieldGen = FieldGenerator(descriptor, enclosingClass, nameResolver);
singleFieldGen.simpleName = "item";
singleFieldGen.modifier = FieldDescriptor::LABEL_OPTIONAL;
singleFieldGen.protoLabel = FieldDescriptor::LABEL_OPTIONAL;
// TODO: maybe refactor this in name_resolving or separate method at least
// Note that primitive types are packed by default in proto3, i.e. they are should be written without tag
@@ -167,7 +173,7 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
Example: output.writeEnum(42, enumField.ord)
*/
if (descriptor->type() == FieldDescriptor::TYPE_ENUM) {
vars["converter"] = underlyingType + ".fromIntTo" + underlyingType;
vars["converter"] = getEnumFromIntConverter();
if (isRead) {
printer->Print(vars, "$fieldName$ = $converter$(input.read$type$($maybeFieldNumber$))\n");
}
@@ -185,7 +191,7 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
*/
if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) {
if (isRead) {
vars["fieldNumber"] = std::to_string(fieldNumber);
vars["fieldNumber"] = std::to_string(getFieldNumber());
vars["dollar"] = "$";
// read tag
@@ -205,7 +211,7 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
"\") }\n");
}
else {
vars["fieldNumber"] = std::to_string(fieldNumber);
vars["fieldNumber"] = std::to_string(getFieldNumber());
// write tag
printer->Print(vars, "output.writeTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n");
@@ -233,8 +239,8 @@ void FieldGenerator::generateSetter(io::Printer *printer) const {
map <string, string> vars;
vars["camelCaseName"] = name_resolving::makeFirstLetterUpper(simpleName);
vars["fieldName"] = simpleName;
vars["builderName"] = nameResolver->getBuilderName(enclosingClass->simpleName);
vars["type"] = fullType;
vars["builderName"] = enclosingClass->getBuilderFullType();
vars["type"] = getFullType();
printer->Print(vars,
"fun set$camelCaseName$(value: $type$): $builderName$ {\n");
printer->Indent();
@@ -247,10 +253,10 @@ void FieldGenerator::generateSetter(io::Printer *printer) const {
void FieldGenerator::generateRepeatedMethods(io::Printer * printer, bool isBuilder) const {
map <string, string> vars;
vars["elementType"] = underlyingType;
vars["elementType"] = getUnderlyingTypeGenerator().getSimpleType();
vars["arg"] = "value";
vars["fieldName"] = simpleName;
vars["builderName"] = nameResolver->getBuilderName(underlyingType); // TODO: call to non-existent field in map.
vars["builderName"] = enclosingClass->getBuilderFullType(); // TODO: call to non-existent field in map.
// generate indexed setter for builders
if (isBuilder) {
@@ -292,21 +298,14 @@ string FieldGenerator::getKotlinFunctionSuffix() const {
return name_resolving::protobufTypeToKotlinFunctionSuffix(descriptor->type());
}
string FieldGenerator::getUnderlyingTypeInitValue() const {
if (protoType == FieldDescriptor::TYPE_MESSAGE) {
return "Builder" + underlyingType + "().build()";
}
return name_resolving::protobufTypeToInitValue(this);
}
void FieldGenerator::generateSizeEstimationCode(io::Printer *printer, string varName, bool noTag) const {
map<string, string> vars;
vars["varName"] = varName;
vars["fieldName"] = simpleName;
vars["fieldNumber"] = std::to_string(fieldNumber);
vars["fieldNumber"] = std::to_string(getFieldNumber());
// First of all, generate code for repeated fields
if (modifier == FieldDescriptor::LABEL_REPEATED) {
if (getProtoLabel() == FieldDescriptor::LABEL_REPEATED) {
// We will need total byte size of array, because that size is itself a part of the message and
// adds to total message size.
// For the sake of hygiene, temporary variables are created in anonymous scope
@@ -322,7 +321,7 @@ void FieldGenerator::generateSizeEstimationCode(io::Printer *printer, string var
// hack: reuse generateSizeEstimationCode in the same manner as in generateSerializationCode
FieldGenerator singleFieldGen = FieldGenerator(descriptor, enclosingClass, nameResolver);
singleFieldGen.modifier = FieldDescriptor::LABEL_OPTIONAL;
singleFieldGen.protoLabel = FieldDescriptor::LABEL_OPTIONAL;
singleFieldGen.simpleName = "item";
singleFieldGen.generateSizeEstimationCode(printer, "arraySize");
@@ -348,7 +347,7 @@ void FieldGenerator::generateSizeEstimationCode(io::Printer *printer, string var
// Then, call getSize recursively for nested messages
// TODO: currently suboptimal repeatative calls getSize() are being made. We can optimize it later via caching calls to getSize()
if (protoType == FieldDescriptor::TYPE_MESSAGE) {
if (getProtoType() == FieldDescriptor::TYPE_MESSAGE) {
// don't forget about tag and length annotation
printer->Print(vars, "$varName$ += $fieldName$.getSize()"
" + "
@@ -360,18 +359,82 @@ void FieldGenerator::generateSizeEstimationCode(io::Printer *printer, string var
}
// Next, process enums as they should be casted to ints manually
if (protoType == FieldDescriptor::TYPE_ENUM) {
vars["enumName"] = fullType;
if (getProtoType() == FieldDescriptor::TYPE_ENUM) {
printer->Print(vars, "$varName$ += WireFormat.getEnumSize($fieldNumber$, $fieldName$.ord)\n");
return;
}
// Finally, get size of all primitive types trivially via call to WireFormat in runtime
vars["kotlinSuffix"] = name_resolving::protobufTypeToKotlinFunctionSuffix(descriptor->type());
vars["kotlinSuffix"] = getKotlinFunctionSuffix();
printer->Print(vars, "$varName$ += WireFormat.get$kotlinSuffix$Size($fieldNumber$, $fieldName$)\n");
return;
}
FieldDescriptor::Label FieldGenerator::getProtoLabel() const {
return protoLabel;
}
FieldDescriptor::Type FieldGenerator::getProtoType() const {
return descriptor->type();
}
int FieldGenerator::getFieldNumber() const {
return descriptor->number();
}
string FieldGenerator::getSimpleType() const {
if (getProtoLabel() == FieldDescriptor::LABEL_REPEATED) {
return "MutableList <" + getUnderlyingTypeGenerator().getSimpleType() + ">";
}
if (getProtoType() == FieldDescriptor::TYPE_MESSAGE) {
return descriptor->message_type()->name();
}
if (getProtoType() == FieldDescriptor::TYPE_ENUM) {
return descriptor->enum_type()->name();
}
return name_resolving::protobufToKotlinType(descriptor->type());
}
string FieldGenerator::getFullType() const {
if (getProtoLabel() == FieldDescriptor::LABEL_REPEATED) {
return "MutableList <" + getUnderlyingTypeGenerator().getFullType() + ">";
}
if (getProtoType() == FieldDescriptor::TYPE_MESSAGE ||
getProtoType() == FieldDescriptor::TYPE_ENUM) {
return nameResolver->getClassName(getSimpleType());
}
return name_resolving::protobufToKotlinType(getProtoType());
}
string FieldGenerator::getBuilderFullType() const {
if (getProtoType() != FieldDescriptor::TYPE_MESSAGE) {
throw UnreachableStateException("Error: trying to get builder name for non-message field " + simpleName);
}
return nameResolver->getBuilderName(getSimpleType());
}
string FieldGenerator::getBuilderSimpleType() const {
if (getProtoType() != FieldDescriptor::TYPE_MESSAGE) {
throw UnreachableStateException("Error: trying to get builder name for non-message field " + simpleName);
}
return "Builder" + getSimpleType();
}
string FieldGenerator::getEnumFromIntConverter() const {
return getFullType() + ".fromIntTo" + getSimpleType();
}
FieldGenerator FieldGenerator::getUnderlyingTypeGenerator() const {
if (getProtoLabel() == FieldDescriptor::LABEL_REPEATED) {
FieldGenerator singleFieldGen = FieldGenerator(descriptor, enclosingClass, nameResolver);
singleFieldGen.protoLabel = FieldDescriptor::LABEL_OPTIONAL;
return singleFieldGen;
}
return *this;
}
} // namespace kotlin
} // namespace compiler
} // namespace protobuf
@@ -27,32 +27,58 @@ private:
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.
/**
* Full type of field.
* fullType = Array<underlyingType> for REPEATED fields
* fullType = underlyingType? for OPTIONAL fields
* fullType = underlyingType for all other cases
*/
string fullType;
string builderName;
string fullName;
string getInitValue() const;
string getUnderlyingTypeInitValue() const;
FieldDescriptor::Type protoType;
int fieldNumber;
NameResolver * nameResolver;
string simpleName;
FieldDescriptor::Label protoLabel; // TODO: hack here - this field is used for some dark magic that allows us to drop generics from the generated code
FieldDescriptor::Label getProtoLabel() const;
FieldDescriptor::Type getProtoType() const;
/* Return declared tag number */
int getFieldNumber() const;
/* Returns instance of FieldGenerator, that generated underlying type for repeated fields.
* For non-repeated fields, returns `this` */
FieldGenerator getUnderlyingTypeGenerator() const;
/* For repeated fields, returns simple name of single element.
* For all other cases, returns simple name of field, which is the same as getType()
*/
/* For repeated fields, return simple name of single element, wrapped into corresponding Kotlin array type
* Example: Array<NestedMessage>
* For other types, return simple name (without full-qualification for non-primitive types) of field's type
*/
string getSimpleType() const;
string getBuilderSimpleType() const;
/* Returns full=qualified name of builder if field type is user-defined message */
string getBuilderFullType() const;
/* Returns the same as getType(), but with full-qualification for non-primitive types if necessary.
* Example: Array<EnclosingMessage.NestedMessage>
*/
string getFullType() const;
/* Returns initial value of this field's type.
* Note that full qualification for non-primitive types will always used here.
*/
string getInitValue() const;
/* Return string, that is suitable as suffix for corresponding IO methods in ProtoKot runtime.
* Example: int64-field -> Int64 (readInt64() and writeInt64() methods exist in ProtoKot runtime)
*/
string getKotlinFunctionSuffix() const;
/* Return function name in enum namespace that converts from enum to Int */
string getEnumFromIntConverter() const;
void generateCode(io::Printer * printer, bool isBuilder) const;
void generateSerializationCode(io::Printer * printer, bool isRead = false, bool noTag = false) const;
void generateSizeEstimationCode(io::Printer * printer, string varName, bool noTag = false) const;
FieldGenerator(FieldDescriptor const * descriptor, ClassGenerator const * enclosingClass, NameResolver * nameResolver);
string getKotlinFunctionSuffix() const;
};
} // namespace kotlin
@@ -5,7 +5,7 @@
#include "kotlin_name_resolver.h"
#include "kotlin_field_generator.h"
#include <google/protobuf/descriptor.h>
#include "UnreachableStateException.h"
#include <string>
namespace google {
@@ -33,8 +33,7 @@ string getKotlinOutputByProtoName(string protoName) {
return justName + ".kt";
}
string protobufToKotlinType(FieldDescriptor const * descriptor) {
FieldDescriptor::Type type = descriptor->type();
string protobufToKotlinType(FieldDescriptor::Type type) {
switch(type) {
case FieldDescriptor::TYPE_BOOL:
return "Boolean";
@@ -43,7 +42,8 @@ string protobufToKotlinType(FieldDescriptor const * descriptor) {
case FieldDescriptor::TYPE_DOUBLE:
return "Double";
case FieldDescriptor::TYPE_ENUM:
return string(descriptor->enum_type()->name());
throw UnreachableStateException
("Error: mapping protobuf enum types to kotlin types should be resolved by field generator, not by protobufToKotlinType function");
case FieldDescriptor::TYPE_FIXED32:
// we map uint32 into Int, storing top bit in sign bit
return "Int";
@@ -57,7 +57,8 @@ string protobufToKotlinType(FieldDescriptor const * descriptor) {
case FieldDescriptor::TYPE_INT64:
return "Long";
case FieldDescriptor::TYPE_MESSAGE:
return string(descriptor->message_type()->name());
throw UnreachableStateException
("Error: mapping protobuf message types to kotlin types should be resolved by field generator, not by protobufToKotlinType function");
case FieldDescriptor::TYPE_SFIXED32:
return "Int";
case FieldDescriptor::TYPE_SFIXED64:
@@ -67,7 +68,7 @@ string protobufToKotlinType(FieldDescriptor const * descriptor) {
case FieldDescriptor::TYPE_SINT64:
return "Long";
case FieldDescriptor::TYPE_STRING:
return "kotlin.String";
return "String";
case FieldDescriptor::TYPE_UINT32:
return "Int"; // see notes for TYPE_FIXED32
case FieldDescriptor::TYPE_UINT64:
@@ -75,30 +76,9 @@ string protobufToKotlinType(FieldDescriptor const * descriptor) {
}
}
string protobufToKotlinField(FieldDescriptor const * descriptor) {
FieldDescriptor::Label modifier = descriptor->label();
string preamble = "",
postamble = "";
switch (modifier) {
case FieldDescriptor::LABEL_REQUIRED:
break;
case FieldDescriptor::LABEL_OPTIONAL:
break;
case FieldDescriptor::LABEL_REPEATED:
preamble = "MutableList <";
postamble = "> ";
break;
}
return preamble + protobufToKotlinType(descriptor) + postamble;
}
// TODO: think about nested arrays
string protobufTypeToInitValue(FieldGenerator const * fieldGen) {
if (fieldGen->modifier == FieldDescriptor::LABEL_REPEATED) {
return "mutableListOf()";
}
FieldDescriptor::Type type = fieldGen->protoType;
string protobufTypeToInitValue(FieldDescriptor::Type type) {
switch(type) {
case FieldDescriptor::TYPE_BOOL:
return "false";
@@ -106,9 +86,8 @@ string protobufTypeToInitValue(FieldGenerator const * fieldGen) {
return "ByteArray(0)";
case FieldDescriptor::TYPE_DOUBLE:
return "0.0";
case FieldDescriptor::TYPE_ENUM: {
return fieldGen->nameResolver->getClassName(fieldGen->fullType) + ".fromIntTo" + fieldGen->fullType + "(0)"; // produce enum from 0, as demanded by Google
}
case FieldDescriptor::TYPE_ENUM:
throw UnreachableStateException("Error: getting init values of enums should be handled by FieldGenerator, not by protobufToInitValue");
case FieldDescriptor::TYPE_FIXED32:
return "0";
case FieldDescriptor::TYPE_FIXED64:
@@ -120,7 +99,7 @@ string protobufTypeToInitValue(FieldGenerator const * fieldGen) {
case FieldDescriptor::TYPE_INT64:
return "0L";
case FieldDescriptor::TYPE_MESSAGE:
return fieldGen->nameResolver->getBuilderName(fieldGen->fullType) + "().build()";
throw UnreachableStateException("Error: getting init values of enums should be handled by FieldGenerator, not by protobufToInitValue");
case FieldDescriptor::TYPE_SFIXED32:
return "0";
case FieldDescriptor::TYPE_SFIXED64:
@@ -37,11 +37,9 @@ std::string getFileNameWithoutExtension(std::string fullName);
std::string getKotlinOutputByProtoName(std::string protoName);
std::string protobufTypeToInitValue(FieldGenerator const * fieldGenerator);
std::string protobufTypeToInitValue(FieldDescriptor::Type type);
std::string protobufToKotlinField(FieldDescriptor const * descriptor);
std::string protobufToKotlinType(FieldDescriptor const * descriptor);
std::string protobufToKotlinType(FieldDescriptor::Type type);
/**
* Converts one of protobuf wire types to corresponding Kotlin type with proper
+43 -43
View File
@@ -1,14 +1,14 @@
class Person private constructor (name: kotlin.String = "", id: Int = 0, email: kotlin.String = "", phones: MutableList <PhoneNumber> = mutableListOf(), someBytes: ByteArray = ByteArray(0)) {
var name : kotlin.String
class Person private constructor (name: String = "", id: Int = 0, email: String = "", phones: MutableList <Person.PhoneNumber> = mutableListOf(), someBytes: ByteArray = ByteArray(0)) {
var name : String
private set
var id : Int
private set
var email : kotlin.String
var email : String
private set
var phones : MutableList <PhoneNumber>
var phones : MutableList <Person.PhoneNumber>
private set
var someBytes : ByteArray
@@ -38,11 +38,11 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
}
}
}
class PhoneNumber private constructor (number: kotlin.String = "", type: PhoneType = Person.PhoneType.fromIntToPhoneType(0)) {
var number : kotlin.String
class PhoneNumber private constructor (number: String = "", type: Person.PhoneType = Person.PhoneType.fromIntToPhoneType(0)) {
var number : String
private set
var type : PhoneType
var type : Person.PhoneType
private set
@@ -56,17 +56,17 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
output.writeEnum (2, type.ord)
}
class BuilderPhoneNumber constructor (number: kotlin.String = "", type: PhoneType = Person.PhoneType.fromIntToPhoneType(0)) {
var number : kotlin.String
class BuilderPhoneNumber constructor (number: String = "", type: Person.PhoneType = Person.PhoneType.fromIntToPhoneType(0)) {
var number : String
private set
fun setNumber(value: kotlin.String): Person.PhoneNumber.BuilderPhoneNumber {
fun setNumber(value: String): Person.PhoneNumber.BuilderPhoneNumber {
number = value
return this
}
var type : PhoneType
var type : Person.PhoneType
private set
fun setType(value: PhoneType): Person.PhoneNumber.BuilderPhoneNumber {
fun setType(value: Person.PhoneType): Person.PhoneNumber.BuilderPhoneNumber {
type = value
return this
}
@@ -79,12 +79,12 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
fun readFrom (input: CodedInputStream): Person.PhoneNumber.BuilderPhoneNumber {
number = input.readString(1)
type = PhoneType.fromIntToPhoneType(input.readEnum(2))
type = Person.PhoneType.fromIntToPhoneType(input.readEnum(2))
return this
}
fun build(): PhoneNumber {
return PhoneNumber(number, type)
fun build(): Person.PhoneNumber {
return Person.PhoneNumber(number, type)
}
fun parseFieldFrom(input: CodedInputStream): Boolean {
@@ -95,7 +95,7 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
val wireType = WireFormat.getTagWireType(tag)
when(fieldNumber) {
1 -> number = input.readStringNoTag()
2 -> type = PhoneType.fromIntToPhoneType(input.readEnumNoTag())
2 -> type = Person.PhoneType.fromIntToPhoneType(input.readEnumNoTag())
}
return true}
fun parseFrom(input: CodedInputStream): Person.PhoneNumber.BuilderPhoneNumber {
@@ -111,7 +111,7 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
}
fun mergeWith (other: PhoneNumber) {
fun mergeWith (other: Person.PhoneNumber) {
number = other.number
type = other.type
}
@@ -152,10 +152,10 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
output.writeBytes (5, someBytes)
}
class BuilderPerson constructor (name: kotlin.String = "", id: Int = 0, email: kotlin.String = "", phones: MutableList <PhoneNumber> = mutableListOf(), someBytes: ByteArray = ByteArray(0)) {
var name : kotlin.String
class BuilderPerson constructor (name: String = "", id: Int = 0, email: String = "", phones: MutableList <Person.PhoneNumber> = mutableListOf(), someBytes: ByteArray = ByteArray(0)) {
var name : String
private set
fun setName(value: kotlin.String): Person.BuilderPerson {
fun setName(value: String): Person.BuilderPerson {
name = value
return this
}
@@ -167,28 +167,28 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
return this
}
var email : kotlin.String
var email : String
private set
fun setEmail(value: kotlin.String): Person.BuilderPerson {
fun setEmail(value: String): Person.BuilderPerson {
email = value
return this
}
var phones : MutableList <PhoneNumber>
var phones : MutableList <Person.PhoneNumber>
private set
fun setPhones(value: MutableList <PhoneNumber> ): Person.BuilderPerson {
fun setPhones(value: MutableList <Person.PhoneNumber>): Person.BuilderPerson {
phones = value
return this
}
fun setPhoneNumber(index: Int, value: PhoneNumber): Person.PhoneNumber.BuilderPhoneNumber {
fun setPhoneNumber(index: Int, value: PhoneNumber): Person.BuilderPerson {
phones[index] = value
return this
}
fun addPhoneNumber(value: PhoneNumber): Person.PhoneNumber.BuilderPhoneNumber {
fun addPhoneNumber(value: PhoneNumber): Person.BuilderPerson {
phones.add(value)
return this
}
fun addAllPhoneNumber(value: Iterable<PhoneNumber>): Person.PhoneNumber.BuilderPhoneNumber {
fun addAllPhoneNumber(value: Iterable<PhoneNumber>): Person.BuilderPerson {
for (item in value) {
phones.add(item)
}
@@ -219,13 +219,13 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
val expectedSize = input.readInt32NoTag()
var readSize = 0
while(readSize != expectedSize) {
val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber()
val tmp: Person.PhoneNumber = Person.PhoneNumber.BuilderPhoneNumber().build()
input.readTag(4, WireType.LENGTH_DELIMITED)
val expectedSize = input.readInt32NoTag()
tmp.mergeFrom(input)
if (expectedSize != tmp.getSize()) { throw InvalidProtocolBufferException ("Expected size ${expectedSize} got ${tmp.getSize()}") }
readSize += tmp.getSize() + WireFormat.getTagSize(4, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(tmp.getSize())
phones.add(tmp.build())
phones.add(tmp)
}
someBytes = input.readBytes(5)
return this
@@ -249,13 +249,13 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
val expectedSize = input.readInt32NoTag()
var readSize = 0
while(readSize != expectedSize) {
val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber()
val tmp: Person.PhoneNumber = Person.PhoneNumber.BuilderPhoneNumber().build()
input.readTag(4, WireType.LENGTH_DELIMITED)
val expectedSize = input.readInt32NoTag()
tmp.mergeFrom(input)
if (expectedSize != tmp.getSize()) { throw InvalidProtocolBufferException ("Expected size ${expectedSize} got ${tmp.getSize()}") }
readSize += tmp.getSize() + WireFormat.getTagSize(4, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(tmp.getSize())
phones.add(tmp.build())
phones.add(tmp)
}
}
5 -> someBytes = input.readBytesNoTag()
@@ -312,8 +312,8 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
}
class AddressBook private constructor (people: MutableList <Person> = mutableListOf()) {
var people : MutableList <Person>
class AddressBook private constructor (people: MutableList <Person> = mutableListOf()) {
var people : MutableList <Person>
private set
@@ -341,22 +341,22 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
}
}
class BuilderAddressBook constructor (people: MutableList <Person> = mutableListOf()) {
var people : MutableList <Person>
class BuilderAddressBook constructor (people: MutableList <Person> = mutableListOf()) {
var people : MutableList <Person>
private set
fun setPeople(value: MutableList <Person> ): AddressBook.BuilderAddressBook {
fun setPeople(value: MutableList <Person>): AddressBook.BuilderAddressBook {
people = value
return this
}
fun setPerson(index: Int, value: Person): Person.BuilderPerson {
fun setPerson(index: Int, value: Person): AddressBook.BuilderAddressBook {
people[index] = value
return this
}
fun addPerson(value: Person): Person.BuilderPerson {
fun addPerson(value: Person): AddressBook.BuilderAddressBook {
people.add(value)
return this
}
fun addAllPerson(value: Iterable<Person>): Person.BuilderPerson {
fun addAllPerson(value: Iterable<Person>): AddressBook.BuilderAddressBook {
for (item in value) {
people.add(item)
}
@@ -373,13 +373,13 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
val expectedSize = input.readInt32NoTag()
var readSize = 0
while(readSize != expectedSize) {
val tmp: Person.BuilderPerson = Person.BuilderPerson()
val tmp: Person = Person.BuilderPerson().build()
input.readTag(1, WireType.LENGTH_DELIMITED)
val expectedSize = input.readInt32NoTag()
tmp.mergeFrom(input)
if (expectedSize != tmp.getSize()) { throw InvalidProtocolBufferException ("Expected size ${expectedSize} got ${tmp.getSize()}") }
readSize += tmp.getSize() + WireFormat.getTagSize(1, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(tmp.getSize())
people.add(tmp.build())
people.add(tmp)
}
return this
}
@@ -399,13 +399,13 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
val expectedSize = input.readInt32NoTag()
var readSize = 0
while(readSize != expectedSize) {
val tmp: Person.BuilderPerson = Person.BuilderPerson()
val tmp: Person = Person.BuilderPerson().build()
input.readTag(1, WireType.LENGTH_DELIMITED)
val expectedSize = input.readInt32NoTag()
tmp.mergeFrom(input)
if (expectedSize != tmp.getSize()) { throw InvalidProtocolBufferException ("Expected size ${expectedSize} got ${tmp.getSize()}") }
readSize += tmp.getSize() + WireFormat.getTagSize(1, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(tmp.getSize())
people.add(tmp.build())
people.add(tmp)
}
}
}
+29 -29
View File
@@ -1,29 +1,29 @@
class Level1 private constructor (field1: Level2 = Level1.Level2.BuilderLevel2().build()) {
var field1 : Level2
class Level1 private constructor (field1: Level1.Level2 = Level1.Level2.BuilderLevel2().build()) {
var field1 : Level1.Level2
private set
init {
this.field1 = field1
}
class Level2 private constructor (field2: Level3 = Level1.Level2.Level3.BuilderLevel3().build()) {
var field2 : Level3
class Level2 private constructor (field2: Level1.Level2.Level3 = Level1.Level2.Level3.BuilderLevel3().build()) {
var field2 : Level1.Level2.Level3
private set
init {
this.field2 = field2
}
class Level3 private constructor (field3: Level4 = Level1.Level2.Level3.Level4.BuilderLevel4().build()) {
var field3 : Level4
class Level3 private constructor (field3: Level1.Level2.Level3.Level4 = Level1.Level2.Level3.Level4.BuilderLevel4().build()) {
var field3 : Level1.Level2.Level3.Level4
private set
init {
this.field3 = field3
}
class Level4 private constructor (field4: kotlin.String = "") {
var field4 : kotlin.String
class Level4 private constructor (field4: String = "") {
var field4 : String
private set
@@ -35,10 +35,10 @@ class Level1 private constructor (field1: Level2 = Level1.Level2.BuilderLevel2()
output.writeString (4, field4)
}
class BuilderLevel4 constructor (field4: kotlin.String = "") {
var field4 : kotlin.String
class BuilderLevel4 constructor (field4: String = "") {
var field4 : String
private set
fun setField4(value: kotlin.String): Level1.Level2.Level3.Level4.BuilderLevel4 {
fun setField4(value: String): Level1.Level2.Level3.Level4.BuilderLevel4 {
field4 = value
return this
}
@@ -53,8 +53,8 @@ class Level1 private constructor (field1: Level2 = Level1.Level2.BuilderLevel2()
return this
}
fun build(): Level4 {
return Level4(field4)
fun build(): Level1.Level2.Level3.Level4 {
return Level1.Level2.Level3.Level4(field4)
}
fun parseFieldFrom(input: CodedInputStream): Boolean {
@@ -79,7 +79,7 @@ class Level1 private constructor (field1: Level2 = Level1.Level2.BuilderLevel2()
}
fun mergeWith (other: Level4) {
fun mergeWith (other: Level1.Level2.Level3.Level4) {
field4 = other.field4
}
@@ -100,10 +100,10 @@ class Level1 private constructor (field1: Level2 = Level1.Level2.BuilderLevel2()
field3.writeTo(output)
}
class BuilderLevel3 constructor (field3: Level4 = Level1.Level2.Level3.Level4.BuilderLevel4().build()) {
var field3 : Level4
class BuilderLevel3 constructor (field3: Level1.Level2.Level3.Level4 = Level1.Level2.Level3.Level4.BuilderLevel4().build()) {
var field3 : Level1.Level2.Level3.Level4
private set
fun setField3(value: Level4): Level1.Level2.Level3.BuilderLevel3 {
fun setField3(value: Level1.Level2.Level3.Level4): Level1.Level2.Level3.BuilderLevel3 {
field3 = value
return this
}
@@ -121,8 +121,8 @@ class Level1 private constructor (field1: Level2 = Level1.Level2.BuilderLevel2()
return this
}
fun build(): Level3 {
return Level3(field3)
fun build(): Level1.Level2.Level3 {
return Level1.Level2.Level3(field3)
}
fun parseFieldFrom(input: CodedInputStream): Boolean {
@@ -152,7 +152,7 @@ class Level1 private constructor (field1: Level2 = Level1.Level2.BuilderLevel2()
}
fun mergeWith (other: Level3) {
fun mergeWith (other: Level1.Level2.Level3) {
field3 = other.field3
}
@@ -173,10 +173,10 @@ class Level1 private constructor (field1: Level2 = Level1.Level2.BuilderLevel2()
field2.writeTo(output)
}
class BuilderLevel2 constructor (field2: Level3 = Level1.Level2.Level3.BuilderLevel3().build()) {
var field2 : Level3
class BuilderLevel2 constructor (field2: Level1.Level2.Level3 = Level1.Level2.Level3.BuilderLevel3().build()) {
var field2 : Level1.Level2.Level3
private set
fun setField2(value: Level3): Level1.Level2.BuilderLevel2 {
fun setField2(value: Level1.Level2.Level3): Level1.Level2.BuilderLevel2 {
field2 = value
return this
}
@@ -194,8 +194,8 @@ class Level1 private constructor (field1: Level2 = Level1.Level2.BuilderLevel2()
return this
}
fun build(): Level2 {
return Level2(field2)
fun build(): Level1.Level2 {
return Level1.Level2(field2)
}
fun parseFieldFrom(input: CodedInputStream): Boolean {
@@ -225,7 +225,7 @@ class Level1 private constructor (field1: Level2 = Level1.Level2.BuilderLevel2()
}
fun mergeWith (other: Level2) {
fun mergeWith (other: Level1.Level2) {
field2 = other.field2
}
@@ -246,10 +246,10 @@ class Level1 private constructor (field1: Level2 = Level1.Level2.BuilderLevel2()
field1.writeTo(output)
}
class BuilderLevel1 constructor (field1: Level2 = Level1.Level2.BuilderLevel2().build()) {
var field1 : Level2
class BuilderLevel1 constructor (field1: Level1.Level2 = Level1.Level2.BuilderLevel2().build()) {
var field1 : Level1.Level2
private set
fun setField1(value: Level2): Level1.BuilderLevel1 {
fun setField1(value: Level1.Level2): Level1.BuilderLevel1 {
field1 = value
return this
}