text: // 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>
  #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/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>
  #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 {
  
  // The mode of operation for bytes fields. Historically JSPB always carried
  // bytes as JS {string}, containing base64 content by convention. With binary
  // and proto3 serialization the new convention is to represent it as binary
  // data in Uint8Array. See b/26173701 for background on the migration.
  enum BytesMode {
    BYTES_DEFAULT,  // Default type for getBytesField to return.
    BYTES_B64,      // Explicitly coerce to base64 string where needed.
    BYTES_U8,       // Explicitly coerce to Uint8Array where needed.
  };
  
  bool IsReserved(const string& ident) {
    for (int i = 0; i < kNumKeyword; i++) {
      if (ident == kKeyword[i]) {
        return true;
      }
    }
    return false;
  }
  
  bool StrEndsWith(StringPiece sp, StringPiece x) {
    return sp.size() >= x.size() && sp.substr(sp.size() - x.size()) == x;
  }
  
  // Returns a copy of |filename| with any trailing ".protodevel" or ".proto
  // suffix stripped.
  // TODO(haberman): Unify with copy in compiler/cpp/internal/helpers.cc.
  string StripProto(const string& filename) {
    const char* suffix =
        StrEndsWith(filename, ".protodevel") ? ".protodevel" : ".proto";
    return StripSuffixString(filename, suffix);
  }
  
  // Given a filename like foo/bar/baz.proto, returns the corresponding JavaScript
  // file foo/bar/baz.js.
  string GetJSFilename(const GeneratorOptions& options, const string& filename) {
    return StripProto(filename) + options.GetFileNameExtension();
  }
  
  // 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(), '/');
    if (slashes == 0) {
      return "./";
    }
    string result = "";
    for (size_t i = 0; i < slashes; i++) {
      result += "../";
    }
    return result;
  }
  
  // Returns the alias we assign to the module of the given .proto filename
  // when importing.
  string ModuleAlias(const string& filename) {
    // This scheme could technically cause problems if a file includes any 2 of:
    //   foo/bar_baz.proto
    //   foo_bar_baz.proto
    //   foo_bar/baz.proto
    //
    // We'll worry about this problem if/when we actually see it.  This name isn't
    // exposed to users so we can change it later if we need to.
    string basename = StripProto(filename);
    ReplaceCharacters(&basename, "-", '$');
    ReplaceCharacters(&basename, "/", '_');
    ReplaceCharacters(&basename, ".", '_');
    return basename + "_pb";
  }
  
  // Returns the fully normalized JavaScript path for the given
  // file descriptor's package.
  string GetFilePath(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";
    }
  }
  
  // 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;
  }
  
  // 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 = GetFilePath(options, file_descriptor) +
                    GetNestedMessageName(containing_type);
    if (!prefix.empty()) {
      prefix += ".";
    }
    return prefix;
  }
  
  // Returns the fully normalized JavaScript path prefix for the given
  // message descriptor.
  string GetMessagePathPrefix(const GeneratorOptions& options,
                              const Descriptor* descriptor) {
    return GetPrefix(
        options, descriptor->file(),
        descriptor->containing_type());
  }
  
  // Returns the fully normalized JavaScript path for the given
  // message descriptor.
  string GetMessagePath(const GeneratorOptions& options,
                        const Descriptor* descriptor) {
    return GetMessagePathPrefix(options, descriptor) + descriptor->name();
  }
  
  // Returns the fully normalized JavaScript path prefix for the given
  // enumeration descriptor.
  string GetEnumPathPrefix(const GeneratorOptions& options,
                     const EnumDescriptor* enum_descriptor) {
    return GetPrefix(options, enum_descriptor->file(),
        enum_descriptor->containing_type());
  }
  
  // Returns the fully normalized JavaScript path for the given
  // enumeration descriptor.
  string GetEnumPath(const GeneratorOptions& options,
                     const EnumDescriptor* enum_descriptor) {
    return GetEnumPathPrefix(options, enum_descriptor) + enum_descriptor->name();
  }
  
  string MaybeCrossFileRef(const GeneratorOptions& options,
                           const FileDescriptor* from_file,
                           const Descriptor* to_message) {
    if ((options.import_style == GeneratorOptions::kImportCommonJs ||
         options.import_style == GeneratorOptions::kImportCommonJsStrict) &&
        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();
    } else {
      // Within a single file we use a full name.
      return GetMessagePath(options, to_message);
    }
  }
  
  string SubmessageTypeRef(const GeneratorOptions& options,
                           const FieldDescriptor* field) {
    GOOGLE_CHECK(field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE);
    return MaybeCrossFileRef(options, field->file(), field->message_type());
  }
  
  // - 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;
    }
  }
  
  std::vector<string> ParseLowerUnderscore(const string& input) {
    std::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;
  }
  
  std::vector<string> ParseUpperCamel(const string& input) {
    std::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 std::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 std::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;
  }
  
  // When we're generating one output file per type name, this is the filename
  // that top-level extensions should go in.
  string GetExtensionFileName(const GeneratorOptions& options,
                              const FileDescriptor* file) {
    return options.output_dir + "/" + ToFileName(GetFilePath(options, file)) +
           options.GetFileNameExtension();
  }
  
  // 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();
  }
  
  // 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();
  }
  
  // Returns the message/response ID, if set.
  string GetMessageId(const Descriptor* desc) {
    return string();
  }
  
  bool IgnoreExtensionField(const FieldDescriptor* field) {
    // Exclude descriptor extensions from output "to avoid clutter" (from original
    // codegen).
    return field->is_extension() &&
           field->containing_type()->file()->name() ==
               "google/protobuf/descriptor.proto";
  }
  
  
  // Used inside Google only -- do not remove.
  bool IsResponse(const Descriptor* desc) { return false; }
  
  bool IgnoreField(const FieldDescriptor* field) {
    return IgnoreExtensionField(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++) {
      if (!IgnoreField(oneof->field(i))) {
        return false;
      }
    }
    return true;
  }
  
  string JSIdent(const GeneratorOptions& options, const FieldDescriptor* field,
                 bool is_upper_camel, bool is_map, bool drop_list) {
    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 || IsMap(options, field)) {
      // JSPB-style or proto3-style map.
      result += "Map";
    } else if (!drop_list && field->is_repeated()) {
      // Repeated field.
      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);
    if (IsReserved(name)) {
      name = "pb_" + name;
    }
    return name;
  }
  
  string JSByteGetterSuffix(BytesMode bytes_mode) {
    switch (bytes_mode) {
      case BYTES_DEFAULT:
        return "";
      case BYTES_B64:
        return "B64";
      case BYTES_U8:
        return "U8";
      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,
                          /* is_upper_camel = */ true,
                          /* is_map = */ false, drop_list);
    if (field->type() == FieldDescriptor::TYPE_BYTES) {
      string suffix = JSByteGetterSuffix(bytes_mode);
      if (!suffix.empty()) {
        name += "_as" + suffix;
      }
    }
    if (name == "Extension" || name == "JsPbMessageId") {
      // Avoid conflicts with base-class names.
      name += "$";
    }
    return name;
  }
  
  
  
  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.
  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. The input data should be a UTF-8 encoded C++ string of chars.
  // Returns false if |out| was truncated because |in| contained invalid UTF-8 or
  // codepoints outside the BMP.
  // TODO(lukestebbing): Support codepoints outside the BMP.
  bool EscapeJSString(const string& in, string* out) {
    size_t decoded = 0;
    for (size_t i = 0; i < in.size(); i += decoded) {
      uint16 codepoint = 0;
      // 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) {
        return false;
      }
      decoded = have_bytes;
  
      switch (codepoint) {
        case '\'': *out += "\\x27"; break;
        case '"': *out += "\\x22"; break;
        case '<': *out += "\\x3c"; break;
        case '=': *out += "\\x3d"; break;
        case '>': *out += "\\x3e"; break;
        case '&': *out += "\\x26"; break;
        case '\b': *out += "\\b"; break;
        case '\t': *out += "\\t"; break;
        case '\n': *out += "\\n"; break;
        case '\f': *out += "\\f"; break;
        case '\r': *out += "\\r"; break;
        case '\\': *out += "\\\\"; break;
        default:
          // TODO(lukestebbing): Once we're supporting codepoints outside the BMP,
          // use a single Unicode codepoint escape if the output language is
          // ECMAScript 2015 or above. Otherwise, use a surrogate pair.
          // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#String_literals
          if (codepoint >= 0x20 && codepoint <= 0x7e) {
            *out += static_cast<char>(codepoint);
          } else if (codepoint >= 0x100) {
            *out += StringPrintf("\\u%04x", codepoint);
          } else {
            *out += StringPrintf("\\x%02x", codepoint);
          }
          break;
      }
    }
    return true;
  }
  
  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);
  }
  
  // Return true if this is an integral field that should be represented as string
  // in JS.
  bool IsIntegralFieldWithStringJSType(const FieldDescriptor* field) {
    switch (field->cpp_type()) {
      case FieldDescriptor::CPPTYPE_INT64:
      case FieldDescriptor::CPPTYPE_UINT64:
        // The default value of JSType is JS_NORMAL, which behaves the same as
        // JS_NUMBER.
        return field->options().jstype() == google::protobuf::FieldOptions::JS_STRING;
      default:
        return false;
    }
  }
  
  string MaybeNumberString(const FieldDescriptor* field, const string& orig) {
    return IsIntegralFieldWithStringJSType(field) ? ("\"" + orig + "\"") : orig;
  }
  
  string JSFieldDefault(const FieldDescriptor* field) {
    if (field->is_repeated()) {
      return "[]";
    }
  
    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) {
          string out;
          bool is_valid = EscapeJSString(field->default_value_string(), &out);
          if (!is_valid) {
            // TODO(lukestebbing): Decide whether this should be a hard error.
            GOOGLE_LOG(WARNING) << "The default value for field " << field->full_name()
                         << " was truncated since it contained invalid UTF-8 or"
                            " codepoints outside the basic multilingual plane.";
          }
          return "\"" + out + "\"";
        } else {  // Bytes
          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 GetMessagePath(options, field->message_type());
      case FieldDescriptor::TYPE_ENUM:
        return GetEnumPath(options, field->enum_type());
      case FieldDescriptor::TYPE_MESSAGE:
        return GetMessagePath(options, field->message_type());
      default:
        return "";
    }
  }
  
  string JSIntegerTypeName(const FieldDescriptor* field) {
    return IsIntegralFieldWithStringJSType(field) ? "string" : "number";
  }
  
  string JSStringTypeName(const GeneratorOptions& options,
                          const FieldDescriptor* field,
                          BytesMode bytes_mode) {
    if (field->type() == FieldDescriptor::TYPE_BYTES) {
      switch (bytes_mode) {
        case BYTES_DEFAULT:
          return "(string|Uint8Array)";
        case BYTES_B64:
          return "string";
        case BYTES_U8:
          return "Uint8Array";
        default:
          assert(false);
      }
    }
    return "string";
  }
  
  string JSTypeName(const GeneratorOptions& options,
                    const FieldDescriptor* field,
                    BytesMode bytes_mode) {
    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 JSStringTypeName(options, field, bytes_mode);
      case FieldDescriptor::CPPTYPE_ENUM:
        return GetEnumPath(options, field->enum_type());
      case FieldDescriptor::CPPTYPE_MESSAGE:
        return GetMessagePath(options, field->message_type());
      default:
        return "";
    }
  }
  
  // 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";
  }
  
  string JSFieldTypeAnnotation(const GeneratorOptions& options,
                               const FieldDescriptor* field,
                               bool is_setter_argument,
                               bool force_present,
                               bool singular_if_not_packed,
                               BytesMode bytes_mode = BYTES_DEFAULT) {
    GOOGLE_CHECK(!(is_setter_argument && force_present));
    string jstype = JSTypeName(options, field, bytes_mode);
  
    if (field->is_repeated() &&
        (field->is_packed() || !singular_if_not_packed)) {
      if (field->type() == FieldDescriptor::TYPE_BYTES &&
          bytes_mode == BYTES_DEFAULT) {
        jstype = "(Array<!Uint8Array>|Array<string>)";
      } else {
        if (!IsPrimitive(jstype)) {
          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 (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 (!is_null_or_undefined && !IsPrimitive(jstype)) {
      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 IsIntegralFieldWithStringJSType(field) ? (name + "String") : name;
  }
  
  string JSBinaryReadWriteMethodName(const FieldDescriptor* field,
                                     bool is_writer) {
    string name = JSBinaryReaderMethodType(field);
    if (field->is_packed()) {
      name = "Packed" + name;
    } else if (is_writer && field->is_repeated()) {
      name = "Repeated" + name;
    }
    return name;
  }
  
  string JSBinaryReaderMethodName(const GeneratorOptions& options,
                                  const FieldDescriptor* field) {
    return "jspb.BinaryReader.prototype.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 JSReturnClause(const FieldDescriptor* desc) {
    return "";
  }
  
  string JSTypeTag(const FieldDescriptor* desc) {
    switch (desc->type()) {
      case FieldDescriptor::TYPE_DOUBLE:
      case FieldDescriptor::TYPE_FLOAT:
        return "Float";
      case FieldDescriptor::TYPE_INT32:
      case FieldDescriptor::TYPE_UINT32:
      case FieldDescriptor::TYPE_INT64:
      case FieldDescriptor::TYPE_UINT64:
      case FieldDescriptor::TYPE_FIXED32:
      case FieldDescriptor::TYPE_FIXED64:
      case FieldDescriptor::TYPE_SINT32:
      case FieldDescriptor::TYPE_SINT64:
      case FieldDescriptor::TYPE_SFIXED32:
      case FieldDescriptor::TYPE_SFIXED64:
        if (IsIntegralFieldWithStringJSType(desc)) {
          return "StringInt";
        } else {
          return "Int";
        }
      case FieldDescriptor::TYPE_BOOL:
        return "Boolean";
      case FieldDescriptor::TYPE_STRING:
        return "String";
      case FieldDescriptor::TYPE_BYTES:
        return "Bytes";
      case FieldDescriptor::TYPE_ENUM:
        return "Enum";
      default:
        assert(false);
    }
    return "";
  }
  
  string JSReturnDoc(const GeneratorOptions& options,
                     const FieldDescriptor* desc) {
    return "";
  }
  
  bool HasRepeatedFields(const GeneratorOptions& options,
                         const Descriptor* desc) {
    for (int i = 0; i < desc->field_count(); i++) {
      if (desc->field(i)->is_repeated() && !IsMap(options, desc->field(i))) {
        return true;
      }
    }
    return false;
  }
  
  static const char* kRepeatedFieldArrayName = ".repeatedFields_";
  
  string RepeatedFieldsArrayName(const GeneratorOptions& options,
                                 const Descriptor* desc) {
    return HasRepeatedFields(options, desc)
               ? (GetMessagePath(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)
               ? (GetMessagePath(options, desc) + kOneofGroupArrayName)
               : "null";
  }
  
  string RepeatedFieldNumberList(const GeneratorOptions& options,
                                 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))) {
        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 FileDescriptor* from_file,
                                const Descriptor* desc) {
    if (desc->full_name() == "google.protobuf.bridge.MessageSet") {
      // TODO(haberman): fix this for the kImportCommonJs 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());
    } 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());
    }
  }
  
  string FieldComments(const FieldDescriptor* field, BytesMode bytes_mode) {
    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->type() == FieldDescriptor::TYPE_BYTES && bytes_mode == BYTES_U8) {
      comments +=
          " * Note that Uint8Array is not supported on all browsers.\n"
          " * @see http://caniuse.com/Uint8Array\n";
    }
    return comments;
  }
  
  bool ShouldGenerateExtension(const FieldDescriptor* field) {
    return
        field->is_extension() &&
        !IgnoreField(field);
  }
  
  bool HasExtensions(const Descriptor* desc) {
    for (int i = 0; i < desc->extension_count(); i++) {
      if (ShouldGenerateExtension(desc->extension(i))) {
        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 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;
  }
  
  // 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 = 500;
  
    // 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) || (max_field_number >= kDefaultPivot)) {
      pivot = ((max_field_number + 1) < kDefaultPivot) ?
          (max_field_number + 1) : kDefaultPivot;
    }
  
    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;
    }
  
    if (UseBrokenPresenceSemantics(options, field)) {
      // Proto3 files with broken presence semantics have field presence.
      return true;
    }
  
    return field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ||
           field->containing_oneof() != NULL ||
           field->file()->syntax() == FileDescriptor::SYNTAX_PROTO2;
  }
  
  // We use this to implement the semantics that same file can be generated
  // multiple times, but the last one wins.  We never actually write the files,
  // but we keep a set of which descriptors were the final one for a given
  // filename.
  class FileDeduplicator {
   public:
    explicit FileDeduplicator(const GeneratorOptions& options)
        : error_on_conflict_(options.error_on_name_conflict) {}
  
    bool AddFile(const string& filename, const void* desc, string* error) {
      if (descs_by_filename_.find(filename) != descs_by_filename_.end()) {
        if (error_on_conflict_) {
          *error = "Name conflict: file name " + filename +
                   " would be generated by two descriptors";
          return false;
        }
        allowed_descs_.erase(descs_by_filename_[filename]);
      }
  
      descs_by_filename_[filename] = desc;
      allowed_descs_.insert(desc);
      return true;
    }
  
    void GetAllowedSet(std::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_;
  };
  
  void DepthFirstSearch(const FileDescriptor* file,
                        std::vector<const FileDescriptor*>* list,
                        std::set<const FileDescriptor*>* seen) {
    if (!seen->insert(file).second) {
      return;
    }
  
    // Add all dependencies.
    for (int i = 0; i < file->dependency_count(); i++) {
      DepthFirstSearch(file->dependency(i), list, seen);
    }
  
    // Add this file.
    list->push_back(file);
  }
  
  // A functor for the predicate to remove_if() below.  Returns true if a given
  // FileDescriptor is not in the given set.
  class NotInSet {
   public:
    explicit NotInSet(const std::set<const FileDescriptor*>& file_set)
        : file_set_(file_set) {}
  
    bool operator()(const FileDescriptor* file) {
      return file_set_.count(file) == 0;
    }
  
   private:
    const std::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) {
    // 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;
    for (int i = 0; i < input.size(); i++) {
      DepthFirstSearch(input[i], ordered, &seen);
      input_set.insert(input[i]);
    }
  
    // Now remove the entries that are not actually in our input list.
    ordered->erase(
        std::remove_if(ordered->begin(), ordered->end(), NotInSet(input_set)),
        ordered->end());
  }
  
  // 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.
  
  bool GenerateJspbAllowedSet(const GeneratorOptions& options,
                              const std::vector<const FileDescriptor*>& files,
                              std::set<const void*>* allowed_set,
                              string* error) {
    std::vector<const FileDescriptor*> files_ordered;
    GenerateJspbFileOrder(files, &files_ordered);
  
    // Choose the last descriptor for each filename.
    FileDeduplicator dedup(options);
    for (int i = 0; i < files_ordered.size(); i++) {
      for (int j = 0; j < files_ordered[i]->message_type_count(); j++) {
        const Descriptor* desc = files_ordered[i]->message_type(j);
        if (!dedup.AddFile(GetMessageFileName(options, desc), desc, error)) {
          return false;
        }
      }
      for (int j = 0; j < files_ordered[i]->enum_type_count(); j++) {
        const EnumDescriptor* desc = files_ordered[i]->enum_type(j);
        if (!dedup.AddFile(GetEnumFileName(options, desc), desc, error)) {
          return false;
        }
      }
  
      // Pull out all free-floating extensions and generate files for those too.
      bool has_extension = false;
  
      for (int j = 0; j < files_ordered[i]->extension_count(); j++) {
        if (ShouldGenerateExtension(files_ordered[i]->extension(j))) {
          has_extension = true;
        }
      }
  
      if (has_extension) {
        if (!dedup.AddFile(GetExtensionFileName(options, files_ordered[i]),
                           files_ordered[i], error)) {
          return false;
        }
      }
    }
  
    dedup.GetAllowedSet(allowed_set);
  
    return true;
  }
  
  // Embeds base64 encoded GeneratedCodeInfo proto in a comment at the end of
  // file.
  void EmbedCodeAnnotations(const GeneratedCodeInfo& annotations,
                            io::Printer* printer) {
    // Serialize annotations proto into base64 string.
    string meta_content;
    annotations.SerializeToString(&meta_content);
    string meta_64;
    Base64Escape(meta_content, &meta_64);
  
    // Print base64 encoded annotations at the end of output file in
    // a comment.
    printer->Print("\n// Below is base64 encoded GeneratedCodeInfo proto");
    printer->Print("\n// $encoded_proto$\n", "encoded_proto", meta_64);
  }
  
  }  // anonymous namespace
  
  void Generator::GenerateHeader(const GeneratorOptions& options,
                                 io::Printer* printer) const {
    printer->Print("/**\n"
                   " * @fileoverview\n"
                   " * @enhanceable\n"
                   " * @suppress {messageConventions} JS Compiler reports an "
                   "error if a variable or\n"
                   " *     field starts with 'MSG_' and isn't a translatable "
                   "message.\n"
                   " * @public\n"
                   " */\n"
                   "// GENERATED CODE -- DO NOT EDIT!\n"
                   "\n");
  }
  
  void Generator::FindProvidesForFile(const GeneratorOptions& options,
                                      io::Printer* printer,
                                      const FileDescriptor* file,
                                      std::set<string>* provided) const {
    for (int i = 0; i < file->message_type_count(); i++) {
      FindProvidesForMessage(options, printer, file->message_type(i), provided);
    }
    for (int i = 0; i < file->enum_type_count(); i++) {
      FindProvidesForEnum(options, printer, file->enum_type(i), provided);
    }
  }
  
  void Generator::FindProvides(const GeneratorOptions& options,
                               io::Printer* printer,
                               const std::vector<const FileDescriptor*>& files,
                               std::set<string>* provided) const {
    for (int i = 0; i < files.size(); i++) {
      FindProvidesForFile(options, printer, files[i], provided);
    }
  
    printer->Print("\n");
  }
  
  void Generator::FindProvidesForMessage(
      const GeneratorOptions& options,
      io::Printer* printer,
      const Descriptor* desc,
      std::set<string>* provided) const {
    if (IgnoreMessage(options, desc)) {
      return;
    }
  
    string name = GetMessagePath(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 = GetEnumPath(options, enumdesc);
    provided->insert(name);
  }
  
  void Generator::FindProvidesForFields(
      const GeneratorOptions& options,
      io::Printer* printer,
      const std::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 = GetFilePath(options, field->file()) + "." +
                    JSObjectFieldName(options, 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) {
      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() { /* ... */ }
  
        // Do not use global scope in strict mode
        if (options.import_style == GeneratorOptions::kImportCommonJsStrict) {
          string namespaceObject = *it;
          // Remove "proto." from the namespace object
          GOOGLE_CHECK(namespaceObject.compare(0, 6, "proto."));
          namespaceObject.erase(0, 6);
          printer->Print("goog.exportSymbol('$name$', null, proto);\n", "name",
                  namespaceObject);
        } else {
          printer->Print("goog.exportSymbol('$name$', null, global);\n", "name",
                  *it);
        }
      }
    }
  }
  
  void Generator::GenerateRequiresForMessage(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),
                         /* require_map = */ HasMap(options, desc));
  }
  
  void Generator::GenerateRequiresForLibrary(
      const GeneratorOptions& options, io::Printer* printer,
      const std::vector<const FileDescriptor*>& files,
      std::set<string>* provided) const {
    GOOGLE_CHECK_EQ(options.import_style, GeneratorOptions::kImportClosure);
    // 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);
        }
      }
  
      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)) {
          continue;
        }
        if (extension->containing_type()->full_name() !=
          "google.protobuf.bridge.MessageSet") {
          required.insert(GetMessagePath(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,
                         /* require_map = */ have_map);
  }
  
  void Generator::GenerateRequiresForExtensions(
      const GeneratorOptions& options, io::Printer* printer,
      const std::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,
                         /* require_map = */ false);
  }
  
  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,
                                       bool require_map) const {
    if (require_jspb) {
      required->insert("jspb.Message");
      required->insert("jspb.BinaryReader");
      required->insert("jspb.BinaryWriter");
    }
    if (require_extension) {
      required->insert("jspb.ExtensionFieldBinaryInfo");
      required->insert("jspb.ExtensionFieldInfo");
    }
    if (require_map) {
      required->insert("jspb.Map");
    }
  
    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(GetEnumPath(options, field->enum_type()));
        } else {
          forwards->insert(GetEnumPath(options, field->enum_type()));
        }
      } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
        if (!IgnoreMessage(options, field->message_type())) {
          required->insert(GetMessagePath(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(GetMessagePath(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 (IgnoreMessage(options, desc)) {
      return;
    }
  
    if (!NamespaceOnly(desc)) {
      printer->Print("\n");
      GenerateClassConstructor(options, printer, desc);
      GenerateClassFieldInfo(options, printer, desc);
  
  
      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);
    }
  
    // Recurse on nested types. These must come *before* the extension-field
    // info generation in GenerateClassRegistration so that extensions that
    // reference nested types proceed the definitions of the 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));
    }
  
    if (!NamespaceOnly(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) {
        for (int i = 0; i < desc->extension_count(); i++) {
          GenerateExtension(options, printer, desc->extension(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"
        "$classprefix$$classname$ = function(opt_data) {\n",
        "classprefix", GetMessagePathPrefix(options, desc),
        "classname", desc->name());
    printer->Annotate("classname", 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", GetMessagePath(options, desc));
  }
  
  void Generator::GenerateClassFieldInfo(const GeneratorOptions& options,
                                         io::Printer* printer,
                                         const Descriptor* desc) const {
    if (HasRepeatedFields(options, 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", GetMessagePath(options, desc),
          "rptfieldarray", kRepeatedFieldArrayName,
          "rptfields", RepeatedFieldNumberList(options, 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", GetMessagePath(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", GetMessagePath(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", GetMessagePath(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->Annotate("upcase", 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", GetMessagePath(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"
        " * @suppress {unusedLocalVariables} f is only used for nested messages\n"
        " */\n"
        "$classname$.toObject = function(includeInstance, msg) {\n"
        "  var f, obj = {",
        "classname", GetMessagePath(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->file(), desc),
          "class", GetMessagePath(options, desc));
    }
  
    printer->Print(
        "  if (includeInstance) {\n"
        "    obj.$$jspbMessageInstance = msg;\n"
        "  }\n"
        "  return obj;\n"
        "};\n"
        "}\n"
        "\n"
        "\n",
        "classname", GetMessagePath(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.get$cardinality$Field($obj$, $index$)",
                       "cardinality", field->is_repeated() ? "Repeated" : "",
                       "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 =
            GetMessagePath(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) {
      // Message field.
      if (field->is_repeated()) {
        {
          printer->Print("jspb.Message.toObjectList(msg.get$getter$(),\n"
                         "    $type$.toObject, includeInstance)",
                         "getter", JSGetterName(options, field),
                         "type", SubmessageTypeRef(options, field));
        }
      } else {
        printer->Print("(f = msg.get$getter$()) && "
                       "$type$.toObject(includeInstance, f)",
                       "getter", JSGetterName(options, 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;
      }
  
      // 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);
    }
  }
  
  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", GetMessagePath(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 (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", GetMessagePath(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) {
      // 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(options, field),
              "index", JSFieldIndex(field),
              "fieldclass", SubmessageTypeRef(options, field));
        }
      } else {
        printer->Print(
            "  goog.isDef(obj.$name$) && jspb.Message.setWrapperField(\n"
            "      msg, $index$, $fieldclass$.fromObject(obj.$name$));\n",
            "name", JSObjectFieldName(options, field),
            "index", JSFieldIndex(field),
            "fieldclass", SubmessageTypeRef(options, field));
      }
    } else {
      // Simple (primitive) field.
      printer->Print(
          "  goog.isDef(obj.$name$) && jspb.Message.setField(msg, $index$, "
          "obj.$name$);\n",
          "name", JSObjectFieldName(options, field),
          "index", JSFieldIndex(field));
    }
  }
  
  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 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);
    printer->Print(
        "/**\n"
        " * $fielddef$\n"
        "$comment$"
        " * This is a type-conversion wrapper around `get$defname$()`\n"
        " * @return {$type$}\n"
        " */\n"
        "$class$.prototype.get$name$ = function() {\n"
        "  return /** @type {$type$} */ (jspb.Message.bytes$list$As$suffix$(\n"
        "      this.get$defname$()));\n"
        "};\n"
        "\n"
        "\n",
        "fielddef", FieldDefinition(options, field),
        "comment", FieldComments(field, bytes_mode),
        "type", type,
        "class", GetMessagePath(options, field->containing_type()),
        "name", JSGetterName(options, field, bytes_mode),
        "list", field->is_repeated() ? "List" : "",
        "suffix", JSByteGetterSuffix(bytes_mode),
        "defname", JSGetterName(options, 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.$gettername$ = function(opt_noLazyCreate) {\n"
          "  return /** @type {!jspb.Map<$keytype$,$valuetype$>} */ (\n",
          "class", GetMessagePath(options, field->containing_type()),
          "gettername", "get" + JSGetterName(options, field),
          "keytype", key_type,
          "valuetype", value_type);
      printer->Annotate("gettername", field);
      printer->Print(
          "      jspb.Message.getMapField(this, $index$, opt_noLazyCreate",
          "index", JSFieldIndex(field));
  
      if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
        printer->Print(
            ",\n"
            "      $messageType$",
            "messageType", GetMessagePath(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.
  
      printer->Print(
          "/**\n"
          " * $fielddef$\n"
          "$comment$"
          " * @return {$type$}\n"
          " */\n",
          "fielddef", FieldDefinition(options, field),
          "comment", FieldComments(field, BYTES_DEFAULT),
          "type", JSFieldTypeAnnotation(options, field,
                                        /* is_setter_argument = */ false,
                                        /* force_present = */ false,
                                        /* singular_if_not_packed = */ false));
      printer->Print(
          "$class$.prototype.$gettername$ = function() {\n"
          "  return /** @type{$type$} */ (\n"
          "    jspb.Message.get$rpt$WrapperField(this, $wrapperclass$, "
          "$index$$required$));\n"
          "};\n"
          "\n"
          "\n",
          "class", GetMessagePath(options, field->containing_type()),
          "gettername", "get" + JSGetterName(options, field),
          "type", JSFieldTypeAnnotation(options, field,
                                        /* is_setter_argument = */ false,
                                        /* force_present = */ false,
                                        /* singular_if_not_packed = */ false),
          "rpt", (field->is_repeated() ? "Repeated" : ""),
          "index", JSFieldIndex(field),
          "wrapperclass", SubmessageTypeRef(options, field),
          "required", (field->label() == FieldDescriptor::LABEL_REQUIRED ?
                       ", 1" : ""));
      printer->Annotate("gettername", field);
      printer->Print(
          "/** @param {$optionaltype$} value$returndoc$ */\n"
          "$class$.prototype.$settername$ = function(value) {\n"
          "  jspb.Message.set$oneoftag$$repeatedtag$WrapperField(",
          "optionaltype",
          JSFieldTypeAnnotation(options, field,
                                /* is_setter_argument = */ true,
                                /* force_present = */ false,
                                /* singular_if_not_packed = */ false),
          "returndoc", JSReturnDoc(options, field),
          "class", GetMessagePath(options, field->containing_type()),
          "settername", "set" + JSGetterName(options, field),
          "oneoftag", (field->containing_oneof() ? "Oneof" : ""),
          "repeatedtag", (field->is_repeated() ? "Repeated" : ""));
      printer->Annotate("settername", field);
  
      printer->Print(
          "this, $index$$oneofgroup$, value);$returnvalue$\n"
          "};\n"
          "\n"
          "\n",
          "index", JSFieldIndex(field),
          "oneofgroup", (field->containing_oneof() ?
                         (", " + JSOneofArray(options, field)) : ""),
          "returnvalue", JSReturnClause(field));
  
      if (field->is_repeated()) {
        GenerateRepeatedMessageHelperMethods(options, printer, field);
      }
  
    } else {
      bool untyped =
          false;
  
      // Simple (primitive) field, either singular or repeated.
  
      // TODO(b/26173701): Always use BYTES_DEFAULT for the getter return type;
      // at this point we "lie" to non-binary users and tell the return
      // type is always base64 string, pending a LSC to migrate to typed getters.
      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);
      if (untyped) {
        printer->Print(
            "/**\n"
            " * @return {?} Raw field, untyped.\n"
            " */\n");
      } else {
        printer->Print(
            "/**\n"
            " * $fielddef$\n"
            "$comment$"
            " * @return {$type$}\n"
            " */\n",
            "fielddef", FieldDefinition(options, field),
            "comment", FieldComments(field, bytes_mode),
            "type", typed_annotation);
      }
  
      printer->Print(
          "$class$.prototype.$gettername$ = function() {\n",
          "class", GetMessagePath(options, field->containing_type()),
          "gettername", "get" + JSGetterName(options, field));
      printer->Annotate("gettername", field);
  
      if (untyped) {
        printer->Print(
            "  return ");
      } else {
        printer->Print(
            "  return /** @type {$type$} */ (",
            "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;
      }
  
      GenerateFieldValueExpression(printer, "this", field, use_default);
  
      if (untyped) {
        printer->Print(
            ";\n"
            "};\n"
            "\n"
            "\n");
      } else {
        printer->Print(
            ");\n"
            "};\n"
            "\n"
            "\n");
      }
  
      if (field->type() == FieldDescriptor::TYPE_BYTES && !untyped) {
        GenerateBytesWrapper(options, printer, field, BYTES_B64);
        GenerateBytesWrapper(options, printer, field, BYTES_U8);
      }
  
      if (untyped) {
        printer->Print(
            "/**\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),
            "returndoc", JSReturnDoc(options, field));
      }
  
      if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 &&
          !field->is_repeated() && !field->is_map() &&
          !HasFieldPresence(options, field)) {
        // Proto3 non-repeated and non-map fields without presence use the
        // setProto3*Field function.
        printer->Print(
            "$class$.prototype.$settername$ = function(value) {\n"
            "  jspb.Message.setProto3$typetag$Field(this, $index$, "
            "value);$returnvalue$\n"
            "};\n"
            "\n"
            "\n",
            "class", GetMessagePath(options, field->containing_type()),
            "settername", "set" + JSGetterName(options, field), "typetag",
            JSTypeTag(field), "index", JSFieldIndex(field), "returnvalue",
            JSReturnClause(field));
        printer->Annotate("settername", field);
      } else {
        // Otherwise, use the regular setField function.
        printer->Print(
            "$class$.prototype.$settername$ = function(value) {\n"
            "  jspb.Message.set$oneoftag$Field(this, $index$",
            "class", GetMessagePath(options, field->containing_type()),
            "settername", "set" + JSGetterName(options, field), "oneoftag",
            (field->containing_oneof() ? "Oneof" : ""), "index",
            JSFieldIndex(field));
        printer->Annotate("settername", field);
        printer->Print(
            "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);$returnvalue$\n"
            "};\n"
            "\n"
            "\n",
            "type",
            untyped ? "/** @type{string|number|boolean|Array|undefined} */(" : "",
            "typeclose", untyped ? ")" : "", "oneofgroup",
            (field->containing_oneof() ? (", " + JSOneofArray(options, field))
                                       : ""),
            "returnvalue", JSReturnClause(field), "rptvalueinit",
            (field->is_repeated() ? " || []" : ""));
      }
  
      if (untyped) {
        printer->Print(
            "/**\n"
            " * Clears the value.$returndoc$\n"
            " */\n",
            "returndoc", JSReturnDoc(options, field));
      }
  
  
      if (field->is_repeated()) {
        GenerateRepeatedPrimitiveHelperMethods(options, printer, field, untyped);
      }
    }
  
    // Generate clearFoo() method for map fields, repeated fields, and other
    // fields with presence.
    if (IsMap(options, field)) {
      printer->Print(
          "$class$.prototype.$clearername$ = function() {\n"
          "  this.$gettername$().clear();$returnvalue$\n"
          "};\n"
          "\n"
          "\n",
          "class", GetMessagePath(options, field->containing_type()),
          "clearername", "clear" + JSGetterName(options, field),
          "gettername", "get" + JSGetterName(options, field),
          "returnvalue", JSReturnClause(field));
      printer->Annotate("clearername", 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.$clearername$ = function() {\n"
          "  this.$settername$($clearedvalue$);$returnvalue$\n"
          "};\n"
          "\n"
          "\n",
          "class", GetMessagePath(options, field->containing_type()),
          "clearername", "clear" + JSGetterName(options, field),
          "settername", "set" + JSGetterName(options, field),
          "clearedvalue", (field->is_repeated() ? "[]" : "undefined"),
          "returnvalue", JSReturnClause(field));
      printer->Annotate("clearername", 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.$clearername$ = function() {\n"
          "  jspb.Message.set$maybeoneof$Field(this, "
          "$index$$maybeoneofgroup$, ",
          "class", GetMessagePath(options, field->containing_type()),
          "clearername", "clear" + JSGetterName(options, field),
          "maybeoneof", (field->containing_oneof() ? "Oneof" : ""),
          "maybeoneofgroup", (field->containing_oneof() ?
                             (", " + JSOneofArray(options, field)) : ""),
          "index", JSFieldIndex(field));
      printer->Annotate("clearername", 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.$hasername$ = function() {\n"
          "  return jspb.Message.getField(this, $index$) != null;\n"
          "};\n"
          "\n"
          "\n",
          "class", GetMessagePath(options, field->containing_type()),
          "hasername", "has" + JSGetterName(options, field),
          "index", JSFieldIndex(field));
      printer->Annotate("hasername", field);
    }
  }
  
  void Generator::GenerateRepeatedPrimitiveHelperMethods(
      const GeneratorOptions& options, io::Printer* printer,
      const FieldDescriptor* field, bool untyped) const {
    // clang-format off
    printer->Print(
        "/**\n"
        " * @param {!$optionaltype$} value\n"
        " * @param {number=} opt_index$returndoc$\n"
        " */\n"
        "$class$.prototype.$addername$ = function(value, opt_index) {\n"
        "  jspb.Message.addToRepeatedField(this, $index$",
        "class", GetMessagePath(options, field->containing_type()), "addername",
        "add" + JSGetterName(options, field, BYTES_DEFAULT,
                             /* drop_list = */ true),
        "optionaltype", JSTypeName(options, field, BYTES_DEFAULT),
        "index", JSFieldIndex(field),
        "returndoc", JSReturnDoc(options, field));
    printer->Annotate("addername", field);
    printer->Print(
        "$oneofgroup$, $type$value$rptvalueinit$$typeclose$, "
        "opt_index);$returnvalue$\n"
        "};\n"
        "\n"
        "\n",
        "type", untyped ? "/** @type{string|number|boolean|!Uint8Array} */(" : "",
        "typeclose", untyped ? ")" : "", "oneofgroup",
        (field->containing_oneof() ? (", " + JSOneofArray(options, field)) : ""),
        "rptvalueinit", "",
        "returnvalue", JSReturnClause(field));
    // clang-format on
  }
  
  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", GetMessagePath(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", GetMessagePath(options, field->message_type()));
  }
  
  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", GetMessagePath(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", GetMessagePath(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", GetMessagePath(options, desc));
  
    for (int i = 0; i < desc->field_count(); i++) {
      if (!IgnoreField(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"
          "        $class$.prototype.getExtension,\n"
          "        $class$.prototype.setExtension);\n"
          "      break;\n",
          "extobj", JSExtensionsObjectName(options, desc->file(), desc),
          "class", GetMessagePath(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 (IsMap(options, field)) {
      const FieldDescriptor* key_field = MapFieldKey(field);
      const FieldDescriptor* value_field = MapFieldValue(field);
      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", GetMessagePath(options, value_field->message_type()));
      } else {
        printer->Print(", null");
      }
      printer->Print(", $defaultKey$",
            "defaultKey", JSFieldDefault(key_field)
      );
      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",
          "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));
      }
    }
  
    printer->Print("      break;\n");
  }
  
  void Generator::GenerateClassSerializeBinary(const GeneratorOptions& options,
                                               io::Printer* printer,
                                               const Descriptor* desc) const {
    printer->Print(
        "/**\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"
        "  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"
        " * @param {!jspb.BinaryWriter} writer\n"
        " * @suppress {unusedLocalVariables} f is only used for nested messages\n"
        " */\n"
        "$class$.serializeBinaryToWriter = function(message, "
        "writer) {\n"
        "  var f = undefined;\n",
        "class", GetMessagePath(options, desc));
  
    for (int i = 0; i < desc->field_count(); i++) {
      if (!IgnoreField(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",
          "extobj", JSExtensionsObjectName(options, desc->file(), desc),
          "class", GetMessagePath(options, desc));
    }
  
    printer->Print(
        "};\n"
        "\n"
        "\n");
  }
  
  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" : "");
    }
  
    // 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()) {
      printer->Print(
          "  if (f.length > 0) {\n");
    } else {
      if (HasFieldPresence(options, 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: {
            if (IsIntegralFieldWithStringJSType(field)) {
              // We can use `parseInt` here even though it will not be precise for
              // 64-bit quantities because we are only testing for zero/nonzero,
              // and JS numbers (64-bit floating point values, i.e., doubles) are
              // integer-precise in the range that includes zero.
              printer->Print("  if (parseInt(f, 10) !== 0) {\n");
            } else {
              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;
        }
      }
    }
  
    // 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", GetMessagePath(options, value_field->message_type()));
      }
  
      printer->Print(");\n");
    } else {
      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",
          "submsg", SubmessageTypeRef(options, field));
      } else {
        printer->Print("\n");
      }
  
      printer->Print(
          "    );\n");
    }
  
    // Close the `if`.
    printer->Print(
        "  }\n");
  }
  
  void Generator::GenerateEnum(const GeneratorOptions& options,
                               io::Printer* printer,
                               const EnumDescriptor* enumdesc) const {
    printer->Print(
        "/**\n"
        " * @enum {number}\n"
        " */\n"
        "$enumprefix$$name$ = {\n",
        "enumprefix", GetEnumPathPrefix(options, enumdesc),
        "name", enumdesc->name());
    printer->Annotate("name", 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->Annotate("name", value);
    }
  
    printer->Print(
        "};\n"
        "\n");
  }
  
  void Generator::GenerateExtension(const GeneratorOptions& options,
                                    io::Printer* printer,
                                    const FieldDescriptor* field) const {
    string extension_scope =
        (field->extension_scope()
             ? GetMessagePath(options, field->extension_scope())
             : GetFilePath(options, field->file()));
  
    const string extension_object_name = JSObjectFieldName(options, field);
    printer->Print(
        "\n"
        "/**\n"
        " * A tuple of {field number, class constructor} for the extension\n"
        " * field named `$nameInComment$`.\n"
        " * @type {!jspb.ExtensionFieldInfo<$extensionType$>}\n"
        " */\n"
        "$class$.$name$ = new jspb.ExtensionFieldInfo(\n",
        "nameInComment", extension_object_name,
        "name", extension_object_name,
        "class", extension_scope,
        "extensionType", JSFieldTypeAnnotation(
            options, field,
            /* is_setter_argument = */ false,
            /* force_present = */ true,
            /* singular_if_not_packed = */ false));
    printer->Annotate("name", field);
    printer->Print(
        "    $index$,\n"
        "    {$name$: 0},\n"
        "    $ctor$,\n"
        "     /** @type {?function((boolean|undefined),!jspb.Message=): "
        "!Object} */ (\n"
        "         $toObject$),\n"
        "    $repeated$);\n",
        "index", SimpleItoa(field->number()),
        "name", extension_object_name,
        "ctor", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ?
                 SubmessageTypeRef(options, field) : string("null")),
        "toObject", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ?
                     (SubmessageTypeRef(options, field) + ".toObject") :
                     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",
        extension_object_name, "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"));
  
    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->file(),
                                             field->containing_type()),
        "index", SimpleItoa(field->number()),
        "class", extension_scope,
        "name", extension_object_name);
  }
  
  bool GeneratorOptions::ParseFromOptions(
      const std::vector< std::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 if (options[i].first == "import_style") {
        if (options[i].second == "closure") {
          import_style = kImportClosure;
        } else if (options[i].second == "commonjs") {
          import_style = kImportCommonJs;
        } else if (options[i].second == "commonjs_strict") {
          import_style = kImportCommonJsStrict;
        } else if (options[i].second == "browser") {
          import_style = kImportBrowser;
        } else if (options[i].second == "es6") {
          import_style = kImportEs6;
        } 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 if (options[i].first == "annotate_code") {
        if (!options[i].second.empty()) {
          *error = "Unexpected option value for annotate_code";
          return false;
        }
        annotate_code = true;
      } 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;
      }
    }
  
    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;
    }
  
    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 {
    // 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);
    }
  }
  
  void Generator::GenerateFile(const GeneratorOptions& options,
                               io::Printer* printer,
                               const FileDescriptor* file) const {
    GenerateHeader(options, printer);
  
    // Generate "require" statements.
    if ((options.import_style == GeneratorOptions::kImportCommonJs ||
         options.import_style == GeneratorOptions::kImportCommonJsStrict)) {
      printer->Print("var jspb = require('google-protobuf');\n");
      printer->Print("var goog = jspb;\n");
  
      // Do not use global scope in strict mode
      if (options.import_style == GeneratorOptions::kImportCommonJsStrict) {
        printer->Print("var proto = {};\n\n");
      } else {
        printer->Print("var global = Function('return this')();\n\n");
      }
  
      for (int i = 0; i < file->dependency_count(); i++) {
        const string& name = file->dependency(i)->name();
        printer->Print(
            "var $alias$ = require('$file$');\n"
            "goog.object.extend(proto, $alias$);\n",
            "alias", ModuleAlias(name),
            "file",
            GetRootPath(file->name(), name) + GetJSFilename(options, name));
      }
    }
  
    std::set<string> provided;
    std::set<const FieldDescriptor*> 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(GetFilePath(options, file) + "." +
                      JSObjectFieldName(options, file->extension(i)));
      extensions.insert(file->extension(i));
    }
  
    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);
    }
  
    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);
    }
  
    // if provided is empty, do not export anything
    if (options.import_style == GeneratorOptions::kImportCommonJs && !provided.empty()) {
      printer->Print("goog.object.extend(exports, $package$);\n",
                     "package", GetFilePath(options, file));
    } else if(options.import_style == GeneratorOptions::kImportCommonJsStrict) {
      printer->Print("goog.object.extend(exports, proto);\n",
                     "package", GetFilePath(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,
                              const string& parameter,
                              GeneratorContext* context,
                              string* error) const {
    std::vector< std::pair< string, string > > option_pairs;
    ParseGeneratorParameter(parameter, &option_pairs);
    GeneratorOptions options;
    if (!options.ParseFromOptions(option_pairs, error)) {
      return false;
    }
  
  
    if (options.output_mode() == GeneratorOptions::kEverythingInOneFile) {
      // All output should go in a single file.
      string filename = options.output_dir + "/" + options.library +
                        options.GetFileNameExtension();
      std::unique_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;
      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);
      GenerateRequiresForLibrary(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 if (options.output_mode() == GeneratorOptions::kOneOutputFilePerType) {
      std::set<const void*> allowed_set;
      if (!GenerateJspbAllowedSet(options, files, &allowed_set, error)) {
        return false;
      }
  
      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_set.count(desc) == 0) {
            continue;
          }
  
          string filename = GetMessageFileName(options, desc);
          std::unique_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);
          GenerateRequiresForMessage(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_set.count(enumdesc) == 0) {
            continue;
          }
  
          string filename = GetEnumFileName(options, enumdesc);
          std::unique_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;
          }
        }
        // File-level extensions (message-level extensions are generated under
        // the enclosing message).
        if (allowed_set.count(file) == 1) {
          string filename = GetExtensionFileName(options, file);
  
          std::unique_ptr<io::ZeroCopyOutputStream> output(
              context->Open(filename));
          GOOGLE_CHECK(output.get());
          io::Printer printer(output.get(), '$');
  
          GenerateHeader(options, &printer);
  
          std::set<string> provided;
          std::vector<const FieldDescriptor*> fields;
  
          for (int j = 0; j < files[i]->extension_count(); j++) {
            if (ShouldGenerateExtension(files[i]->extension(j))) {
              fields.push_back(files[i]->extension(j));
            }
          }
  
          FindProvidesForFields(options, &printer, fields, &provided);
          GenerateProvides(options, &printer, &provided);
          GenerateTestOnly(options, &printer);
          GenerateRequiresForExtensions(options, &printer, fields, &provided);
  
          for (int j = 0; j < files[i]->extension_count(); j++) {
            if (ShouldGenerateExtension(files[i]->extension(j))) {
              GenerateExtension(options, &printer, files[i]->extension(j));
            }
          }
        }
      }
    } else /* options.output_mode() == kOneOutputFilePerInputFile */ {
      // 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());
        std::unique_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
        GOOGLE_CHECK(output.get());
        GeneratedCodeInfo annotations;
        io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
            &annotations);
        io::Printer printer(output.get(), '$',
                            options.annotate_code ? &annotation_collector : NULL);
  
  
        GenerateFile(options, &printer, file);
  
        if (printer.failed()) {
          return false;
        }
  
        if (options.annotate_code) {
          EmbedCodeAnnotations(annotations, &printer);
        }
      }
    }
    return true;
  }
  
  }  // namespace js
  }  // namespace compiler
  }  // namespace protobuf
  }  // namespace google
  