Index: third_party/protobuf/src/google/protobuf/compiler/js/js_generator.cc |
diff --git a/third_party/protobuf/src/google/protobuf/compiler/js/js_generator.cc b/third_party/protobuf/src/google/protobuf/compiler/js/js_generator.cc |
index e6571f6fdfd3e538dca3749f4e63f96ad8a58667..a66d28efe58465ef941d0aefe484643b869acc33 100755 |
--- a/third_party/protobuf/src/google/protobuf/compiler/js/js_generator.cc |
+++ b/third_party/protobuf/src/google/protobuf/compiler/js/js_generator.cc |
@@ -45,7 +45,6 @@ |
#include <google/protobuf/stubs/logging.h> |
#include <google/protobuf/stubs/common.h> |
#include <google/protobuf/stubs/stringprintf.h> |
-#include <google/protobuf/compiler/js/well_known_types_embed.h> |
#include <google/protobuf/io/printer.h> |
#include <google/protobuf/io/zero_copy_stream.h> |
#include <google/protobuf/descriptor.pb.h> |
@@ -152,24 +151,16 @@ string StripProto(const string& filename) { |
return StripSuffixString(filename, suffix); |
} |
-// Given a filename like foo/bar/baz.proto, returns the corresponding JavaScript |
+// Given a filename like foo/bar/baz.proto, returns the correspoding JavaScript |
// file foo/bar/baz.js. |
-string GetJSFilename(const GeneratorOptions& options, const string& filename) { |
- return StripProto(filename) + options.GetFileNameExtension(); |
+string GetJSFilename(const string& filename) { |
+ return StripProto(filename) + "_pb.js"; |
} |
// Given a filename like foo/bar/baz.proto, returns the root directory |
// path ../../ |
-string GetRootPath(const string& from_filename, const string& to_filename) { |
- if (to_filename.find("google/protobuf") == 0) { |
- // Well-known types (.proto files in the google/protobuf directory) are |
- // assumed to come from the 'google-protobuf' npm package. We may want to |
- // generalize this exception later by letting others put generated code in |
- // their own npm packages. |
- return "google-protobuf/"; |
- } |
- |
- size_t slashes = std::count(from_filename.begin(), from_filename.end(), '/'); |
+string GetRootPath(const string& filename) { |
+ size_t slashes = std::count(filename.begin(), filename.end(), '/'); |
if (slashes == 0) { |
return "./"; |
} |
@@ -209,33 +200,28 @@ string GetPath(const GeneratorOptions& options, |
} |
} |
-// Returns the name of the message with a leading dot and taking into account |
-// nesting, for example ".OuterMessage.InnerMessage", or returns empty if |
-// descriptor is null. This function does not handle namespacing, only message |
-// nesting. |
-string GetNestedMessageName(const Descriptor* descriptor) { |
- if (descriptor == NULL) { |
- return ""; |
- } |
- string result = |
- StripPrefixString(descriptor->full_name(), descriptor->file()->package()); |
- // Add a leading dot if one is not already present. |
- if (!result.empty() && result[0] != '.') { |
- result = "." + result; |
- } |
- return result; |
-} |
+// Forward declare, so that GetPrefix can call this method, |
+// which in turn, calls GetPrefix. |
+string GetPath(const GeneratorOptions& options, |
+ const Descriptor* descriptor); |
// Returns the path prefix for a message or enumeration that |
// lives under the given file and containing type. |
string GetPrefix(const GeneratorOptions& options, |
const FileDescriptor* file_descriptor, |
const Descriptor* containing_type) { |
- string prefix = |
- GetPath(options, file_descriptor) + GetNestedMessageName(containing_type); |
+ string prefix = ""; |
+ |
+ if (containing_type == NULL) { |
+ prefix = GetPath(options, file_descriptor); |
+ } else { |
+ prefix = GetPath(options, containing_type); |
+ } |
+ |
if (!prefix.empty()) { |
prefix += "."; |
} |
+ |
return prefix; |
} |
@@ -279,13 +265,11 @@ string GetPath(const GeneratorOptions& options, |
string MaybeCrossFileRef(const GeneratorOptions& options, |
const FileDescriptor* from_file, |
const Descriptor* to_message) { |
- if (options.import_style == GeneratorOptions::kImportCommonJs && |
+ if (options.import_style == GeneratorOptions::IMPORT_COMMONJS && |
from_file != to_message->file()) { |
// Cross-file ref in CommonJS needs to use the module alias instead of |
// the global name. |
- return ModuleAlias(to_message->file()->name()) + |
- GetNestedMessageName(to_message->containing_type()) + "." + |
- to_message->name(); |
+ return ModuleAlias(to_message->file()->name()) + "." + to_message->name(); |
} else { |
// Within a single file we use a full name. |
return GetPath(options, to_message); |
@@ -315,8 +299,8 @@ char ToLowerASCII(char c) { |
} |
} |
-std::vector<string> ParseLowerUnderscore(const string& input) { |
- std::vector<string> words; |
+vector<string> ParseLowerUnderscore(const string& input) { |
+ vector<string> words; |
string running = ""; |
for (int i = 0; i < input.size(); i++) { |
if (input[i] == '_') { |
@@ -334,8 +318,8 @@ std::vector<string> ParseLowerUnderscore(const string& input) { |
return words; |
} |
-std::vector<string> ParseUpperCamel(const string& input) { |
- std::vector<string> words; |
+vector<string> ParseUpperCamel(const string& input) { |
+ vector<string> words; |
string running = ""; |
for (int i = 0; i < input.size(); i++) { |
if (input[i] >= 'A' && input[i] <= 'Z' && !running.empty()) { |
@@ -350,7 +334,7 @@ std::vector<string> ParseUpperCamel(const string& input) { |
return words; |
} |
-string ToLowerCamel(const std::vector<string>& words) { |
+string ToLowerCamel(const vector<string>& words) { |
string result; |
for (int i = 0; i < words.size(); i++) { |
string word = words[i]; |
@@ -364,7 +348,7 @@ string ToLowerCamel(const std::vector<string>& words) { |
return result; |
} |
-string ToUpperCamel(const std::vector<string>& words) { |
+string ToUpperCamel(const vector<string>& words) { |
string result; |
for (int i = 0; i < words.size(); i++) { |
string word = words[i]; |
@@ -413,24 +397,21 @@ string ToFileName(const string& input) { |
// that top-level extensions should go in. |
string GetExtensionFileName(const GeneratorOptions& options, |
const FileDescriptor* file) { |
- return options.output_dir + "/" + ToFileName(GetPath(options, file)) + |
- options.GetFileNameExtension(); |
+ return options.output_dir + "/" + ToFileName(GetPath(options, file)) + ".js"; |
} |
// When we're generating one output file per type name, this is the filename |
// that a top-level message should go in. |
string GetMessageFileName(const GeneratorOptions& options, |
const Descriptor* desc) { |
- return options.output_dir + "/" + ToFileName(desc->name()) + |
- options.GetFileNameExtension(); |
+ return options.output_dir + "/" + ToFileName(desc->name()) + ".js"; |
} |
// When we're generating one output file per type name, this is the filename |
// that a top-level message should go in. |
string GetEnumFileName(const GeneratorOptions& options, |
const EnumDescriptor* desc) { |
- return options.output_dir + "/" + ToFileName(desc->name()) + |
- options.GetFileNameExtension(); |
+ return options.output_dir + "/" + ToFileName(desc->name()) + ".js"; |
} |
// Returns the message/response ID, if set. |
@@ -455,21 +436,6 @@ bool IgnoreField(const FieldDescriptor* field) { |
} |
-// Used inside Google only -- do not remove. |
-bool ShouldTreatMapsAsRepeatedFields(const FileDescriptor& descriptor) { |
- return false; |
-} |
- |
-// Do we ignore this message type? |
-bool IgnoreMessage(const GeneratorOptions& options, const Descriptor* d) { |
- return d->options().map_entry() && |
- !ShouldTreatMapsAsRepeatedFields(*d->file()); |
-} |
- |
-bool IsMap(const GeneratorOptions& options, const FieldDescriptor* field) { |
- return field->is_map() && !ShouldTreatMapsAsRepeatedFields(*field->file()); |
-} |
- |
// Does JSPB ignore this entire oneof? True only if all fields are ignored. |
bool IgnoreOneof(const OneofDescriptor* oneof) { |
for (int i = 0; i < oneof->field_count(); i++) { |
@@ -480,8 +446,9 @@ bool IgnoreOneof(const OneofDescriptor* oneof) { |
return true; |
} |
-string JSIdent(const GeneratorOptions& options, const FieldDescriptor* field, |
- bool is_upper_camel, bool is_map, bool drop_list) { |
+string JSIdent(const FieldDescriptor* field, |
+ bool is_upper_camel, |
+ bool is_map) { |
string result; |
if (field->type() == FieldDescriptor::TYPE_GROUP) { |
result = is_upper_camel ? |
@@ -492,22 +459,19 @@ string JSIdent(const GeneratorOptions& options, const FieldDescriptor* field, |
ToUpperCamel(ParseLowerUnderscore(field->name())) : |
ToLowerCamel(ParseLowerUnderscore(field->name())); |
} |
- if (is_map || IsMap(options, field)) { |
- // JSPB-style or proto3-style map. |
+ if (is_map) { |
result += "Map"; |
- } else if (!drop_list && field->is_repeated()) { |
- // Repeated field. |
+ } else if (field->is_repeated()) { |
result += "List"; |
} |
return result; |
} |
-string JSObjectFieldName(const GeneratorOptions& options, |
- const FieldDescriptor* field) { |
- string name = JSIdent(options, field, |
- /* is_upper_camel = */ false, |
- /* is_map = */ false, |
- /* drop_list = */ false); |
+string JSObjectFieldName(const FieldDescriptor* field) { |
+ string name = JSIdent( |
+ field, |
+ /* is_upper_camel = */ false, |
+ /* is_map = */ false); |
if (IsReserved(name)) { |
name = "pb_" + name; |
} |
@@ -525,18 +489,15 @@ string JSByteGetterSuffix(BytesMode bytes_mode) { |
default: |
assert(false); |
} |
- return ""; |
} |
// Returns the field name as a capitalized portion of a getter/setter method |
// name, e.g. MyField for .getMyField(). |
-string JSGetterName(const GeneratorOptions& options, |
- const FieldDescriptor* field, |
- BytesMode bytes_mode = BYTES_DEFAULT, |
- bool drop_list = false) { |
- string name = JSIdent(options, field, |
+string JSGetterName(const FieldDescriptor* field, |
+ BytesMode bytes_mode = BYTES_DEFAULT) { |
+ string name = JSIdent(field, |
/* is_upper_camel = */ true, |
- /* is_map = */ false, drop_list); |
+ /* is_map = */ false); |
if (field->type() == FieldDescriptor::TYPE_BYTES) { |
string suffix = JSByteGetterSuffix(bytes_mode); |
if (!suffix.empty()) { |
@@ -550,12 +511,10 @@ string JSGetterName(const GeneratorOptions& options, |
return name; |
} |
-string JSMapGetterName(const GeneratorOptions& options, |
- const FieldDescriptor* field) { |
- return JSIdent(options, field, |
+string JSMapGetterName(const FieldDescriptor* field) { |
+ return JSIdent(field, |
/* is_upper_camel = */ true, |
- /* is_map = */ true, |
- /* drop_list = */ false); |
+ /* is_map = */ true); |
} |
@@ -788,10 +747,7 @@ string MaybeNumberString(const FieldDescriptor* field, const string& orig) { |
} |
string JSFieldDefault(const FieldDescriptor* field) { |
- if (field->is_repeated()) { |
- return "[]"; |
- } |
- |
+ assert(field->has_default_value()); |
switch (field->cpp_type()) { |
case FieldDescriptor::CPPTYPE_INT32: |
return MaybeNumberString( |
@@ -933,85 +889,20 @@ string JSTypeName(const GeneratorOptions& options, |
} |
} |
-// Used inside Google only -- do not remove. |
-bool UseBrokenPresenceSemantics(const GeneratorOptions& options, |
- const FieldDescriptor* field) { |
- return false; |
-} |
- |
-// Returns true for fields that return "null" from accessors when they are |
-// unset. This should normally only be true for non-repeated submessages, but |
-// we have legacy users who relied on old behavior where accessors behaved this |
-// way. |
-bool ReturnsNullWhenUnset(const GeneratorOptions& options, |
- const FieldDescriptor* field) { |
- if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && |
- field->is_optional()) { |
- return true; |
- } |
- |
- // TODO(haberman): remove this case and unconditionally return false. |
- return UseBrokenPresenceSemantics(options, field) && !field->is_repeated() && |
- !field->has_default_value(); |
-} |
- |
-// In a sane world, this would be the same as ReturnsNullWhenUnset(). But in |
-// the status quo, some fields declare that they never return null/undefined |
-// even though they actually do: |
-// * required fields |
-// * optional enum fields |
-// * proto3 primitive fields. |
-bool DeclaredReturnTypeIsNullable(const GeneratorOptions& options, |
- const FieldDescriptor* field) { |
- if (field->is_required() || field->type() == FieldDescriptor::TYPE_ENUM) { |
- return false; |
- } |
- |
- if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 && |
- field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { |
- return false; |
- } |
- |
- return ReturnsNullWhenUnset(options, field); |
-} |
- |
-bool SetterAcceptsUndefined(const GeneratorOptions& options, |
- const FieldDescriptor* field) { |
- if (ReturnsNullWhenUnset(options, field)) { |
- return true; |
- } |
- |
- // Broken presence semantics always accepts undefined for setters. |
- return UseBrokenPresenceSemantics(options, field); |
-} |
- |
-bool SetterAcceptsNull(const GeneratorOptions& options, |
- const FieldDescriptor* field) { |
- if (ReturnsNullWhenUnset(options, field)) { |
- return true; |
- } |
- |
- // With broken presence semantics, fields with defaults accept "null" for |
- // setters, but other fields do not. This is a strange quirk of the old |
- // codegen. |
- return UseBrokenPresenceSemantics(options, field) && |
- field->has_default_value(); |
-} |
- |
-// Returns types which are known to by non-nullable by default. |
-// The style guide requires that we omit "!" in this case. |
-bool IsPrimitive(const string& type) { |
- return type == "undefined" || type == "string" || type == "number" || |
- type == "boolean"; |
-} |
+bool HasFieldPresence(const FieldDescriptor* field); |
string JSFieldTypeAnnotation(const GeneratorOptions& options, |
const FieldDescriptor* field, |
- bool is_setter_argument, |
+ bool force_optional, |
bool force_present, |
bool singular_if_not_packed, |
BytesMode bytes_mode = BYTES_DEFAULT) { |
- GOOGLE_CHECK(!(is_setter_argument && force_present)); |
+ bool is_primitive = |
+ (field->cpp_type() != FieldDescriptor::CPPTYPE_ENUM && |
+ field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE && |
+ (field->type() != FieldDescriptor::TYPE_BYTES || |
+ bytes_mode == BYTES_B64)); |
+ |
string jstype = JSTypeName(options, field, bytes_mode); |
if (field->is_repeated() && |
@@ -1020,35 +911,27 @@ string JSFieldTypeAnnotation(const GeneratorOptions& options, |
bytes_mode == BYTES_DEFAULT) { |
jstype = "(Array<!Uint8Array>|Array<string>)"; |
} else { |
- if (!IsPrimitive(jstype)) { |
+ if (!is_primitive) { |
jstype = "!" + jstype; |
} |
jstype = "Array.<" + jstype + ">"; |
} |
- } |
- |
- bool is_null_or_undefined = false; |
- |
- if (is_setter_argument) { |
- if (SetterAcceptsNull(options, field)) { |
- jstype = "?" + jstype; |
- is_null_or_undefined = true; |
+ if (!force_optional) { |
+ jstype = "!" + jstype; |
} |
+ } |
- if (SetterAcceptsUndefined(options, field)) { |
- jstype += "|undefined"; |
- is_null_or_undefined = true; |
- } |
- } else if (force_present) { |
- // Don't add null or undefined. |
- } else { |
- if (DeclaredReturnTypeIsNullable(options, field)) { |
- jstype = "?" + jstype; |
- is_null_or_undefined = true; |
- } |
+ if (field->is_optional() && is_primitive && |
+ (!field->has_default_value() || force_optional) && !force_present) { |
+ jstype += "?"; |
+ } else if (field->is_required() && !is_primitive && !force_optional) { |
+ jstype = "!" + jstype; |
} |
- if (!is_null_or_undefined && !IsPrimitive(jstype)) { |
+ if (force_optional && HasFieldPresence(field)) { |
+ jstype += "|undefined"; |
+ } |
+ if (force_present && jstype[0] != '!' && !is_primitive) { |
jstype = "!" + jstype; |
} |
@@ -1075,16 +958,12 @@ string JSBinaryReadWriteMethodName(const FieldDescriptor* field, |
return name; |
} |
-string JSBinaryReaderMethodName(const GeneratorOptions& options, |
- const FieldDescriptor* field) { |
- return "jspb.BinaryReader.prototype.read" + |
- JSBinaryReadWriteMethodName(field, /* is_writer = */ false); |
+string JSBinaryReaderMethodName(const FieldDescriptor* field) { |
+ return "read" + JSBinaryReadWriteMethodName(field, /* is_writer = */ false); |
} |
-string JSBinaryWriterMethodName(const GeneratorOptions& options, |
- const FieldDescriptor* field) { |
- return "jspb.BinaryWriter.prototype.write" + |
- JSBinaryReadWriteMethodName(field, /* is_writer = */ true); |
+string JSBinaryWriterMethodName(const FieldDescriptor* field) { |
+ return "write" + JSBinaryReadWriteMethodName(field, /* is_writer = */ true); |
} |
string JSReturnClause(const FieldDescriptor* desc) { |
@@ -1096,10 +975,9 @@ string JSReturnDoc(const GeneratorOptions& options, |
return ""; |
} |
-bool HasRepeatedFields(const GeneratorOptions& options, |
- const Descriptor* desc) { |
+bool HasRepeatedFields(const Descriptor* desc) { |
for (int i = 0; i < desc->field_count(); i++) { |
- if (desc->field(i)->is_repeated() && !IsMap(options, desc->field(i))) { |
+ if (desc->field(i)->is_repeated()) { |
return true; |
} |
} |
@@ -1110,9 +988,8 @@ static const char* kRepeatedFieldArrayName = ".repeatedFields_"; |
string RepeatedFieldsArrayName(const GeneratorOptions& options, |
const Descriptor* desc) { |
- return HasRepeatedFields(options, desc) |
- ? (GetPath(options, desc) + kRepeatedFieldArrayName) |
- : "null"; |
+ return HasRepeatedFields(desc) ? |
+ (GetPath(options, desc) + kRepeatedFieldArrayName) : "null"; |
} |
bool HasOneofFields(const Descriptor* desc) { |
@@ -1132,11 +1009,10 @@ string OneofFieldsArrayName(const GeneratorOptions& options, |
(GetPath(options, desc) + kOneofGroupArrayName) : "null"; |
} |
-string RepeatedFieldNumberList(const GeneratorOptions& options, |
- const Descriptor* desc) { |
+string RepeatedFieldNumberList(const Descriptor* desc) { |
std::vector<string> numbers; |
for (int i = 0; i < desc->field_count(); i++) { |
- if (desc->field(i)->is_repeated() && !IsMap(options, desc->field(i))) { |
+ if (desc->field(i)->is_repeated()) { |
numbers.push_back(JSFieldIndex(desc->field(i))); |
} |
} |
@@ -1200,65 +1076,34 @@ string JSExtensionsObjectName(const GeneratorOptions& options, |
const FileDescriptor* from_file, |
const Descriptor* desc) { |
if (desc->full_name() == "google.protobuf.bridge.MessageSet") { |
- // TODO(haberman): fix this for the kImportCommonJs case. |
+ // TODO(haberman): fix this for the IMPORT_COMMONJS case. |
return "jspb.Message.messageSetExtensions"; |
} else { |
return MaybeCrossFileRef(options, from_file, desc) + ".extensions"; |
} |
} |
-static const int kMapKeyField = 1; |
-static const int kMapValueField = 2; |
- |
-const FieldDescriptor* MapFieldKey(const FieldDescriptor* field) { |
- assert(field->is_map()); |
- return field->message_type()->FindFieldByNumber(kMapKeyField); |
-} |
- |
-const FieldDescriptor* MapFieldValue(const FieldDescriptor* field) { |
- assert(field->is_map()); |
- return field->message_type()->FindFieldByNumber(kMapValueField); |
-} |
- |
string FieldDefinition(const GeneratorOptions& options, |
const FieldDescriptor* field) { |
- if (IsMap(options, field)) { |
- const FieldDescriptor* key_field = MapFieldKey(field); |
- const FieldDescriptor* value_field = MapFieldValue(field); |
- string key_type = ProtoTypeName(options, key_field); |
- string value_type; |
- if (value_field->type() == FieldDescriptor::TYPE_ENUM || |
- value_field->type() == FieldDescriptor::TYPE_MESSAGE) { |
- value_type = RelativeTypeName(value_field); |
- } else { |
- value_type = ProtoTypeName(options, value_field); |
- } |
- return StringPrintf("map<%s, %s> %s = %d;", |
- key_type.c_str(), |
- value_type.c_str(), |
- field->name().c_str(), |
- field->number()); |
+ string qualifier = field->is_repeated() ? "repeated" : |
+ (field->is_optional() ? "optional" : "required"); |
+ string type, name; |
+ if (field->type() == FieldDescriptor::TYPE_ENUM || |
+ field->type() == FieldDescriptor::TYPE_MESSAGE) { |
+ type = RelativeTypeName(field); |
+ name = field->name(); |
+ } else if (field->type() == FieldDescriptor::TYPE_GROUP) { |
+ type = "group"; |
+ name = field->message_type()->name(); |
} else { |
- string qualifier = field->is_repeated() ? "repeated" : |
- (field->is_optional() ? "optional" : "required"); |
- string type, name; |
- if (field->type() == FieldDescriptor::TYPE_ENUM || |
- field->type() == FieldDescriptor::TYPE_MESSAGE) { |
- type = RelativeTypeName(field); |
- name = field->name(); |
- } else if (field->type() == FieldDescriptor::TYPE_GROUP) { |
- type = "group"; |
- name = field->message_type()->name(); |
- } else { |
- type = ProtoTypeName(options, field); |
- name = field->name(); |
- } |
- return StringPrintf("%s %s %s = %d;", |
- qualifier.c_str(), |
- type.c_str(), |
- name.c_str(), |
- field->number()); |
+ type = ProtoTypeName(options, field); |
+ name = field->name(); |
} |
+ return StringPrintf("%s %s %s = %d;", |
+ qualifier.c_str(), |
+ type.c_str(), |
+ name.c_str(), |
+ field->number()); |
} |
string FieldComments(const FieldDescriptor* field, BytesMode bytes_mode) { |
@@ -1319,29 +1164,6 @@ bool HasExtensions(const FileDescriptor* file) { |
return false; |
} |
-bool HasMap(const GeneratorOptions& options, const Descriptor* desc) { |
- for (int i = 0; i < desc->field_count(); i++) { |
- if (IsMap(options, desc->field(i))) { |
- return true; |
- } |
- } |
- for (int i = 0; i < desc->nested_type_count(); i++) { |
- if (HasMap(options, desc->nested_type(i))) { |
- return true; |
- } |
- } |
- return false; |
-} |
- |
-bool FileHasMap(const GeneratorOptions& options, const FileDescriptor* desc) { |
- for (int i = 0; i < desc->message_type_count(); i++) { |
- if (HasMap(options, desc->message_type(i))) { |
- return true; |
- } |
- } |
- return false; |
-} |
- |
bool IsExtendable(const Descriptor* desc) { |
return desc->extension_range_count() > 0; |
} |
@@ -1369,24 +1191,43 @@ string GetPivot(const Descriptor* desc) { |
return SimpleItoa(pivot); |
} |
-// Whether this field represents presence. For fields with presence, we |
-// generate extra methods (clearFoo() and hasFoo()) for this field. |
-bool HasFieldPresence(const GeneratorOptions& options, |
- const FieldDescriptor* field) { |
- if (field->is_repeated() || field->is_map()) { |
- // We say repeated fields and maps don't have presence, but we still do |
- // generate clearFoo() methods for them through a special case elsewhere. |
- return false; |
- } |
+// Returns true for fields that represent "null" as distinct from the default |
+// value. See http://go/proto3#heading=h.kozewqqcqhuz for more information. |
+bool HasFieldPresence(const FieldDescriptor* field) { |
+ return |
+ (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) || |
+ (field->containing_oneof() != NULL) || |
+ (field->file()->syntax() != FileDescriptor::SYNTAX_PROTO3); |
+} |
- if (UseBrokenPresenceSemantics(options, field)) { |
- // Proto3 files with broken presence semantics have field presence. |
- return true; |
- } |
+// For proto3 fields without presence, returns a string representing the default |
+// value in JavaScript. See http://go/proto3#heading=h.kozewqqcqhuz for more |
+// information. |
+string Proto3PrimitiveFieldDefault(const FieldDescriptor* field) { |
+ switch (field->cpp_type()) { |
+ case FieldDescriptor::CPPTYPE_INT32: |
+ case FieldDescriptor::CPPTYPE_INT64: |
+ case FieldDescriptor::CPPTYPE_UINT32: |
+ case FieldDescriptor::CPPTYPE_UINT64: { |
+ return "0"; |
+ } |
+ |
+ case FieldDescriptor::CPPTYPE_ENUM: |
+ case FieldDescriptor::CPPTYPE_FLOAT: |
+ case FieldDescriptor::CPPTYPE_DOUBLE: |
+ return "0"; |
+ |
+ case FieldDescriptor::CPPTYPE_BOOL: |
+ return "false"; |
+ |
+ case FieldDescriptor::CPPTYPE_STRING: // includes BYTES |
+ return "\"\""; |
- return field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE || |
- field->containing_oneof() != NULL || |
- field->file()->syntax() == FileDescriptor::SYNTAX_PROTO2; |
+ default: |
+ // MESSAGE is handled separately. |
+ assert(false); |
+ return ""; |
+ } |
} |
// We use this to implement the semantics that same file can be generated |
@@ -1413,19 +1254,19 @@ class FileDeduplicator { |
return true; |
} |
- void GetAllowedSet(std::set<const void*>* allowed_set) { |
+ void GetAllowedSet(set<const void*>* allowed_set) { |
*allowed_set = allowed_descs_; |
} |
private: |
bool error_on_conflict_; |
- std::map<string, const void*> descs_by_filename_; |
- std::set<const void*> allowed_descs_; |
+ map<string, const void*> descs_by_filename_; |
+ set<const void*> allowed_descs_; |
}; |
void DepthFirstSearch(const FileDescriptor* file, |
- std::vector<const FileDescriptor*>* list, |
- std::set<const FileDescriptor*>* seen) { |
+ vector<const FileDescriptor*>* list, |
+ set<const FileDescriptor*>* seen) { |
if (!seen->insert(file).second) { |
return; |
} |
@@ -1443,7 +1284,7 @@ void DepthFirstSearch(const FileDescriptor* file, |
// FileDescriptor is not in the given set. |
class NotInSet { |
public: |
- explicit NotInSet(const std::set<const FileDescriptor*>& file_set) |
+ explicit NotInSet(const set<const FileDescriptor*>& file_set) |
: file_set_(file_set) {} |
bool operator()(const FileDescriptor* file) { |
@@ -1451,21 +1292,21 @@ class NotInSet { |
} |
private: |
- const std::set<const FileDescriptor*>& file_set_; |
+ const set<const FileDescriptor*>& file_set_; |
}; |
// This function generates an ordering of the input FileDescriptors that matches |
// the logic of the old code generator. The order is significant because two |
// different input files can generate the same output file, and the last one |
// needs to win. |
-void GenerateJspbFileOrder(const std::vector<const FileDescriptor*>& input, |
- std::vector<const FileDescriptor*>* ordered) { |
+void GenerateJspbFileOrder(const vector<const FileDescriptor*>& input, |
+ vector<const FileDescriptor*>* ordered) { |
// First generate an ordering of all reachable files (including dependencies) |
// with depth-first search. This mimics the behavior of --include_imports, |
// which is what the old codegen used. |
ordered->clear(); |
- std::set<const FileDescriptor*> seen; |
- std::set<const FileDescriptor*> input_set; |
+ set<const FileDescriptor*> seen; |
+ set<const FileDescriptor*> input_set; |
for (int i = 0; i < input.size(); i++) { |
DepthFirstSearch(input[i], ordered, &seen); |
input_set.insert(input[i]); |
@@ -1482,10 +1323,10 @@ void GenerateJspbFileOrder(const std::vector<const FileDescriptor*>& input, |
// only those to generate code. |
bool GenerateJspbAllowedSet(const GeneratorOptions& options, |
- const std::vector<const FileDescriptor*>& files, |
- std::set<const void*>* allowed_set, |
+ const vector<const FileDescriptor*>& files, |
+ set<const void*>* allowed_set, |
string* error) { |
- std::vector<const FileDescriptor*> files_ordered; |
+ vector<const FileDescriptor*> files_ordered; |
GenerateJspbFileOrder(files, &files_ordered); |
// Choose the last descriptor for each filename. |
@@ -1553,7 +1394,7 @@ void Generator::FindProvidesForFile(const GeneratorOptions& options, |
void Generator::FindProvides(const GeneratorOptions& options, |
io::Printer* printer, |
- const std::vector<const FileDescriptor*>& files, |
+ const vector<const FileDescriptor*>& files, |
std::set<string>* provided) const { |
for (int i = 0; i < files.size(); i++) { |
FindProvidesForFile(options, printer, files[i], provided); |
@@ -1567,10 +1408,6 @@ void Generator::FindProvidesForMessage( |
io::Printer* printer, |
const Descriptor* desc, |
std::set<string>* provided) const { |
- if (IgnoreMessage(options, desc)) { |
- return; |
- } |
- |
string name = GetPath(options, desc); |
provided->insert(name); |
@@ -1595,7 +1432,7 @@ void Generator::FindProvidesForEnum(const GeneratorOptions& options, |
void Generator::FindProvidesForFields( |
const GeneratorOptions& options, |
io::Printer* printer, |
- const std::vector<const FieldDescriptor*>& fields, |
+ const vector<const FieldDescriptor*>& fields, |
std::set<string>* provided) const { |
for (int i = 0; i < fields.size(); i++) { |
const FieldDescriptor* field = fields[i]; |
@@ -1605,8 +1442,7 @@ void Generator::FindProvidesForFields( |
} |
string name = |
- GetPath(options, field->file()) + "." + |
- JSObjectFieldName(options, field); |
+ GetPath(options, field->file()) + "." + JSObjectFieldName(field); |
provided->insert(name); |
} |
} |
@@ -1616,19 +1452,8 @@ void Generator::GenerateProvides(const GeneratorOptions& options, |
std::set<string>* provided) const { |
for (std::set<string>::iterator it = provided->begin(); |
it != provided->end(); ++it) { |
- if (options.import_style == GeneratorOptions::kImportClosure) { |
- printer->Print("goog.provide('$name$');\n", "name", *it); |
- } else { |
- // We aren't using Closure's import system, but we use goog.exportSymbol() |
- // to construct the expected tree of objects, eg. |
- // |
- // goog.exportSymbol('foo.bar.Baz', null, this); |
- // |
- // // Later generated code expects foo.bar = {} to exist: |
- // foo.bar.Baz = function() { /* ... */ } |
- printer->Print("goog.exportSymbol('$name$', null, global);\n", "name", |
- *it); |
- } |
+ printer->Print("goog.provide('$name$');\n", |
+ "name", *it); |
} |
} |
@@ -1644,39 +1469,30 @@ void Generator::GenerateRequiresForMessage(const GeneratorOptions& options, |
GenerateRequiresImpl(options, printer, &required, &forwards, provided, |
/* require_jspb = */ have_message, |
- /* require_extension = */ HasExtensions(desc), |
- /* require_map = */ HasMap(options, desc)); |
+ /* require_extension = */ HasExtensions(desc)); |
} |
void Generator::GenerateRequiresForLibrary( |
const GeneratorOptions& options, io::Printer* printer, |
- const std::vector<const FileDescriptor*>& files, |
+ const vector<const FileDescriptor*>& files, |
std::set<string>* provided) const { |
- GOOGLE_CHECK_EQ(options.import_style, GeneratorOptions::kImportClosure); |
+ GOOGLE_CHECK_EQ(options.import_style, GeneratorOptions::IMPORT_CLOSURE); |
// For Closure imports we need to import every message type individually. |
std::set<string> required; |
std::set<string> forwards; |
bool have_extensions = false; |
- bool have_map = false; |
bool have_message = false; |
for (int i = 0; i < files.size(); i++) { |
for (int j = 0; j < files[i]->message_type_count(); j++) { |
- const Descriptor* desc = files[i]->message_type(j); |
- if (!IgnoreMessage(options, desc)) { |
- FindRequiresForMessage(options, desc, &required, &forwards, |
- &have_message); |
- } |
+ FindRequiresForMessage(options, |
+ files[i]->message_type(j), |
+ &required, &forwards, &have_message); |
} |
- |
if (!have_extensions && HasExtensions(files[i])) { |
have_extensions = true; |
} |
- if (!have_map && FileHasMap(options, files[i])) { |
- have_map = true; |
- } |
- |
for (int j = 0; j < files[i]->extension_count(); j++) { |
const FieldDescriptor* extension = files[i]->extension(j); |
if (IgnoreField(extension)) { |
@@ -1693,13 +1509,12 @@ void Generator::GenerateRequiresForLibrary( |
GenerateRequiresImpl(options, printer, &required, &forwards, provided, |
/* require_jspb = */ have_message, |
- /* require_extension = */ have_extensions, |
- /* require_map = */ have_map); |
+ /* require_extension = */ have_extensions); |
} |
void Generator::GenerateRequiresForExtensions( |
const GeneratorOptions& options, io::Printer* printer, |
- const std::vector<const FieldDescriptor*>& fields, |
+ const vector<const FieldDescriptor*>& fields, |
std::set<string>* provided) const { |
std::set<string> required; |
std::set<string> forwards; |
@@ -1713,8 +1528,7 @@ void Generator::GenerateRequiresForExtensions( |
GenerateRequiresImpl(options, printer, &required, &forwards, provided, |
/* require_jspb = */ false, |
- /* require_extension = */ fields.size() > 0, |
- /* require_map = */ false); |
+ /* require_extension = */ fields.size() > 0); |
} |
void Generator::GenerateRequiresImpl(const GeneratorOptions& options, |
@@ -1722,22 +1536,21 @@ void Generator::GenerateRequiresImpl(const GeneratorOptions& options, |
std::set<string>* required, |
std::set<string>* forwards, |
std::set<string>* provided, |
- bool require_jspb, bool require_extension, |
- bool require_map) const { |
+ bool require_jspb, |
+ bool require_extension) const { |
if (require_jspb) { |
printer->Print( |
- "goog.require('jspb.Message');\n" |
- "goog.require('jspb.BinaryReader');\n" |
- "goog.require('jspb.BinaryWriter');\n"); |
+ "goog.require('jspb.Message');\n"); |
+ if (options.binary) { |
+ printer->Print( |
+ "goog.require('jspb.BinaryReader');\n" |
+ "goog.require('jspb.BinaryWriter');\n"); |
+ } |
} |
if (require_extension) { |
- printer->Print("goog.require('jspb.ExtensionFieldBinaryInfo');\n"); |
printer->Print( |
"goog.require('jspb.ExtensionFieldInfo');\n"); |
} |
- if (require_map) { |
- printer->Print("goog.require('jspb.Map');\n"); |
- } |
std::set<string>::iterator it; |
for (it = required->begin(); it != required->end(); ++it) { |
@@ -1810,9 +1623,7 @@ void Generator::FindRequiresForField(const GeneratorOptions& options, |
forwards->insert(GetPath(options, field->enum_type())); |
} |
} else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { |
- if (!IgnoreMessage(options, field->message_type())) { |
- required->insert(GetPath(options, field->message_type())); |
- } |
+ required->insert(GetPath(options, field->message_type())); |
} |
} |
@@ -1848,10 +1659,6 @@ void Generator::GenerateClassesAndEnums(const GeneratorOptions& options, |
void Generator::GenerateClass(const GeneratorOptions& options, |
io::Printer* printer, |
const Descriptor* desc) const { |
- if (IgnoreMessage(options, desc)) { |
- return; |
- } |
- |
if (!NamespaceOnly(desc)) { |
printer->Print("\n"); |
GenerateClassConstructor(options, printer, desc); |
@@ -1859,20 +1666,22 @@ void Generator::GenerateClass(const GeneratorOptions& options, |
GenerateClassToObject(options, printer, desc); |
- // These must come *before* the extension-field info generation in |
- // GenerateClassRegistration so that references to the binary |
- // serialization/deserialization functions may be placed in the extension |
- // objects. |
- GenerateClassDeserializeBinary(options, printer, desc); |
- GenerateClassSerializeBinary(options, printer, desc); |
- |
+ if (options.binary) { |
+ // These must come *before* the extension-field info generation in |
+ // GenerateClassRegistration so that references to the binary |
+ // serialization/deserialization functions may be placed in the extension |
+ // objects. |
+ GenerateClassDeserializeBinary(options, printer, desc); |
+ GenerateClassSerializeBinary(options, printer, desc); |
+ } |
+ GenerateClassClone(options, printer, desc); |
GenerateClassRegistration(options, printer, desc); |
GenerateClassFields(options, printer, desc); |
if (IsExtendable(desc) && desc->full_name() != "google.protobuf.bridge.MessageSet") { |
GenerateClassExtensionFieldInfo(options, printer, desc); |
} |
- if (options.import_style != GeneratorOptions::kImportClosure) { |
+ if (options.import_style != GeneratorOptions:: IMPORT_CLOSURE) { |
for (int i = 0; i < desc->extension_count(); i++) { |
GenerateExtension(options, printer, desc->extension(i)); |
} |
@@ -1930,7 +1739,7 @@ void Generator::GenerateClassConstructor(const GeneratorOptions& options, |
void Generator::GenerateClassFieldInfo(const GeneratorOptions& options, |
io::Printer* printer, |
const Descriptor* desc) const { |
- if (HasRepeatedFields(options, desc)) { |
+ if (HasRepeatedFields(desc)) { |
printer->Print( |
"/**\n" |
" * List of repeated fields within this message type.\n" |
@@ -1941,7 +1750,7 @@ void Generator::GenerateClassFieldInfo(const GeneratorOptions& options, |
"\n", |
"classname", GetPath(options, desc), |
"rptfieldarray", kRepeatedFieldArrayName, |
- "rptfields", RepeatedFieldNumberList(options, desc)); |
+ "rptfields", RepeatedFieldNumberList(desc)); |
} |
if (HasOneofFields(desc)) { |
@@ -2110,110 +1919,62 @@ void Generator::GenerateClassToObject(const GeneratorOptions& options, |
"classname", GetPath(options, desc)); |
} |
-void Generator::GenerateFieldValueExpression(io::Printer* printer, |
- const char *obj_reference, |
- const FieldDescriptor* field, |
- bool use_default) const { |
- bool is_float_or_double = |
- field->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT || |
- field->cpp_type() == FieldDescriptor::CPPTYPE_DOUBLE; |
- if (use_default) { |
- if (is_float_or_double) { |
- // Coerce "Nan" and "Infinity" to actual float values. |
- // |
- // This will change null to 0, but that doesn't matter since we're getting |
- // with a default. |
- printer->Print("+"); |
- } |
- |
- printer->Print( |
- "jspb.Message.getFieldWithDefault($obj$, $index$, $default$)", |
- "obj", obj_reference, |
- "index", JSFieldIndex(field), |
- "default", JSFieldDefault(field)); |
- } else { |
- if (is_float_or_double) { |
- if (field->is_required()) { |
- // Use "+" to convert all fields to numeric (including null). |
- printer->Print( |
- "+jspb.Message.getField($obj$, $index$)", |
- "index", JSFieldIndex(field), |
- "obj", obj_reference); |
- } else { |
- // Converts "NaN" and "Infinity" while preserving null. |
- printer->Print( |
- "jspb.Message.get$cardinality$FloatingPointField($obj$, $index$)", |
- "cardinality", field->is_repeated() ? "Repeated" : "Optional", |
- "index", JSFieldIndex(field), |
- "obj", obj_reference); |
- } |
- } else { |
- printer->Print("jspb.Message.getField($obj$, $index$)", |
- "index", JSFieldIndex(field), |
- "obj", obj_reference); |
- } |
- } |
-} |
- |
void Generator::GenerateClassFieldToObject(const GeneratorOptions& options, |
io::Printer* printer, |
const FieldDescriptor* field) const { |
printer->Print("$fieldname$: ", |
- "fieldname", JSObjectFieldName(options, field)); |
- |
- if (IsMap(options, field)) { |
- const FieldDescriptor* value_field = MapFieldValue(field); |
- // If the map values are of a message type, we must provide their static |
- // toObject() method; otherwise we pass undefined for that argument. |
- string value_to_object; |
- if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { |
- value_to_object = |
- GetPath(options, value_field->message_type()) + ".toObject"; |
- } else { |
- value_to_object = "undefined"; |
- } |
- printer->Print( |
- "(f = msg.get$name$()) ? f.toObject(includeInstance, $valuetoobject$) " |
- ": []", |
- "name", JSGetterName(options, field), "valuetoobject", value_to_object); |
- } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { |
+ "fieldname", JSObjectFieldName(field)); |
+ |
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { |
// Message field. |
if (field->is_repeated()) { |
{ |
printer->Print("jspb.Message.toObjectList(msg.get$getter$(),\n" |
" $type$.toObject, includeInstance)", |
- "getter", JSGetterName(options, field), |
+ "getter", JSGetterName(field), |
"type", SubmessageTypeRef(options, field)); |
} |
} else { |
printer->Print("(f = msg.get$getter$()) && " |
"$type$.toObject(includeInstance, f)", |
- "getter", JSGetterName(options, field), |
+ "getter", JSGetterName(field), |
"type", SubmessageTypeRef(options, field)); |
} |
- } else if (field->type() == FieldDescriptor::TYPE_BYTES) { |
- // For bytes fields we want to always return the B64 data. |
- printer->Print("msg.get$getter$()", |
- "getter", JSGetterName(options, field, BYTES_B64)); |
} else { |
- bool use_default = field->has_default_value(); |
- |
- if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 && |
- // Repeated fields get initialized to their default in the constructor |
- // (why?), so we emit a plain getField() call for them. |
- !field->is_repeated() && !UseBrokenPresenceSemantics(options, field)) { |
- // Proto3 puts all defaults (including implicit defaults) in toObject(). |
- // But for proto2 we leave the existing semantics unchanged: unset fields |
- // without default are unset. |
- use_default = true; |
+ // Simple field (singular or repeated). |
+ if ((!HasFieldPresence(field) && !field->is_repeated()) || |
+ field->type() == FieldDescriptor::TYPE_BYTES) { |
+ // Delegate to the generated get<field>() method in order not to duplicate |
+ // the proto3-field-default-value or byte-coercion logic here. |
+ printer->Print("msg.get$getter$()", |
+ "getter", JSGetterName(field, BYTES_B64)); |
+ } else { |
+ if (field->has_default_value()) { |
+ printer->Print("jspb.Message.getField(msg, $index$) == null ? " |
+ "$defaultValue$ : ", |
+ "index", JSFieldIndex(field), |
+ "defaultValue", JSFieldDefault(field)); |
+ } |
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT || |
+ field->cpp_type() == FieldDescriptor::CPPTYPE_DOUBLE) { |
+ if (field->is_repeated()) { |
+ printer->Print("jspb.Message.getRepeatedFloatingPointField(" |
+ "msg, $index$)", |
+ "index", JSFieldIndex(field)); |
+ } else if (field->is_optional() && !field->has_default_value()) { |
+ printer->Print("jspb.Message.getOptionalFloatingPointField(" |
+ "msg, $index$)", |
+ "index", JSFieldIndex(field)); |
+ } else { |
+ // Convert "NaN" to NaN. |
+ printer->Print("+jspb.Message.getField(msg, $index$)", |
+ "index", JSFieldIndex(field)); |
+ } |
+ } else { |
+ printer->Print("jspb.Message.getField(msg, $index$)", |
+ "index", JSFieldIndex(field)); |
+ } |
} |
- |
- // We don't implement this by calling the accessors, because the semantics |
- // of the accessors are changing independently of the toObject() semantics. |
- // We are migrating the accessors to return defaults instead of null, but |
- // it may take longer to migrate toObject (or we might not want to do it at |
- // all). So we want to generate independent code. |
- GenerateFieldValueExpression(printer, "msg", field, use_default); |
} |
} |
@@ -2247,29 +2008,7 @@ void Generator::GenerateClassFieldFromObject( |
const GeneratorOptions& options, |
io::Printer* printer, |
const FieldDescriptor* field) const { |
- if (IsMap(options, field)) { |
- const FieldDescriptor* value_field = MapFieldValue(field); |
- if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) { |
- // Since the map values are of message type, we have to do some extra work |
- // to recursively call fromObject() on them before setting the map field. |
- printer->Print( |
- " goog.isDef(obj.$name$) && jspb.Message.setWrapperField(\n" |
- " msg, $index$, jspb.Map.fromObject(obj.$name$, $fieldclass$, " |
- "$fieldclass$.fromObject));\n", |
- "name", JSObjectFieldName(options, field), |
- "index", JSFieldIndex(field), |
- "fieldclass", GetPath(options, value_field->message_type())); |
- } else { |
- // `msg` is a newly-constructed message object that has not yet built any |
- // map containers wrapping underlying arrays, so we can simply directly |
- // set the array here without fear of a stale wrapper. |
- printer->Print( |
- " goog.isDef(obj.$name$) && " |
- "jspb.Message.setField(msg, $index$, obj.$name$);\n", |
- "name", JSObjectFieldName(options, field), |
- "index", JSFieldIndex(field)); |
- } |
- } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { |
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { |
// Message field (singular or repeated) |
if (field->is_repeated()) { |
{ |
@@ -2279,7 +2018,7 @@ void Generator::GenerateClassFieldFromObject( |
" msg, $index$, goog.array.map(obj.$name$, function(i) {\n" |
" return $fieldclass$.fromObject(i);\n" |
" }));\n", |
- "name", JSObjectFieldName(options, field), |
+ "name", JSObjectFieldName(field), |
"index", JSFieldIndex(field), |
"fieldclass", SubmessageTypeRef(options, field)); |
} |
@@ -2287,7 +2026,7 @@ void Generator::GenerateClassFieldFromObject( |
printer->Print( |
" goog.isDef(obj.$name$) && jspb.Message.setWrapperField(\n" |
" msg, $index$, $fieldclass$.fromObject(obj.$name$));\n", |
- "name", JSObjectFieldName(options, field), |
+ "name", JSObjectFieldName(field), |
"index", JSFieldIndex(field), |
"fieldclass", SubmessageTypeRef(options, field)); |
} |
@@ -2296,11 +2035,26 @@ void Generator::GenerateClassFieldFromObject( |
printer->Print( |
" goog.isDef(obj.$name$) && jspb.Message.setField(msg, $index$, " |
"obj.$name$);\n", |
- "name", JSObjectFieldName(options, field), |
+ "name", JSObjectFieldName(field), |
"index", JSFieldIndex(field)); |
} |
} |
+void Generator::GenerateClassClone(const GeneratorOptions& options, |
+ io::Printer* printer, |
+ const Descriptor* desc) const { |
+ printer->Print( |
+ "/**\n" |
+ " * Creates a deep clone of this proto. No data is shared with the " |
+ "original.\n" |
+ " * @return {!$name$} The clone.\n" |
+ " */\n" |
+ "$name$.prototype.cloneMessage = function() {\n" |
+ " return /** @type {!$name$} */ (jspb.Message.cloneMessage(this));\n" |
+ "};\n\n\n", |
+ "name", GetPath(options, desc)); |
+} |
+ |
void Generator::GenerateClassRegistration(const GeneratorOptions& options, |
io::Printer* printer, |
const Descriptor* desc) const { |
@@ -2328,11 +2082,12 @@ void GenerateBytesWrapper(const GeneratorOptions& options, |
io::Printer* printer, |
const FieldDescriptor* field, |
BytesMode bytes_mode) { |
- string type = JSFieldTypeAnnotation( |
- options, field, |
- /* is_setter_argument = */ false, |
- /* force_present = */ false, |
- /* singular_if_not_packed = */ false, bytes_mode); |
+ string type = |
+ JSFieldTypeAnnotation(options, field, |
+ /* force_optional = */ false, |
+ /* force_present = */ !HasFieldPresence(field), |
+ /* singular_if_not_packed = */ false, |
+ bytes_mode); |
printer->Print( |
"/**\n" |
" * $fielddef$\n" |
@@ -2350,74 +2105,17 @@ void GenerateBytesWrapper(const GeneratorOptions& options, |
"comment", FieldComments(field, bytes_mode), |
"type", type, |
"class", GetPath(options, field->containing_type()), |
- "name", JSGetterName(options, field, bytes_mode), |
+ "name", JSGetterName(field, bytes_mode), |
"list", field->is_repeated() ? "List" : "", |
"suffix", JSByteGetterSuffix(bytes_mode), |
- "defname", JSGetterName(options, field, BYTES_DEFAULT)); |
+ "defname", JSGetterName(field, BYTES_DEFAULT)); |
} |
void Generator::GenerateClassField(const GeneratorOptions& options, |
io::Printer* printer, |
const FieldDescriptor* field) const { |
- if (IsMap(options, field)) { |
- const FieldDescriptor* key_field = MapFieldKey(field); |
- const FieldDescriptor* value_field = MapFieldValue(field); |
- // Map field: special handling to instantiate the map object on demand. |
- string key_type = |
- JSFieldTypeAnnotation( |
- options, key_field, |
- /* is_setter_argument = */ false, |
- /* force_present = */ true, |
- /* singular_if_not_packed = */ false); |
- string value_type = |
- JSFieldTypeAnnotation( |
- options, value_field, |
- /* is_setter_argument = */ false, |
- /* force_present = */ true, |
- /* singular_if_not_packed = */ false); |
- |
- printer->Print( |
- "/**\n" |
- " * $fielddef$\n" |
- " * @param {boolean=} opt_noLazyCreate Do not create the map if\n" |
- " * empty, instead returning `undefined`\n" |
- " * @return {!jspb.Map<$keytype$,$valuetype$>}\n" |
- " */\n", |
- "fielddef", FieldDefinition(options, field), |
- "keytype", key_type, |
- "valuetype", value_type); |
- printer->Print( |
- "$class$.prototype.get$name$ = function(opt_noLazyCreate) {\n" |
- " return /** @type {!jspb.Map<$keytype$,$valuetype$>} */ (\n", |
- "class", GetPath(options, field->containing_type()), |
- "name", JSGetterName(options, field), |
- "keytype", key_type, |
- "valuetype", value_type); |
- printer->Print( |
- " jspb.Message.getMapField(this, $index$, opt_noLazyCreate", |
- "index", JSFieldIndex(field)); |
- |
- if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) { |
- printer->Print(",\n" |
- " $messageType$", |
- "messageType", GetPath(options, value_field->message_type())); |
- } else { |
- printer->Print(",\n" |
- " null"); |
- } |
- |
- printer->Print( |
- "));\n"); |
- |
- printer->Print( |
- "};\n" |
- "\n" |
- "\n"); |
- } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { |
- // Message field: special handling in order to wrap the underlying data |
- // array with a message object. |
- |
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { |
printer->Print( |
"/**\n" |
" * $fielddef$\n" |
@@ -2427,7 +2125,7 @@ void Generator::GenerateClassField(const GeneratorOptions& options, |
"fielddef", FieldDefinition(options, field), |
"comment", FieldComments(field, BYTES_DEFAULT), |
"type", JSFieldTypeAnnotation(options, field, |
- /* is_setter_argument = */ false, |
+ /* force_optional = */ false, |
/* force_present = */ false, |
/* singular_if_not_packed = */ false)); |
printer->Print( |
@@ -2439,9 +2137,9 @@ void Generator::GenerateClassField(const GeneratorOptions& options, |
"\n" |
"\n", |
"class", GetPath(options, field->containing_type()), |
- "name", JSGetterName(options, field), |
+ "name", JSGetterName(field), |
"type", JSFieldTypeAnnotation(options, field, |
- /* is_setter_argument = */ false, |
+ /* force_optional = */ false, |
/* force_present = */ false, |
/* singular_if_not_packed = */ false), |
"rpt", (field->is_repeated() ? "Repeated" : ""), |
@@ -2450,17 +2148,17 @@ void Generator::GenerateClassField(const GeneratorOptions& options, |
"required", (field->label() == FieldDescriptor::LABEL_REQUIRED ? |
", 1" : "")); |
printer->Print( |
- "/** @param {$optionaltype$} value$returndoc$ */\n" |
+ "/** @param {$optionaltype$} value $returndoc$ */\n" |
"$class$.prototype.set$name$ = function(value) {\n" |
" jspb.Message.set$oneoftag$$repeatedtag$WrapperField(", |
"optionaltype", |
JSFieldTypeAnnotation(options, field, |
- /* is_setter_argument = */ true, |
+ /* force_optional = */ true, |
/* force_present = */ false, |
/* singular_if_not_packed = */ false), |
"returndoc", JSReturnDoc(options, field), |
"class", GetPath(options, field->containing_type()), |
- "name", JSGetterName(options, field), |
+ "name", JSGetterName(field), |
"oneoftag", (field->containing_oneof() ? "Oneof" : ""), |
"repeatedtag", (field->is_repeated() ? "Repeated" : "")); |
@@ -2474,9 +2172,16 @@ void Generator::GenerateClassField(const GeneratorOptions& options, |
(", " + JSOneofArray(options, field)) : ""), |
"returnvalue", JSReturnClause(field)); |
- if (field->is_repeated()) { |
- GenerateRepeatedMessageHelperMethods(options, printer, field); |
- } |
+ printer->Print( |
+ "$class$.prototype.clear$name$ = function() {\n" |
+ " this.set$name$($clearedvalue$);$returnvalue$\n" |
+ "};\n" |
+ "\n" |
+ "\n", |
+ "class", GetPath(options, field->containing_type()), |
+ "name", JSGetterName(field), |
+ "clearedvalue", (field->is_repeated() ? "[]" : "undefined"), |
+ "returnvalue", JSReturnClause(field)); |
} else { |
bool untyped = |
@@ -2490,12 +2195,12 @@ void Generator::GenerateClassField(const GeneratorOptions& options, |
BytesMode bytes_mode = |
field->type() == FieldDescriptor::TYPE_BYTES && !options.binary ? |
BYTES_B64 : BYTES_DEFAULT; |
- string typed_annotation = JSFieldTypeAnnotation( |
- options, field, |
- /* is_setter_argument = */ false, |
- /* force_present = */ false, |
- /* singular_if_not_packed = */ false, |
- /* bytes_mode = */ bytes_mode); |
+ string typed_annotation = |
+ JSFieldTypeAnnotation(options, field, |
+ /* force_optional = */ false, |
+ /* force_present = */ !HasFieldPresence(field), |
+ /* singular_if_not_packed = */ false, |
+ /* bytes_mode = */ bytes_mode); |
if (untyped) { |
printer->Print( |
"/**\n" |
@@ -2516,7 +2221,7 @@ void Generator::GenerateClassField(const GeneratorOptions& options, |
printer->Print( |
"$class$.prototype.get$name$ = function() {\n", |
"class", GetPath(options, field->containing_type()), |
- "name", JSGetterName(options, field)); |
+ "name", JSGetterName(field)); |
if (untyped) { |
printer->Print( |
@@ -2527,21 +2232,41 @@ void Generator::GenerateClassField(const GeneratorOptions& options, |
"type", typed_annotation); |
} |
- bool use_default = !ReturnsNullWhenUnset(options, field); |
- |
- // Raw fields with no default set should just return undefined. |
- if (untyped && !field->has_default_value()) { |
- use_default = false; |
- } |
- |
- // Repeated fields get initialized to their default in the constructor |
- // (why?), so we emit a plain getField() call for them. |
- if (field->is_repeated()) { |
- use_default = false; |
+ // For proto3 fields without presence, use special getters that will return |
+ // defaults when the field is unset, possibly constructing a value if |
+ // required. |
+ if (!HasFieldPresence(field) && !field->is_repeated()) { |
+ printer->Print("jspb.Message.getFieldProto3(this, $index$, $default$)", |
+ "index", JSFieldIndex(field), |
+ "default", Proto3PrimitiveFieldDefault(field)); |
+ } else { |
+ if (field->has_default_value()) { |
+ printer->Print("jspb.Message.getField(this, $index$) == null ? " |
+ "$defaultValue$ : ", |
+ "index", JSFieldIndex(field), |
+ "defaultValue", JSFieldDefault(field)); |
+ } |
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT || |
+ field->cpp_type() == FieldDescriptor::CPPTYPE_DOUBLE) { |
+ if (field->is_repeated()) { |
+ printer->Print("jspb.Message.getRepeatedFloatingPointField(" |
+ "this, $index$)", |
+ "index", JSFieldIndex(field)); |
+ } else if (field->is_optional() && !field->has_default_value()) { |
+ printer->Print("jspb.Message.getOptionalFloatingPointField(" |
+ "this, $index$)", |
+ "index", JSFieldIndex(field)); |
+ } else { |
+ // Convert "NaN" to NaN. |
+ printer->Print("+jspb.Message.getField(this, $index$)", |
+ "index", JSFieldIndex(field)); |
+ } |
+ } else { |
+ printer->Print("jspb.Message.getField(this, $index$)", |
+ "index", JSFieldIndex(field)); |
+ } |
} |
- GenerateFieldValueExpression(printer, "this", field, use_default); |
- |
if (untyped) { |
printer->Print( |
";\n" |
@@ -2564,24 +2289,24 @@ void Generator::GenerateClassField(const GeneratorOptions& options, |
if (untyped) { |
printer->Print( |
"/**\n" |
- " * @param {*} value$returndoc$\n" |
+ " * @param {*} value $returndoc$\n" |
" */\n", |
"returndoc", JSReturnDoc(options, field)); |
} else { |
printer->Print( |
- "/** @param {$optionaltype$} value$returndoc$ */\n", "optionaltype", |
- JSFieldTypeAnnotation( |
- options, field, |
- /* is_setter_argument = */ true, |
- /* force_present = */ false, |
- /* singular_if_not_packed = */ false), |
+ "/** @param {$optionaltype$} value $returndoc$ */\n", |
+ "optionaltype", |
+ JSFieldTypeAnnotation(options, field, |
+ /* force_optional = */ true, |
+ /* force_present = */ !HasFieldPresence(field), |
+ /* singular_if_not_packed = */ false), |
"returndoc", JSReturnDoc(options, field)); |
} |
printer->Print( |
"$class$.prototype.set$name$ = function(value) {\n" |
" jspb.Message.set$oneoftag$Field(this, $index$", |
"class", GetPath(options, field->containing_type()), |
- "name", JSGetterName(options, field), |
+ "name", JSGetterName(field), |
"oneoftag", (field->containing_oneof() ? "Oneof" : ""), |
"index", JSFieldIndex(field)); |
printer->Print( |
@@ -2601,133 +2326,30 @@ void Generator::GenerateClassField(const GeneratorOptions& options, |
if (untyped) { |
printer->Print( |
"/**\n" |
- " * Clears the value.$returndoc$\n" |
+ " * Clears the value. $returndoc$\n" |
" */\n", |
"returndoc", JSReturnDoc(options, field)); |
} |
- |
- if (field->is_repeated()) { |
- GenerateRepeatedPrimitiveHelperMethods(options, printer, field, untyped); |
+ if (HasFieldPresence(field)) { |
+ printer->Print( |
+ "$class$.prototype.clear$name$ = function() {\n" |
+ " jspb.Message.set$oneoftag$Field(this, $index$$oneofgroup$, ", |
+ "class", GetPath(options, field->containing_type()), |
+ "name", JSGetterName(field), |
+ "oneoftag", (field->containing_oneof() ? "Oneof" : ""), |
+ "oneofgroup", (field->containing_oneof() ? |
+ (", " + JSOneofArray(options, field)) : ""), |
+ "index", JSFieldIndex(field)); |
+ printer->Print( |
+ "$clearedvalue$);$returnvalue$\n" |
+ "};\n" |
+ "\n" |
+ "\n", |
+ "clearedvalue", (field->is_repeated() ? "[]" : "undefined"), |
+ "returnvalue", JSReturnClause(field)); |
} |
} |
- |
- // Generate clearFoo() method for map fields, repeated fields, and other |
- // fields with presence. |
- if (IsMap(options, field)) { |
- printer->Print( |
- "$class$.prototype.clear$name$ = function() {\n" |
- " this.get$name$().clear();$returnvalue$\n" |
- "};\n" |
- "\n" |
- "\n", |
- "class", GetPath(options, field->containing_type()), |
- "name", JSGetterName(options, field), |
- "returnvalue", JSReturnClause(field)); |
- } else if (field->is_repeated() || |
- (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && |
- !field->is_required())) { |
- // Fields where we can delegate to the regular setter. |
- printer->Print( |
- "$class$.prototype.clear$name$ = function() {\n" |
- " this.set$name$($clearedvalue$);$returnvalue$\n" |
- "};\n" |
- "\n" |
- "\n", |
- "class", GetPath(options, field->containing_type()), |
- "name", JSGetterName(options, field), |
- "clearedvalue", (field->is_repeated() ? "[]" : "undefined"), |
- "returnvalue", JSReturnClause(field)); |
- } else if (HasFieldPresence(options, field)) { |
- // Fields where we can't delegate to the regular setter because it doesn't |
- // accept "undefined" as an argument. |
- printer->Print( |
- "$class$.prototype.clear$name$ = function() {\n" |
- " jspb.Message.set$maybeoneof$Field(this, " |
- "$index$$maybeoneofgroup$, ", |
- "class", GetPath(options, field->containing_type()), |
- "name", JSGetterName(options, field), |
- "maybeoneof", (field->containing_oneof() ? "Oneof" : ""), |
- "maybeoneofgroup", (field->containing_oneof() ? |
- (", " + JSOneofArray(options, field)) : ""), |
- "index", JSFieldIndex(field)); |
- printer->Print( |
- "$clearedvalue$);$returnvalue$\n" |
- "};\n" |
- "\n" |
- "\n", |
- "clearedvalue", (field->is_repeated() ? "[]" : "undefined"), |
- "returnvalue", JSReturnClause(field)); |
- } |
- |
- if (HasFieldPresence(options, field)) { |
- printer->Print( |
- "/**\n" |
- " * Returns whether this field is set.\n" |
- " * @return {!boolean}\n" |
- " */\n" |
- "$class$.prototype.has$name$ = function() {\n" |
- " return jspb.Message.getField(this, $index$) != null;\n" |
- "};\n" |
- "\n" |
- "\n", |
- "class", GetPath(options, field->containing_type()), |
- "name", JSGetterName(options, field), |
- "index", JSFieldIndex(field)); |
- } |
-} |
- |
-void Generator::GenerateRepeatedPrimitiveHelperMethods( |
- const GeneratorOptions& options, io::Printer* printer, |
- const FieldDescriptor* field, bool untyped) const { |
- printer->Print( |
- "/**\n" |
- " * @param {!$optionaltype$} value\n" |
- " * @param {number=} opt_index\n" |
- " */\n" |
- "$class$.prototype.add$name$ = function(value, opt_index) {\n" |
- " jspb.Message.addToRepeatedField(this, $index$", |
- "class", GetPath(options, field->containing_type()), "name", |
- JSGetterName(options, field, BYTES_DEFAULT, |
- /* drop_list = */ true), |
- "optionaltype", JSTypeName(options, field, BYTES_DEFAULT), "index", |
- JSFieldIndex(field)); |
- printer->Print( |
- "$oneofgroup$, $type$value$rptvalueinit$$typeclose$, opt_index);\n" |
- "};\n" |
- "\n" |
- "\n", |
- "type", untyped ? "/** @type{string|number|boolean|!Uint8Array} */(" : "", |
- "typeclose", untyped ? ")" : "", "oneofgroup", |
- (field->containing_oneof() ? (", " + JSOneofArray(options, field)) : ""), |
- "rptvalueinit", ""); |
-} |
- |
-void Generator::GenerateRepeatedMessageHelperMethods( |
- const GeneratorOptions& options, io::Printer* printer, |
- const FieldDescriptor* field) const { |
- printer->Print( |
- "/**\n" |
- " * @param {!$optionaltype$=} opt_value\n" |
- " * @param {number=} opt_index\n" |
- " * @return {!$optionaltype$}\n" |
- " */\n" |
- "$class$.prototype.add$name$ = function(opt_value, opt_index) {\n" |
- " return jspb.Message.addTo$repeatedtag$WrapperField(", |
- "optionaltype", JSTypeName(options, field, BYTES_DEFAULT), "class", |
- GetPath(options, field->containing_type()), "name", |
- JSGetterName(options, field, BYTES_DEFAULT, |
- /* drop_list = */ true), |
- "repeatedtag", (field->is_repeated() ? "Repeated" : "")); |
- |
- printer->Print( |
- "this, $index$$oneofgroup$, opt_value, $ctor$, opt_index);\n" |
- "};\n" |
- "\n" |
- "\n", |
- "index", JSFieldIndex(field), "oneofgroup", |
- (field->containing_oneof() ? (", " + JSOneofArray(options, field)) : ""), |
- "ctor", GetPath(options, field->message_type())); |
} |
void Generator::GenerateClassExtensionFieldInfo(const GeneratorOptions& options, |
@@ -2754,27 +2376,6 @@ void Generator::GenerateClassExtensionFieldInfo(const GeneratorOptions& options, |
"$class$.extensions = {};\n" |
"\n", |
"class", GetPath(options, desc)); |
- |
- printer->Print( |
- "\n" |
- "/**\n" |
- " * The extensions registered with this message class. This is a " |
- "map of\n" |
- " * extension field number to fieldInfo object.\n" |
- " *\n" |
- " * For example:\n" |
- " * { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, " |
- "ctor: proto.example.MyMessage} }\n" |
- " *\n" |
- " * fieldName contains the JsCompiler renamed field name property " |
- "so that it\n" |
- " * works in OPTIMIZED mode.\n" |
- " *\n" |
- " * @type {!Object.<number, jspb.ExtensionFieldBinaryInfo>}\n" |
- " */\n" |
- "$class$.extensionsBinary = {};\n" |
- "\n", |
- "class", GetPath(options, desc)); |
} |
} |
@@ -2815,16 +2416,14 @@ void Generator::GenerateClassDeserializeBinary(const GeneratorOptions& options, |
"class", GetPath(options, desc)); |
for (int i = 0; i < desc->field_count(); i++) { |
- if (!IgnoreField(desc->field(i))) { |
- GenerateClassDeserializeBinaryField(options, printer, desc->field(i)); |
- } |
+ GenerateClassDeserializeBinaryField(options, printer, desc->field(i)); |
} |
printer->Print( |
" default:\n"); |
if (IsExtendable(desc)) { |
printer->Print( |
- " jspb.Message.readBinaryExtension(msg, reader, $extobj$Binary,\n" |
+ " jspb.Message.readBinaryExtension(msg, reader, $extobj$,\n" |
" $class$.prototype.getExtension,\n" |
" $class$.prototype.setExtension);\n" |
" break;\n", |
@@ -2853,60 +2452,40 @@ void Generator::GenerateClassDeserializeBinaryField( |
printer->Print(" case $num$:\n", |
"num", SimpleItoa(field->number())); |
- if (IsMap(options, field)) { |
- const FieldDescriptor* key_field = MapFieldKey(field); |
- const FieldDescriptor* value_field = MapFieldValue(field); |
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { |
printer->Print( |
- " var value = msg.get$name$();\n" |
- " reader.readMessage(value, function(message, reader) {\n", |
- "name", JSGetterName(options, field)); |
- |
- printer->Print(" jspb.Map.deserializeBinary(message, reader, " |
- "$keyReaderFn$, $valueReaderFn$", |
- "keyReaderFn", JSBinaryReaderMethodName(options, key_field), |
- "valueReaderFn", JSBinaryReaderMethodName(options, value_field)); |
- |
- if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) { |
- printer->Print(", $messageType$.deserializeBinaryFromReader", |
- "messageType", GetPath(options, value_field->message_type())); |
- } |
- |
- printer->Print(");\n"); |
- printer->Print(" });\n"); |
- } else { |
- if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { |
- printer->Print( |
- " var value = new $fieldclass$;\n" |
- " reader.read$msgOrGroup$($grpfield$value," |
- "$fieldclass$.deserializeBinaryFromReader);\n", |
+ " var value = new $fieldclass$;\n" |
+ " reader.read$msgOrGroup$($grpfield$value," |
+ "$fieldclass$.deserializeBinaryFromReader);\n", |
"fieldclass", SubmessageTypeRef(options, field), |
- "msgOrGroup", (field->type() == FieldDescriptor::TYPE_GROUP) ? |
- "Group" : "Message", |
- "grpfield", (field->type() == FieldDescriptor::TYPE_GROUP) ? |
- (SimpleItoa(field->number()) + ", ") : ""); |
- } else { |
- printer->Print( |
- " var value = /** @type {$fieldtype$} */ " |
- "(reader.read$reader$());\n", |
- "fieldtype", JSFieldTypeAnnotation(options, field, false, true, |
- /* singular_if_not_packed */ true, |
- BYTES_U8), |
- "reader", |
- JSBinaryReadWriteMethodName(field, /* is_writer = */ false)); |
- } |
- |
- if (field->is_repeated() && !field->is_packed()) { |
- printer->Print( |
- " msg.add$name$(value);\n", "name", |
- JSGetterName(options, field, BYTES_DEFAULT, /* drop_list = */ true)); |
- } else { |
- // Singular fields, and packed repeated fields, receive a |value| either |
- // as the field's value or as the array of all the field's values; set |
- // this as the field's value directly. |
- printer->Print( |
- " msg.set$name$(value);\n", |
- "name", JSGetterName(options, field)); |
- } |
+ "msgOrGroup", (field->type() == FieldDescriptor::TYPE_GROUP) ? |
+ "Group" : "Message", |
+ "grpfield", (field->type() == FieldDescriptor::TYPE_GROUP) ? |
+ (SimpleItoa(field->number()) + ", ") : ""); |
+ } else { |
+ printer->Print( |
+ " var value = /** @type {$fieldtype$} */ (reader.$reader$());\n", |
+ "fieldtype", JSFieldTypeAnnotation(options, field, false, true, |
+ /* singular_if_not_packed = */ true, |
+ BYTES_U8), |
+ "reader", JSBinaryReaderMethodName(field)); |
+ } |
+ |
+ if (field->is_repeated() && !field->is_packed()) { |
+ // Repeated fields receive a |value| one at at a time; append to array |
+ // returned by get$name$(). Annoyingly, we have to call 'set' after |
+ // changing the array. |
+ printer->Print(" msg.get$name$().push(value);\n", "name", |
+ JSGetterName(field)); |
+ printer->Print(" msg.set$name$(msg.get$name$());\n", "name", |
+ JSGetterName(field)); |
+ } else { |
+ // Singular fields, and packed repeated fields, receive a |value| either as |
+ // the field's value or as the array of all the field's values; set this as |
+ // the field's value directly. |
+ printer->Print( |
+ " msg.set$name$(value);\n", |
+ "name", JSGetterName(field)); |
} |
printer->Print(" break;\n"); |
@@ -2917,37 +2496,45 @@ void Generator::GenerateClassSerializeBinary(const GeneratorOptions& options, |
const Descriptor* desc) const { |
printer->Print( |
"/**\n" |
+ " * Class method variant: serializes the given message to binary data\n" |
+ " * (in protobuf wire format), writing to the given BinaryWriter.\n" |
+ " * @param {!$class$} message\n" |
+ " * @param {!jspb.BinaryWriter} writer\n" |
+ " */\n" |
+ "$class$.serializeBinaryToWriter = function(message, " |
+ "writer) {\n" |
+ " message.serializeBinaryToWriter(writer);\n" |
+ "};\n" |
+ "\n" |
+ "\n" |
+ "/**\n" |
" * Serializes the message to binary data (in protobuf wire format).\n" |
" * @return {!Uint8Array}\n" |
" */\n" |
"$class$.prototype.serializeBinary = function() {\n" |
" var writer = new jspb.BinaryWriter();\n" |
- " $class$.serializeBinaryToWriter(this, writer);\n" |
+ " this.serializeBinaryToWriter(writer);\n" |
" return writer.getResultBuffer();\n" |
"};\n" |
"\n" |
"\n" |
"/**\n" |
- " * Serializes the given message to binary data (in protobuf wire\n" |
- " * format), writing to the given BinaryWriter.\n" |
- " * @param {!$class$} message\n" |
+ " * Serializes the message to binary data (in protobuf wire format),\n" |
+ " * writing to the given BinaryWriter.\n" |
" * @param {!jspb.BinaryWriter} writer\n" |
" */\n" |
- "$class$.serializeBinaryToWriter = function(message, " |
- "writer) {\n" |
+ "$class$.prototype.serializeBinaryToWriter = function (writer) {\n" |
" var f = undefined;\n", |
"class", GetPath(options, desc)); |
for (int i = 0; i < desc->field_count(); i++) { |
- if (!IgnoreField(desc->field(i))) { |
- GenerateClassSerializeBinaryField(options, printer, desc->field(i)); |
- } |
+ GenerateClassSerializeBinaryField(options, printer, desc->field(i)); |
} |
if (IsExtendable(desc)) { |
printer->Print( |
- " jspb.Message.serializeBinaryExtensions(message, writer,\n" |
- " $extobj$Binary, $class$.prototype.getExtension);\n", |
+ " jspb.Message.serializeBinaryExtensions(this, writer, $extobj$,\n" |
+ " $class$.prototype.getExtension);\n", |
"extobj", JSExtensionsObjectName(options, desc->file(), desc), |
"class", GetPath(options, desc)); |
} |
@@ -2962,37 +2549,15 @@ void Generator::GenerateClassSerializeBinaryField( |
const GeneratorOptions& options, |
io::Printer* printer, |
const FieldDescriptor* field) const { |
- if (HasFieldPresence(options, field) && |
- field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { |
- string typed_annotation = JSFieldTypeAnnotation( |
- options, field, |
- /* is_setter_argument = */ false, |
- /* force_present = */ false, |
- /* singular_if_not_packed = */ false, |
- /* bytes_mode = */ BYTES_DEFAULT); |
- printer->Print( |
- " f = /** @type {$type$} */ " |
- "(jspb.Message.getField(message, $index$));\n", |
- "index", JSFieldIndex(field), |
- "type", typed_annotation); |
- } else { |
- printer->Print( |
- " f = message.get$name$($nolazy$);\n", |
- "name", JSGetterName(options, field, BYTES_U8), |
- // No lazy creation for maps containers -- fastpath the empty case. |
- "nolazy", IsMap(options, field) ? "true" : ""); |
- } |
+ printer->Print( |
+ " f = this.get$name$();\n", |
+ "name", JSGetterName(field, BYTES_U8)); |
- // Print an `if (condition)` statement that evaluates to true if the field |
- // goes on the wire. |
- if (IsMap(options, field)) { |
- printer->Print( |
- " if (f && f.getLength() > 0) {\n"); |
- } else if (field->is_repeated()) { |
+ if (field->is_repeated()) { |
printer->Print( |
" if (f.length > 0) {\n"); |
} else { |
- if (HasFieldPresence(options, field)) { |
+ if (HasFieldPresence(field)) { |
printer->Print( |
" if (f != null) {\n"); |
} else { |
@@ -3031,47 +2596,23 @@ void Generator::GenerateClassSerializeBinaryField( |
} |
} |
- // Write the field on the wire. |
- if (IsMap(options, field)) { |
- const FieldDescriptor* key_field = MapFieldKey(field); |
- const FieldDescriptor* value_field = MapFieldValue(field); |
- printer->Print( |
- " f.serializeBinary($index$, writer, " |
- "$keyWriterFn$, $valueWriterFn$", |
- "index", SimpleItoa(field->number()), |
- "keyWriterFn", JSBinaryWriterMethodName(options, key_field), |
- "valueWriterFn", JSBinaryWriterMethodName(options, value_field)); |
- |
- if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) { |
- printer->Print(", $messageType$.serializeBinaryToWriter", |
- "messageType", GetPath(options, value_field->message_type())); |
- } |
+ printer->Print( |
+ " writer.$writer$(\n" |
+ " $index$,\n" |
+ " f", |
+ "writer", JSBinaryWriterMethodName(field), |
+ "index", SimpleItoa(field->number())); |
- printer->Print(");\n"); |
- } else { |
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { |
printer->Print( |
- " writer.write$method$(\n" |
- " $index$,\n" |
- " f", |
- "method", JSBinaryReadWriteMethodName(field, /* is_writer = */ true), |
- "index", SimpleItoa(field->number())); |
- |
- if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && |
- !IsMap(options, field)) { |
- printer->Print( |
- ",\n" |
- " $submsg$.serializeBinaryToWriter\n", |
+ ",\n" |
+ " $submsg$.serializeBinaryToWriter\n", |
"submsg", SubmessageTypeRef(options, field)); |
- } else { |
- printer->Print("\n"); |
- } |
- |
- printer->Print( |
- " );\n"); |
+ } else { |
+ printer->Print("\n"); |
} |
- |
- // Close the `if`. |
printer->Print( |
+ " );\n" |
" }\n"); |
} |
@@ -3115,11 +2656,11 @@ void Generator::GenerateExtension(const GeneratorOptions& options, |
" * @type {!jspb.ExtensionFieldInfo.<$extensionType$>}\n" |
" */\n" |
"$class$.$name$ = new jspb.ExtensionFieldInfo(\n", |
- "name", JSObjectFieldName(options, field), |
+ "name", JSObjectFieldName(field), |
"class", extension_scope, |
"extensionType", JSFieldTypeAnnotation( |
options, field, |
- /* is_setter_argument = */ false, |
+ /* force_optional = */ false, |
/* force_present = */ true, |
/* singular_if_not_packed = */ false)); |
printer->Print( |
@@ -3129,9 +2670,9 @@ void Generator::GenerateExtension(const GeneratorOptions& options, |
" /** @type {?function((boolean|undefined),!jspb.Message=): " |
"!Object} */ (\n" |
" $toObject$),\n" |
- " $repeated$);\n", |
+ " $repeated$", |
"index", SimpleItoa(field->number()), |
- "name", JSObjectFieldName(options, field), |
+ "name", JSObjectFieldName(field), |
"ctor", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ? |
SubmessageTypeRef(options, field) : string("null")), |
"toObject", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ? |
@@ -3139,30 +2680,28 @@ void Generator::GenerateExtension(const GeneratorOptions& options, |
string("null")), |
"repeated", (field->is_repeated() ? "1" : "0")); |
- printer->Print( |
- "\n" |
- "$extendName$Binary[$index$] = new jspb.ExtensionFieldBinaryInfo(\n" |
- " $class$.$name$,\n" |
- " $binaryReaderFn$,\n" |
- " $binaryWriterFn$,\n" |
- " $binaryMessageSerializeFn$,\n" |
- " $binaryMessageDeserializeFn$,\n", |
- "extendName", |
- JSExtensionsObjectName(options, field->file(), field->containing_type()), |
- "index", SimpleItoa(field->number()), "class", extension_scope, "name", |
- JSObjectFieldName(options, field), "binaryReaderFn", |
- JSBinaryReaderMethodName(options, field), "binaryWriterFn", |
- JSBinaryWriterMethodName(options, field), "binaryMessageSerializeFn", |
- (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) |
- ? (SubmessageTypeRef(options, field) + ".serializeBinaryToWriter") |
- : "undefined", |
- "binaryMessageDeserializeFn", |
- (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) |
- ? (SubmessageTypeRef(options, field) + ".deserializeBinaryFromReader") |
- : "undefined"); |
- |
- printer->Print(" $isPacked$);\n", "isPacked", |
- (field->is_packed() ? "true" : "false")); |
+ if (options.binary) { |
+ printer->Print( |
+ ",\n" |
+ " jspb.BinaryReader.prototype.$binaryReaderFn$,\n" |
+ " jspb.BinaryWriter.prototype.$binaryWriterFn$,\n" |
+ " $binaryMessageSerializeFn$,\n" |
+ " $binaryMessageDeserializeFn$,\n" |
+ " $isPacked$);\n", |
+ "binaryReaderFn", JSBinaryReaderMethodName(field), |
+ "binaryWriterFn", JSBinaryWriterMethodName(field), |
+ "binaryMessageSerializeFn", |
+ (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ? |
+ (SubmessageTypeRef(options, field) + |
+ ".serializeBinaryToWriter") : "null", |
+ "binaryMessageDeserializeFn", |
+ (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ? |
+ (SubmessageTypeRef(options, field) + |
+ ".deserializeBinaryFromReader") : "null", |
+ "isPacked", (field->is_packed() ? "true" : "false")); |
+ } else { |
+ printer->Print(");\n"); |
+ } |
printer->Print( |
"// This registers the extension field with the extended class, so that\n" |
@@ -3173,11 +2712,11 @@ void Generator::GenerateExtension(const GeneratorOptions& options, |
field->containing_type()), |
"index", SimpleItoa(field->number()), |
"class", extension_scope, |
- "name", JSObjectFieldName(options, field)); |
+ "name", JSObjectFieldName(field)); |
} |
bool GeneratorOptions::ParseFromOptions( |
- const std::vector< std::pair< string, string > >& options, |
+ const vector< pair< string, string > >& options, |
string* error) { |
for (int i = 0; i < options.size(); i++) { |
if (options[i].first == "add_require_for_enums") { |
@@ -3212,25 +2751,17 @@ bool GeneratorOptions::ParseFromOptions( |
library = options[i].second; |
} else if (options[i].first == "import_style") { |
if (options[i].second == "closure") { |
- import_style = kImportClosure; |
+ import_style = IMPORT_CLOSURE; |
} else if (options[i].second == "commonjs") { |
- import_style = kImportCommonJs; |
+ import_style = IMPORT_COMMONJS; |
} else if (options[i].second == "browser") { |
- import_style = kImportBrowser; |
+ import_style = IMPORT_BROWSER; |
} else if (options[i].second == "es6") { |
- import_style = kImportEs6; |
+ import_style = IMPORT_ES6; |
} else { |
*error = "Unknown import style " + options[i].second + ", expected " + |
"one of: closure, commonjs, browser, es6."; |
} |
- } else if (options[i].first == "extension") { |
- extension = options[i].second; |
- } else if (options[i].first == "one_output_file_per_input_file") { |
- if (!options[i].second.empty()) { |
- *error = "Unexpected option value for one_output_file_per_input_file"; |
- return false; |
- } |
- one_output_file_per_input_file = true; |
} else { |
// Assume any other option is an output directory, as long as it is a bare |
// `key` rather than a `key=value` option. |
@@ -3242,40 +2773,18 @@ bool GeneratorOptions::ParseFromOptions( |
} |
} |
- if (import_style != kImportClosure && |
- (add_require_for_enums || testonly || !library.empty() || |
- error_on_name_conflict || extension != ".js" || |
- one_output_file_per_input_file)) { |
- *error = |
- "The add_require_for_enums, testonly, library, error_on_name_conflict, " |
- "extension, and one_output_file_per_input_file options should only be " |
- "used for import_style=closure"; |
- return false; |
+ if (!library.empty() && import_style != IMPORT_CLOSURE) { |
+ *error = "The library option should only be used for " |
+ "import_style=closure"; |
} |
return true; |
} |
-GeneratorOptions::OutputMode GeneratorOptions::output_mode() const { |
- // We use one output file per input file if we are not using Closure or if |
- // this is explicitly requested. |
- if (import_style != kImportClosure || one_output_file_per_input_file) { |
- return kOneOutputFilePerInputFile; |
- } |
- |
- // If a library name is provided, we put everything in that one file. |
- if (!library.empty()) { |
- return kEverythingInOneFile; |
- } |
- |
- // Otherwise, we create one output file per type. |
- return kOneOutputFilePerType; |
-} |
- |
void Generator::GenerateFilesInDepOrder( |
const GeneratorOptions& options, |
io::Printer* printer, |
- const std::vector<const FileDescriptor*>& files) const { |
+ const vector<const FileDescriptor*>& files) const { |
// Build a std::set over all files so that the DFS can detect when it recurses |
// into a dep not specified in the user's command line. |
std::set<const FileDescriptor*> all_files(files.begin(), files.end()); |
@@ -3318,7 +2827,7 @@ void Generator::GenerateFile(const GeneratorOptions& options, |
GenerateHeader(options, printer); |
// Generate "require" statements. |
- if (options.import_style == GeneratorOptions::kImportCommonJs) { |
+ if (options.import_style == GeneratorOptions::IMPORT_COMMONJS) { |
printer->Print("var jspb = require('google-protobuf');\n"); |
printer->Print("var goog = jspb;\n"); |
printer->Print("var global = Function('return this')();\n\n"); |
@@ -3328,61 +2837,52 @@ void Generator::GenerateFile(const GeneratorOptions& options, |
printer->Print( |
"var $alias$ = require('$file$');\n", |
"alias", ModuleAlias(name), |
- "file", GetRootPath(file->name(), name) + GetJSFilename(options, name)); |
+ "file", GetRootPath(file->name()) + GetJSFilename(name)); |
} |
} |
- std::set<string> provided; |
- std::set<const FieldDescriptor*> extensions; |
+ // We aren't using Closure's import system, but we use goog.exportSymbol() |
+ // to construct the expected tree of objects, eg. |
+ // |
+ // goog.exportSymbol('foo.bar.Baz', null, this); |
+ // |
+ // // Later generated code expects foo.bar = {} to exist: |
+ // foo.bar.Baz = function() { /* ... */ } |
+ set<string> provided; |
+ |
+ // Cover the case where this file declares extensions but no messages. |
+ // This will ensure that the file-level object will be declared to hold |
+ // the extensions. |
for (int i = 0; i < file->extension_count(); i++) { |
- // We honor the jspb::ignore option here only when working with |
- // Closure-style imports. Use of this option is discouraged and so we want |
- // to avoid adding new support for it. |
- if (options.import_style == GeneratorOptions::kImportClosure && |
- IgnoreField(file->extension(i))) { |
- continue; |
- } |
- provided.insert(GetPath(options, file) + "." + |
- JSObjectFieldName(options, file->extension(i))); |
- extensions.insert(file->extension(i)); |
+ provided.insert(file->extension(i)->full_name()); |
} |
FindProvidesForFile(options, printer, file, &provided); |
- GenerateProvides(options, printer, &provided); |
- std::vector<const FileDescriptor*> files; |
- files.push_back(file); |
- if (options.import_style == GeneratorOptions::kImportClosure) { |
- GenerateRequiresForLibrary(options, printer, files, &provided); |
+ for (std::set<string>::iterator it = provided.begin(); |
+ it != provided.end(); ++it) { |
+ printer->Print("goog.exportSymbol('$name$', null, global);\n", |
+ "name", *it); |
} |
GenerateClassesAndEnums(options, printer, file); |
- // Generate code for top-level extensions. Extensions nested inside messages |
- // are emitted inside GenerateClassesAndEnums(). |
- for (std::set<const FieldDescriptor*>::const_iterator it = extensions.begin(); |
- it != extensions.end(); ++it) { |
- GenerateExtension(options, printer, *it); |
+ // Extensions nested inside messages are emitted inside |
+ // GenerateClassesAndEnums(). |
+ for (int i = 0; i < file->extension_count(); i++) { |
+ GenerateExtension(options, printer, file->extension(i)); |
} |
- if (options.import_style == GeneratorOptions::kImportCommonJs) { |
+ if (options.import_style == GeneratorOptions::IMPORT_COMMONJS) { |
printer->Print("goog.object.extend(exports, $package$);\n", |
"package", GetPath(options, file)); |
} |
- |
- // Emit well-known type methods. |
- for (FileToc* toc = well_known_types_js; toc->name != NULL; toc++) { |
- string name = string("google/protobuf/") + toc->name; |
- if (name == StripProto(file->name()) + ".js") { |
- printer->Print(toc->data); |
- } |
- } |
} |
-bool Generator::GenerateAll(const std::vector<const FileDescriptor*>& files, |
+bool Generator::GenerateAll(const vector<const FileDescriptor*>& files, |
const string& parameter, |
GeneratorContext* context, |
string* error) const { |
- std::vector< std::pair< string, string > > option_pairs; |
+ vector< pair< string, string > > option_pairs; |
ParseGeneratorParameter(parameter, &option_pairs); |
GeneratorOptions options; |
if (!options.ParseFromOptions(option_pairs, error)) { |
@@ -3390,17 +2890,22 @@ bool Generator::GenerateAll(const std::vector<const FileDescriptor*>& files, |
} |
- if (options.output_mode() == GeneratorOptions::kEverythingInOneFile) { |
+ // There are three schemes for where output files go: |
+ // |
+ // - import_style = IMPORT_CLOSURE, library non-empty: all output in one file |
+ // - import_style = IMPORT_CLOSURE, library empty: one output file per type |
+ // - import_style != IMPORT_CLOSURE: one output file per .proto file |
+ if (options.import_style == GeneratorOptions::IMPORT_CLOSURE && |
+ options.library != "") { |
// All output should go in a single file. |
- string filename = options.output_dir + "/" + options.library + |
- options.GetFileNameExtension(); |
+ string filename = options.output_dir + "/" + options.library + ".js"; |
google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->Open(filename)); |
GOOGLE_CHECK(output.get()); |
io::Printer printer(output.get(), '$'); |
// Pull out all extensions -- we need these to generate all |
// provides/requires. |
- std::vector<const FieldDescriptor*> extensions; |
+ vector<const FieldDescriptor*> extensions; |
for (int i = 0; i < files.size(); i++) { |
for (int j = 0; j < files[i]->extension_count(); j++) { |
const FieldDescriptor* extension = files[i]->extension(j); |
@@ -3428,8 +2933,8 @@ bool Generator::GenerateAll(const std::vector<const FileDescriptor*>& files, |
if (printer.failed()) { |
return false; |
} |
- } else if (options.output_mode() == GeneratorOptions::kOneOutputFilePerType) { |
- std::set<const void*> allowed_set; |
+ } else if (options.import_style == GeneratorOptions::IMPORT_CLOSURE) { |
+ set<const void*> allowed_set; |
if (!GenerateJspbAllowedSet(options, files, &allowed_set, error)) { |
return false; |
} |
@@ -3500,7 +3005,7 @@ bool Generator::GenerateAll(const std::vector<const FileDescriptor*>& files, |
GenerateHeader(options, &printer); |
std::set<string> provided; |
- std::vector<const FieldDescriptor*> fields; |
+ vector<const FieldDescriptor*> fields; |
for (int j = 0; j < files[i]->extension_count(); j++) { |
if (ShouldGenerateExtension(files[i]->extension(j))) { |
@@ -3520,14 +3025,13 @@ bool Generator::GenerateAll(const std::vector<const FileDescriptor*>& files, |
} |
} |
} |
- } else /* options.output_mode() == kOneOutputFilePerInputFile */ { |
+ } else { |
// Generate one output file per input (.proto) file. |
for (int i = 0; i < files.size(); i++) { |
const google::protobuf::FileDescriptor* file = files[i]; |
- string filename = |
- options.output_dir + "/" + GetJSFilename(options, file->name()); |
+ string filename = options.output_dir + "/" + GetJSFilename(file->name()); |
google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->Open(filename)); |
GOOGLE_CHECK(output.get()); |
io::Printer printer(output.get(), '$'); |