| 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
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..e6c3b36aab9f8097cd4b359292c53a7173aef98c
|
| --- /dev/null
|
| +++ b/third_party/protobuf/src/google/protobuf/compiler/js/js_generator.cc
|
| @@ -0,0 +1,2622 @@
|
| +// Protocol Buffers - Google's data interchange format
|
| +// Copyright 2008 Google Inc. All rights reserved.
|
| +// https://developers.google.com/protocol-buffers/
|
| +//
|
| +// Redistribution and use in source and binary forms, with or without
|
| +// modification, are permitted provided that the following conditions are
|
| +// met:
|
| +//
|
| +// * Redistributions of source code must retain the above copyright
|
| +// notice, this list of conditions and the following disclaimer.
|
| +// * Redistributions in binary form must reproduce the above
|
| +// copyright notice, this list of conditions and the following disclaimer
|
| +// in the documentation and/or other materials provided with the
|
| +// distribution.
|
| +// * Neither the name of Google Inc. nor the names of its
|
| +// contributors may be used to endorse or promote products derived from
|
| +// this software without specific prior written permission.
|
| +//
|
| +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
| +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| +
|
| +#include <google/protobuf/compiler/js/js_generator.h>
|
| +
|
| +#include <assert.h>
|
| +#include <algorithm>
|
| +#include <limits>
|
| +#include <map>
|
| +#include <memory>
|
| +#ifndef _SHARED_PTR_H
|
| +#include <google/protobuf/stubs/shared_ptr.h>
|
| +#endif
|
| +#include <string>
|
| +#include <utility>
|
| +#include <vector>
|
| +
|
| +#include <google/protobuf/stubs/logging.h>
|
| +#include <google/protobuf/stubs/common.h>
|
| +#include <google/protobuf/stubs/stringprintf.h>
|
| +#include <google/protobuf/io/printer.h>
|
| +#include <google/protobuf/io/zero_copy_stream.h>
|
| +#include <google/protobuf/descriptor.pb.h>
|
| +#include <google/protobuf/descriptor.h>
|
| +#include <google/protobuf/stubs/strutil.h>
|
| +
|
| +namespace google {
|
| +namespace protobuf {
|
| +namespace compiler {
|
| +namespace js {
|
| +
|
| +// Sorted list of JavaScript keywords. These cannot be used as names. If they
|
| +// appear, we prefix them with "pb_".
|
| +const char* kKeyword[] = {
|
| + "abstract",
|
| + "boolean",
|
| + "break",
|
| + "byte",
|
| + "case",
|
| + "catch",
|
| + "char",
|
| + "class",
|
| + "const",
|
| + "continue",
|
| + "debugger",
|
| + "default",
|
| + "delete",
|
| + "do",
|
| + "double",
|
| + "else",
|
| + "enum",
|
| + "export",
|
| + "extends",
|
| + "false",
|
| + "final",
|
| + "finally",
|
| + "float",
|
| + "for",
|
| + "function",
|
| + "goto",
|
| + "if",
|
| + "implements",
|
| + "import",
|
| + "in",
|
| + "instanceof",
|
| + "int",
|
| + "interface",
|
| + "long",
|
| + "native",
|
| + "new",
|
| + "null",
|
| + "package",
|
| + "private",
|
| + "protected",
|
| + "public",
|
| + "return",
|
| + "short",
|
| + "static",
|
| + "super",
|
| + "switch",
|
| + "synchronized",
|
| + "this",
|
| + "throw",
|
| + "throws",
|
| + "transient",
|
| + "try",
|
| + "typeof",
|
| + "var",
|
| + "void",
|
| + "volatile",
|
| + "while",
|
| + "with",
|
| +};
|
| +
|
| +static const int kNumKeyword = sizeof(kKeyword) / sizeof(char*);
|
| +
|
| +namespace {
|
| +
|
| +bool IsReserved(const string& ident) {
|
| + for (int i = 0; i < kNumKeyword; i++) {
|
| + if (ident == kKeyword[i]) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +// Returns a copy of |filename| with any trailing ".protodevel" or ".proto
|
| +// suffix stripped.
|
| +string StripProto(const string& filename) {
|
| + const char* suffix = HasSuffixString(filename, ".protodevel")
|
| + ? ".protodevel" : ".proto";
|
| + return StripSuffixString(filename, suffix);
|
| +}
|
| +
|
| +// Returns the fully normalized JavaScript path for the given
|
| +// file descriptor's package.
|
| +string GetPath(const GeneratorOptions& options,
|
| + const FileDescriptor* file) {
|
| + if (!options.namespace_prefix.empty()) {
|
| + return options.namespace_prefix;
|
| + } else if (!file->package().empty()) {
|
| + return "proto." + file->package();
|
| + } else {
|
| + return "proto";
|
| + }
|
| +}
|
| +
|
| +// 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 = "";
|
| +
|
| + if (containing_type == NULL) {
|
| + prefix = GetPath(options, file_descriptor);
|
| + } else {
|
| + prefix = GetPath(options, containing_type);
|
| + }
|
| +
|
| + if (!prefix.empty()) {
|
| + prefix += ".";
|
| + }
|
| +
|
| + return prefix;
|
| +}
|
| +
|
| +
|
| +// Returns the fully normalized JavaScript path for the given
|
| +// message descriptor.
|
| +string GetPath(const GeneratorOptions& options,
|
| + const Descriptor* descriptor) {
|
| + return GetPrefix(
|
| + options, descriptor->file(),
|
| + descriptor->containing_type()) + descriptor->name();
|
| +}
|
| +
|
| +
|
| +// Returns the fully normalized JavaScript path for the given
|
| +// field's containing message descriptor.
|
| +string GetPath(const GeneratorOptions& options,
|
| + const FieldDescriptor* descriptor) {
|
| + return GetPath(options, descriptor->containing_type());
|
| +}
|
| +
|
| +// Returns the fully normalized JavaScript path for the given
|
| +// enumeration descriptor.
|
| +string GetPath(const GeneratorOptions& options,
|
| + const EnumDescriptor* enum_descriptor) {
|
| + return GetPrefix(
|
| + options, enum_descriptor->file(),
|
| + enum_descriptor->containing_type()) + enum_descriptor->name();
|
| +}
|
| +
|
| +
|
| +// Returns the fully normalized JavaScript path for the given
|
| +// enumeration value descriptor.
|
| +string GetPath(const GeneratorOptions& options,
|
| + const EnumValueDescriptor* value_descriptor) {
|
| + return GetPath(
|
| + options,
|
| + value_descriptor->type()) + "." + value_descriptor->name();
|
| +}
|
| +
|
| +// - Object field name: LOWER_UNDERSCORE -> LOWER_CAMEL, except for group fields
|
| +// (UPPER_CAMEL -> LOWER_CAMEL), with "List" (or "Map") appended if appropriate,
|
| +// and with reserved words triggering a "pb_" prefix.
|
| +// - Getters/setters: LOWER_UNDERSCORE -> UPPER_CAMEL, except for group fields
|
| +// (use the name directly), then append "List" if appropriate, then append "$"
|
| +// if resulting name is equal to a reserved word.
|
| +// - Enums: just uppercase.
|
| +
|
| +// Locale-independent version of ToLower that deals only with ASCII A-Z.
|
| +char ToLowerASCII(char c) {
|
| + if (c >= 'A' && c <= 'Z') {
|
| + return (c - 'A') + 'a';
|
| + } else {
|
| + return c;
|
| + }
|
| +}
|
| +
|
| +vector<string> ParseLowerUnderscore(const string& input) {
|
| + vector<string> words;
|
| + string running = "";
|
| + for (int i = 0; i < input.size(); i++) {
|
| + if (input[i] == '_') {
|
| + if (!running.empty()) {
|
| + words.push_back(running);
|
| + running.clear();
|
| + }
|
| + } else {
|
| + running += ToLowerASCII(input[i]);
|
| + }
|
| + }
|
| + if (!running.empty()) {
|
| + words.push_back(running);
|
| + }
|
| + return 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()) {
|
| + words.push_back(running);
|
| + running.clear();
|
| + }
|
| + running += ToLowerASCII(input[i]);
|
| + }
|
| + if (!running.empty()) {
|
| + words.push_back(running);
|
| + }
|
| + return words;
|
| +}
|
| +
|
| +string ToLowerCamel(const vector<string>& words) {
|
| + string result;
|
| + for (int i = 0; i < words.size(); i++) {
|
| + string word = words[i];
|
| + if (i == 0 && (word[0] >= 'A' && word[0] <= 'Z')) {
|
| + word[0] = (word[0] - 'A') + 'a';
|
| + } else if (i != 0 && (word[0] >= 'a' && word[0] <= 'z')) {
|
| + word[0] = (word[0] - 'a') + 'A';
|
| + }
|
| + result += word;
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +string ToUpperCamel(const vector<string>& words) {
|
| + string result;
|
| + for (int i = 0; i < words.size(); i++) {
|
| + string word = words[i];
|
| + if (word[0] >= 'a' && word[0] <= 'z') {
|
| + word[0] = (word[0] - 'a') + 'A';
|
| + }
|
| + result += word;
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +// Based on code from descriptor.cc (Thanks Kenton!)
|
| +// Uppercases the entire string, turning ValueName into
|
| +// VALUENAME.
|
| +string ToEnumCase(const string& input) {
|
| + string result;
|
| + result.reserve(input.size());
|
| +
|
| + for (int i = 0; i < input.size(); i++) {
|
| + if ('a' <= input[i] && input[i] <= 'z') {
|
| + result.push_back(input[i] - 'a' + 'A');
|
| + } else {
|
| + result.push_back(input[i]);
|
| + }
|
| + }
|
| +
|
| + return result;
|
| +}
|
| +
|
| +string ToFileName(const string& input) {
|
| + string result;
|
| + result.reserve(input.size());
|
| +
|
| + for (int i = 0; i < input.size(); i++) {
|
| + if ('A' <= input[i] && input[i] <= 'Z') {
|
| + result.push_back(input[i] - 'A' + 'a');
|
| + } else {
|
| + result.push_back(input[i]);
|
| + }
|
| + }
|
| +
|
| + return result;
|
| +}
|
| +
|
| +// Returns the message/response ID, if set.
|
| +string GetMessageId(const Descriptor* desc) {
|
| + return string();
|
| +}
|
| +
|
| +
|
| +// Used inside Google only -- do not remove.
|
| +bool IsResponse(const Descriptor* desc) { return false; }
|
| +bool IgnoreField(const FieldDescriptor* field) { return false; }
|
| +
|
| +
|
| +// 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++) {
|
| + if (!IgnoreField(oneof->field(i))) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +string JSIdent(const FieldDescriptor* field,
|
| + bool is_upper_camel,
|
| + bool is_map) {
|
| + string result;
|
| + if (field->type() == FieldDescriptor::TYPE_GROUP) {
|
| + result = is_upper_camel ?
|
| + ToUpperCamel(ParseUpperCamel(field->message_type()->name())) :
|
| + ToLowerCamel(ParseUpperCamel(field->message_type()->name()));
|
| + } else {
|
| + result = is_upper_camel ?
|
| + ToUpperCamel(ParseLowerUnderscore(field->name())) :
|
| + ToLowerCamel(ParseLowerUnderscore(field->name()));
|
| + }
|
| + if (is_map) {
|
| + result += "Map";
|
| + } else if (field->is_repeated()) {
|
| + result += "List";
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +string JSObjectFieldName(const FieldDescriptor* field) {
|
| + string name = JSIdent(
|
| + field,
|
| + /* is_upper_camel = */ false,
|
| + /* is_map = */ false);
|
| + if (IsReserved(name)) {
|
| + name = "pb_" + name;
|
| + }
|
| + return name;
|
| +}
|
| +
|
| +// Returns the field name as a capitalized portion of a getter/setter method
|
| +// name, e.g. MyField for .getMyField().
|
| +string JSGetterName(const FieldDescriptor* field) {
|
| + string name = JSIdent(field,
|
| + /* is_upper_camel = */ true,
|
| + /* is_map = */ false);
|
| + if (name == "Extension" || name == "JsPbMessageId") {
|
| + // Avoid conflicts with base-class names.
|
| + name += "$";
|
| + }
|
| + return name;
|
| +}
|
| +
|
| +string JSMapGetterName(const FieldDescriptor* field) {
|
| + return JSIdent(field,
|
| + /* is_upper_camel = */ true,
|
| + /* is_map = */ true);
|
| +}
|
| +
|
| +
|
| +
|
| +string JSOneofName(const OneofDescriptor* oneof) {
|
| + return ToUpperCamel(ParseLowerUnderscore(oneof->name()));
|
| +}
|
| +
|
| +// Returns the index corresponding to this field in the JSPB array (underlying
|
| +// data storage array).
|
| +string JSFieldIndex(const FieldDescriptor* field) {
|
| + // Determine whether this field is a member of a group. Group fields are a bit
|
| + // wonky: their "containing type" is a message type created just for the
|
| + // group, and that type's parent type has a field with the group-message type
|
| + // as its message type and TYPE_GROUP as its field type. For such fields, the
|
| + // index we use is relative to the field number of the group submessage field.
|
| + // For all other fields, we just use the field number.
|
| + const Descriptor* containing_type = field->containing_type();
|
| + const Descriptor* parent_type = containing_type->containing_type();
|
| + if (parent_type != NULL) {
|
| + for (int i = 0; i < parent_type->field_count(); i++) {
|
| + if (parent_type->field(i)->type() == FieldDescriptor::TYPE_GROUP &&
|
| + parent_type->field(i)->message_type() == containing_type) {
|
| + return SimpleItoa(field->number() - parent_type->field(i)->number());
|
| + }
|
| + }
|
| + }
|
| + return SimpleItoa(field->number());
|
| +}
|
| +
|
| +string JSOneofIndex(const OneofDescriptor* oneof) {
|
| + int index = -1;
|
| + for (int i = 0; i < oneof->containing_type()->oneof_decl_count(); i++) {
|
| + const OneofDescriptor* o = oneof->containing_type()->oneof_decl(i);
|
| + // If at least one field in this oneof is not JSPB-ignored, count the oneof.
|
| + for (int j = 0; j < o->field_count(); j++) {
|
| + const FieldDescriptor* f = o->field(j);
|
| + if (!IgnoreField(f)) {
|
| + index++;
|
| + break; // inner loop
|
| + }
|
| + }
|
| + if (o == oneof) {
|
| + break;
|
| + }
|
| + }
|
| + return SimpleItoa(index);
|
| +}
|
| +
|
| +// Decodes a codepoint in \x0000 -- \xFFFF. Since JS strings are UTF-16, we only
|
| +// need to handle the BMP (16-bit range) here.
|
| +uint16 DecodeUTF8Codepoint(uint8* bytes, size_t* length) {
|
| + if (*length == 0) {
|
| + return 0;
|
| + }
|
| + size_t expected = 0;
|
| + if ((*bytes & 0x80) == 0) {
|
| + expected = 1;
|
| + } else if ((*bytes & 0xe0) == 0xc0) {
|
| + expected = 2;
|
| + } else if ((*bytes & 0xf0) == 0xe0) {
|
| + expected = 3;
|
| + } else {
|
| + // Too long -- don't accept.
|
| + *length = 0;
|
| + return 0;
|
| + }
|
| +
|
| + if (*length < expected) {
|
| + // Not enough bytes -- don't accept.
|
| + *length = 0;
|
| + return 0;
|
| + }
|
| +
|
| + *length = expected;
|
| + switch (expected) {
|
| + case 1: return bytes[0];
|
| + case 2: return ((bytes[0] & 0x1F) << 6) |
|
| + ((bytes[1] & 0x3F) << 0);
|
| + case 3: return ((bytes[0] & 0x0F) << 12) |
|
| + ((bytes[1] & 0x3F) << 6) |
|
| + ((bytes[2] & 0x3F) << 0);
|
| + default: return 0;
|
| + }
|
| +}
|
| +
|
| +// Escapes the contents of a string to be included within double-quotes ("") in
|
| +// JavaScript. |is_utf8| determines whether the input data (in a C++ string of
|
| +// chars) is UTF-8 encoded (in which case codepoints become JavaScript string
|
| +// characters, escaped with 16-bit hex escapes where necessary) or raw binary
|
| +// (in which case bytes become JavaScript string characters 0 -- 255).
|
| +string EscapeJSString(const string& in, bool is_utf8) {
|
| + string result;
|
| + size_t decoded = 0;
|
| + for (size_t i = 0; i < in.size(); i += decoded) {
|
| + uint16 codepoint = 0;
|
| + if (is_utf8) {
|
| + // Decode the next UTF-8 codepoint.
|
| + size_t have_bytes = in.size() - i;
|
| + uint8 bytes[3] = {
|
| + static_cast<uint8>(in[i]),
|
| + static_cast<uint8>(((i + 1) < in.size()) ? in[i + 1] : 0),
|
| + static_cast<uint8>(((i + 2) < in.size()) ? in[i + 2] : 0),
|
| + };
|
| + codepoint = DecodeUTF8Codepoint(bytes, &have_bytes);
|
| + if (have_bytes == 0) {
|
| + break;
|
| + }
|
| + decoded = have_bytes;
|
| + } else {
|
| + codepoint = static_cast<uint16>(static_cast<uint8>(in[i]));
|
| + decoded = 1;
|
| + }
|
| +
|
| + // Next byte -- used for minimal octal escapes below.
|
| + char next_byte = (i + decoded) < in.size() ?
|
| + in[i + decoded] : 0;
|
| + bool pad_octal = (next_byte >= '0' && next_byte <= '7');
|
| +
|
| + switch (codepoint) {
|
| + case '\0': result += pad_octal ? "\\000" : "\\0"; break;
|
| + case '\b': result += "\\\b"; break;
|
| + case '\t': result += "\\\t"; break;
|
| + case '\n': result += "\\\n"; break;
|
| + case '\r': result += "\\\r"; break;
|
| + case '\f': result += "\\\f"; break;
|
| + case '\\': result += "\\\\"; break;
|
| + case '"': result += pad_octal ? "\\042" : "\\42"; break;
|
| + case '&': result += pad_octal ? "\\046" : "\\46"; break;
|
| + case '\'': result += pad_octal ? "\\047" : "\\47"; break;
|
| + case '<': result += pad_octal ? "\\074" : "\\74"; break;
|
| + case '=': result += pad_octal ? "\\075" : "\\75"; break;
|
| + case '>': result += pad_octal ? "\\076" : "\\76"; break;
|
| + default:
|
| + // All other non-ASCII codepoints are escaped.
|
| + // Original codegen uses hex for >= 0x100 and octal for others.
|
| + if (codepoint >= 0x20 && codepoint <= 0x7e) {
|
| + result += static_cast<char>(codepoint);
|
| + } else {
|
| + if (codepoint >= 0x100) {
|
| + result += StringPrintf("\\u%04x", codepoint);
|
| + } else {
|
| + if (pad_octal || codepoint >= 0100) {
|
| + result += "\\";
|
| + result += ('0' + ((codepoint >> 6) & 07));
|
| + result += ('0' + ((codepoint >> 3) & 07));
|
| + result += ('0' + ((codepoint >> 0) & 07));
|
| + } else if (codepoint >= 010) {
|
| + result += "\\";
|
| + result += ('0' + ((codepoint >> 3) & 07));
|
| + result += ('0' + ((codepoint >> 0) & 07));
|
| + } else {
|
| + result += "\\";
|
| + result += ('0' + ((codepoint >> 0) & 07));
|
| + }
|
| + }
|
| + }
|
| + break;
|
| + }
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +string EscapeBase64(const string& in) {
|
| + static const char* kAlphabet =
|
| + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
| + string result;
|
| +
|
| + for (size_t i = 0; i < in.size(); i += 3) {
|
| + int value = (in[i] << 16) |
|
| + (((i + 1) < in.size()) ? (in[i + 1] << 8) : 0) |
|
| + (((i + 2) < in.size()) ? (in[i + 2] << 0) : 0);
|
| + result += kAlphabet[(value >> 18) & 0x3f];
|
| + result += kAlphabet[(value >> 12) & 0x3f];
|
| + if ((i + 1) < in.size()) {
|
| + result += kAlphabet[(value >> 6) & 0x3f];
|
| + } else {
|
| + result += '=';
|
| + }
|
| + if ((i + 2) < in.size()) {
|
| + result += kAlphabet[(value >> 0) & 0x3f];
|
| + } else {
|
| + result += '=';
|
| + }
|
| + }
|
| +
|
| + return result;
|
| +}
|
| +
|
| +// Post-process the result of SimpleFtoa/SimpleDtoa to *exactly* match the
|
| +// original codegen's formatting (which is just .toString() on java.lang.Double
|
| +// or java.lang.Float).
|
| +string PostProcessFloat(string result) {
|
| + // If inf, -inf or nan, replace with +Infinity, -Infinity or NaN.
|
| + if (result == "inf") {
|
| + return "Infinity";
|
| + } else if (result == "-inf") {
|
| + return "-Infinity";
|
| + } else if (result == "nan") {
|
| + return "NaN";
|
| + }
|
| +
|
| + // If scientific notation (e.g., "1e10"), (i) capitalize the "e", (ii)
|
| + // ensure that the mantissa (portion prior to the "e") has at least one
|
| + // fractional digit (after the decimal point), and (iii) strip any unnecessary
|
| + // leading zeroes and/or '+' signs from the exponent.
|
| + string::size_type exp_pos = result.find('e');
|
| + if (exp_pos != string::npos) {
|
| + string mantissa = result.substr(0, exp_pos);
|
| + string exponent = result.substr(exp_pos + 1);
|
| +
|
| + // Add ".0" to mantissa if no fractional part exists.
|
| + if (mantissa.find('.') == string::npos) {
|
| + mantissa += ".0";
|
| + }
|
| +
|
| + // Strip the sign off the exponent and store as |exp_neg|.
|
| + bool exp_neg = false;
|
| + if (!exponent.empty() && exponent[0] == '+') {
|
| + exponent = exponent.substr(1);
|
| + } else if (!exponent.empty() && exponent[0] == '-') {
|
| + exp_neg = true;
|
| + exponent = exponent.substr(1);
|
| + }
|
| +
|
| + // Strip any leading zeroes off the exponent.
|
| + while (exponent.size() > 1 && exponent[0] == '0') {
|
| + exponent = exponent.substr(1);
|
| + }
|
| +
|
| + return mantissa + "E" + string(exp_neg ? "-" : "") + exponent;
|
| + }
|
| +
|
| + // Otherwise, this is an ordinary decimal number. Append ".0" if result has no
|
| + // decimal/fractional part in order to match output of original codegen.
|
| + if (result.find('.') == string::npos) {
|
| + result += ".0";
|
| + }
|
| +
|
| + return result;
|
| +}
|
| +
|
| +string FloatToString(float value) {
|
| + string result = SimpleFtoa(value);
|
| + return PostProcessFloat(result);
|
| +}
|
| +
|
| +string DoubleToString(double value) {
|
| + string result = SimpleDtoa(value);
|
| + return PostProcessFloat(result);
|
| +}
|
| +
|
| +string MaybeNumberString(const FieldDescriptor* field, const string& orig) {
|
| + return orig;
|
| +}
|
| +
|
| +string JSFieldDefault(const FieldDescriptor* field) {
|
| + assert(field->has_default_value());
|
| + switch (field->cpp_type()) {
|
| + case FieldDescriptor::CPPTYPE_INT32:
|
| + return MaybeNumberString(
|
| + field, SimpleItoa(field->default_value_int32()));
|
| + case FieldDescriptor::CPPTYPE_UINT32:
|
| + // The original codegen is in Java, and Java protobufs store unsigned
|
| + // integer values as signed integer values. In order to exactly match the
|
| + // output, we need to reinterpret as base-2 signed. Ugh.
|
| + return MaybeNumberString(
|
| + field, SimpleItoa(static_cast<int32>(field->default_value_uint32())));
|
| + case FieldDescriptor::CPPTYPE_INT64:
|
| + return MaybeNumberString(
|
| + field, SimpleItoa(field->default_value_int64()));
|
| + case FieldDescriptor::CPPTYPE_UINT64:
|
| + // See above note for uint32 -- reinterpreting as signed.
|
| + return MaybeNumberString(
|
| + field, SimpleItoa(static_cast<int64>(field->default_value_uint64())));
|
| + case FieldDescriptor::CPPTYPE_ENUM:
|
| + return SimpleItoa(field->default_value_enum()->number());
|
| + case FieldDescriptor::CPPTYPE_BOOL:
|
| + return field->default_value_bool() ? "true" : "false";
|
| + case FieldDescriptor::CPPTYPE_FLOAT:
|
| + return FloatToString(field->default_value_float());
|
| + case FieldDescriptor::CPPTYPE_DOUBLE:
|
| + return DoubleToString(field->default_value_double());
|
| + case FieldDescriptor::CPPTYPE_STRING:
|
| + if (field->type() == FieldDescriptor::TYPE_STRING) {
|
| + return "\"" + EscapeJSString(field->default_value_string(), true) +
|
| + "\"";
|
| + } else {
|
| + return "\"" + EscapeBase64(field->default_value_string()) +
|
| + "\"";
|
| + }
|
| + case FieldDescriptor::CPPTYPE_MESSAGE:
|
| + return "null";
|
| + }
|
| + GOOGLE_LOG(FATAL) << "Shouldn't reach here.";
|
| + return "";
|
| +}
|
| +
|
| +string ProtoTypeName(const GeneratorOptions& options,
|
| + const FieldDescriptor* field) {
|
| + switch (field->type()) {
|
| + case FieldDescriptor::TYPE_BOOL:
|
| + return "bool";
|
| + case FieldDescriptor::TYPE_INT32:
|
| + return "int32";
|
| + case FieldDescriptor::TYPE_UINT32:
|
| + return "uint32";
|
| + case FieldDescriptor::TYPE_SINT32:
|
| + return "sint32";
|
| + case FieldDescriptor::TYPE_FIXED32:
|
| + return "fixed32";
|
| + case FieldDescriptor::TYPE_SFIXED32:
|
| + return "sfixed32";
|
| + case FieldDescriptor::TYPE_INT64:
|
| + return "int64";
|
| + case FieldDescriptor::TYPE_UINT64:
|
| + return "uint64";
|
| + case FieldDescriptor::TYPE_SINT64:
|
| + return "sint64";
|
| + case FieldDescriptor::TYPE_FIXED64:
|
| + return "fixed64";
|
| + case FieldDescriptor::TYPE_SFIXED64:
|
| + return "sfixed64";
|
| + case FieldDescriptor::TYPE_FLOAT:
|
| + return "float";
|
| + case FieldDescriptor::TYPE_DOUBLE:
|
| + return "double";
|
| + case FieldDescriptor::TYPE_STRING:
|
| + return "string";
|
| + case FieldDescriptor::TYPE_BYTES:
|
| + return "bytes";
|
| + case FieldDescriptor::TYPE_GROUP:
|
| + return GetPath(options, field->message_type());
|
| + case FieldDescriptor::TYPE_ENUM:
|
| + return GetPath(options, field->enum_type());
|
| + case FieldDescriptor::TYPE_MESSAGE:
|
| + return GetPath(options, field->message_type());
|
| + default:
|
| + return "";
|
| + }
|
| +}
|
| +
|
| +string JSIntegerTypeName(const FieldDescriptor* field) {
|
| + return "number";
|
| +}
|
| +
|
| +string JSTypeName(const GeneratorOptions& options,
|
| + const FieldDescriptor* field) {
|
| + switch (field->cpp_type()) {
|
| + case FieldDescriptor::CPPTYPE_BOOL:
|
| + return "boolean";
|
| + case FieldDescriptor::CPPTYPE_INT32:
|
| + return JSIntegerTypeName(field);
|
| + case FieldDescriptor::CPPTYPE_INT64:
|
| + return JSIntegerTypeName(field);
|
| + case FieldDescriptor::CPPTYPE_UINT32:
|
| + return JSIntegerTypeName(field);
|
| + case FieldDescriptor::CPPTYPE_UINT64:
|
| + return JSIntegerTypeName(field);
|
| + case FieldDescriptor::CPPTYPE_FLOAT:
|
| + return "number";
|
| + case FieldDescriptor::CPPTYPE_DOUBLE:
|
| + return "number";
|
| + case FieldDescriptor::CPPTYPE_STRING:
|
| + return "string";
|
| + case FieldDescriptor::CPPTYPE_ENUM:
|
| + return GetPath(options, field->enum_type());
|
| + case FieldDescriptor::CPPTYPE_MESSAGE:
|
| + return GetPath(options, field->message_type());
|
| + default:
|
| + return "";
|
| + }
|
| +}
|
| +
|
| +bool HasFieldPresence(const FieldDescriptor* field);
|
| +
|
| +string JSFieldTypeAnnotation(const GeneratorOptions& options,
|
| + const FieldDescriptor* field,
|
| + bool force_optional,
|
| + bool force_present,
|
| + bool singular_if_not_packed,
|
| + bool always_singular) {
|
| + bool is_primitive =
|
| + (field->cpp_type() != FieldDescriptor::CPPTYPE_ENUM &&
|
| + field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE);
|
| +
|
| + string jstype = JSTypeName(options, field);
|
| +
|
| + if (field->is_repeated() &&
|
| + !always_singular &&
|
| + (field->is_packed() || !singular_if_not_packed)) {
|
| + if (!is_primitive) {
|
| + jstype = "!" + jstype;
|
| + }
|
| + jstype = "Array.<" + jstype + ">";
|
| + if (!force_optional) {
|
| + jstype = "!" + jstype;
|
| + }
|
| + }
|
| +
|
| + 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 (force_optional && HasFieldPresence(field)) {
|
| + jstype += "|undefined";
|
| + }
|
| + if (force_present && jstype[0] != '!' && !is_primitive) {
|
| + jstype = "!" + jstype;
|
| + }
|
| +
|
| + return jstype;
|
| +}
|
| +
|
| +string JSBinaryReaderMethodType(const FieldDescriptor* field) {
|
| + string name = field->type_name();
|
| + if (name[0] >= 'a' && name[0] <= 'z') {
|
| + name[0] = (name[0] - 'a') + 'A';
|
| + }
|
| +
|
| + return name;
|
| +}
|
| +
|
| +string JSBinaryReadWriteMethodName(const FieldDescriptor* field,
|
| + bool is_writer) {
|
| + string name = JSBinaryReaderMethodType(field);
|
| + if (is_writer && field->type() == FieldDescriptor::TYPE_BYTES) {
|
| + // Override for `bytes` fields: treat string as raw bytes, not base64.
|
| + name = "BytesRawString";
|
| + }
|
| + if (field->is_packed()) {
|
| + name = "Packed" + name;
|
| + } else if (is_writer && field->is_repeated()) {
|
| + name = "Repeated" + name;
|
| + }
|
| + return name;
|
| +}
|
| +
|
| +string JSBinaryReaderMethodName(const FieldDescriptor* field) {
|
| + return "read" + JSBinaryReadWriteMethodName(field, /* is_writer = */ false);
|
| +}
|
| +
|
| +string JSBinaryWriterMethodName(const FieldDescriptor* field) {
|
| + return "write" + JSBinaryReadWriteMethodName(field, /* is_writer = */ true);
|
| +}
|
| +
|
| +string JSReturnClause(const FieldDescriptor* desc) {
|
| + return "";
|
| +}
|
| +
|
| +string JSReturnDoc(const GeneratorOptions& options,
|
| + const FieldDescriptor* desc) {
|
| + return "";
|
| +}
|
| +
|
| +bool HasRepeatedFields(const Descriptor* desc) {
|
| + for (int i = 0; i < desc->field_count(); i++) {
|
| + if (desc->field(i)->is_repeated()) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +static const char* kRepeatedFieldArrayName = ".repeatedFields_";
|
| +
|
| +string RepeatedFieldsArrayName(const GeneratorOptions& options,
|
| + const Descriptor* desc) {
|
| + return HasRepeatedFields(desc) ?
|
| + (GetPath(options, desc) + kRepeatedFieldArrayName) : "null";
|
| +}
|
| +
|
| +bool HasOneofFields(const Descriptor* desc) {
|
| + for (int i = 0; i < desc->field_count(); i++) {
|
| + if (desc->field(i)->containing_oneof()) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +static const char* kOneofGroupArrayName = ".oneofGroups_";
|
| +
|
| +string OneofFieldsArrayName(const GeneratorOptions& options,
|
| + const Descriptor* desc) {
|
| + return HasOneofFields(desc) ?
|
| + (GetPath(options, desc) + kOneofGroupArrayName) : "null";
|
| +}
|
| +
|
| +string RepeatedFieldNumberList(const Descriptor* desc) {
|
| + std::vector<string> numbers;
|
| + for (int i = 0; i < desc->field_count(); i++) {
|
| + if (desc->field(i)->is_repeated()) {
|
| + numbers.push_back(JSFieldIndex(desc->field(i)));
|
| + }
|
| + }
|
| + return "[" + Join(numbers, ",") + "]";
|
| +}
|
| +
|
| +string OneofGroupList(const Descriptor* desc) {
|
| + // List of arrays (one per oneof), each of which is a list of field indices
|
| + std::vector<string> oneof_entries;
|
| + for (int i = 0; i < desc->oneof_decl_count(); i++) {
|
| + const OneofDescriptor* oneof = desc->oneof_decl(i);
|
| + if (IgnoreOneof(oneof)) {
|
| + continue;
|
| + }
|
| +
|
| + std::vector<string> oneof_fields;
|
| + for (int j = 0; j < oneof->field_count(); j++) {
|
| + if (IgnoreField(oneof->field(j))) {
|
| + continue;
|
| + }
|
| + oneof_fields.push_back(JSFieldIndex(oneof->field(j)));
|
| + }
|
| + oneof_entries.push_back("[" + Join(oneof_fields, ",") + "]");
|
| + }
|
| + return "[" + Join(oneof_entries, ",") + "]";
|
| +}
|
| +
|
| +string JSOneofArray(const GeneratorOptions& options,
|
| + const FieldDescriptor* field) {
|
| + return OneofFieldsArrayName(options, field->containing_type()) + "[" +
|
| + JSOneofIndex(field->containing_oneof()) + "]";
|
| +}
|
| +
|
| +string RelativeTypeName(const FieldDescriptor* field) {
|
| + assert(field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM ||
|
| + field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE);
|
| + // For a field with an enum or message type, compute a name relative to the
|
| + // path name of the message type containing this field.
|
| + string package = field->file()->package();
|
| + string containing_type = field->containing_type()->full_name() + ".";
|
| + string type = (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) ?
|
| + field->enum_type()->full_name() : field->message_type()->full_name();
|
| +
|
| + // |prefix| is advanced as we find separators '.' past the common package
|
| + // prefix that yield common prefixes in the containing type's name and this
|
| + // type's name.
|
| + int prefix = 0;
|
| + for (int i = 0; i < type.size() && i < containing_type.size(); i++) {
|
| + if (type[i] != containing_type[i]) {
|
| + break;
|
| + }
|
| + if (type[i] == '.' && i >= package.size()) {
|
| + prefix = i + 1;
|
| + }
|
| + }
|
| +
|
| + return type.substr(prefix);
|
| +}
|
| +
|
| +string JSExtensionsObjectName(const GeneratorOptions& options,
|
| + const Descriptor* desc) {
|
| + if (desc->full_name() == "google.protobuf.bridge.MessageSet") {
|
| + return "jspb.Message.messageSetExtensions";
|
| + } else {
|
| + return GetPath(options, desc) + ".extensions";
|
| + }
|
| +}
|
| +
|
| +string FieldDefinition(const GeneratorOptions& options,
|
| + const FieldDescriptor* field) {
|
| + 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());
|
| +}
|
| +
|
| +string FieldComments(const FieldDescriptor* field) {
|
| + string comments;
|
| + if (field->cpp_type() == FieldDescriptor::CPPTYPE_BOOL) {
|
| + comments +=
|
| + " * Note that Boolean fields may be set to 0/1 when serialized from "
|
| + "a Java server.\n"
|
| + " * You should avoid comparisons like {@code val === true/false} in "
|
| + "those cases.\n";
|
| + }
|
| + if (field->is_repeated()) {
|
| + comments +=
|
| + " * If you change this array by adding, removing or replacing "
|
| + "elements, or if you\n"
|
| + " * replace the array itself, then you must call the setter to "
|
| + "update it.\n";
|
| + }
|
| + return comments;
|
| +}
|
| +
|
| +bool ShouldGenerateExtension(const FieldDescriptor* field) {
|
| + return
|
| + field->is_extension() &&
|
| + !IgnoreField(field);
|
| +}
|
| +
|
| +bool HasExtensions(const Descriptor* desc) {
|
| + if (desc->extension_count() > 0) {
|
| + return true;
|
| + }
|
| + for (int i = 0; i < desc->nested_type_count(); i++) {
|
| + if (HasExtensions(desc->nested_type(i))) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +bool HasExtensions(const FileDescriptor* file) {
|
| + for (int i = 0; i < file->extension_count(); i++) {
|
| + if (ShouldGenerateExtension(file->extension(i))) {
|
| + return true;
|
| + }
|
| + }
|
| + for (int i = 0; i < file->message_type_count(); i++) {
|
| + if (HasExtensions(file->message_type(i))) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +bool IsExtendable(const Descriptor* desc) {
|
| + return desc->extension_range_count() > 0;
|
| +}
|
| +
|
| +// Returns the max index in the underlying data storage array beyond which the
|
| +// extension object is used.
|
| +string GetPivot(const Descriptor* desc) {
|
| + static const int kDefaultPivot = (1 << 29); // max field number (29 bits)
|
| +
|
| + // Find the max field number
|
| + int max_field_number = 0;
|
| + for (int i = 0; i < desc->field_count(); i++) {
|
| + if (!IgnoreField(desc->field(i)) &&
|
| + desc->field(i)->number() > max_field_number) {
|
| + max_field_number = desc->field(i)->number();
|
| + }
|
| + }
|
| +
|
| + int pivot = -1;
|
| + if (IsExtendable(desc)) {
|
| + pivot = ((max_field_number + 1) < kDefaultPivot) ?
|
| + (max_field_number + 1) : kDefaultPivot;
|
| + }
|
| +
|
| + return SimpleItoa(pivot);
|
| +}
|
| +
|
| +// Returns true for fields that represent "null" as distinct from the default
|
| +// value. See https://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);
|
| +}
|
| +
|
| +// For proto3 fields without presence, returns a string representing the default
|
| +// value in JavaScript. See https://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:
|
| + return "\"\"";
|
| +
|
| + default:
|
| + // BYTES and MESSAGE are handled separately.
|
| + assert(false);
|
| + return "";
|
| + }
|
| +}
|
| +
|
| +} // anonymous namespace
|
| +
|
| +void Generator::GenerateHeader(const GeneratorOptions& options,
|
| + io::Printer* printer) const {
|
| + printer->Print("/**\n"
|
| + " * @fileoverview\n"
|
| + " * @enhanceable\n"
|
| + " * @public\n"
|
| + " */\n"
|
| + "// GENERATED CODE -- DO NOT EDIT!\n"
|
| + "\n");
|
| +}
|
| +
|
| +void Generator::FindProvides(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const vector<const FileDescriptor*>& files,
|
| + std::set<string>* provided) const {
|
| + for (int i = 0; i < files.size(); i++) {
|
| + for (int j = 0; j < files[i]->message_type_count(); j++) {
|
| + FindProvidesForMessage(options, printer, files[i]->message_type(j),
|
| + provided);
|
| + }
|
| + for (int j = 0; j < files[i]->enum_type_count(); j++) {
|
| + FindProvidesForEnum(options, printer, files[i]->enum_type(j),
|
| + provided);
|
| + }
|
| + }
|
| +
|
| + printer->Print("\n");
|
| +}
|
| +
|
| +void Generator::FindProvidesForMessage(
|
| + const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const Descriptor* desc,
|
| + std::set<string>* provided) const {
|
| + string name = GetPath(options, desc);
|
| + provided->insert(name);
|
| +
|
| + for (int i = 0; i < desc->enum_type_count(); i++) {
|
| + FindProvidesForEnum(options, printer, desc->enum_type(i),
|
| + provided);
|
| + }
|
| + for (int i = 0; i < desc->nested_type_count(); i++) {
|
| + FindProvidesForMessage(options, printer, desc->nested_type(i),
|
| + provided);
|
| + }
|
| +}
|
| +
|
| +void Generator::FindProvidesForEnum(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const EnumDescriptor* enumdesc,
|
| + std::set<string>* provided) const {
|
| + string name = GetPath(options, enumdesc);
|
| + provided->insert(name);
|
| +}
|
| +
|
| +void Generator::FindProvidesForFields(
|
| + const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const vector<const FieldDescriptor*>& fields,
|
| + std::set<string>* provided) const {
|
| + for (int i = 0; i < fields.size(); i++) {
|
| + const FieldDescriptor* field = fields[i];
|
| +
|
| + if (IgnoreField(field)) {
|
| + continue;
|
| + }
|
| +
|
| + string name =
|
| + GetPath(options, field->file()) + "." + JSObjectFieldName(field);
|
| + provided->insert(name);
|
| + }
|
| +}
|
| +
|
| +void Generator::GenerateProvides(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + std::set<string>* provided) const {
|
| + for (std::set<string>::iterator it = provided->begin();
|
| + it != provided->end(); ++it) {
|
| + printer->Print("goog.provide('$name$');\n",
|
| + "name", *it);
|
| + }
|
| +}
|
| +
|
| +void Generator::GenerateRequires(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const Descriptor* desc,
|
| + std::set<string>* provided) const {
|
| + std::set<string> required;
|
| + std::set<string> forwards;
|
| + bool have_message = false;
|
| + FindRequiresForMessage(options, desc,
|
| + &required, &forwards, &have_message);
|
| +
|
| + GenerateRequiresImpl(options, printer, &required, &forwards, provided,
|
| + /* require_jspb = */ have_message,
|
| + /* require_extension = */ HasExtensions(desc));
|
| +}
|
| +
|
| +void Generator::GenerateRequires(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const vector<const FileDescriptor*>& files,
|
| + std::set<string>* provided) const {
|
| + std::set<string> required;
|
| + std::set<string> forwards;
|
| + bool have_extensions = false;
|
| + bool have_message = false;
|
| +
|
| + for (int i = 0; i < files.size(); i++) {
|
| + for (int j = 0; j < files[i]->message_type_count(); j++) {
|
| + FindRequiresForMessage(options,
|
| + files[i]->message_type(j),
|
| + &required, &forwards, &have_message);
|
| + }
|
| + if (!have_extensions && HasExtensions(files[i])) {
|
| + have_extensions = true;
|
| + }
|
| +
|
| + for (int j = 0; j < files[i]->extension_count(); j++) {
|
| + const FieldDescriptor* extension = files[i]->extension(j);
|
| + if (IgnoreField(extension)) {
|
| + continue;
|
| + }
|
| + if (extension->containing_type()->full_name() !=
|
| + "google.protobuf.bridge.MessageSet") {
|
| + required.insert(GetPath(options, extension->containing_type()));
|
| + }
|
| + FindRequiresForField(options, extension, &required, &forwards);
|
| + have_extensions = true;
|
| + }
|
| + }
|
| +
|
| + GenerateRequiresImpl(options, printer, &required, &forwards, provided,
|
| + /* require_jspb = */ have_message,
|
| + /* require_extension = */ have_extensions);
|
| +}
|
| +
|
| +void Generator::GenerateRequires(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const vector<const FieldDescriptor*>& fields,
|
| + std::set<string>* provided) const {
|
| + std::set<string> required;
|
| + std::set<string> forwards;
|
| + for (int i = 0; i < fields.size(); i++) {
|
| + const FieldDescriptor* field = fields[i];
|
| + if (IgnoreField(field)) {
|
| + continue;
|
| + }
|
| + FindRequiresForExtension(options, field, &required, &forwards);
|
| + }
|
| +
|
| + GenerateRequiresImpl(options, printer, &required, &forwards, provided,
|
| + /* require_jspb = */ false,
|
| + /* require_extension = */ fields.size() > 0);
|
| +}
|
| +
|
| +void Generator::GenerateRequiresImpl(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + std::set<string>* required,
|
| + std::set<string>* forwards,
|
| + std::set<string>* provided,
|
| + bool require_jspb,
|
| + bool require_extension) const {
|
| + if (require_jspb) {
|
| + printer->Print(
|
| + "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.ExtensionFieldInfo');\n");
|
| + }
|
| +
|
| + std::set<string>::iterator it;
|
| + for (it = required->begin(); it != required->end(); ++it) {
|
| + if (provided->find(*it) != provided->end()) {
|
| + continue;
|
| + }
|
| + printer->Print("goog.require('$name$');\n",
|
| + "name", *it);
|
| + }
|
| +
|
| + printer->Print("\n");
|
| +
|
| + for (it = forwards->begin(); it != forwards->end(); ++it) {
|
| + if (provided->find(*it) != provided->end()) {
|
| + continue;
|
| + }
|
| + printer->Print("goog.forwardDeclare('$name$');\n",
|
| + "name", *it);
|
| + }
|
| +}
|
| +
|
| +bool NamespaceOnly(const Descriptor* desc) {
|
| + return false;
|
| +}
|
| +
|
| +void Generator::FindRequiresForMessage(
|
| + const GeneratorOptions& options,
|
| + const Descriptor* desc,
|
| + std::set<string>* required,
|
| + std::set<string>* forwards,
|
| + bool* have_message) const {
|
| +
|
| +
|
| + if (!NamespaceOnly(desc)) {
|
| + *have_message = true;
|
| + for (int i = 0; i < desc->field_count(); i++) {
|
| + const FieldDescriptor* field = desc->field(i);
|
| + if (IgnoreField(field)) {
|
| + continue;
|
| + }
|
| + FindRequiresForField(options, field, required, forwards);
|
| + }
|
| + }
|
| +
|
| + for (int i = 0; i < desc->extension_count(); i++) {
|
| + const FieldDescriptor* field = desc->extension(i);
|
| + if (IgnoreField(field)) {
|
| + continue;
|
| + }
|
| + FindRequiresForExtension(options, field, required, forwards);
|
| + }
|
| +
|
| + for (int i = 0; i < desc->nested_type_count(); i++) {
|
| + FindRequiresForMessage(options, desc->nested_type(i), required, forwards,
|
| + have_message);
|
| + }
|
| +}
|
| +
|
| +void Generator::FindRequiresForField(const GeneratorOptions& options,
|
| + const FieldDescriptor* field,
|
| + std::set<string>* required,
|
| + std::set<string>* forwards) const {
|
| + if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM &&
|
| + // N.B.: file-level extensions with enum type do *not* create
|
| + // dependencies, as per original codegen.
|
| + !(field->is_extension() && field->extension_scope() == NULL)) {
|
| + if (options.add_require_for_enums) {
|
| + required->insert(GetPath(options, field->enum_type()));
|
| + } else {
|
| + forwards->insert(GetPath(options, field->enum_type()));
|
| + }
|
| + } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
|
| + required->insert(GetPath(options, field->message_type()));
|
| + }
|
| +}
|
| +
|
| +void Generator::FindRequiresForExtension(const GeneratorOptions& options,
|
| + const FieldDescriptor* field,
|
| + std::set<string>* required,
|
| + std::set<string>* forwards) const {
|
| + if (field->containing_type()->full_name() != "google.protobuf.bridge.MessageSet") {
|
| + required->insert(GetPath(options, field->containing_type()));
|
| + }
|
| + FindRequiresForField(options, field, required, forwards);
|
| +}
|
| +
|
| +void Generator::GenerateTestOnly(const GeneratorOptions& options,
|
| + io::Printer* printer) const {
|
| + if (options.testonly) {
|
| + printer->Print("goog.setTestOnly();\n\n");
|
| + }
|
| + printer->Print("\n");
|
| +}
|
| +
|
| +void Generator::GenerateClassesAndEnums(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const FileDescriptor* file) const {
|
| + for (int i = 0; i < file->message_type_count(); i++) {
|
| + GenerateClass(options, printer, file->message_type(i));
|
| + }
|
| + for (int i = 0; i < file->enum_type_count(); i++) {
|
| + GenerateEnum(options, printer, file->enum_type(i));
|
| + }
|
| +}
|
| +
|
| +void Generator::GenerateClass(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const Descriptor* desc) const {
|
| + if (!NamespaceOnly(desc)) {
|
| + printer->Print("\n");
|
| + GenerateClassConstructor(options, printer, desc);
|
| + GenerateClassFieldInfo(options, printer, desc);
|
| +
|
| +
|
| + GenerateClassToObject(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);
|
| + }
|
| + }
|
| +
|
| + // Recurse on nested types.
|
| + for (int i = 0; i < desc->enum_type_count(); i++) {
|
| + GenerateEnum(options, printer, desc->enum_type(i));
|
| + }
|
| + for (int i = 0; i < desc->nested_type_count(); i++) {
|
| + GenerateClass(options, printer, desc->nested_type(i));
|
| + }
|
| +}
|
| +
|
| +void Generator::GenerateClassConstructor(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const Descriptor* desc) const {
|
| + printer->Print(
|
| + "/**\n"
|
| + " * Generated by JsPbCodeGenerator.\n"
|
| + " * @param {Array=} opt_data Optional initial data array, typically "
|
| + "from a\n"
|
| + " * server response, or constructed directly in Javascript. The array "
|
| + "is used\n"
|
| + " * in place and becomes part of the constructed object. It is not "
|
| + "cloned.\n"
|
| + " * If no data is provided, the constructed object will be empty, but "
|
| + "still\n"
|
| + " * valid.\n"
|
| + " * @extends {jspb.Message}\n"
|
| + " * @constructor\n"
|
| + " */\n"
|
| + "$classname$ = function(opt_data) {\n",
|
| + "classname", GetPath(options, desc));
|
| + string message_id = GetMessageId(desc);
|
| + printer->Print(
|
| + " jspb.Message.initialize(this, opt_data, $messageId$, $pivot$, "
|
| + "$rptfields$, $oneoffields$);\n",
|
| + "messageId", !message_id.empty() ?
|
| + ("'" + message_id + "'") :
|
| + (IsResponse(desc) ? "''" : "0"),
|
| + "pivot", GetPivot(desc),
|
| + "rptfields", RepeatedFieldsArrayName(options, desc),
|
| + "oneoffields", OneofFieldsArrayName(options, desc));
|
| + printer->Print(
|
| + "};\n"
|
| + "goog.inherits($classname$, jspb.Message);\n"
|
| + "if (goog.DEBUG && !COMPILED) {\n"
|
| + " $classname$.displayName = '$classname$';\n"
|
| + "}\n",
|
| + "classname", GetPath(options, desc));
|
| +}
|
| +
|
| +void Generator::GenerateClassFieldInfo(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const Descriptor* desc) const {
|
| + if (HasRepeatedFields(desc)) {
|
| + printer->Print(
|
| + "/**\n"
|
| + " * List of repeated fields within this message type.\n"
|
| + " * @private {!Array<number>}\n"
|
| + " * @const\n"
|
| + " */\n"
|
| + "$classname$$rptfieldarray$ = $rptfields$;\n"
|
| + "\n",
|
| + "classname", GetPath(options, desc),
|
| + "rptfieldarray", kRepeatedFieldArrayName,
|
| + "rptfields", RepeatedFieldNumberList(desc));
|
| + }
|
| +
|
| + if (HasOneofFields(desc)) {
|
| + printer->Print(
|
| + "/**\n"
|
| + " * Oneof group definitions for this message. Each group defines the "
|
| + "field\n"
|
| + " * numbers belonging to that group. When of these fields' value is "
|
| + "set, all\n"
|
| + " * other fields in the group are cleared. During deserialization, if "
|
| + "multiple\n"
|
| + " * fields are encountered for a group, only the last value seen will "
|
| + "be kept.\n"
|
| + " * @private {!Array<!Array<number>>}\n"
|
| + " * @const\n"
|
| + " */\n"
|
| + "$classname$$oneofgrouparray$ = $oneofgroups$;\n"
|
| + "\n",
|
| + "classname", GetPath(options, desc),
|
| + "oneofgrouparray", kOneofGroupArrayName,
|
| + "oneofgroups", OneofGroupList(desc));
|
| +
|
| + for (int i = 0; i < desc->oneof_decl_count(); i++) {
|
| + if (IgnoreOneof(desc->oneof_decl(i))) {
|
| + continue;
|
| + }
|
| + GenerateOneofCaseDefinition(options, printer, desc->oneof_decl(i));
|
| + }
|
| + }
|
| +}
|
| +
|
| +void Generator::GenerateClassXid(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const Descriptor* desc) const {
|
| + printer->Print(
|
| + "\n"
|
| + "\n"
|
| + "$class$.prototype.messageXid = xid('$class$');\n",
|
| + "class", GetPath(options, desc));
|
| +}
|
| +
|
| +void Generator::GenerateOneofCaseDefinition(
|
| + const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const OneofDescriptor* oneof) const {
|
| + printer->Print(
|
| + "/**\n"
|
| + " * @enum {number}\n"
|
| + " */\n"
|
| + "$classname$.$oneof$Case = {\n"
|
| + " $upcase$_NOT_SET: 0",
|
| + "classname", GetPath(options, oneof->containing_type()),
|
| + "oneof", JSOneofName(oneof),
|
| + "upcase", ToEnumCase(oneof->name()));
|
| +
|
| + for (int i = 0; i < oneof->field_count(); i++) {
|
| + if (IgnoreField(oneof->field(i))) {
|
| + continue;
|
| + }
|
| +
|
| + printer->Print(
|
| + ",\n"
|
| + " $upcase$: $number$",
|
| + "upcase", ToEnumCase(oneof->field(i)->name()),
|
| + "number", JSFieldIndex(oneof->field(i)));
|
| + }
|
| +
|
| + printer->Print(
|
| + "\n"
|
| + "};\n"
|
| + "\n"
|
| + "/**\n"
|
| + " * @return {$class$.$oneof$Case}\n"
|
| + " */\n"
|
| + "$class$.prototype.get$oneof$Case = function() {\n"
|
| + " return /** @type {$class$.$oneof$Case} */(jspb.Message."
|
| + "computeOneofCase(this, $class$.oneofGroups_[$oneofindex$]));\n"
|
| + "};\n"
|
| + "\n",
|
| + "class", GetPath(options, oneof->containing_type()),
|
| + "oneof", JSOneofName(oneof),
|
| + "oneofindex", JSOneofIndex(oneof));
|
| +}
|
| +
|
| +void Generator::GenerateClassToObject(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const Descriptor* desc) const {
|
| + printer->Print(
|
| + "\n"
|
| + "\n"
|
| + "if (jspb.Message.GENERATE_TO_OBJECT) {\n"
|
| + "/**\n"
|
| + " * Creates an object representation of this proto suitable for use in "
|
| + "Soy templates.\n"
|
| + " * Field names that are reserved in JavaScript and will be renamed to "
|
| + "pb_name.\n"
|
| + " * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.\n"
|
| + " * For the list of reserved names please see:\n"
|
| + " * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.\n"
|
| + " * @param {boolean=} opt_includeInstance Whether to include the JSPB "
|
| + "instance\n"
|
| + " * for transitional soy proto support: http://goto/soy-param-"
|
| + "migration\n"
|
| + " * @return {!Object}\n"
|
| + " */\n"
|
| + "$classname$.prototype.toObject = function(opt_includeInstance) {\n"
|
| + " return $classname$.toObject(opt_includeInstance, this);\n"
|
| + "};\n"
|
| + "\n"
|
| + "\n"
|
| + "/**\n"
|
| + " * Static version of the {@see toObject} method.\n"
|
| + " * @param {boolean|undefined} includeInstance Whether to include the "
|
| + "JSPB\n"
|
| + " * instance for transitional soy proto support:\n"
|
| + " * http://goto/soy-param-migration\n"
|
| + " * @param {!$classname$} msg The msg instance to transform.\n"
|
| + " * @return {!Object}\n"
|
| + " */\n"
|
| + "$classname$.toObject = function(includeInstance, msg) {\n"
|
| + " var f, obj = {",
|
| + "classname", GetPath(options, desc));
|
| +
|
| + bool first = true;
|
| + for (int i = 0; i < desc->field_count(); i++) {
|
| + const FieldDescriptor* field = desc->field(i);
|
| + if (IgnoreField(field)) {
|
| + continue;
|
| + }
|
| +
|
| + if (!first) {
|
| + printer->Print(",\n ");
|
| + } else {
|
| + printer->Print("\n ");
|
| + first = false;
|
| + }
|
| +
|
| + GenerateClassFieldToObject(options, printer, field);
|
| + }
|
| +
|
| + if (!first) {
|
| + printer->Print("\n };\n\n");
|
| + } else {
|
| + printer->Print("\n\n };\n\n");
|
| + }
|
| +
|
| + if (IsExtendable(desc)) {
|
| + printer->Print(
|
| + " jspb.Message.toObjectExtension(/** @type {!jspb.Message} */ (msg), "
|
| + "obj,\n"
|
| + " $extObject$, $class$.prototype.getExtension,\n"
|
| + " includeInstance);\n",
|
| + "extObject", JSExtensionsObjectName(options, desc),
|
| + "class", GetPath(options, desc));
|
| + }
|
| +
|
| + printer->Print(
|
| + " if (includeInstance) {\n"
|
| + " obj.$$jspbMessageInstance = msg\n"
|
| + " }\n"
|
| + " return obj;\n"
|
| + "};\n"
|
| + "}\n"
|
| + "\n"
|
| + "\n",
|
| + "classname", GetPath(options, desc));
|
| +}
|
| +
|
| +void Generator::GenerateClassFieldToObject(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const FieldDescriptor* field) const {
|
| + printer->Print("$fieldname$: ",
|
| + "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(field),
|
| + "type", GetPath(options, field->message_type()));
|
| + }
|
| + } else {
|
| + printer->Print("(f = msg.get$getter$()) && "
|
| + "$type$.toObject(includeInstance, f)",
|
| + "getter", JSGetterName(field),
|
| + "type", GetPath(options, field->message_type()));
|
| + }
|
| + } else {
|
| + // Simple field (singular or repeated).
|
| + if (!HasFieldPresence(field) && !field->is_repeated()) {
|
| + // Delegate to the generated get<field>() method in order not to duplicate
|
| + // the proto3-field-default-value logic here.
|
| + printer->Print("msg.get$getter$()",
|
| + "getter", JSGetterName(field));
|
| + } else {
|
| + if (field->has_default_value()) {
|
| + printer->Print("jspb.Message.getField(msg, $index$) != null ? "
|
| + "jspb.Message.getField(msg, $index$) : $defaultValue$",
|
| + "index", JSFieldIndex(field),
|
| + "defaultValue", JSFieldDefault(field));
|
| + } else {
|
| + printer->Print("jspb.Message.getField(msg, $index$)",
|
| + "index", JSFieldIndex(field));
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +void Generator::GenerateClassFromObject(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const Descriptor* desc) const {
|
| + printer->Print(
|
| + "if (jspb.Message.GENERATE_FROM_OBJECT) {\n"
|
| + "/**\n"
|
| + " * Loads data from an object into a new instance of this proto.\n"
|
| + " * @param {!Object} obj The object representation of this proto to\n"
|
| + " * load the data from.\n"
|
| + " * @return {!$classname$}\n"
|
| + " */\n"
|
| + "$classname$.fromObject = function(obj) {\n"
|
| + " var f, msg = new $classname$();\n",
|
| + "classname", GetPath(options, desc));
|
| +
|
| + for (int i = 0; i < desc->field_count(); i++) {
|
| + const FieldDescriptor* field = desc->field(i);
|
| + GenerateClassFieldFromObject(options, printer, field);
|
| + }
|
| +
|
| + printer->Print(
|
| + " return msg;\n"
|
| + "};\n"
|
| + "}\n");
|
| +}
|
| +
|
| +void Generator::GenerateClassFieldFromObject(
|
| + const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const FieldDescriptor* field) const {
|
| + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
|
| + // Message field (singular or repeated)
|
| + if (field->is_repeated()) {
|
| + {
|
| + printer->Print(
|
| + " goog.isDef(obj.$name$) && "
|
| + "jspb.Message.setRepeatedWrapperField(\n"
|
| + " msg, $index$, goog.array.map(obj.$name$, function(i) {\n"
|
| + " return $fieldclass$.fromObject(i);\n"
|
| + " }));\n",
|
| + "name", JSObjectFieldName(field),
|
| + "index", JSFieldIndex(field),
|
| + "fieldclass", GetPath(options, field->message_type()));
|
| + }
|
| + } else {
|
| + printer->Print(
|
| + " goog.isDef(obj.$name$) && jspb.Message.setWrapperField(\n"
|
| + " msg, $index$, $fieldclass$.fromObject(obj.$name$));\n",
|
| + "name", JSObjectFieldName(field),
|
| + "index", JSFieldIndex(field),
|
| + "fieldclass", GetPath(options, field->message_type()));
|
| + }
|
| + } else {
|
| + // Simple (primitive) field.
|
| + printer->Print(
|
| + " goog.isDef(obj.$name$) && jspb.Message.setField(msg, $index$, "
|
| + "obj.$name$);\n",
|
| + "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 {
|
| + // Register any extensions defined inside this message type.
|
| + for (int i = 0; i < desc->extension_count(); i++) {
|
| + const FieldDescriptor* extension = desc->extension(i);
|
| + if (ShouldGenerateExtension(extension)) {
|
| + GenerateExtension(options, printer, extension);
|
| + }
|
| + }
|
| +
|
| +}
|
| +
|
| +void Generator::GenerateClassFields(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const Descriptor* desc) const {
|
| + for (int i = 0; i < desc->field_count(); i++) {
|
| + if (!IgnoreField(desc->field(i))) {
|
| + GenerateClassField(options, printer, desc->field(i));
|
| + }
|
| + }
|
| +}
|
| +
|
| +void Generator::GenerateClassField(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const FieldDescriptor* field) const {
|
| + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
|
| + printer->Print(
|
| + "/**\n"
|
| + " * $fielddef$\n"
|
| + "$comment$"
|
| + " * @return {$type$}\n"
|
| + " */\n",
|
| + "fielddef", FieldDefinition(options, field),
|
| + "comment", FieldComments(field),
|
| + "type", JSFieldTypeAnnotation(options, field,
|
| + /* force_optional = */ false,
|
| + /* force_present = */ false,
|
| + /* singular_if_not_packed = */ false,
|
| + /* always_singular = */ false));
|
| + printer->Print(
|
| + "$class$.prototype.get$name$ = function() {\n"
|
| + " return /** @type{$type$} */ (\n"
|
| + " jspb.Message.get$rpt$WrapperField(this, $wrapperclass$, "
|
| + "$index$$required$));\n"
|
| + "};\n"
|
| + "\n"
|
| + "\n",
|
| + "class", GetPath(options, field->containing_type()),
|
| + "name", JSGetterName(field),
|
| + "type", JSFieldTypeAnnotation(options, field,
|
| + /* force_optional = */ false,
|
| + /* force_present = */ false,
|
| + /* singular_if_not_packed = */ false,
|
| + /* always_singular = */ false),
|
| + "rpt", (field->is_repeated() ? "Repeated" : ""),
|
| + "index", JSFieldIndex(field),
|
| + "wrapperclass", GetPath(options, field->message_type()),
|
| + "required", (field->label() == FieldDescriptor::LABEL_REQUIRED ?
|
| + ", 1" : ""));
|
| + printer->Print(
|
| + "/** @param {$optionaltype$} value $returndoc$ */\n"
|
| + "$class$.prototype.set$name$ = function(value) {\n"
|
| + " jspb.Message.set$oneoftag$$repeatedtag$WrapperField(",
|
| + "optionaltype",
|
| + JSFieldTypeAnnotation(options, field,
|
| + /* force_optional = */ true,
|
| + /* force_present = */ false,
|
| + /* singular_if_not_packed = */ false,
|
| + /* always_singular = */ false),
|
| + "returndoc", JSReturnDoc(options, field),
|
| + "class", GetPath(options, field->containing_type()),
|
| + "name", JSGetterName(field),
|
| + "oneoftag", (field->containing_oneof() ? "Oneof" : ""),
|
| + "repeatedtag", (field->is_repeated() ? "Repeated" : ""));
|
| +
|
| + printer->Print(
|
| + "this, $index$$oneofgroup$, value);$returnvalue$\n"
|
| + "};\n"
|
| + "\n"
|
| + "\n",
|
| + "index", JSFieldIndex(field),
|
| + "oneofgroup", (field->containing_oneof() ?
|
| + (", " + JSOneofArray(options, field)) : ""),
|
| + "returnvalue", JSReturnClause(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 {
|
| + string typed_annotation;
|
| +
|
| + // Simple (primitive) field, either singular or repeated.
|
| + {
|
| + typed_annotation = JSFieldTypeAnnotation(options, field,
|
| + /* force_optional = */ false,
|
| + /* force_present = */ !HasFieldPresence(field),
|
| + /* singular_if_not_packed = */ false,
|
| + /* always_singular = */ false),
|
| + printer->Print(
|
| + "/**\n"
|
| + " * $fielddef$\n"
|
| + "$comment$"
|
| + " * @return {$type$}\n"
|
| + " */\n",
|
| + "fielddef", FieldDefinition(options, field),
|
| + "comment", FieldComments(field),
|
| + "type", typed_annotation);
|
| + }
|
| +
|
| + printer->Print(
|
| + "$class$.prototype.get$name$ = function() {\n",
|
| + "class", GetPath(options, field->containing_type()),
|
| + "name", JSGetterName(field));
|
| +
|
| + {
|
| + printer->Print(
|
| + " return /** @type {$type$} */ (",
|
| + "type", typed_annotation);
|
| + }
|
| +
|
| + // 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 ? "
|
| + "jspb.Message.getField(this, $index$) : $defaultValue$",
|
| + "index", JSFieldIndex(field),
|
| + "defaultValue", JSFieldDefault(field));
|
| + } else {
|
| + printer->Print("jspb.Message.getField(this, $index$)",
|
| + "index", JSFieldIndex(field));
|
| + }
|
| + }
|
| +
|
| + {
|
| + printer->Print(
|
| + ");\n"
|
| + "};\n"
|
| + "\n"
|
| + "\n");
|
| + }
|
| +
|
| + {
|
| + printer->Print(
|
| + "/** @param {$optionaltype$} value $returndoc$ */\n",
|
| + "optionaltype",
|
| + JSFieldTypeAnnotation(options, field,
|
| + /* force_optional = */ true,
|
| + /* force_present = */ !HasFieldPresence(field),
|
| + /* singular_if_not_packed = */ false,
|
| + /* always_singular = */ 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(field),
|
| + "oneoftag", (field->containing_oneof() ? "Oneof" : ""),
|
| + "index", JSFieldIndex(field));
|
| + printer->Print(
|
| + "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);$returnvalue$\n"
|
| + "};\n"
|
| + "\n"
|
| + "\n",
|
| + "type", "",
|
| + "typeclose", "",
|
| + "oneofgroup",
|
| + (field->containing_oneof() ? (", " + JSOneofArray(options, field))
|
| + : ""),
|
| + "returnvalue", JSReturnClause(field), "rptvalueinit",
|
| + (field->is_repeated() ? " || []" : ""));
|
| +
|
| +
|
| + 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));
|
| + }
|
| + }
|
| +}
|
| +
|
| +void Generator::GenerateClassExtensionFieldInfo(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const Descriptor* desc) const {
|
| + if (IsExtendable(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.ExtensionFieldInfo>}\n"
|
| + " */\n"
|
| + "$class$.extensions = {};\n"
|
| + "\n",
|
| + "class", GetPath(options, desc));
|
| + }
|
| +}
|
| +
|
| +
|
| +void Generator::GenerateClassDeserializeBinary(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const Descriptor* desc) const {
|
| + // TODO(cfallin): Handle lazy decoding when requested by field option and/or
|
| + // by default for 'bytes' fields and packed repeated fields.
|
| +
|
| + printer->Print(
|
| + "/**\n"
|
| + " * Deserializes binary data (in protobuf wire format).\n"
|
| + " * @param {jspb.ByteSource} bytes The bytes to deserialize.\n"
|
| + " * @return {!$class$}\n"
|
| + " */\n"
|
| + "$class$.deserializeBinary = function(bytes) {\n"
|
| + " var reader = new jspb.BinaryReader(bytes);\n"
|
| + " var msg = new $class$;\n"
|
| + " return $class$.deserializeBinaryFromReader(msg, reader);\n"
|
| + "};\n"
|
| + "\n"
|
| + "\n"
|
| + "/**\n"
|
| + " * Deserializes binary data (in protobuf wire format) from the\n"
|
| + " * given reader into the given message object.\n"
|
| + " * @param {!$class$} msg The message object to deserialize into.\n"
|
| + " * @param {!jspb.BinaryReader} reader The BinaryReader to use.\n"
|
| + " * @return {!$class$}\n"
|
| + " */\n"
|
| + "$class$.deserializeBinaryFromReader = function(msg, reader) {\n"
|
| + " while (reader.nextField()) {\n"
|
| + " if (reader.isEndGroup()) {\n"
|
| + " break;\n"
|
| + " }\n"
|
| + " var field = reader.getFieldNumber();\n"
|
| + " switch (field) {\n",
|
| + "class", GetPath(options, desc));
|
| +
|
| + for (int i = 0; i < desc->field_count(); i++) {
|
| + GenerateClassDeserializeBinaryField(options, printer, desc->field(i));
|
| + }
|
| +
|
| + printer->Print(
|
| + " default:\n");
|
| + if (IsExtendable(desc)) {
|
| + printer->Print(
|
| + " jspb.Message.readBinaryExtension(msg, reader, $extobj$,\n"
|
| + " $class$.prototype.getExtension,\n"
|
| + " $class$.prototype.setExtension);\n"
|
| + " break;\n",
|
| + "extobj", JSExtensionsObjectName(options, desc),
|
| + "class", GetPath(options, desc));
|
| + } else {
|
| + printer->Print(
|
| + " reader.skipField();\n"
|
| + " break;\n");
|
| + }
|
| +
|
| + printer->Print(
|
| + " }\n"
|
| + " }\n"
|
| + " return msg;\n"
|
| + "};\n"
|
| + "\n"
|
| + "\n");
|
| +}
|
| +
|
| +void Generator::GenerateClassDeserializeBinaryField(
|
| + const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const FieldDescriptor* field) const {
|
| +
|
| + printer->Print(" case $num$:\n",
|
| + "num", SimpleItoa(field->number()));
|
| +
|
| + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
|
| + printer->Print(
|
| + " var value = new $fieldclass$;\n"
|
| + " reader.read$msgOrGroup$($grpfield$value,"
|
| + "$fieldclass$.deserializeBinaryFromReader);\n",
|
| + "fieldclass", GetPath(options, field->message_type()),
|
| + "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,
|
| + /* always_singular = */ false),
|
| + "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$().
|
| + printer->Print(
|
| + " msg.get$name$().push(value);\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");
|
| +}
|
| +
|
| +void Generator::GenerateClassSerializeBinary(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + 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"
|
| + " this.serializeBinaryToWriter(writer);\n"
|
| + " return writer.getResultBuffer();\n"
|
| + "};\n"
|
| + "\n"
|
| + "\n"
|
| + "/**\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$.prototype.serializeBinaryToWriter = function (writer) {\n"
|
| + " var f = undefined;\n",
|
| + "class", GetPath(options, desc));
|
| +
|
| + for (int i = 0; i < desc->field_count(); i++) {
|
| + GenerateClassSerializeBinaryField(options, printer, desc->field(i));
|
| + }
|
| +
|
| + if (IsExtendable(desc)) {
|
| + printer->Print(
|
| + " jspb.Message.serializeBinaryExtensions(this, writer, $extobj$,\n"
|
| + " $class$.prototype.getExtension);\n",
|
| + "extobj", JSExtensionsObjectName(options, desc),
|
| + "class", GetPath(options, desc));
|
| + }
|
| +
|
| + printer->Print(
|
| + "};\n"
|
| + "\n"
|
| + "\n");
|
| +}
|
| +
|
| +void Generator::GenerateClassSerializeBinaryField(
|
| + const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const FieldDescriptor* field) const {
|
| + printer->Print(
|
| + " f = this.get$name$();\n",
|
| + "name", JSGetterName(field));
|
| +
|
| + if (field->is_repeated()) {
|
| + printer->Print(
|
| + " if (f.length > 0) {\n");
|
| + } else {
|
| + if (HasFieldPresence(field)) {
|
| + printer->Print(
|
| + " if (f != null) {\n");
|
| + } else {
|
| + // No field presence: serialize onto the wire only if value is
|
| + // non-default. Defaults are documented here:
|
| + // https://goto.google.com/lhdfm
|
| + switch (field->cpp_type()) {
|
| + case FieldDescriptor::CPPTYPE_INT32:
|
| + case FieldDescriptor::CPPTYPE_INT64:
|
| + case FieldDescriptor::CPPTYPE_UINT32:
|
| + case FieldDescriptor::CPPTYPE_UINT64: {
|
| + {
|
| + printer->Print(" if (f !== 0) {\n");
|
| + }
|
| + break;
|
| + }
|
| +
|
| + case FieldDescriptor::CPPTYPE_ENUM:
|
| + case FieldDescriptor::CPPTYPE_FLOAT:
|
| + case FieldDescriptor::CPPTYPE_DOUBLE:
|
| + printer->Print(
|
| + " if (f !== 0.0) {\n");
|
| + break;
|
| + case FieldDescriptor::CPPTYPE_BOOL:
|
| + printer->Print(
|
| + " if (f) {\n");
|
| + break;
|
| + case FieldDescriptor::CPPTYPE_STRING:
|
| + printer->Print(
|
| + " if (f.length > 0) {\n");
|
| + break;
|
| + default:
|
| + assert(false);
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + printer->Print(
|
| + " writer.$writer$(\n"
|
| + " $index$,\n"
|
| + " f",
|
| + "writer", JSBinaryWriterMethodName(field),
|
| + "name", JSGetterName(field),
|
| + "index", SimpleItoa(field->number()));
|
| +
|
| + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
|
| + printer->Print(
|
| + ",\n"
|
| + " $submsg$.serializeBinaryToWriter\n",
|
| + "submsg", GetPath(options, field->message_type()));
|
| + } else {
|
| + printer->Print("\n");
|
| + }
|
| + printer->Print(
|
| + " );\n"
|
| + " }\n");
|
| +}
|
| +
|
| +void Generator::GenerateEnum(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const EnumDescriptor* enumdesc) const {
|
| + printer->Print(
|
| + "/**\n"
|
| + " * @enum {number}\n"
|
| + " */\n"
|
| + "$name$ = {\n",
|
| + "name", GetPath(options, enumdesc));
|
| +
|
| + for (int i = 0; i < enumdesc->value_count(); i++) {
|
| + const EnumValueDescriptor* value = enumdesc->value(i);
|
| + printer->Print(
|
| + " $name$: $value$$comma$\n",
|
| + "name", ToEnumCase(value->name()),
|
| + "value", SimpleItoa(value->number()),
|
| + "comma", (i == enumdesc->value_count() - 1) ? "" : ",");
|
| + }
|
| +
|
| + printer->Print(
|
| + "};\n"
|
| + "\n");
|
| +}
|
| +
|
| +void Generator::GenerateExtension(const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const FieldDescriptor* field) const {
|
| + string extension_scope =
|
| + (field->extension_scope() ?
|
| + GetPath(options, field->extension_scope()) :
|
| + GetPath(options, field->file()));
|
| +
|
| + printer->Print(
|
| + "\n"
|
| + "/**\n"
|
| + " * A tuple of {field number, class constructor} for the extension\n"
|
| + " * field named `$name$`.\n"
|
| + " * @type {!jspb.ExtensionFieldInfo.<$extensionType$>}\n"
|
| + " */\n"
|
| + "$class$.$name$ = new jspb.ExtensionFieldInfo(\n",
|
| + "name", JSObjectFieldName(field),
|
| + "class", extension_scope,
|
| + "extensionType", JSFieldTypeAnnotation(
|
| + options, field,
|
| + /* force_optional = */ false,
|
| + /* force_present = */ true,
|
| + /* singular_if_not_packed = */ false,
|
| + /* always_singular = */ false));
|
| + printer->Print(
|
| + " $index$,\n"
|
| + " {$name$: 0},\n"
|
| + " $ctor$,\n"
|
| + " /** @type {?function((boolean|undefined),!jspb.Message=): "
|
| + "!Object} */ (\n"
|
| + " $toObject$),\n"
|
| + " $repeated$",
|
| + "index", SimpleItoa(field->number()),
|
| + "name", JSObjectFieldName(field),
|
| + "ctor", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ?
|
| + GetPath(options, field->message_type()) : string("null")),
|
| + "toObject", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ?
|
| + (GetPath(options, field->message_type()) + ".toObject") :
|
| + string("null")),
|
| + "repeated", (field->is_repeated() ? "1" : "0"));
|
| +
|
| + 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) ?
|
| + (GetPath(options, field->message_type()) +
|
| + ".serializeBinaryToWriter") : "null",
|
| + "binaryMessageDeserializeFn",
|
| + (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
|
| + (GetPath(options, field->message_type()) +
|
| + ".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"
|
| + "// toObject() will function correctly.\n"
|
| + "$extendName$[$index$] = $class$.$name$;\n"
|
| + "\n",
|
| + "extendName", JSExtensionsObjectName(options, field->containing_type()),
|
| + "index", SimpleItoa(field->number()),
|
| + "class", extension_scope,
|
| + "name", JSObjectFieldName(field));
|
| +}
|
| +
|
| +bool GeneratorOptions::ParseFromOptions(
|
| + const vector< pair< string, string > >& options,
|
| + string* error) {
|
| + for (int i = 0; i < options.size(); i++) {
|
| + if (options[i].first == "add_require_for_enums") {
|
| + if (options[i].second != "") {
|
| + *error = "Unexpected option value for add_require_for_enums";
|
| + return false;
|
| + }
|
| + add_require_for_enums = true;
|
| + } else if (options[i].first == "binary") {
|
| + if (options[i].second != "") {
|
| + *error = "Unexpected option value for binary";
|
| + return false;
|
| + }
|
| + binary = true;
|
| + } else if (options[i].first == "testonly") {
|
| + if (options[i].second != "") {
|
| + *error = "Unexpected option value for testonly";
|
| + return false;
|
| + }
|
| + testonly = true;
|
| + } else if (options[i].first == "error_on_name_conflict") {
|
| + if (options[i].second != "") {
|
| + *error = "Unexpected option value for error_on_name_conflict";
|
| + return false;
|
| + }
|
| + error_on_name_conflict = true;
|
| + } else if (options[i].first == "output_dir") {
|
| + output_dir = options[i].second;
|
| + } else if (options[i].first == "namespace_prefix") {
|
| + namespace_prefix = options[i].second;
|
| + } else if (options[i].first == "library") {
|
| + library = options[i].second;
|
| + } else {
|
| + // Assume any other option is an output directory, as long as it is a bare
|
| + // `key` rather than a `key=value` option.
|
| + if (options[i].second != "") {
|
| + *error = "Unknown option: " + options[i].first;
|
| + return false;
|
| + }
|
| + output_dir = options[i].first;
|
| + }
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void Generator::GenerateFilesInDepOrder(
|
| + const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + 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());
|
| + // Track the in-progress set of files that have been generated already.
|
| + std::set<const FileDescriptor*> generated;
|
| + for (int i = 0; i < files.size(); i++) {
|
| + GenerateFileAndDeps(options, printer, files[i], &all_files, &generated);
|
| + }
|
| +}
|
| +
|
| +void Generator::GenerateFileAndDeps(
|
| + const GeneratorOptions& options,
|
| + io::Printer* printer,
|
| + const FileDescriptor* root,
|
| + std::set<const FileDescriptor*>* all_files,
|
| + std::set<const FileDescriptor*>* generated) const {
|
| + // Skip if already generated.
|
| + if (generated->find(root) != generated->end()) {
|
| + return;
|
| + }
|
| + generated->insert(root);
|
| +
|
| + // Generate all dependencies before this file's content.
|
| + for (int i = 0; i < root->dependency_count(); i++) {
|
| + const FileDescriptor* dep = root->dependency(i);
|
| + GenerateFileAndDeps(options, printer, dep, all_files, generated);
|
| + }
|
| +
|
| + // Generate this file's content. Only generate if the file is part of the
|
| + // original set requested to be generated; i.e., don't take all transitive
|
| + // deps down to the roots.
|
| + if (all_files->find(root) != all_files->end()) {
|
| + GenerateClassesAndEnums(options, printer, root);
|
| + }
|
| +}
|
| +
|
| +bool Generator::GenerateAll(const vector<const FileDescriptor*>& files,
|
| + const string& parameter,
|
| + GeneratorContext* context,
|
| + string* error) const {
|
| + vector< pair< string, string > > option_pairs;
|
| + ParseGeneratorParameter(parameter, &option_pairs);
|
| + GeneratorOptions options;
|
| + if (!options.ParseFromOptions(option_pairs, error)) {
|
| + return false;
|
| + }
|
| +
|
| +
|
| + // We're either generating a single library file with definitions for message
|
| + // and enum types in *all* FileDescriptor inputs, or we're generating a single
|
| + // file for each type.
|
| + if (options.library != "") {
|
| + 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.
|
| + 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);
|
| + extensions.push_back(extension);
|
| + }
|
| + }
|
| +
|
| + GenerateHeader(options, &printer);
|
| +
|
| + std::set<string> provided;
|
| + FindProvides(options, &printer, files, &provided);
|
| + FindProvidesForFields(options, &printer, extensions, &provided);
|
| + GenerateProvides(options, &printer, &provided);
|
| + GenerateTestOnly(options, &printer);
|
| + GenerateRequires(options, &printer, files, &provided);
|
| +
|
| + GenerateFilesInDepOrder(options, &printer, files);
|
| +
|
| + for (int i = 0; i < extensions.size(); i++) {
|
| + if (ShouldGenerateExtension(extensions[i])) {
|
| + GenerateExtension(options, &printer, extensions[i]);
|
| + }
|
| + }
|
| +
|
| + if (printer.failed()) {
|
| + return false;
|
| + }
|
| + } else {
|
| + // Collect all types, and print each type to a separate file. Pull out
|
| + // free-floating extensions while we make this pass.
|
| + map< string, vector<const FieldDescriptor*> > extensions_by_namespace;
|
| +
|
| + // If we're generating code in file-per-type mode, avoid overwriting files
|
| + // by choosing the last descriptor that writes each filename and permitting
|
| + // only those to generate code.
|
| +
|
| + // Current descriptor that will generate each filename, indexed by filename.
|
| + map<string, const void*> desc_by_filename;
|
| + // Set of descriptors allowed to generate files.
|
| + set<const void*> allowed_descs;
|
| +
|
| + for (int i = 0; i < files.size(); i++) {
|
| + // Collect all (descriptor, filename) pairs.
|
| + map<const void*, string> descs_in_file;
|
| + for (int j = 0; j < files[i]->message_type_count(); j++) {
|
| + const Descriptor* desc = files[i]->message_type(j);
|
| + string filename =
|
| + options.output_dir + "/" + ToFileName(desc->name()) + ".js";
|
| + descs_in_file[desc] = filename;
|
| + }
|
| + for (int j = 0; j < files[i]->enum_type_count(); j++) {
|
| + const EnumDescriptor* desc = files[i]->enum_type(j);
|
| + string filename =
|
| + options.output_dir + "/" + ToFileName(desc->name()) + ".js";
|
| + descs_in_file[desc] = filename;
|
| + }
|
| +
|
| + // For each (descriptor, filename) pair, update the
|
| + // descriptors-by-filename map, and if a previous descriptor was already
|
| + // writing the filename, remove it from the allowed-descriptors set.
|
| + map<const void*, string>::iterator it;
|
| + for (it = descs_in_file.begin(); it != descs_in_file.end(); ++it) {
|
| + const void* desc = it->first;
|
| + const string& filename = it->second;
|
| + if (desc_by_filename.find(filename) != desc_by_filename.end()) {
|
| + if (options.error_on_name_conflict) {
|
| + *error = "Name conflict: file name " + filename +
|
| + " would be generated by two descriptors";
|
| + return false;
|
| + }
|
| + allowed_descs.erase(desc_by_filename[filename]);
|
| + }
|
| + desc_by_filename[filename] = desc;
|
| + allowed_descs.insert(desc);
|
| + }
|
| + }
|
| +
|
| + // Generate code.
|
| + for (int i = 0; i < files.size(); i++) {
|
| + const FileDescriptor* file = files[i];
|
| + for (int j = 0; j < file->message_type_count(); j++) {
|
| + const Descriptor* desc = file->message_type(j);
|
| + if (allowed_descs.find(desc) == allowed_descs.end()) {
|
| + continue;
|
| + }
|
| +
|
| + string filename = options.output_dir + "/" +
|
| + ToFileName(desc->name()) + ".js";
|
| + google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(
|
| + context->Open(filename));
|
| + GOOGLE_CHECK(output.get());
|
| + io::Printer printer(output.get(), '$');
|
| +
|
| + GenerateHeader(options, &printer);
|
| +
|
| + std::set<string> provided;
|
| + FindProvidesForMessage(options, &printer, desc, &provided);
|
| + GenerateProvides(options, &printer, &provided);
|
| + GenerateTestOnly(options, &printer);
|
| + GenerateRequires(options, &printer, desc, &provided);
|
| +
|
| + GenerateClass(options, &printer, desc);
|
| +
|
| + if (printer.failed()) {
|
| + return false;
|
| + }
|
| + }
|
| + for (int j = 0; j < file->enum_type_count(); j++) {
|
| + const EnumDescriptor* enumdesc = file->enum_type(j);
|
| + if (allowed_descs.find(enumdesc) == allowed_descs.end()) {
|
| + continue;
|
| + }
|
| +
|
| + string filename = options.output_dir + "/" +
|
| + ToFileName(enumdesc->name()) + ".js";
|
| +
|
| + google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(
|
| + context->Open(filename));
|
| + GOOGLE_CHECK(output.get());
|
| + io::Printer printer(output.get(), '$');
|
| +
|
| + GenerateHeader(options, &printer);
|
| +
|
| + std::set<string> provided;
|
| + FindProvidesForEnum(options, &printer, enumdesc, &provided);
|
| + GenerateProvides(options, &printer, &provided);
|
| + GenerateTestOnly(options, &printer);
|
| +
|
| + GenerateEnum(options, &printer, enumdesc);
|
| +
|
| + if (printer.failed()) {
|
| + return false;
|
| + }
|
| + }
|
| + // Pull out all free-floating extensions and generate files for those too.
|
| + for (int j = 0; j < file->extension_count(); j++) {
|
| + const FieldDescriptor* extension = file->extension(j);
|
| + extensions_by_namespace[GetPath(options, files[i])]
|
| + .push_back(extension);
|
| + }
|
| + }
|
| +
|
| + // Generate extensions in separate files.
|
| + map< string, vector<const FieldDescriptor*> >::iterator it;
|
| + for (it = extensions_by_namespace.begin();
|
| + it != extensions_by_namespace.end();
|
| + ++it) {
|
| + string filename = options.output_dir + "/" +
|
| + ToFileName(it->first) + ".js";
|
| +
|
| + google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(
|
| + context->Open(filename));
|
| + GOOGLE_CHECK(output.get());
|
| + io::Printer printer(output.get(), '$');
|
| +
|
| + GenerateHeader(options, &printer);
|
| +
|
| + std::set<string> provided;
|
| + FindProvidesForFields(options, &printer, it->second, &provided);
|
| + GenerateProvides(options, &printer, &provided);
|
| + GenerateTestOnly(options, &printer);
|
| + GenerateRequires(options, &printer, it->second, &provided);
|
| +
|
| + for (int j = 0; j < it->second.size(); j++) {
|
| + if (ShouldGenerateExtension(it->second[j])) {
|
| + GenerateExtension(options, &printer, it->second[j]);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +} // namespace js
|
| +} // namespace compiler
|
| +} // namespace protobuf
|
| +} // namespace google
|
|
|