void Generator::GenerateClassField()

in cdk/extra/protobuf/protobuf-3.19.6/src/google/protobuf/compiler/js/js_generator.cc [2544:2897]


void Generator::GenerateClassField(const GeneratorOptions& options,
                                   io::Printer* printer,
                                   const FieldDescriptor* field) const {
  if (field->is_map()) {
    const FieldDescriptor* key_field = MapFieldKey(field);
    const FieldDescriptor* value_field = MapFieldValue(field);
    // Map field: special handling to instantiate the map object on demand.
    std::string key_type =
        JSFieldTypeAnnotation(options, key_field,
                              /* is_setter_argument = */ false,
                              /* force_present = */ true,
                              /* singular_if_not_packed = */ false);
    std::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(
        "/**\n"
        " * @param {$optionaltype$} value\n"
        " * @return {!$class$} returns this\n"
        "*/\n"
        "$class$.prototype.$settername$ = function(value) {\n"
        "  return jspb.Message.set$oneoftag$$repeatedtag$WrapperField(",
        "optionaltype",
        JSFieldTypeAnnotation(options, field,
                              /* is_setter_argument = */ true,
                              /* force_present = */ false,
                              /* singular_if_not_packed = */ false),
        "class", GetMessagePath(options, field->containing_type()),
        "settername", "set" + JSGetterName(options, field), "oneoftag",
        (InRealOneof(field) ? "Oneof" : ""), "repeatedtag",
        (field->is_repeated() ? "Repeated" : ""));
    printer->Annotate("settername", field);

    printer->Print(
        "this, $index$$oneofgroup$, value);\n"
        "};\n"
        "\n"
        "\n",
        "index", JSFieldIndex(field), "oneofgroup",
        (InRealOneof(field) ? (", " + JSOneofArray(options, 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;
    std::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);
    }

    printer->Print(
        "/**\n"
        " * @param {$optionaltype$} value\n"
        " * @return {!$class$} returns this\n"
        " */\n",
        "class", GetMessagePath(options, field->containing_type()),
        "optionaltype",
        untyped ? "*"
                : JSFieldTypeAnnotation(options, field,
                                        /* is_setter_argument = */ true,
                                        /* force_present = */ false,
                                        /* singular_if_not_packed = */ false));

    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"
          "  return jspb.Message.setProto3$typetag$Field(this, $index$, "
          "value);"
          "\n"
          "};\n"
          "\n"
          "\n",
          "class", GetMessagePath(options, field->containing_type()),
          "settername", "set" + JSGetterName(options, field), "typetag",
          JSTypeTag(field), "index", JSFieldIndex(field));
      printer->Annotate("settername", field);
    } else {
      // Otherwise, use the regular setField function.
      printer->Print(
          "$class$.prototype.$settername$ = function(value) {\n"
          "  return jspb.Message.set$oneoftag$Field(this, $index$",
          "class", GetMessagePath(options, field->containing_type()),
          "settername", "set" + JSGetterName(options, field), "oneoftag",
          (InRealOneof(field) ? "Oneof" : ""), "index", JSFieldIndex(field));
      printer->Annotate("settername", field);
      printer->Print(
          "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);\n"
          "};\n"
          "\n"
          "\n",
          "type",
          untyped ? "/** @type{string|number|boolean|Array|undefined} */(" : "",
          "typeclose", untyped ? ")" : "", "oneofgroup",
          (InRealOneof(field) ? (", " + JSOneofArray(options, field)) : ""),
          "rptvalueinit", (field->is_repeated() ? " || []" : ""));
    }

    if (untyped) {
      printer->Print(
          "/**\n"
          " * Clears the value.\n"
          " * @return {!$class$} returns this\n"
          " */\n",
          "class", GetMessagePath(options, field->containing_type()));
    }

    if (field->is_repeated()) {
      GenerateRepeatedPrimitiveHelperMethods(options, printer, field, untyped);
    }
  }

  // Generate clearFoo() method for map fields, repeated fields, and other
  // fields with presence.
  if (field->is_map()) {
    // clang-format off
    printer->Print(
        "/**\n"
        " * Clears values from the map. The map will be non-null.\n"
        " * @return {!$class$} returns this\n"
        " */\n"
        "$class$.prototype.$clearername$ = function() {\n"
        "  this.$gettername$().clear();\n"
        "  return this;"
        "};\n"
        "\n"
        "\n",
        "class", GetMessagePath(options, field->containing_type()),
        "clearername", "clear" + JSGetterName(options, field),
        "gettername", "get" + JSGetterName(options, field));
    // clang-format on
    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.
    // clang-format off
    printer->Print(
        "/**\n"
        " * $jsdoc$\n"
        " * @return {!$class$} returns this\n"
        " */\n"
        "$class$.prototype.$clearername$ = function() {\n"
        "  return this.$settername$($clearedvalue$);\n"
        "};\n"
        "\n"
        "\n",
       "jsdoc", field->is_repeated()
           ? "Clears the list making it empty but non-null."
           : "Clears the message field making it undefined.",
        "class", GetMessagePath(options, field->containing_type()),
        "clearername", "clear" + JSGetterName(options, field),
        "settername", "set" + JSGetterName(options, field),
        "clearedvalue", (field->is_repeated() ? "[]" : "undefined"));
    // clang-format on
    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.
    // clang-format off
    printer->Print(
        "/**\n"
        " * Clears the field making it undefined.\n"
        " * @return {!$class$} returns this\n"
        " */\n"
        "$class$.prototype.$clearername$ = function() {\n"
        "  return jspb.Message.set$maybeoneof$Field(this, "
            "$index$$maybeoneofgroup$, ",
        "class", GetMessagePath(options, field->containing_type()),
        "clearername", "clear" + JSGetterName(options, field),
        "maybeoneof", (InRealOneof(field) ? "Oneof" : ""),
        "maybeoneofgroup", (InRealOneof(field)
                            ? (", " + JSOneofArray(options, field))
                            : ""),
        "index", JSFieldIndex(field));
    // clang-format on
    printer->Annotate("clearername", field);
    printer->Print(
        "$clearedvalue$);\n"
        "};\n"
        "\n"
        "\n",
        "clearedvalue", (field->is_repeated() ? "[]" : "undefined"));
  }

  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);
  }
}