diff --git a/java/BUILD.bazel b/java/BUILD.bazel index 14838ae37..2a4bcb438 100644 --- a/java/BUILD.bazel +++ b/java/BUILD.bazel @@ -1,4 +1,4 @@ -load("@rules_pkg//:mappings.bzl", "pkg_filegroup", "pkg_files", "strip_prefix") +load("@rules_pkg//pkg:mappings.bzl", "pkg_filegroup", "pkg_files", "strip_prefix") # Run Linkage Monitor sh_test( diff --git a/java/README.md b/java/README.md index 6ed798b47..a6d9a6316 100644 --- a/java/README.md +++ b/java/README.md @@ -23,10 +23,14 @@ If you are using Maven, use the following: com.google.protobuf protobuf-java - 3.25.4 + ``` +And **replace `` with a version from the +[Maven Protocol Buffers Repository](https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java).** +For example, `3.25.3`. + Make sure the version number of the runtime matches (or is newer than) the version number of the protoc. @@ -37,17 +41,10 @@ protobuf-java-util package: com.google.protobuf protobuf-java-util - 3.25.4 + ``` -### Gradle - -If you are using Gradle, add the following to your `build.gradle` file's -dependencies: `implementation 'com.google.protobuf:protobuf-java:3.25.4'` Again, -be sure to check that the version number matches (or is newer than) the version -number of protoc that you are using. - ### Use Java Protocol Buffers on Android For Android users, it's recommended to use protobuf Java Lite runtime because @@ -70,8 +67,36 @@ If you are contributing code to protobuf or want to use a protobuf version that hasn't been officially released yet, you can follow the instructions below to build protobuf from source code. +### Build from Source + +You may follow these instructions to build from source. This does not require +Maven to be installed. Note that these instructions skip running unit tests and +only describes how to install the core protobuf library (without the util +package). + +1) Build the C++ code, or obtain a binary distribution of protoc (see + the toplevel [README.md](../README.md)). If you install a binary + distribution, make sure that it is the same version as this package. + If in doubt, run: + + $ protoc --version + + If you built the C++ code without installing, the compiler binary + should be located in ../src. + +2) Invoke protoc to build DescriptorProtos.java: + + $ protoc --java_out=core/src/main/java -I../src \ + ../src/google/protobuf/descriptor.proto + +3) Compile the code in core/src/main/java using whatever means you prefer. + +4) Install the classes wherever you prefer. + ### Build from Source - With Maven +WARNING: Building from source with Maven is deprecated and will be removed in the 4.28.x release. + 1) Install Apache Maven if you don't have it: http://maven.apache.org/ @@ -112,31 +137,6 @@ The above instructions will install 2 maven artifacts: as well as utilities to work with proto3 well-known types. -### Build from Source - Without Maven - -If you would rather not install Maven to build the library, you may -follow these instructions instead. Note that these instructions skip -running unit tests and only describes how to install the core protobuf -library (without the util package). - -1) Build the C++ code, or obtain a binary distribution of protoc. If - you install a binary distribution, make sure that it is the same - version as this package. If in doubt, run: - - $ protoc --version - - If you built the C++ code without installing, the compiler binary - should be located in ../src. - -2) Invoke protoc to build DescriptorProtos.java: - - $ protoc --java_out=core/src/main/java -I../src \ - ../src/google/protobuf/descriptor.proto - -3) Compile the code in core/src/main/java using whatever means you prefer. - -4) Install the classes wherever you prefer. - ## Compatibility Notice * Protobuf minor version releases are backwards-compatible. If your code diff --git a/java/bom/pom.xml b/java/bom/pom.xml index 7d890239a..3fd0371f4 100644 --- a/java/bom/pom.xml +++ b/java/bom/pom.xml @@ -4,7 +4,7 @@ com.google.protobuf protobuf-bom - 3.25.5 + 4.28.3 pom Protocol Buffers [BOM] diff --git a/java/core/BUILD.bazel b/java/core/BUILD.bazel index b896c426b..4e079e1b6 100644 --- a/java/core/BUILD.bazel +++ b/java/core/BUILD.bazel @@ -1,15 +1,18 @@ load("@bazel_skylib//rules:build_test.bzl", "build_test") -load("@rules_java//java:defs.bzl", "java_lite_proto_library", "java_proto_library") -load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix") -load("@rules_proto//proto:defs.bzl", "proto_lang_toolchain", "proto_library") -load("//build_defs:java_opts.bzl", "protobuf_java_export", "protobuf_java_library", "protobuf_versioned_java_library") -load("//conformance:defs.bzl", "conformance_test") +load("@rules_pkg//pkg:mappings.bzl", "pkg_files", "strip_prefix") load("//:protobuf.bzl", "internal_gen_well_known_protos_java") load("//:protobuf_version.bzl", "PROTOBUF_JAVA_VERSION") +load("//bazel:java_lite_proto_library.bzl", "java_lite_proto_library") +load("//bazel:java_proto_library.bzl", "java_proto_library") +load("//bazel:proto_library.bzl", "proto_library") +load("//bazel/toolchains:proto_lang_toolchain.bzl", "proto_lang_toolchain") +load("//build_defs:java_opts.bzl", "protobuf_java_export", "protobuf_java_library", "protobuf_versioned_java_library") +load("//conformance:defs.bzl", "conformance_test") +load("//editions:defaults.bzl", "compile_edition_defaults", "embed_edition_defaults") load("//java/internal:testing.bzl", "junit_tests") +load("//upb/cmake:build_defs.bzl", "staleness_test") LITE_SRCS = [ - # Keep in sync with `//java/lite:pom.xml`. "src/main/java/com/google/protobuf/AbstractMessageLite.java", "src/main/java/com/google/protobuf/AbstractParser.java", "src/main/java/com/google/protobuf/AbstractProtobufList.java", @@ -56,6 +59,8 @@ LITE_SRCS = [ "src/main/java/com/google/protobuf/LazyStringArrayList.java", "src/main/java/com/google/protobuf/LazyStringList.java", "src/main/java/com/google/protobuf/ListFieldSchema.java", + "src/main/java/com/google/protobuf/ListFieldSchemaLite.java", + "src/main/java/com/google/protobuf/ListFieldSchemas.java", "src/main/java/com/google/protobuf/LongArrayList.java", "src/main/java/com/google/protobuf/ManifestSchemaFactory.java", "src/main/java/com/google/protobuf/MapEntryLite.java", @@ -74,7 +79,6 @@ LITE_SRCS = [ "src/main/java/com/google/protobuf/NewInstanceSchema.java", "src/main/java/com/google/protobuf/NewInstanceSchemaLite.java", "src/main/java/com/google/protobuf/NewInstanceSchemas.java", - "src/main/java/com/google/protobuf/NioByteString.java", "src/main/java/com/google/protobuf/OneofInfo.java", "src/main/java/com/google/protobuf/Parser.java", "src/main/java/com/google/protobuf/PrimitiveNonBoxingCollection.java", @@ -85,6 +89,7 @@ LITE_SRCS = [ "src/main/java/com/google/protobuf/RawMessageInfo.java", "src/main/java/com/google/protobuf/Reader.java", "src/main/java/com/google/protobuf/RopeByteString.java", + "src/main/java/com/google/protobuf/RuntimeVersion.java", "src/main/java/com/google/protobuf/Schema.java", "src/main/java/com/google/protobuf/SchemaFactory.java", "src/main/java/com/google/protobuf/SchemaUtil.java", @@ -103,15 +108,26 @@ LITE_SRCS = [ "src/main/java/com/google/protobuf/Writer.java", ] +FULL_SRCS = glob( + [ + "src/main/java/com/google/protobuf/*.java", + ], + exclude = LITE_SRCS, +) + [ + ":gen_well_known_protos_java", +] + internal_gen_well_known_protos_java( name = "gen_well_known_protos_javalite", javalite = True, deps = [ "//:any_proto", "//:api_proto", + "//:descriptor_proto", "//:duration_proto", "//:empty_proto", "//:field_mask_proto", + "//:java_features_proto", "//:source_context_proto", "//:struct_proto", "//:timestamp_proto", @@ -153,7 +169,9 @@ protobuf_java_export( maven_coordinates = "com.google.protobuf:protobuf-javalite:%s" % PROTOBUF_JAVA_VERSION, pom_template = "//java/lite:pom_template.xml", resources = [ + "//:java_features_proto", "//:lite_well_known_protos", + "//src/google/protobuf:descriptor_proto_srcs", ], tags = ["manual"], runtime_deps = [":lite_bundle"], @@ -166,11 +184,55 @@ protobuf_java_library( proto_library( name = "java_features_proto", - srcs = ["src/main/java/com/google/protobuf/java_features.proto"], - visibility = ["//pkg:__pkg__"], + srcs = ["src/main/resources/google/protobuf/java_features.proto"], + strip_import_prefix = "/java/core/src/main/resources", + visibility = [ + "//:__pkg__", + "//editions:__pkg__", + "//java/__subpackages__", + "//pkg:__pkg__", + ], deps = ["//:descriptor_proto"], ) +cc_proto_library( + name = "java_features_cc_proto", + visibility = ["//editions:__pkg__"], + deps = [":java_features_proto"], +) + +filegroup( + name = "java_features_proto_srcs", + srcs = ["src/main/resources/google/protobuf/java_features.proto"], + visibility = ["//pkg:__pkg__"], +) + +compile_edition_defaults( + name = "java_edition_defaults", + srcs = [ + "//:descriptor_proto", + "//:java_features_proto", + ], + maximum_edition = "2023", + minimum_edition = "PROTO2", +) + +embed_edition_defaults( + name = "embedded_java_edition_defaults_generate", + defaults = "java_edition_defaults", + output = "generated/src/main/java/com/google/protobuf/JavaEditionDefaults.java", + placeholder = "DEFAULTS_VALUE", + template = "src/main/java/com/google/protobuf/JavaEditionDefaults.java.template", +) + +staleness_test( + name = "generated_java_defaults_staleness_test", + outs = ["src/main/java/com/google/protobuf/JavaEditionDefaults.java"], + generated_pattern = "generated/%s", + tags = ["manual"], + target_files = ["src/main/java/com/google/protobuf/JavaEditionDefaults.java"], +) + internal_gen_well_known_protos_java( name = "gen_well_known_protos_java", deps = [ @@ -181,6 +243,7 @@ internal_gen_well_known_protos_java( "//:duration_proto", "//:empty_proto", "//:field_mask_proto", + "//:java_features_proto", "//:source_context_proto", "//:struct_proto", "//:timestamp_proto", @@ -191,14 +254,7 @@ internal_gen_well_known_protos_java( java_library( name = "core", - srcs = glob( - [ - "src/main/java/com/google/protobuf/*.java", - ], - exclude = LITE_SRCS, - ) + [ - ":gen_well_known_protos_java", - ], + srcs = FULL_SRCS, visibility = ["//visibility:public"], exports = [ ":lite_runtime_only", @@ -210,14 +266,7 @@ java_library( protobuf_versioned_java_library( name = "core_bundle", - srcs = glob( - [ - "src/main/java/com/google/protobuf/*.java", - ], - exclude = LITE_SRCS, - ) + [ - ":gen_well_known_protos_java", - ], + srcs = FULL_SRCS, automatic_module_name = "com.google.protobuf", bundle_description = "Core Protocol Buffers library. Protocol Buffers " + "are a way of encoding structured data in an " + @@ -239,6 +288,7 @@ protobuf_java_export( maven_coordinates = "com.google.protobuf:protobuf-java:%s" % PROTOBUF_JAVA_VERSION, pom_template = "pom_template.xml", resources = [ + ":java_features_proto_srcs", "//:well_known_type_protos", "//src/google/protobuf:descriptor_proto_srcs", ], @@ -265,6 +315,7 @@ proto_lang_toolchain( name = "toolchain", # keep this in sync w/ WELL_KNOWN_PROTO_MAP in //:BUILD blacklisted_protos = [ + "//:java_features_proto", "//:any_proto", "//:api_proto", "//:compiler_plugin_proto", @@ -290,6 +341,7 @@ proto_library( deps = [ "//:any_proto", "//:descriptor_proto", + "//:java_features_proto", "//:lite_test_protos", "//:wrappers_proto", "//src/google/protobuf:generic_test_protos", @@ -304,6 +356,14 @@ java_proto_library( deps = ["//src/google/protobuf:generic_test_protos"], ) +java_proto_library( + name = "generic_test_protos_editions_java_proto", + visibility = [ + "//java:__subpackages__", + ], + deps = ["//src/google/protobuf:generic_test_editions_protos"], +) + java_proto_library( name = "lite_test_protos_java_proto", visibility = [ @@ -354,6 +414,7 @@ build_test( conformance_test( name = "conformance_test", failure_list = "//conformance:failure_list_java.txt", + maximum_edition = "2023", testee = "//conformance:conformance_java", text_format_failure_list = "//conformance:text_format_failure_list_java.txt", ) @@ -368,11 +429,13 @@ junit_tests( "src/test/java/com/google/protobuf/IsValidUtf8Test.java", "src/test/java/com/google/protobuf/TestUtil.java", "src/test/java/com/google/protobuf/TestUtilLite.java", + "src/test/java/com/google/protobuf/RuntimeVersionTest.java", ], ), data = ["//src/google/protobuf:testdata"], deps = [ ":core", + ":generic_test_protos_editions_java_proto", ":generic_test_protos_java_proto", ":java_test_protos_java_proto", ":lite_test_protos_java_proto", @@ -459,12 +522,12 @@ protobuf_java_library( ) LITE_TEST_EXCLUSIONS = [ - # Keep in sync with //java/lite:pom.xml id=copy-test-source-files execution. "src/test/java/com/google/protobuf/AbstractMessageTest.java", "src/test/java/com/google/protobuf/AbstractProto2SchemaTest.java", "src/test/java/com/google/protobuf/AnyTest.java", "src/test/java/com/google/protobuf/CodedInputStreamTest.java", "src/test/java/com/google/protobuf/DeprecatedFieldTest.java", + "src/test/java/com/google/protobuf/DebugFormatTest.java", "src/test/java/com/google/protobuf/DescriptorsTest.java", "src/test/java/com/google/protobuf/DiscardUnknownFieldsTest.java", "src/test/java/com/google/protobuf/DynamicMessageTest.java", @@ -472,8 +535,10 @@ LITE_TEST_EXCLUSIONS = [ "src/test/java/com/google/protobuf/FieldPresenceTest.java", "src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java", "src/test/java/com/google/protobuf/GeneratedMessageTest.java", + "src/test/java/com/google/protobuf/LazilyParsedMessageSetTest.java", "src/test/java/com/google/protobuf/LazyFieldTest.java", "src/test/java/com/google/protobuf/LazyStringEndToEndTest.java", + "src/test/java/com/google/protobuf/LegacyUnredactedTextFormatTest.java", "src/test/java/com/google/protobuf/MapForProto2Test.java", "src/test/java/com/google/protobuf/MapTest.java", "src/test/java/com/google/protobuf/MessageTest.java", @@ -484,9 +549,9 @@ LITE_TEST_EXCLUSIONS = [ "src/test/java/com/google/protobuf/Proto2ExtensionLookupSchemaTest.java", "src/test/java/com/google/protobuf/Proto2SchemaTest.java", "src/test/java/com/google/protobuf/Proto2UnknownEnumValueTest.java", - "src/test/java/com/google/protobuf/RepeatedFieldBuilderV3Test.java", + "src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java", "src/test/java/com/google/protobuf/ServiceTest.java", - "src/test/java/com/google/protobuf/SingleFieldBuilderV3Test.java", + "src/test/java/com/google/protobuf/SingleFieldBuilderTest.java", "src/test/java/com/google/protobuf/TestBadIdentifiers.java", "src/test/java/com/google/protobuf/TextFormatParseInfoTreeTest.java", "src/test/java/com/google/protobuf/TextFormatParseLocationTest.java", @@ -524,17 +589,115 @@ junit_tests( ], ) +protobuf_java_library( + name = "v25_test_util_srcjar", + testonly = True, + srcs = [ + "src/test/java/com/google/protobuf/TestUtil.java", + "src/test/java/com/google/protobuf/TestUtilLite.java", + ], + deps = [ + ":core", + "//compatibility:v25_test_protos_srcjar", + "@maven//:com_google_guava_guava", + "@maven//:junit_junit", + ], +) + +# Tests source compatibility against v25 gencode jar compiled against current runtime +junit_tests( + name = "v25_core_tests_srcjar", + size = "small", + srcs = glob( + ["src/test/java/**/*.java"], + exclude = [ + # Depends on test protos or API changes added in v4.x.x (e.g. editions) + "src/test/java/com/google/protobuf/TextFormatTest.java", + "src/test/java/com/google/protobuf/DescriptorsTest.java", + "src/test/java/com/google/protobuf/DebugFormatTest.java", + "src/test/java/com/google/protobuf/CodedOutputStreamTest.java", + "src/test/java/com/google/protobuf/CodedInputStreamTest.java", + "src/test/java/com/google/protobuf/ProtobufToStringOutputTest.java", + # Excluded in core_tests + "src/test/java/com/google/protobuf/DecodeUtf8Test.java", + "src/test/java/com/google/protobuf/IsValidUtf8Test.java", + "src/test/java/com/google/protobuf/TestUtil.java", + "src/test/java/com/google/protobuf/TestUtilLite.java", + "src/test/java/com/google/protobuf/RuntimeVersionTest.java", + ], + ), + test_prefix = "v25SrcJar", + deps = [ + ":core", + ":v25_test_util_srcjar", + "//compatibility:v25_test_protos_srcjar", + "@maven//:com_google_guava_guava", + "@maven//:com_google_truth_truth", + "@maven//:junit_junit", + "@maven//:org_mockito_mockito_core", + ], +) + +protobuf_java_library( + name = "v25_test_util_jar", + testonly = True, + srcs = [ + "src/test/java/com/google/protobuf/TestUtil.java", + "src/test/java/com/google/protobuf/TestUtilLite.java", + ], + deps = [ + ":core", + "//compatibility:v25_test_protos_jar", + "@maven//:com_google_guava_guava", + "@maven//:junit_junit", + ], +) + +# Tests binary compatibility against v25 gencode ja compiled against v25 runtime +junit_tests( + name = "v25_core_tests_jar", + size = "small", + srcs = glob( + ["src/test/java/**/*.java"], + exclude = [ + # Depends on test protos or API changes added in v4.x.x (e.g. editions) + "src/test/java/com/google/protobuf/TextFormatTest.java", + "src/test/java/com/google/protobuf/DescriptorsTest.java", + "src/test/java/com/google/protobuf/DebugFormatTest.java", + "src/test/java/com/google/protobuf/CodedOutputStreamTest.java", + "src/test/java/com/google/protobuf/CodedInputStreamTest.java", + "src/test/java/com/google/protobuf/ProtobufToStringOutputTest.java", + # Excluded in core_tests + "src/test/java/com/google/protobuf/DecodeUtf8Test.java", + "src/test/java/com/google/protobuf/IsValidUtf8Test.java", + "src/test/java/com/google/protobuf/TestUtil.java", + "src/test/java/com/google/protobuf/TestUtilLite.java", + "src/test/java/com/google/protobuf/RuntimeVersionTest.java", + ], + ), + test_prefix = "v25Jar", + deps = [ + ":core", + ":v25_test_util_jar", + "//compatibility:v25_test_protos_jar", + "@maven//:com_google_guava_guava", + "@maven//:com_google_truth_truth", + "@maven//:junit_junit", + "@maven//:org_mockito_mockito_core", + ], +) + pkg_files( name = "dist_files", srcs = glob([ "src/main/java/com/google/protobuf/*.java", + "src/main/resources/google/protobuf/*.proto", "src/test/java/**/*.java", "src/test/proto/**/*.proto", ]) + [ "BUILD.bazel", "generate-sources-build.xml", "generate-test-sources-build.xml", - "pom.xml", "pom_template.xml", ], strip_prefix = strip_prefix.from_root(""), diff --git a/java/core/generate-sources-build.xml b/java/core/generate-sources-build.xml index 0996e5fff..7021aa8e2 100644 --- a/java/core/generate-sources-build.xml +++ b/java/core/generate-sources-build.xml @@ -4,6 +4,8 @@ + + diff --git a/java/core/generate-test-sources-build.xml b/java/core/generate-test-sources-build.xml index 68edf0bfc..66e841561 100644 --- a/java/core/generate-test-sources-build.xml +++ b/java/core/generate-test-sources-build.xml @@ -6,6 +6,7 @@ + @@ -18,7 +19,10 @@ + + + diff --git a/java/core/pom.xml b/java/core/pom.xml deleted file mode 100644 index 644a4a7bc..000000000 --- a/java/core/pom.xml +++ /dev/null @@ -1,158 +0,0 @@ - - - 4.0.0 - - com.google.protobuf - protobuf-parent - 3.25.5 - - - protobuf-java - bundle - - Protocol Buffers [Core] - - Core Protocol Buffers library. Protocol Buffers are a way of encoding structured data in an - efficient yet extensible format. - - - - - junit - junit - test - - - org.mockito - mockito-core - test - - - com.google.guava - guava - test - - - com.google.truth - truth - test - - - - - - - - ${protobuf.source.dir} - - google/protobuf/any.proto - google/protobuf/api.proto - google/protobuf/descriptor.proto - google/protobuf/duration.proto - google/protobuf/empty.proto - google/protobuf/field_mask.proto - google/protobuf/source_context.proto - google/protobuf/struct.proto - google/protobuf/timestamp.proto - google/protobuf/type.proto - google/protobuf/wrappers.proto - google/protobuf/compiler/plugin.proto - - - - - - ${protobuf.source.dir} - - google/protobuf/testdata/golden_message_oneof_implemented - google/protobuf/testdata/golden_packed_fields_message - - - - - - - - maven-antrun-plugin - - - - generate-sources - generate-sources - - - - - - - run - - - - - - generate-test-sources - generate-test-sources - - - - - - - run - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - add-generated-sources - generate-sources - - add-source - - - - ${generated.sources.dir} - - - - - add-generated-test-sources - generate-test-sources - - add-test-source - - - - ${generated.testsources.dir} - - - - - - - - - org.apache.felix - maven-bundle-plugin - true - - - com.google.protobuf - https://developers.google.com/protocol-buffers/ - com.google.protobuf - com.google.protobuf;version=${project.version} - sun.misc;resolution:=optional,* - - - - - - - diff --git a/java/core/src/main/java/com/google/protobuf/AbstractMessage.java b/java/core/src/main/java/com/google/protobuf/AbstractMessage.java index f383625b2..30eccc1ec 100644 --- a/java/core/src/main/java/com/google/protobuf/AbstractMessage.java +++ b/java/core/src/main/java/com/google/protobuf/AbstractMessage.java @@ -538,44 +538,4 @@ public abstract class AbstractMessage return (BuilderType) super.mergeFrom(input, extensionRegistry); } } - - /** - * @deprecated from v3.0.0-beta-3+, for compatibility with v2.5.0 and v2.6.1 - * generated code. - */ - @Deprecated - protected static int hashLong(long n) { - return (int) (n ^ (n >>> 32)); - } - - /** - * @deprecated from v3.0.0-beta-3+, for compatibility with v2.5.0 and v2.6.1 - * generated code. - */ - @Deprecated - protected static int hashBoolean(boolean b) { - return b ? 1231 : 1237; - } - - /** - * @deprecated from v3.0.0-beta-3+, for compatibility with v2.5.0 and v2.6.1 - * generated code. - */ - @Deprecated - protected static int hashEnum(EnumLite e) { - return e.getNumber(); - } - - /** - * @deprecated from v3.0.0-beta-3+, for compatibility with v2.5.0 and v2.6.1 - * generated code. - */ - @Deprecated - protected static int hashEnumList(List list) { - int hash = 1; - for (EnumLite e : list) { - hash = 31 * hash + hashEnum(e); - } - return hash; - } } diff --git a/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java b/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java index 6a9d7bacf..dde30c1b7 100644 --- a/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java +++ b/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java @@ -113,12 +113,6 @@ public abstract class AbstractMessageLite< } } - // For binary compatibility - @Deprecated - protected static void addAll(final Iterable values, final Collection list) { - Builder.addAll(values, (List) list); - } - protected static void addAll(final Iterable values, final List list) { Builder.addAll(values, list); } @@ -403,6 +397,8 @@ public abstract class AbstractMessageLite< } if (value instanceof ByteString) { lazyList.add((ByteString) value); + } else if (value instanceof byte[]) { + lazyList.add(ByteString.copyFrom((byte[]) value)); } else { lazyList.add((String) value); } diff --git a/java/core/src/main/java/com/google/protobuf/ByteString.java b/java/core/src/main/java/com/google/protobuf/ByteString.java index 8ba729c8e..7b2455f12 100644 --- a/java/core/src/main/java/com/google/protobuf/ByteString.java +++ b/java/core/src/main/java/com/google/protobuf/ByteString.java @@ -7,6 +7,7 @@ package com.google.protobuf; +import static com.google.protobuf.Internal.checkNotNull; import static com.google.protobuf.TextFormatEscaper.escapeBytes; import static java.lang.Integer.toHexString; import static java.lang.System.identityHashCode; @@ -21,6 +22,8 @@ import java.io.OutputStream; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.InvalidMarkException; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; @@ -422,6 +425,11 @@ public abstract class ByteString implements Iterable, Serializable { } } + // For use in tests + static ByteString nioByteString(ByteBuffer buffer) { + return new NioByteString(buffer); + } + /** * Wraps the given bytes into a {@code ByteString}. Intended for internal usage within the library * to force a classload of ByteString before LiteralByteString. @@ -953,6 +961,8 @@ public abstract class ByteString implements Iterable, Serializable { * @return true for equality of substrings, else false. */ abstract boolean equalsRange(ByteString other, int offset, int length); + + private LeafByteString() {} } /** @@ -1662,4 +1672,254 @@ public abstract class ByteString implements Iterable, Serializable { "BoundedByteStream instances are not to be serialized directly"); } } + + /** A {@link ByteString} that wraps around a {@link ByteBuffer}. */ + // Keep this class private to avoid deadlocks in classloading across threads as ByteString's + // static initializer loads LiteralByteString and another thread loads BoundedByteString. + private static final class NioByteString extends ByteString.LeafByteString { + private final ByteBuffer buffer; + + NioByteString(ByteBuffer buffer) { + checkNotNull(buffer, "buffer"); + + // Use native byte order for fast fixed32/64 operations. + this.buffer = buffer.slice().order(ByteOrder.nativeOrder()); + } + + // ================================================================= + // Serializable + + /** Magic method that lets us override serialization behavior. */ + private Object writeReplace() { + return ByteString.copyFrom(buffer.slice()); + } + + /** Magic method that lets us override deserialization behavior. */ + private void readObject(@SuppressWarnings("unused") ObjectInputStream in) throws IOException { + throw new InvalidObjectException("NioByteString instances are not to be serialized directly"); + } + + // ================================================================= + + @Override + public byte byteAt(int index) { + try { + return buffer.get(index); + } catch (ArrayIndexOutOfBoundsException e) { + throw e; + } catch (IndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException(e.getMessage()); + } + } + + @Override + public byte internalByteAt(int index) { + // it isn't possible to avoid the bounds checking inside of ByteBuffer, so just use the + // default + // implementation. + return byteAt(index); + } + + @Override + public int size() { + return buffer.remaining(); + } + + @Override + public ByteString substring(int beginIndex, int endIndex) { + try { + ByteBuffer slice = slice(beginIndex, endIndex); + return new NioByteString(slice); + } catch (ArrayIndexOutOfBoundsException e) { + throw e; + } catch (IndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException(e.getMessage()); + } + } + + @Override + protected void copyToInternal( + byte[] target, int sourceOffset, int targetOffset, int numberToCopy) { + ByteBuffer slice = buffer.slice(); + Java8Compatibility.position(slice, sourceOffset); + slice.get(target, targetOffset, numberToCopy); + } + + @Override + public void copyTo(ByteBuffer target) { + target.put(buffer.slice()); + } + + @Override + public void writeTo(OutputStream out) throws IOException { + out.write(toByteArray()); + } + + @Override + boolean equalsRange(ByteString other, int offset, int length) { + return substring(0, length).equals(other.substring(offset, offset + length)); + } + + @Override + void writeToInternal(OutputStream out, int sourceOffset, int numberToWrite) throws IOException { + if (buffer.hasArray()) { + // Optimized write for array-backed buffers. + // Note that we're taking the risk that a malicious OutputStream could modify the array. + int bufferOffset = buffer.arrayOffset() + buffer.position() + sourceOffset; + out.write(buffer.array(), bufferOffset, numberToWrite); + return; + } + + ByteBufferWriter.write(slice(sourceOffset, sourceOffset + numberToWrite), out); + } + + @Override + void writeTo(ByteOutput output) throws IOException { + output.writeLazy(buffer.slice()); + } + + @Override + public ByteBuffer asReadOnlyByteBuffer() { + return buffer.asReadOnlyBuffer(); + } + + @Override + public List asReadOnlyByteBufferList() { + return Collections.singletonList(asReadOnlyByteBuffer()); + } + + @Override + protected String toStringInternal(Charset charset) { + final byte[] bytes; + final int offset; + final int length; + if (buffer.hasArray()) { + bytes = buffer.array(); + offset = buffer.arrayOffset() + buffer.position(); + length = buffer.remaining(); + } else { + // TODO: Can we optimize this? + bytes = toByteArray(); + offset = 0; + length = bytes.length; + } + return new String(bytes, offset, length, charset); + } + + @Override + public boolean isValidUtf8() { + return Utf8.isValidUtf8(buffer); + } + + @Override + protected int partialIsValidUtf8(int state, int offset, int length) { + return Utf8.partialIsValidUtf8(state, buffer, offset, offset + length); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (!(other instanceof ByteString)) { + return false; + } + ByteString otherString = ((ByteString) other); + if (size() != otherString.size()) { + return false; + } + if (size() == 0) { + return true; + } + if (other instanceof NioByteString) { + return buffer.equals(((NioByteString) other).buffer); + } + if (other instanceof RopeByteString) { + return other.equals(this); + } + return buffer.equals(otherString.asReadOnlyByteBuffer()); + } + + @Override + protected int partialHash(int h, int offset, int length) { + for (int i = offset; i < offset + length; i++) { + h = h * 31 + buffer.get(i); + } + return h; + } + + @Override + public InputStream newInput() { + return new InputStream() { + private final ByteBuffer buf = buffer.slice(); + + @Override + public void mark(int readlimit) { + Java8Compatibility.mark(buf); + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public void reset() throws IOException { + try { + Java8Compatibility.reset(buf); + } catch (InvalidMarkException e) { + throw new IOException(e); + } + } + + @Override + public int available() throws IOException { + return buf.remaining(); + } + + @Override + public int read() throws IOException { + if (!buf.hasRemaining()) { + return -1; + } + return buf.get() & 0xFF; + } + + @Override + public int read(byte[] bytes, int off, int len) throws IOException { + if (!buf.hasRemaining()) { + return -1; + } + + len = Math.min(len, buf.remaining()); + buf.get(bytes, off, len); + return len; + } + }; + } + + @Override + public CodedInputStream newCodedInput() { + return CodedInputStream.newInstance(buffer, true); + } + + /** + * Creates a slice of a range of this buffer. + * + * @param beginIndex the beginning index of the slice (inclusive). + * @param endIndex the end index of the slice (exclusive). + * @return the requested slice. + */ + private ByteBuffer slice(int beginIndex, int endIndex) { + if (beginIndex < buffer.position() || endIndex > buffer.limit() || beginIndex > endIndex) { + throw new IllegalArgumentException( + String.format("Invalid indices [%d, %d]", beginIndex, endIndex)); + } + + ByteBuffer slice = buffer.slice(); + Java8Compatibility.position(slice, beginIndex - buffer.position()); + Java8Compatibility.limit(slice, endIndex - buffer.position()); + return slice; + } + } } diff --git a/java/core/src/main/java/com/google/protobuf/CodedInputStream.java b/java/core/src/main/java/com/google/protobuf/CodedInputStream.java index fd2991356..85b7a5bca 100644 --- a/java/core/src/main/java/com/google/protobuf/CodedInputStream.java +++ b/java/core/src/main/java/com/google/protobuf/CodedInputStream.java @@ -177,6 +177,7 @@ public abstract class CodedInputStream { throw InvalidProtocolBufferException.recursionLimitExceeded(); } } + /** Disable construction/inheritance outside of this class. */ private CodedInputStream() {} @@ -1993,6 +1994,7 @@ public abstract class CodedInputStream { private static final class StreamDecoder extends CodedInputStream { private final InputStream input; private final byte[] buffer; + /** bufferSize represents how many bytes are currently filled in the buffer */ private int bufferSize; @@ -2407,7 +2409,7 @@ public abstract class CodedInputStream { throw InvalidProtocolBufferException.negativeSize(); } // Slow path: Build a byte array first then copy it. - + // We must copy as the byte array was handed off to the InputStream and a malicious // implementation could retain a reference. return ByteBuffer.wrap(readRawBytesSlowPath(size, /* ensureNoLeakedReferences= */ true)); @@ -2656,6 +2658,9 @@ public abstract class CodedInputStream { throw InvalidProtocolBufferException.negativeSize(); } byteLimit += totalBytesRetired + pos; + if (byteLimit < 0) { + throw InvalidProtocolBufferException.parseFailure(); + } final int oldLimit = currentLimit; if (byteLimit > oldLimit) { throw InvalidProtocolBufferException.truncatedMessage(); @@ -2822,12 +2827,12 @@ public abstract class CodedInputStream { /** * Exactly like readRawBytes, but caller must have already checked the fast path: (size <= * (bufferSize - pos) && size > 0) - * - * If ensureNoLeakedReferences is true, the value is guaranteed to have not escaped to + * + *

If ensureNoLeakedReferences is true, the value is guaranteed to have not escaped to * untrusted code. */ - private byte[] readRawBytesSlowPath( - final int size, boolean ensureNoLeakedReferences) throws IOException { + private byte[] readRawBytesSlowPath(final int size, boolean ensureNoLeakedReferences) + throws IOException { // Attempt to read the data in one byte array when it's safe to do. byte[] result = readRawBytesSlowPathOneChunk(size); if (result != null) { @@ -2927,8 +2932,8 @@ public abstract class CodedInputStream { /** * Reads the remaining data in small chunks from the input stream. - * - * Returns a byte[] that may have escaped to user code via InputStream APIs. + * + *

Returns a byte[] that may have escaped to user code via InputStream APIs. */ private List readRawBytesSlowPathRemainingChunks(int sizeLeft) throws IOException { // The size is very large. For security reasons, we can't allocate the @@ -2998,7 +3003,7 @@ public abstract class CodedInputStream { System.arraycopy(chunk, 0, bytes, tempPos, chunk.length); tempPos += chunk.length; } - + return ByteString.wrap(bytes); } @@ -3086,41 +3091,54 @@ public abstract class CodedInputStream { private static final class IterableDirectByteBufferDecoder extends CodedInputStream { /** The object that need to decode. */ private final Iterable input; + /** The {@link Iterator} with type {@link ByteBuffer} of {@code input} */ private final Iterator iterator; + /** The current ByteBuffer; */ private ByteBuffer currentByteBuffer; + /** * If {@code true}, indicates that all the buffers are backing a {@link ByteString} and are * therefore considered to be an immutable input source. */ private final boolean immutable; + /** * If {@code true}, indicates that calls to read {@link ByteString} or {@code byte[]} * may return slices of the underlying buffer, rather than copies. */ private boolean enableAliasing; + /** The global total message length limit */ private int totalBufferSize; + /** The amount of available data in the input beyond {@link #currentLimit}. */ private int bufferSizeAfterCurrentLimit; + /** The absolute position of the end of the current message. */ private int currentLimit = Integer.MAX_VALUE; + /** The last tag that was read from this stream. */ private int lastTag; + /** Total Bytes have been Read from the {@link Iterable} {@link ByteBuffer} */ private int totalBytesRead; + /** The start position offset of the whole message, used as to reset the totalBytesRead */ private int startOffset; + /** The current position for current ByteBuffer */ private long currentByteBufferPos; private long currentByteBufferStartPos; + /** * If the current ByteBuffer is unsafe-direct based, currentAddress is the start address of this * ByteBuffer; otherwise should be zero. */ private long currentAddress; + /** The limit position for current ByteBuffer */ private long currentByteBufferLimit; diff --git a/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java index 37bb44a05..2c28536f4 100644 --- a/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java +++ b/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java @@ -40,7 +40,9 @@ public abstract class CodedOutputStream extends ByteOutput { /** Used to adapt to the experimental {@link Writer} interface. */ CodedOutputStreamWriter wrapper; - /** @deprecated Use {@link #computeFixed32SizeNoTag(int)} instead. */ + /** + * @deprecated Use {@link #computeFixed32SizeNoTag(int)} instead. + */ @Deprecated public static final int LITTLE_ENDIAN_32_SIZE = FIXED32_SIZE; /** The buffer size used in {@link #newInstance(OutputStream)}. */ @@ -669,9 +671,8 @@ public abstract class CodedOutputStream extends ByteOutput { } /** - * Compute the number of bytes that would be needed to encode a lazily parsed MessageSet - * extension field to the stream. For historical reasons, the wire format differs from normal - * fields. + * Compute the number of bytes that would be needed to encode a lazily parsed MessageSet extension + * field to the stream. For historical reasons, the wire format differs from normal fields. */ public static int computeLazyFieldMessageSetExtensionSize( final int fieldNumber, final LazyFieldLite value) { @@ -692,29 +693,52 @@ public abstract class CodedOutputStream extends ByteOutput { * tag. */ public static int computeInt32SizeNoTag(final int value) { - if (value >= 0) { - return computeUInt32SizeNoTag(value); - } else { - // Must sign-extend. - return MAX_VARINT_SIZE; - } + return computeUInt64SizeNoTag((long) value); } /** Compute the number of bytes that would be needed to encode a {@code uint32} field. */ public static int computeUInt32SizeNoTag(final int value) { - if ((value & (~0 << 7)) == 0) { - return 1; - } - if ((value & (~0 << 14)) == 0) { - return 2; - } - if ((value & (~0 << 21)) == 0) { - return 3; - } - if ((value & (~0 << 28)) == 0) { - return 4; - } - return 5; + /* + This code is ported from the C++ varint implementation. + Implementation notes: + + To calcuate varint size, we want to count the number of 7 bit chunks required. Rather than using + division by 7 to accomplish this, we use multiplication by 9/64. This has a number of important + properties: + * It's roughly 1/7.111111. This makes the 0 bits set case have the same value as the 7 bits set + case, so offsetting by 1 gives us the correct value we want for integers up to 448 bits. + * Multiplying by 9 is special. x * 9 = x << 3 + x, and so this multiplication can be done by a + single shifted add on arm (add w0, w0, w0, lsl #3), or a single lea instruction + (leal (%rax,%rax,8), %eax)) on x86. + * Dividing by 64 is a 6 bit right shift. + + An explicit non-sign-extended right shift is used instead of the more obvious '/ 64' because + that actually produces worse code on android arm64 at time of authoring because of sign + extension. Rather than + lsr w0, w0, #6 + It would emit: + add w16, w0, #0x3f (63) + cmp w0, #0x0 (0) + csel w0, w16, w0, lt + asr w0, w0, #6 + + Summarized: + floor(((Integer.SIZE - clz) / 7.1111) + 1 + ((Integer.SIZE - clz) * 9) / 64 + 1 + (((Integer.SIZE - clz) * 9) >>> 6) + 1 + ((Integer.SIZE - clz) * 9 + (1 << 6)) >>> 6 + (Integer.SIZE * 9 + (1 << 6) - clz * 9) >>> 6 + (352 - clz * 9) >>> 6 + on arm: + (352 - clz - (clz << 3)) >>> 6 + on x86: + (352 - lea(clz, clz, 8)) >>> 6 + + If you make changes here, please validate their compiled output on different architectures and + runtimes. + */ + int clz = Integer.numberOfLeadingZeros(value); + return ((Integer.SIZE * 9 + (1 << 6)) - (clz * 9)) >>> 6; } /** Compute the number of bytes that would be needed to encode an {@code sint32} field. */ @@ -745,27 +769,9 @@ public abstract class CodedOutputStream extends ByteOutput { * tag. */ public static int computeUInt64SizeNoTag(long value) { - // handle two popular special cases up front ... - if ((value & (~0L << 7)) == 0L) { - return 1; - } - if (value < 0L) { - return 10; - } - // ... leaving us with 8 remaining, which we can divide and conquer - int n = 2; - if ((value & (~0L << 35)) != 0L) { - n += 4; - value >>>= 28; - } - if ((value & (~0L << 21)) != 0L) { - n += 2; - value >>>= 14; - } - if ((value & (~0L << 14)) != 0L) { - n += 1; - } - return n; + int clz = Long.numberOfLeadingZeros(value); + // See computeUInt32SizeNoTag for explanation + return ((Long.SIZE * 9 + (1 << 6)) - (clz * 9)) >>> 6; } /** Compute the number of bytes that would be needed to encode an {@code sint64} field. */ @@ -1326,7 +1332,7 @@ public abstract class CodedOutputStream extends ByteOutput { buffer[position++] = (byte) value; return; } else { - buffer[position++] = (byte) ((value & 0x7F) | 0x80); + buffer[position++] = (byte) ((value | 0x80) & 0xFF); value >>>= 7; } } @@ -1357,7 +1363,7 @@ public abstract class CodedOutputStream extends ByteOutput { UnsafeUtil.putByte(buffer, position++, (byte) value); return; } else { - UnsafeUtil.putByte(buffer, position++, (byte) (((int) value & 0x7F) | 0x80)); + UnsafeUtil.putByte(buffer, position++, (byte) (((int) value | 0x80) & 0xFF)); value >>>= 7; } } @@ -1368,7 +1374,7 @@ public abstract class CodedOutputStream extends ByteOutput { buffer[position++] = (byte) value; return; } else { - buffer[position++] = (byte) (((int) value & 0x7F) | 0x80); + buffer[position++] = (byte) (((int) value | 0x80) & 0xFF); value >>>= 7; } } @@ -1684,7 +1690,7 @@ public abstract class CodedOutputStream extends ByteOutput { buffer.put((byte) value); return; } else { - buffer.put((byte) ((value & 0x7F) | 0x80)); + buffer.put((byte) ((value | 0x80) & 0xFF)); value >>>= 7; } } @@ -1710,7 +1716,7 @@ public abstract class CodedOutputStream extends ByteOutput { buffer.put((byte) value); return; } else { - buffer.put((byte) (((int) value & 0x7F) | 0x80)); + buffer.put((byte) (((int) value | 0x80) & 0xFF)); value >>>= 7; } } @@ -2015,7 +2021,7 @@ public abstract class CodedOutputStream extends ByteOutput { UnsafeUtil.putByte(position++, (byte) value); return; } else { - UnsafeUtil.putByte(position++, (byte) ((value & 0x7F) | 0x80)); + UnsafeUtil.putByte(position++, (byte) ((value | 0x80) & 0xFF)); value >>>= 7; } } @@ -2025,7 +2031,7 @@ public abstract class CodedOutputStream extends ByteOutput { UnsafeUtil.putByte(position++, (byte) value); return; } else { - UnsafeUtil.putByte(position++, (byte) ((value & 0x7F) | 0x80)); + UnsafeUtil.putByte(position++, (byte) ((value | 0x80) & 0xFF)); value >>>= 7; } } @@ -2049,7 +2055,7 @@ public abstract class CodedOutputStream extends ByteOutput { UnsafeUtil.putByte(position++, (byte) value); return; } else { - UnsafeUtil.putByte(position++, (byte) (((int) value & 0x7F) | 0x80)); + UnsafeUtil.putByte(position++, (byte) (((int) value | 0x80) & 0xFF)); value >>>= 7; } } @@ -2059,7 +2065,7 @@ public abstract class CodedOutputStream extends ByteOutput { UnsafeUtil.putByte(position++, (byte) value); return; } else { - UnsafeUtil.putByte(position++, (byte) (((int) value & 0x7F) | 0x80)); + UnsafeUtil.putByte(position++, (byte) (((int) value | 0x80) & 0xFF)); value >>>= 7; } } @@ -2259,7 +2265,7 @@ public abstract class CodedOutputStream extends ByteOutput { UnsafeUtil.putByte(buffer, position++, (byte) value); break; } else { - UnsafeUtil.putByte(buffer, position++, (byte) ((value & 0x7F) | 0x80)); + UnsafeUtil.putByte(buffer, position++, (byte) ((value | 0x80) & 0xFF)); value >>>= 7; } } @@ -2272,7 +2278,7 @@ public abstract class CodedOutputStream extends ByteOutput { totalBytesWritten++; return; } else { - buffer[position++] = (byte) ((value & 0x7F) | 0x80); + buffer[position++] = (byte) ((value | 0x80) & 0xFF); totalBytesWritten++; value >>>= 7; } @@ -2292,7 +2298,7 @@ public abstract class CodedOutputStream extends ByteOutput { UnsafeUtil.putByte(buffer, position++, (byte) value); break; } else { - UnsafeUtil.putByte(buffer, position++, (byte) (((int) value & 0x7F) | 0x80)); + UnsafeUtil.putByte(buffer, position++, (byte) (((int) value | 0x80) & 0xFF)); value >>>= 7; } } @@ -2305,7 +2311,7 @@ public abstract class CodedOutputStream extends ByteOutput { totalBytesWritten++; return; } else { - buffer[position++] = (byte) (((int) value & 0x7F) | 0x80); + buffer[position++] = (byte) (((int) value | 0x80) & 0xFF); totalBytesWritten++; value >>>= 7; } diff --git a/java/core/src/main/java/com/google/protobuf/CodedOutputStreamWriter.java b/java/core/src/main/java/com/google/protobuf/CodedOutputStreamWriter.java index 6c060cc3a..a20a579e3 100644 --- a/java/core/src/main/java/com/google/protobuf/CodedOutputStreamWriter.java +++ b/java/core/src/main/java/com/google/protobuf/CodedOutputStreamWriter.java @@ -167,6 +167,38 @@ final class CodedOutputStreamWriter implements Writer { @Override public void writeInt32List(int fieldNumber, List value, boolean packed) throws IOException { + if (value instanceof IntArrayList) { + writeInt32ListInternal(fieldNumber, (IntArrayList) value, packed); + } else { + writeInt32ListInternal(fieldNumber, value, packed); + } + } + + private void writeInt32ListInternal(int fieldNumber, IntArrayList value, boolean packed) + throws IOException { + if (packed) { + output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); + + // Compute and write the length of the data. + int dataSize = 0; + for (int i = 0; i < value.size(); ++i) { + dataSize += CodedOutputStream.computeInt32SizeNoTag(value.getInt(i)); + } + output.writeUInt32NoTag(dataSize); + + // Write the data itself, without any tags. + for (int i = 0; i < value.size(); ++i) { + output.writeInt32NoTag(value.getInt(i)); + } + } else { + for (int i = 0; i < value.size(); ++i) { + output.writeInt32(fieldNumber, value.getInt(i)); + } + } + } + + private void writeInt32ListInternal(int fieldNumber, List value, boolean packed) + throws IOException { if (packed) { output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); @@ -191,6 +223,38 @@ final class CodedOutputStreamWriter implements Writer { @Override public void writeFixed32List(int fieldNumber, List value, boolean packed) throws IOException { + if (value instanceof IntArrayList) { + writeFixed32ListInternal(fieldNumber, (IntArrayList) value, packed); + } else { + writeFixed32ListInternal(fieldNumber, value, packed); + } + } + + private void writeFixed32ListInternal(int fieldNumber, IntArrayList value, boolean packed) + throws IOException { + if (packed) { + output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); + + // Compute and write the length of the data. + int dataSize = 0; + for (int i = 0; i < value.size(); ++i) { + dataSize += CodedOutputStream.computeFixed32SizeNoTag(value.getInt(i)); + } + output.writeUInt32NoTag(dataSize); + + // Write the data itself, without any tags. + for (int i = 0; i < value.size(); ++i) { + output.writeFixed32NoTag(value.getInt(i)); + } + } else { + for (int i = 0; i < value.size(); ++i) { + output.writeFixed32(fieldNumber, value.getInt(i)); + } + } + } + + private void writeFixed32ListInternal(int fieldNumber, List value, boolean packed) + throws IOException { if (packed) { output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); @@ -214,6 +278,38 @@ final class CodedOutputStreamWriter implements Writer { @Override public void writeInt64List(int fieldNumber, List value, boolean packed) throws IOException { + if (value instanceof LongArrayList) { + writeInt64ListInternal(fieldNumber, (LongArrayList) value, packed); + } else { + writeInt64ListInternal(fieldNumber, value, packed); + } + } + + private void writeInt64ListInternal(int fieldNumber, LongArrayList value, boolean packed) + throws IOException { + if (packed) { + output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); + + // Compute and write the length of the data. + int dataSize = 0; + for (int i = 0; i < value.size(); ++i) { + dataSize += CodedOutputStream.computeInt64SizeNoTag(value.getLong(i)); + } + output.writeUInt32NoTag(dataSize); + + // Write the data itself, without any tags. + for (int i = 0; i < value.size(); ++i) { + output.writeInt64NoTag(value.getLong(i)); + } + } else { + for (int i = 0; i < value.size(); ++i) { + output.writeInt64(fieldNumber, value.getLong(i)); + } + } + } + + private void writeInt64ListInternal(int fieldNumber, List value, boolean packed) + throws IOException { if (packed) { output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); @@ -234,10 +330,41 @@ final class CodedOutputStreamWriter implements Writer { } } } - @Override public void writeUInt64List(int fieldNumber, List value, boolean packed) throws IOException { + if (value instanceof LongArrayList) { + writeUInt64ListInternal(fieldNumber, (LongArrayList) value, packed); + } else { + writeUInt64ListInternal(fieldNumber, value, packed); + } + } + + private void writeUInt64ListInternal(int fieldNumber, LongArrayList value, boolean packed) + throws IOException { + if (packed) { + output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); + + // Compute and write the length of the data. + int dataSize = 0; + for (int i = 0; i < value.size(); ++i) { + dataSize += CodedOutputStream.computeUInt64SizeNoTag(value.getLong(i)); + } + output.writeUInt32NoTag(dataSize); + + // Write the data itself, without any tags. + for (int i = 0; i < value.size(); ++i) { + output.writeUInt64NoTag(value.getLong(i)); + } + } else { + for (int i = 0; i < value.size(); ++i) { + output.writeUInt64(fieldNumber, value.getLong(i)); + } + } + } + + private void writeUInt64ListInternal(int fieldNumber, List value, boolean packed) + throws IOException { if (packed) { output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); @@ -262,6 +389,38 @@ final class CodedOutputStreamWriter implements Writer { @Override public void writeFixed64List(int fieldNumber, List value, boolean packed) throws IOException { + if (value instanceof LongArrayList) { + writeFixed64ListInternal(fieldNumber, (LongArrayList) value, packed); + } else { + writeFixed64ListInternal(fieldNumber, value, packed); + } + } + + private void writeFixed64ListInternal(int fieldNumber, LongArrayList value, boolean packed) + throws IOException { + if (packed) { + output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); + + // Compute and write the length of the data. + int dataSize = 0; + for (int i = 0; i < value.size(); ++i) { + dataSize += CodedOutputStream.computeFixed64SizeNoTag(value.getLong(i)); + } + output.writeUInt32NoTag(dataSize); + + // Write the data itself, without any tags. + for (int i = 0; i < value.size(); ++i) { + output.writeFixed64NoTag(value.getLong(i)); + } + } else { + for (int i = 0; i < value.size(); ++i) { + output.writeFixed64(fieldNumber, value.getLong(i)); + } + } + } + + private void writeFixed64ListInternal(int fieldNumber, List value, boolean packed) + throws IOException { if (packed) { output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); @@ -286,6 +445,38 @@ final class CodedOutputStreamWriter implements Writer { @Override public void writeFloatList(int fieldNumber, List value, boolean packed) throws IOException { + if (value instanceof FloatArrayList) { + writeFloatListInternal(fieldNumber, (FloatArrayList) value, packed); + } else { + writeFloatListInternal(fieldNumber, value, packed); + } + } + + private void writeFloatListInternal(int fieldNumber, FloatArrayList value, boolean packed) + throws IOException { + if (packed) { + output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); + + // Compute and write the length of the data. + int dataSize = 0; + for (int i = 0; i < value.size(); ++i) { + dataSize += CodedOutputStream.computeFloatSizeNoTag(value.getFloat(i)); + } + output.writeUInt32NoTag(dataSize); + + // Write the data itself, without any tags. + for (int i = 0; i < value.size(); ++i) { + output.writeFloatNoTag(value.getFloat(i)); + } + } else { + for (int i = 0; i < value.size(); ++i) { + output.writeFloat(fieldNumber, value.getFloat(i)); + } + } + } + + private void writeFloatListInternal(int fieldNumber, List value, boolean packed) + throws IOException { if (packed) { output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); @@ -310,6 +501,38 @@ final class CodedOutputStreamWriter implements Writer { @Override public void writeDoubleList(int fieldNumber, List value, boolean packed) throws IOException { + if (value instanceof DoubleArrayList) { + writeDoubleListInternal(fieldNumber, (DoubleArrayList) value, packed); + } else { + writeDoubleListInternal(fieldNumber, value, packed); + } + } + + private void writeDoubleListInternal(int fieldNumber, DoubleArrayList value, boolean packed) + throws IOException { + if (packed) { + output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); + + // Compute and write the length of the data. + int dataSize = 0; + for (int i = 0; i < value.size(); ++i) { + dataSize += CodedOutputStream.computeDoubleSizeNoTag(value.getDouble(i)); + } + output.writeUInt32NoTag(dataSize); + + // Write the data itself, without any tags. + for (int i = 0; i < value.size(); ++i) { + output.writeDoubleNoTag(value.getDouble(i)); + } + } else { + for (int i = 0; i < value.size(); ++i) { + output.writeDouble(fieldNumber, value.getDouble(i)); + } + } + } + + private void writeDoubleListInternal(int fieldNumber, List value, boolean packed) + throws IOException { if (packed) { output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); @@ -334,6 +557,38 @@ final class CodedOutputStreamWriter implements Writer { @Override public void writeEnumList(int fieldNumber, List value, boolean packed) throws IOException { + if (value instanceof IntArrayList) { + writeEnumListInternal(fieldNumber, (IntArrayList) value, packed); + } else { + writeEnumListInternal(fieldNumber, value, packed); + } + } + + private void writeEnumListInternal(int fieldNumber, IntArrayList value, boolean packed) + throws IOException { + if (packed) { + output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); + + // Compute and write the length of the data. + int dataSize = 0; + for (int i = 0; i < value.size(); ++i) { + dataSize += CodedOutputStream.computeEnumSizeNoTag(value.getInt(i)); + } + output.writeUInt32NoTag(dataSize); + + // Write the data itself, without any tags. + for (int i = 0; i < value.size(); ++i) { + output.writeEnumNoTag(value.getInt(i)); + } + } else { + for (int i = 0; i < value.size(); ++i) { + output.writeEnum(fieldNumber, value.getInt(i)); + } + } + } + + private void writeEnumListInternal(int fieldNumber, List value, boolean packed) + throws IOException { if (packed) { output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); @@ -358,6 +613,38 @@ final class CodedOutputStreamWriter implements Writer { @Override public void writeBoolList(int fieldNumber, List value, boolean packed) throws IOException { + if (value instanceof BooleanArrayList) { + writeBoolListInternal(fieldNumber, (BooleanArrayList) value, packed); + } else { + writeBoolListInternal(fieldNumber, value, packed); + } + } + + private void writeBoolListInternal(int fieldNumber, BooleanArrayList value, boolean packed) + throws IOException { + if (packed) { + output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); + + // Compute and write the length of the data. + int dataSize = 0; + for (int i = 0; i < value.size(); ++i) { + dataSize += CodedOutputStream.computeBoolSizeNoTag(value.getBoolean(i)); + } + output.writeUInt32NoTag(dataSize); + + // Write the data itself, without any tags. + for (int i = 0; i < value.size(); ++i) { + output.writeBoolNoTag(value.getBoolean(i)); + } + } else { + for (int i = 0; i < value.size(); ++i) { + output.writeBool(fieldNumber, value.getBoolean(i)); + } + } + } + + private void writeBoolListInternal(int fieldNumber, List value, boolean packed) + throws IOException { if (packed) { output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); @@ -411,6 +698,38 @@ final class CodedOutputStreamWriter implements Writer { @Override public void writeUInt32List(int fieldNumber, List value, boolean packed) throws IOException { + if (value instanceof IntArrayList) { + writeUInt32ListInternal(fieldNumber, (IntArrayList) value, packed); + } else { + writeUInt32ListInternal(fieldNumber, value, packed); + } + } + + private void writeUInt32ListInternal(int fieldNumber, IntArrayList value, boolean packed) + throws IOException { + if (packed) { + output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); + + // Compute and write the length of the data. + int dataSize = 0; + for (int i = 0; i < value.size(); ++i) { + dataSize += CodedOutputStream.computeUInt32SizeNoTag(value.getInt(i)); + } + output.writeUInt32NoTag(dataSize); + + // Write the data itself, without any tags. + for (int i = 0; i < value.size(); ++i) { + output.writeUInt32NoTag(value.getInt(i)); + } + } else { + for (int i = 0; i < value.size(); ++i) { + output.writeUInt32(fieldNumber, value.getInt(i)); + } + } + } + + public void writeUInt32ListInternal(int fieldNumber, List value, boolean packed) + throws IOException { if (packed) { output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); @@ -435,6 +754,38 @@ final class CodedOutputStreamWriter implements Writer { @Override public void writeSFixed32List(int fieldNumber, List value, boolean packed) throws IOException { + if (value instanceof IntArrayList) { + writeSFixed32ListInternal(fieldNumber, (IntArrayList) value, packed); + } else { + writeSFixed32ListInternal(fieldNumber, value, packed); + } + } + + private void writeSFixed32ListInternal(int fieldNumber, IntArrayList value, boolean packed) + throws IOException { + if (packed) { + output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); + + // Compute and write the length of the data. + int dataSize = 0; + for (int i = 0; i < value.size(); ++i) { + dataSize += CodedOutputStream.computeSFixed32SizeNoTag(value.getInt(i)); + } + output.writeUInt32NoTag(dataSize); + + // Write the data itself, without any tags. + for (int i = 0; i < value.size(); ++i) { + output.writeSFixed32NoTag(value.getInt(i)); + } + } else { + for (int i = 0; i < value.size(); ++i) { + output.writeSFixed32(fieldNumber, value.getInt(i)); + } + } + } + + private void writeSFixed32ListInternal(int fieldNumber, List value, boolean packed) + throws IOException { if (packed) { output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); @@ -459,6 +810,38 @@ final class CodedOutputStreamWriter implements Writer { @Override public void writeSFixed64List(int fieldNumber, List value, boolean packed) throws IOException { + if (value instanceof LongArrayList) { + writeSFixed64ListInternal(fieldNumber, (LongArrayList) value, packed); + } else { + writeSFixed64ListInternal(fieldNumber, value, packed); + } + } + + private void writeSFixed64ListInternal(int fieldNumber, LongArrayList value, boolean packed) + throws IOException { + if (packed) { + output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); + + // Compute and write the length of the data. + int dataSize = 0; + for (int i = 0; i < value.size(); ++i) { + dataSize += CodedOutputStream.computeSFixed64SizeNoTag(value.getLong(i)); + } + output.writeUInt32NoTag(dataSize); + + // Write the data itself, without any tags. + for (int i = 0; i < value.size(); ++i) { + output.writeSFixed64NoTag(value.getLong(i)); + } + } else { + for (int i = 0; i < value.size(); ++i) { + output.writeSFixed64(fieldNumber, value.getLong(i)); + } + } + } + + private void writeSFixed64ListInternal(int fieldNumber, List value, boolean packed) + throws IOException { if (packed) { output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); @@ -483,6 +866,38 @@ final class CodedOutputStreamWriter implements Writer { @Override public void writeSInt32List(int fieldNumber, List value, boolean packed) throws IOException { + if (value instanceof IntArrayList) { + writeSInt32ListInternal(fieldNumber, (IntArrayList) value, packed); + } else { + writeSInt32ListInternal(fieldNumber, value, packed); + } + } + + private void writeSInt32ListInternal(int fieldNumber, IntArrayList value, boolean packed) + throws IOException { + if (packed) { + output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); + + // Compute and write the length of the data. + int dataSize = 0; + for (int i = 0; i < value.size(); ++i) { + dataSize += CodedOutputStream.computeSInt32SizeNoTag(value.getInt(i)); + } + output.writeUInt32NoTag(dataSize); + + // Write the data itself, without any tags. + for (int i = 0; i < value.size(); ++i) { + output.writeSInt32NoTag(value.getInt(i)); + } + } else { + for (int i = 0; i < value.size(); ++i) { + output.writeSInt32(fieldNumber, value.getInt(i)); + } + } + } + + public void writeSInt32ListInternal(int fieldNumber, List value, boolean packed) + throws IOException { if (packed) { output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); @@ -507,6 +922,38 @@ final class CodedOutputStreamWriter implements Writer { @Override public void writeSInt64List(int fieldNumber, List value, boolean packed) throws IOException { + if (value instanceof LongArrayList) { + writeSInt64ListInternal(fieldNumber, (LongArrayList) value, packed); + } else { + writeSInt64ListInternal(fieldNumber, value, packed); + } + } + + private void writeSInt64ListInternal(int fieldNumber, LongArrayList value, boolean packed) + throws IOException { + if (packed) { + output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); + + // Compute and write the length of the data. + int dataSize = 0; + for (int i = 0; i < value.size(); ++i) { + dataSize += CodedOutputStream.computeSInt64SizeNoTag(value.getLong(i)); + } + output.writeUInt32NoTag(dataSize); + + // Write the data itself, without any tags. + for (int i = 0; i < value.size(); ++i) { + output.writeSInt64NoTag(value.getLong(i)); + } + } else { + for (int i = 0; i < value.size(); ++i) { + output.writeSInt64(fieldNumber, value.getLong(i)); + } + } + } + + private void writeSInt64ListInternal(int fieldNumber, List value, boolean packed) + throws IOException { if (packed) { output.writeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED); diff --git a/java/core/src/main/java/com/google/protobuf/DebugFormat.java b/java/core/src/main/java/com/google/protobuf/DebugFormat.java new file mode 100644 index 000000000..2d63ef171 --- /dev/null +++ b/java/core/src/main/java/com/google/protobuf/DebugFormat.java @@ -0,0 +1,79 @@ +package com.google.protobuf; + +import com.google.protobuf.Descriptors.FieldDescriptor; + +/** + * Provides an explicit API for unstable, redacting debug output suitable for debug logging. This + * implementation is based on TextFormat, but should not be parsed. + */ +public final class DebugFormat { + + private final boolean isSingleLine; + + private DebugFormat(boolean singleLine) { + isSingleLine = singleLine; + } + + public static DebugFormat singleLine() { + return new DebugFormat(true); + } + + public static DebugFormat multiline() { + return new DebugFormat(false); + } + + public String toString(MessageOrBuilder message) { + return TextFormat.printer() + .emittingSingleLine(this.isSingleLine) + .enablingSafeDebugFormat(true) + .printToString(message); + } + + public String toString(FieldDescriptor field, Object value) { + return TextFormat.printer() + .emittingSingleLine(this.isSingleLine) + .enablingSafeDebugFormat(true) + .printFieldToString(field, value); + } + + public String toString(UnknownFieldSet fields) { + return TextFormat.printer() + .emittingSingleLine(this.isSingleLine) + .enablingSafeDebugFormat(true) + .printToString(fields); + } + + public Object lazyToString(MessageOrBuilder message) { + return new LazyDebugOutput(message, this); + } + + public Object lazyToString(UnknownFieldSet fields) { + return new LazyDebugOutput(fields, this); + } + + private static class LazyDebugOutput { + private final MessageOrBuilder message; + private final UnknownFieldSet fields; + private final DebugFormat format; + + LazyDebugOutput(MessageOrBuilder message, DebugFormat format) { + this.message = message; + this.fields = null; + this.format = format; + } + + LazyDebugOutput(UnknownFieldSet fields, DebugFormat format) { + this.message = null; + this.fields = fields; + this.format = format; + } + + @Override + public String toString() { + if (message != null) { + return format.toString(message); + } + return format.toString(fields); + } + } +} diff --git a/java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java b/java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java index 821096903..8ce15422c 100644 --- a/java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java +++ b/java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java @@ -20,7 +20,6 @@ import static com.google.protobuf.FieldInfo.forRepeatedMessageField; import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.Descriptors.FieldDescriptor.Type; -import com.google.protobuf.Descriptors.FileDescriptor; import com.google.protobuf.Descriptors.OneofDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -34,7 +33,7 @@ import java.util.Set; import java.util.Stack; import java.util.concurrent.ConcurrentHashMap; -/** A factory for message info based on protobuf descriptors for a {@link GeneratedMessageV3}. */ +/** A factory for message info based on protobuf descriptors for a {@link GeneratedMessage}. */ @ExperimentalApi final class DescriptorMessageInfoFactory implements MessageInfoFactory { private static final String GET_DEFAULT_INSTANCE_METHOD_NAME = "getDefaultInstance"; @@ -74,12 +73,12 @@ final class DescriptorMessageInfoFactory implements MessageInfoFactory { @Override public boolean isSupported(Class messageType) { - return GeneratedMessageV3.class.isAssignableFrom(messageType); + return GeneratedMessage.class.isAssignableFrom(messageType); } @Override public MessageInfo messageInfoFor(Class messageType) { - if (!GeneratedMessageV3.class.isAssignableFrom(messageType)) { + if (!GeneratedMessage.class.isAssignableFrom(messageType)) { throw new IllegalArgumentException("Unsupported message type: " + messageType.getName()); } @@ -100,16 +99,14 @@ final class DescriptorMessageInfoFactory implements MessageInfoFactory { return getDefaultInstance(messageType).getDescriptorForType(); } - private static ProtoSyntax convertSyntax(FileDescriptor.Syntax syntax) { - switch (syntax) { - case PROTO2: + private static ProtoSyntax convertSyntax(DescriptorProtos.Edition edition) { + switch (edition) { + case EDITION_PROTO2: return ProtoSyntax.PROTO2; - case PROTO3: + case EDITION_PROTO3: return ProtoSyntax.PROTO3; - case EDITIONS: - return ProtoSyntax.EDITIONS; default: - throw new IllegalArgumentException("Unsupported syntax: " + syntax); + return ProtoSyntax.EDITIONS; } } @@ -118,7 +115,7 @@ final class DescriptorMessageInfoFactory implements MessageInfoFactory { StructuralMessageInfo.Builder builder = StructuralMessageInfo.newBuilder(fieldDescriptors.size()); builder.withDefaultInstance(getDefaultInstance(messageType)); - builder.withSyntax(convertSyntax(messageDescriptor.getFile().getSyntax())); + builder.withSyntax(convertSyntax(messageDescriptor.getFile().getEdition())); builder.withMessageSetWireFormat(messageDescriptor.getOptions().getMessageSetWireFormat()); OneofState oneofState = new OneofState(); diff --git a/java/core/src/main/java/com/google/protobuf/Descriptors.java b/java/core/src/main/java/com/google/protobuf/Descriptors.java index fb9b661bb..569fa26e0 100644 --- a/java/core/src/main/java/com/google/protobuf/Descriptors.java +++ b/java/core/src/main/java/com/google/protobuf/Descriptors.java @@ -15,6 +15,9 @@ import com.google.protobuf.DescriptorProtos.EnumDescriptorProto; import com.google.protobuf.DescriptorProtos.EnumOptions; import com.google.protobuf.DescriptorProtos.EnumValueDescriptorProto; import com.google.protobuf.DescriptorProtos.EnumValueOptions; +import com.google.protobuf.DescriptorProtos.FeatureSet; +import com.google.protobuf.DescriptorProtos.FeatureSetDefaults; +import com.google.protobuf.DescriptorProtos.FeatureSetDefaults.FeatureSetEditionDefault; import com.google.protobuf.DescriptorProtos.FieldDescriptorProto; import com.google.protobuf.DescriptorProtos.FieldOptions; import com.google.protobuf.DescriptorProtos.FileDescriptorProto; @@ -26,7 +29,8 @@ import com.google.protobuf.DescriptorProtos.OneofDescriptorProto; import com.google.protobuf.DescriptorProtos.OneofOptions; import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto; import com.google.protobuf.DescriptorProtos.ServiceOptions; -import com.google.protobuf.Descriptors.FileDescriptor.Syntax; +import com.google.protobuf.Descriptors.DescriptorValidationException; +import com.google.protobuf.JavaFeaturesProto.JavaFeatures; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -38,6 +42,7 @@ import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; /** @@ -65,6 +70,84 @@ public final class Descriptors { private static final EnumDescriptor[] EMPTY_ENUM_DESCRIPTORS = new EnumDescriptor[0]; private static final ServiceDescriptor[] EMPTY_SERVICE_DESCRIPTORS = new ServiceDescriptor[0]; private static final OneofDescriptor[] EMPTY_ONEOF_DESCRIPTORS = new OneofDescriptor[0]; + private static final ConcurrentHashMap FEATURE_CACHE = + new ConcurrentHashMap<>(); + + @SuppressWarnings("NonFinalStaticField") + private static volatile FeatureSetDefaults javaEditionDefaults = null; + + /** Sets the default feature mappings used during the build. Exposed for tests. */ + static void setTestJavaEditionDefaults(FeatureSetDefaults defaults) { + javaEditionDefaults = defaults; + } + + /** Gets the default feature mappings used during the build. */ + static FeatureSetDefaults getJavaEditionDefaults() { + // Force explicit initialization before synchronized block which can trigger initialization in + // `JavaFeaturesProto.registerAllExtensions()` and `FeatureSetdefaults.parseFrom()` calls. + // Otherwise, this can result in deadlock if another threads holds the static init block's + // implicit lock. This operation should be cheap if initialization has already occurred. + Descriptor unused1 = FeatureSetDefaults.getDescriptor(); + FileDescriptor unused2 = JavaFeaturesProto.getDescriptor(); + if (javaEditionDefaults == null) { + synchronized (Descriptors.class) { + if (javaEditionDefaults == null) { + try { + ExtensionRegistry registry = ExtensionRegistry.newInstance(); + registry.add(JavaFeaturesProto.java_); + setTestJavaEditionDefaults( + FeatureSetDefaults.parseFrom( + JavaEditionDefaults.PROTOBUF_INTERNAL_JAVA_EDITION_DEFAULTS.getBytes( + Internal.ISO_8859_1), + registry)); + } catch (Exception e) { + throw new AssertionError(e); + } + } + } + } + return javaEditionDefaults; + } + + static FeatureSet getEditionDefaults(Edition edition) { + FeatureSetDefaults javaEditionDefaults = getJavaEditionDefaults(); + if (edition.getNumber() < javaEditionDefaults.getMinimumEdition().getNumber()) { + throw new IllegalArgumentException( + "Edition " + + edition + + " is lower than the minimum supported edition " + + javaEditionDefaults.getMinimumEdition() + + "!"); + } + if (edition.getNumber() > javaEditionDefaults.getMaximumEdition().getNumber()) { + throw new IllegalArgumentException( + "Edition " + + edition + + " is greater than the maximum supported edition " + + javaEditionDefaults.getMaximumEdition() + + "!"); + } + FeatureSetEditionDefault found = null; + for (FeatureSetEditionDefault editionDefault : javaEditionDefaults.getDefaultsList()) { + if (editionDefault.getEdition().getNumber() > edition.getNumber()) { + break; + } + found = editionDefault; + } + if (found == null) { + throw new IllegalArgumentException( + "Edition " + edition + " does not have a valid default FeatureSet!"); + } + return found.getFixedFeatures().toBuilder().mergeFrom(found.getOverridableFeatures()).build(); + } + + private static FeatureSet internFeatures(FeatureSet features) { + FeatureSet cached = FEATURE_CACHE.putIfAbsent(features.hashCode(), features); + if (cached == null) { + return features; + } + return cached; + } /** * Describes a {@code .proto} file, including everything defined within. That includes, in @@ -106,7 +189,21 @@ public final class Descriptors { /** Get the {@code FileOptions}, defined in {@code descriptor.proto}. */ public FileOptions getOptions() { - return proto.getOptions(); + if (this.options == null) { + FileOptions strippedOptions = this.proto.getOptions(); + if (strippedOptions.hasFeatures()) { + // Clients should be using feature accessor methods, not accessing features on the + // options + // proto. + strippedOptions = strippedOptions.toBuilder().clearFeatures().build(); + } + synchronized (this) { + if (this.options == null) { + this.options = strippedOptions; + } + } + } + return this.options; } /** Get a list of top-level message types declared in this file. */ @@ -139,59 +236,28 @@ public final class Descriptors { return Collections.unmodifiableList(Arrays.asList(publicDependencies)); } - /** The syntax of the .proto file. */ - @Deprecated - public - enum Syntax { - UNKNOWN("unknown"), - PROTO2("proto2"), - PROTO3("proto3"), - EDITIONS("editions"); - - Syntax(String name) { - this.name = name; - } - - private final String name; - } - - /** Get the syntax of the .proto file. */ - @Deprecated - public - Syntax getSyntax() { - if (Syntax.PROTO3.name.equals(proto.getSyntax())) { - return Syntax.PROTO3; - } else if (Syntax.EDITIONS.name.equals(proto.getSyntax())) { - return Syntax.EDITIONS; - } - return Syntax.PROTO2; - } - /** Get the edition of the .proto file. */ - public Edition getEdition() { - return proto.getEdition(); - } - - /** Gets the name of the edition as specified in the .proto file. */ - public String getEditionName() { - if (proto.getEdition().equals(Edition.EDITION_UNKNOWN)) { - return ""; + Edition getEdition() { + switch (proto.getSyntax()) { + case "editions": + return proto.getEdition(); + case "proto3": + return Edition.EDITION_PROTO3; + default: + return Edition.EDITION_PROTO2; } - return proto.getEdition().name().substring("EDITION_".length()); } public void copyHeadingTo(FileDescriptorProto.Builder protoBuilder) { - protoBuilder.setName(getName()).setSyntax(getSyntax().name); + protoBuilder.setName(getName()).setSyntax(proto.getSyntax()); if (!getPackage().isEmpty()) { protoBuilder.setPackage(getPackage()); } - - if (getSyntax().equals(Syntax.EDITIONS)) { - protoBuilder.setEdition(getEdition()); + if (proto.getSyntax().equals("editions")) { + protoBuilder.setEdition(proto.getEdition()); } - - if (!getOptions().equals(FileOptions.getDefaultInstance())) { - protoBuilder.setOptions(getOptions()); + if (proto.hasOptions() && !proto.getOptions().equals(FileOptions.getDefaultInstance())) { + protoBuilder.setOptions(proto.getOptions()); } } @@ -293,7 +359,7 @@ public final class Descriptors { * Construct a {@code FileDescriptor}. * * @param proto the protocol message form of the FileDescriptort - * @param dependencies {@code FileDescriptor}s corresponding to all of the file's dependencies + * @param dependencies {@code FileDescriptor}s corresponding to all of the file's dependencies. * @throws DescriptorValidationException {@code proto} is not a valid descriptor. This can occur * for a number of reasons; for instance, because a field has an undefined type or because * two messages were defined with the same name. @@ -318,6 +384,15 @@ public final class Descriptors { public static FileDescriptor buildFrom( FileDescriptorProto proto, FileDescriptor[] dependencies, boolean allowUnknownDependencies) throws DescriptorValidationException { + return buildFrom(proto, dependencies, allowUnknownDependencies, false); + } + + private static FileDescriptor buildFrom( + FileDescriptorProto proto, + FileDescriptor[] dependencies, + boolean allowUnknownDependencies, + boolean allowUnresolvedFeatures) + throws DescriptorValidationException { // Building descriptors involves two steps: translating and linking. // In the translation step (implemented by FileDescriptor's // constructor), we build an object tree mirroring the @@ -331,6 +406,12 @@ public final class Descriptors { FileDescriptor result = new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies); result.crossLink(); + // Skip feature resolution until later for calls from gencode. + if (!allowUnresolvedFeatures) { + // We do not need to force feature resolution for proto1 dependencies + // since dependencies from non-gencode should already be fully feature resolved. + result.resolveAllFeaturesInternal(); + } return result; } @@ -372,50 +453,6 @@ public final class Descriptors { return descriptors.toArray(new FileDescriptor[0]); } - /** - * This method is for backward compatibility with generated code which passed an - * InternalDescriptorAssigner. - */ - @Deprecated - public static void internalBuildGeneratedFileFrom( - final String[] descriptorDataParts, - final FileDescriptor[] dependencies, - final InternalDescriptorAssigner descriptorAssigner) { - final byte[] descriptorBytes = latin1Cat(descriptorDataParts); - - FileDescriptorProto proto; - try { - proto = FileDescriptorProto.parseFrom(descriptorBytes); - } catch (InvalidProtocolBufferException e) { - throw new IllegalArgumentException( - "Failed to parse protocol buffer descriptor for generated code.", e); - } - - final FileDescriptor result; - try { - // When building descriptors for generated code, we allow unknown - // dependencies by default. - result = buildFrom(proto, dependencies, true); - } catch (DescriptorValidationException e) { - throw new IllegalArgumentException( - "Invalid embedded descriptor for \"" + proto.getName() + "\".", e); - } - - final ExtensionRegistry registry = descriptorAssigner.assignDescriptors(result); - - if (registry != null) { - // We must re-parse the proto using the registry. - try { - proto = FileDescriptorProto.parseFrom(descriptorBytes, registry); - } catch (InvalidProtocolBufferException e) { - throw new IllegalArgumentException( - "Failed to parse protocol buffer descriptor for generated code.", e); - } - - result.setProto(proto); - } - } - /** * This method is to be called by generated code only. It is equivalent to {@code buildFrom} * except that the {@code FileDescriptorProto} is encoded in protocol buffer wire format. @@ -434,30 +471,14 @@ public final class Descriptors { try { // When building descriptors for generated code, we allow unknown - // dependencies by default. - return buildFrom(proto, dependencies, true); + // dependencies by default and delay feature resolution until later. + return buildFrom(proto, dependencies, true, true); } catch (DescriptorValidationException e) { throw new IllegalArgumentException( "Invalid embedded descriptor for \"" + proto.getName() + "\".", e); } } - /** - * This method is for backward compatibility with generated code which passed an - * InternalDescriptorAssigner. - */ - @Deprecated - public static void internalBuildGeneratedFileFrom( - final String[] descriptorDataParts, - final Class descriptorOuterClass, - final String[] dependencyClassNames, - final String[] dependencyFileNames, - final InternalDescriptorAssigner descriptorAssigner) { - FileDescriptor[] dependencies = - findDescriptors(descriptorOuterClass, dependencyClassNames, dependencyFileNames); - internalBuildGeneratedFileFrom(descriptorDataParts, dependencies, descriptorAssigner); - } - /** * This method is to be called by generated code only. It uses Java reflection to load the * dependencies' descriptors. @@ -508,6 +529,7 @@ public final class Descriptors { } private FileDescriptorProto proto; + private volatile FileOptions options; private final Descriptor[] messageTypes; private final EnumDescriptor[] enumTypes; private final ServiceDescriptor[] services; @@ -586,6 +608,7 @@ public final class Descriptors { /** Create a placeholder FileDescriptor for a message Descriptor. */ FileDescriptor(String packageName, Descriptor message) throws DescriptorValidationException { + this.parent = null; this.pool = new DescriptorPool(new FileDescriptor[0], true); this.proto = FileDescriptorProto.newBuilder() @@ -605,6 +628,79 @@ public final class Descriptors { pool.addSymbol(message); } + public void resolveAllFeaturesImmutable() { + try { + resolveAllFeaturesInternal(); + } catch (DescriptorValidationException e) { + throw new IllegalArgumentException("Invalid features for \"" + proto.getName() + "\".", e); + } + } + + /** + * This method is to be called by generated code only. It resolves features for the descriptor + * and all of its children. + */ + private void resolveAllFeaturesInternal() throws DescriptorValidationException { + if (this.features != null) { + return; + } + + synchronized (this) { + if (this.features != null) { + return; + } + resolveFeatures(proto.getOptions().getFeatures()); + + for (Descriptor messageType : messageTypes) { + messageType.resolveAllFeatures(); + } + + for (EnumDescriptor enumType : enumTypes) { + enumType.resolveAllFeatures(); + } + + for (ServiceDescriptor service : services) { + service.resolveAllFeatures(); + } + + for (FieldDescriptor extension : extensions) { + extension.resolveAllFeatures(); + } + } + } + + @Override + FeatureSet inferLegacyProtoFeatures() { + FeatureSet.Builder features = FeatureSet.newBuilder(); + if (getEdition().getNumber() >= Edition.EDITION_2023.getNumber()) { + return features.build(); + } + + if (getEdition() == Edition.EDITION_PROTO2) { + if (proto.getOptions().getJavaStringCheckUtf8()) { + features.setExtension( + JavaFeaturesProto.java_, + JavaFeatures.newBuilder() + .setUtf8Validation(JavaFeatures.Utf8Validation.VERIFY) + .build()); + } + } + return features.build(); + } + + @Override + boolean hasInferredLegacyProtoFeatures() { + if (getEdition().getNumber() >= Edition.EDITION_2023.getNumber()) { + return false; + } + if (getEdition() == Edition.EDITION_PROTO2) { + if (proto.getOptions().getJavaStringCheckUtf8()) { + return true; + } + } + return false; + } + /** Look up and cross-link all field types, etc. */ private void crossLink() throws DescriptorValidationException { for (final Descriptor messageType : messageTypes) { @@ -628,23 +724,29 @@ public final class Descriptors { * construct the descriptors we have to have parsed the descriptor protos. So, we have to parse * the descriptor protos a second time after constructing the descriptors. */ - private void setProto(final FileDescriptorProto proto) { + private synchronized void setProto(final FileDescriptorProto proto) { this.proto = proto; + this.options = null; + try { + resolveFeatures(proto.getOptions().getFeatures()); - for (int i = 0; i < messageTypes.length; i++) { - messageTypes[i].setProto(proto.getMessageType(i)); - } + for (int i = 0; i < messageTypes.length; i++) { + messageTypes[i].setProto(proto.getMessageType(i)); + } - for (int i = 0; i < enumTypes.length; i++) { - enumTypes[i].setProto(proto.getEnumType(i)); - } + for (int i = 0; i < enumTypes.length; i++) { + enumTypes[i].setProto(proto.getEnumType(i)); + } - for (int i = 0; i < services.length; i++) { - services[i].setProto(proto.getService(i)); - } + for (int i = 0; i < services.length; i++) { + services[i].setProto(proto.getService(i)); + } - for (int i = 0; i < extensions.length; i++) { - extensions[i].setProto(proto.getExtension(i)); + for (int i = 0; i < extensions.length; i++) { + extensions[i].setProto(proto.getExtension(i)); + } + } catch (DescriptorValidationException e) { + throw new IllegalArgumentException("Invalid features for \"" + proto.getName() + "\".", e); } } } @@ -715,7 +817,21 @@ public final class Descriptors { /** Get the {@code MessageOptions}, defined in {@code descriptor.proto}. */ public MessageOptions getOptions() { - return proto.getOptions(); + if (this.options == null) { + MessageOptions strippedOptions = this.proto.getOptions(); + if (strippedOptions.hasFeatures()) { + // Clients should be using feature accessor methods, not accessing features on the + // options + // proto. + strippedOptions = strippedOptions.toBuilder().clearFeatures().build(); + } + synchronized (this) { + if (this.options == null) { + this.options = strippedOptions; + } + } + } + return this.options; } /** Get a list of this message type's fields. */ @@ -850,6 +966,7 @@ public final class Descriptors { private final int index; private DescriptorProto proto; + private volatile MessageOptions options; private final String fullName; private final FileDescriptor file; private final Descriptor containingType; @@ -893,6 +1010,7 @@ public final class Descriptors { // Create a placeholder FileDescriptor to hold this message. this.file = new FileDescriptor(packageName, this); + this.parent = this.file; extensionRangeLowerBounds = new int[] {1}; extensionRangeUpperBounds = new int[] {536870912}; @@ -904,6 +1022,11 @@ public final class Descriptors { final Descriptor parent, final int index) throws DescriptorValidationException { + if (parent == null) { + this.parent = file; + } else { + this.parent = parent; + } this.index = index; this.proto = proto; fullName = computeFullName(file, parent, proto.getName()); @@ -997,6 +1120,32 @@ public final class Descriptors { } } + /** See {@link FileDescriptor#resolveAllFeatures}. */ + private void resolveAllFeatures() throws DescriptorValidationException { + resolveFeatures(proto.getOptions().getFeatures()); + + for (Descriptor nestedType : nestedTypes) { + nestedType.resolveAllFeatures(); + } + + for (EnumDescriptor enumType : enumTypes) { + enumType.resolveAllFeatures(); + } + + // Oneofs must be resolved before any children oneof fields. + for (OneofDescriptor oneof : oneofs) { + oneof.resolveAllFeatures(); + } + + for (FieldDescriptor field : fields) { + field.resolveAllFeatures(); + } + + for (FieldDescriptor extension : extensions) { + extension.resolveAllFeatures(); + } + } + /** Look up and cross-link all field types, etc. */ private void crossLink() throws DescriptorValidationException { for (final Descriptor nestedType : nestedTypes) { @@ -1033,8 +1182,10 @@ public final class Descriptors { } /** See {@link FileDescriptor#setProto}. */ - private void setProto(final DescriptorProto proto) { + private void setProto(final DescriptorProto proto) throws DescriptorValidationException { this.proto = proto; + this.options = null; + resolveFeatures(proto.getOptions().getFeatures()); for (int i = 0; i < nestedTypes.length; i++) { nestedTypes[i].setProto(proto.getNestedType(i)); @@ -1125,7 +1276,7 @@ public final class Descriptors { * FieldDescriptorProto.Type} maps to exactly one Java type. */ public JavaType getJavaType() { - return type.getJavaType(); + return getType().getJavaType(); } /** For internal use only. */ @@ -1142,34 +1293,47 @@ public final class Descriptors { /** Get the field's declared type. */ public Type getType() { + // Override delimited messages as legacy group type. Leaves unresolved messages as-is + // since these are used before feature resolution when parsing java feature set defaults + // (custom options) into unknown fields. + if (type == Type.MESSAGE + && !(messageType != null && messageType.toProto().getOptions().getMapEntry()) + && !(containingType != null && containingType.toProto().getOptions().getMapEntry()) + && this.features != null + && getFeatures().getMessageEncoding() == FeatureSet.MessageEncoding.DELIMITED) { + return Type.GROUP; + } return type; } /** For internal use only. */ @Override public WireFormat.FieldType getLiteType() { - return table[type.ordinal()]; + return table[getType().ordinal()]; } /** For internal use only. */ public boolean needsUtf8Check() { - if (type != Type.STRING) { + if (getType() != Type.STRING) { return false; } - if (getContainingType().getOptions().getMapEntry()) { + if (getContainingType().toProto().getOptions().getMapEntry()) { // Always enforce strict UTF-8 checking for map fields. return true; } - if (getFile().getSyntax() == Syntax.PROTO3) { + if (getFeatures() + .getExtension(JavaFeaturesProto.java_) + .getUtf8Validation() + .equals(JavaFeatures.Utf8Validation.VERIFY)) { return true; } - return getFile().getOptions().getJavaStringCheckUtf8(); + return getFeatures().getUtf8Validation().equals(FeatureSet.Utf8Validation.VERIFY); } public boolean isMapField() { return getType() == Type.MESSAGE && isRepeated() - && getMessageType().getOptions().getMapEntry(); + && getMessageType().toProto().getOptions().getMapEntry(); } // I'm pretty sure values() constructs a new array every time, since there @@ -1179,12 +1343,15 @@ public final class Descriptors { /** Is this field declared required? */ public boolean isRequired() { - return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED; + return getFeatures().getFieldPresence() + == DescriptorProtos.FeatureSet.FieldPresence.LEGACY_REQUIRED; } /** Is this field declared optional? */ public boolean isOptional() { - return proto.getLabel() == FieldDescriptorProto.Label.LABEL_OPTIONAL; + return proto.getLabel() == FieldDescriptorProto.Label.LABEL_OPTIONAL + && getFeatures().getFieldPresence() + != DescriptorProtos.FeatureSet.FieldPresence.LEGACY_REQUIRED; } /** Is this field declared repeated? */ @@ -1202,11 +1369,9 @@ public final class Descriptors { if (!isPackable()) { return false; } - if (getFile().getSyntax() == FileDescriptor.Syntax.PROTO2) { - return getOptions().getPacked(); - } else { - return !getOptions().hasPacked() || getOptions().getPacked(); - } + return getFeatures() + .getRepeatedFieldEncoding() + .equals(FeatureSet.RepeatedFieldEncoding.PACKED); } /** Can this field be packed? That is, is it a repeated primitive field? */ @@ -1234,7 +1399,21 @@ public final class Descriptors { /** Get the {@code FieldOptions}, defined in {@code descriptor.proto}. */ public FieldOptions getOptions() { - return proto.getOptions(); + if (this.options == null) { + FieldOptions strippedOptions = this.proto.getOptions(); + if (strippedOptions.hasFeatures()) { + // Clients should be using feature accessor methods, not accessing features on the + // options + // proto. + strippedOptions = strippedOptions.toBuilder().clearFeatures().build(); + } + synchronized (this) { + if (this.options == null) { + this.options = strippedOptions; + } + } + } + return this.options; } /** Is this field an extension? */ @@ -1264,11 +1443,11 @@ public final class Descriptors { * Returns true if this field was syntactically written with "optional" in the .proto file. * Excludes singular proto3 fields that do not have a label. */ - @Deprecated - public boolean hasOptionalKeyword() { return isProto3Optional - || (file.getSyntax() == Syntax.PROTO2 && isOptional() && getContainingOneof() == null); + || (file.getEdition() == Edition.EDITION_PROTO2 + && isOptional() + && getContainingOneof() == null); } /** @@ -1285,10 +1464,41 @@ public final class Descriptors { if (isRepeated()) { return false; } - return getType() == Type.MESSAGE + return isProto3Optional + || getType() == Type.MESSAGE || getType() == Type.GROUP + || isExtension() || getContainingOneof() != null - || file.getSyntax() == Syntax.PROTO2; + || getFeatures().getFieldPresence() != DescriptorProtos.FeatureSet.FieldPresence.IMPLICIT; + } + + /** + * Returns true if this field is structured like the synthetic field of a proto2 group. This + * allows us to expand our treatment of delimited fields without breaking proto2 files that have + * been upgraded to editions. + */ + boolean isGroupLike() { + if (getType() != Type.GROUP) { + // Groups are always tag-delimited. + return false; + } + + if (!getMessageType().getName().toLowerCase().equals(getName())) { + // Group fields always are always the lowercase type name. + return false; + } + + if (getMessageType().getFile() != getFile()) { + // Groups could only be defined in the same file they're used. + return false; + } + + // Group messages are always defined in the same scope as the field. File level extensions + // will compare NULL == NULL here, which is why the file comparison above is necessary to + // ensure both come from the same file. + return isExtension() + ? getMessageType().getContainingType() == getExtensionScope() + : getMessageType().getContainingType() == getContainingType(); } /** @@ -1360,7 +1570,17 @@ public final class Descriptors { * handling quirks. */ public boolean legacyEnumFieldTreatedAsClosed() { - return getType() == Type.ENUM && getFile().getSyntax() == Syntax.PROTO2; + // Don't check JavaFeaturesProto extension for files without dependencies. + // This is especially important for descriptor.proto since getting the JavaFeaturesProto + // extension itself involves calling legacyEnumFieldTreatedAsClosed() which would otherwise + // infinite loop. + if (getFile().getDependencies().isEmpty()) { + return getType() == Type.ENUM && enumType.isClosed(); + } + + return getType() == Type.ENUM + && (getFeatures().getExtension(JavaFeaturesProto.java_).getLegacyClosedEnum() + || enumType.isClosed()); } /** @@ -1389,6 +1609,7 @@ public final class Descriptors { private final int index; private FieldDescriptorProto proto; + private volatile FieldOptions options; private final String fullName; private String jsonName; private final FileDescriptor file; @@ -1507,6 +1728,7 @@ public final class Descriptors { final int index, final boolean isExtension) throws DescriptorValidationException { + this.parent = parent; this.index = index; this.proto = proto; fullName = computeFullName(file, parent, proto.getName()); @@ -1532,6 +1754,7 @@ public final class Descriptors { extensionScope = parent; } else { extensionScope = null; + this.parent = file; } if (proto.hasOneofIndex()) { @@ -1555,6 +1778,7 @@ public final class Descriptors { } containingOneof = parent.getOneofs().get(proto.getOneofIndex()); containingOneof.fieldCount++; + this.parent = containingOneof; } else { containingOneof = null; } @@ -1564,6 +1788,79 @@ public final class Descriptors { file.pool.addSymbol(this); } + /** See {@link FileDescriptor#resolveAllFeatures}. */ + private void resolveAllFeatures() throws DescriptorValidationException { + resolveFeatures(proto.getOptions().getFeatures()); + } + + @Override + FeatureSet inferLegacyProtoFeatures() { + FeatureSet.Builder features = FeatureSet.newBuilder(); + if (getFile().getEdition().getNumber() >= Edition.EDITION_2023.getNumber()) { + return features.build(); + } + + if (proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED) { + features.setFieldPresence(FeatureSet.FieldPresence.LEGACY_REQUIRED); + } + + if (proto.getType() == FieldDescriptorProto.Type.TYPE_GROUP) { + features.setMessageEncoding(FeatureSet.MessageEncoding.DELIMITED); + } + + if (getFile().getEdition() == Edition.EDITION_PROTO2 && proto.getOptions().getPacked()) { + features.setRepeatedFieldEncoding(FeatureSet.RepeatedFieldEncoding.PACKED); + } + + if (getFile().getEdition() == Edition.EDITION_PROTO3) { + if (proto.getOptions().hasPacked() && !proto.getOptions().getPacked()) { + features.setRepeatedFieldEncoding(FeatureSet.RepeatedFieldEncoding.EXPANDED); + } + + } + return features.build(); + } + + @Override + boolean hasInferredLegacyProtoFeatures() { + if (getFile().getEdition().getNumber() >= Edition.EDITION_2023.getNumber()) { + return false; + } + + if (proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED) { + return true; + } + + if (proto.getType() == FieldDescriptorProto.Type.TYPE_GROUP) { + return true; + } + + if (proto.getOptions().getPacked()) { + return true; + } + + if (getFile().getEdition() == Edition.EDITION_PROTO3) { + if (proto.getOptions().hasPacked() && !proto.getOptions().getPacked()) { + return true; + } + + } + return false; + } + + @Override + void validateFeatures() throws DescriptorValidationException { + if (containingType != null + && containingType.toProto().getOptions().getMessageSetWireFormat()) { + if (isExtension()) { + if (!isOptional() || getType() != Type.MESSAGE) { + throw new DescriptorValidationException( + this, "Extensions of MessageSets must be optional messages."); + } + } + } + } + /** Look up and cross-link all field types, etc. */ private void crossLink() throws DescriptorValidationException { if (proto.hasExtendee()) { @@ -1604,7 +1901,9 @@ public final class Descriptors { } } - if (getJavaType() == JavaType.MESSAGE) { + // Use raw type since inferred type considers messageType which may not be fully cross + // linked yet. + if (type.getJavaType() == JavaType.MESSAGE) { if (!(typeDescriptor instanceof Descriptor)) { throw new DescriptorValidationException( this, '\"' + proto.getTypeName() + "\" is not a message type."); @@ -1614,7 +1913,7 @@ public final class Descriptors { if (proto.hasDefaultValue()) { throw new DescriptorValidationException(this, "Messages can't have default values."); } - } else if (getJavaType() == JavaType.ENUM) { + } else if (type.getJavaType() == JavaType.ENUM) { if (!(typeDescriptor instanceof EnumDescriptor)) { throw new DescriptorValidationException( this, '\"' + proto.getTypeName() + "\" is not an enum type."); @@ -1624,7 +1923,7 @@ public final class Descriptors { throw new DescriptorValidationException(this, "Field with primitive type has type_name."); } } else { - if (getJavaType() == JavaType.MESSAGE || getJavaType() == JavaType.ENUM) { + if (type.getJavaType() == JavaType.MESSAGE || type.getJavaType() == JavaType.ENUM) { throw new DescriptorValidationException( this, "Field with message or enum type missing type_name."); } @@ -1645,7 +1944,7 @@ public final class Descriptors { } try { - switch (getType()) { + switch (type) { case INT32: case SINT32: case SFIXED32: @@ -1720,7 +2019,7 @@ public final class Descriptors { if (isRepeated()) { defaultValue = Collections.emptyList(); } else { - switch (getJavaType()) { + switch (type.getJavaType()) { case ENUM: // We guarantee elsewhere that an enum type always has at least // one possible value. @@ -1730,28 +2029,18 @@ public final class Descriptors { defaultValue = null; break; default: - defaultValue = getJavaType().defaultDefault; + defaultValue = type.getJavaType().defaultDefault; break; } } } - - if (containingType != null && containingType.getOptions().getMessageSetWireFormat()) { - if (isExtension()) { - if (!isOptional() || getType() != Type.MESSAGE) { - throw new DescriptorValidationException( - this, "Extensions of MessageSets must be optional messages."); - } - } else { - throw new DescriptorValidationException( - this, "MessageSets cannot have fields, only extensions."); - } - } } /** See {@link FileDescriptor#setProto}. */ - private void setProto(final FieldDescriptorProto proto) { + private void setProto(final FieldDescriptorProto proto) throws DescriptorValidationException { this.proto = proto; + this.options = null; + resolveFeatures(proto.getOptions().getFeatures()); } /** For internal use only. This is to satisfy the FieldDescriptorLite interface. */ @@ -1830,7 +2119,7 @@ public final class Descriptors { * handling quirks. */ public boolean isClosed() { - return getFile().getSyntax() != Syntax.PROTO3; + return getFeatures().getEnumType() == DescriptorProtos.FeatureSet.EnumType.CLOSED; } /** If this is a nested type, get the outer descriptor, otherwise null. */ @@ -1840,7 +2129,21 @@ public final class Descriptors { /** Get the {@code EnumOptions}, defined in {@code descriptor.proto}. */ public EnumOptions getOptions() { - return proto.getOptions(); + if (this.options == null) { + EnumOptions strippedOptions = this.proto.getOptions(); + if (strippedOptions.hasFeatures()) { + // Clients should be using feature accessor methods, not accessing features on the + // options + // proto. + strippedOptions = strippedOptions.toBuilder().clearFeatures().build(); + } + synchronized (this) { + if (this.options == null) { + this.options = strippedOptions; + } + } + } + return this.options; } /** Get a list of defined values for this enum. */ @@ -1951,6 +2254,7 @@ public final class Descriptors { private final int index; private EnumDescriptorProto proto; + private volatile EnumOptions options; private final String fullName; private final FileDescriptor file; private final Descriptor containingType; @@ -1966,6 +2270,11 @@ public final class Descriptors { final Descriptor parent, final int index) throws DescriptorValidationException { + if (parent == null) { + this.parent = file; + } else { + this.parent = parent; + } this.index = index; this.proto = proto; fullName = computeFullName(file, parent, proto.getName()); @@ -1999,9 +2308,20 @@ public final class Descriptors { file.pool.addSymbol(this); } + /** See {@link FileDescriptor#resolveAllFeatures}. */ + private void resolveAllFeatures() throws DescriptorValidationException { + resolveFeatures(proto.getOptions().getFeatures()); + + for (EnumValueDescriptor value : values) { + value.resolveAllFeatures(); + } + } + /** See {@link FileDescriptor#setProto}. */ - private void setProto(final EnumDescriptorProto proto) { + private void setProto(final EnumDescriptorProto proto) throws DescriptorValidationException { this.proto = proto; + this.options = null; + resolveFeatures(proto.getOptions().getFeatures()); for (int i = 0; i < values.length; i++) { values[i].setProto(proto.getValue(i)); @@ -2016,6 +2336,7 @@ public final class Descriptors { * number. In generated Java code, all values with the same number after the first become aliases * of the first. However, they still have independent EnumValueDescriptors. */ + @SuppressWarnings("ShouldNotSubclass") public static final class EnumValueDescriptor extends GenericDescriptor implements Internal.EnumLite { static final Comparator BY_NUMBER = @@ -2089,11 +2410,26 @@ public final class Descriptors { /** Get the {@code EnumValueOptions}, defined in {@code descriptor.proto}. */ public EnumValueOptions getOptions() { - return proto.getOptions(); + if (this.options == null) { + EnumValueOptions strippedOptions = this.proto.getOptions(); + if (strippedOptions.hasFeatures()) { + // Clients should be using feature accessor methods, not accessing features on the + // options + // proto. + strippedOptions = strippedOptions.toBuilder().clearFeatures().build(); + } + synchronized (this) { + if (this.options == null) { + this.options = strippedOptions; + } + } + } + return this.options; } private final int index; private EnumValueDescriptorProto proto; + private volatile EnumValueOptions options; private final String fullName; private final EnumDescriptor type; @@ -2103,12 +2439,11 @@ public final class Descriptors { final EnumDescriptor parent, final int index) throws DescriptorValidationException { + this.parent = parent; this.index = index; this.proto = proto; - type = parent; - - fullName = parent.getFullName() + '.' + proto.getName(); - + this.type = parent; + this.fullName = parent.getFullName() + '.' + proto.getName(); file.pool.addSymbol(this); } @@ -2117,6 +2452,7 @@ public final class Descriptors { String name = "UNKNOWN_ENUM_VALUE_" + parent.getName() + "_" + number; EnumValueDescriptorProto proto = EnumValueDescriptorProto.newBuilder().setName(name).setNumber(number).build(); + this.parent = parent; this.index = -1; this.proto = proto; this.type = parent; @@ -2125,9 +2461,17 @@ public final class Descriptors { // Don't add this descriptor into pool. } + /** See {@link FileDescriptor#resolveAllFeatures}. */ + private void resolveAllFeatures() throws DescriptorValidationException { + resolveFeatures(proto.getOptions().getFeatures()); + } + /** See {@link FileDescriptor#setProto}. */ - private void setProto(final EnumValueDescriptorProto proto) { + private void setProto(final EnumValueDescriptorProto proto) + throws DescriptorValidationException { this.proto = proto; + this.options = null; + resolveFeatures(proto.getOptions().getFeatures()); } } @@ -2172,7 +2516,21 @@ public final class Descriptors { /** Get the {@code ServiceOptions}, defined in {@code descriptor.proto}. */ public ServiceOptions getOptions() { - return proto.getOptions(); + if (this.options == null) { + ServiceOptions strippedOptions = this.proto.getOptions(); + if (strippedOptions.hasFeatures()) { + // Clients should be using feature accessor methods, not accessing features on the + // options + // proto. + strippedOptions = strippedOptions.toBuilder().clearFeatures().build(); + } + synchronized (this) { + if (this.options == null) { + this.options = strippedOptions; + } + } + } + return this.options; } /** Get a list of methods for this service. */ @@ -2197,6 +2555,7 @@ public final class Descriptors { private final int index; private ServiceDescriptorProto proto; + private volatile ServiceOptions options; private final String fullName; private final FileDescriptor file; private MethodDescriptor[] methods; @@ -2204,6 +2563,7 @@ public final class Descriptors { private ServiceDescriptor( final ServiceDescriptorProto proto, final FileDescriptor file, final int index) throws DescriptorValidationException { + this.parent = file; this.index = index; this.proto = proto; fullName = computeFullName(file, null, proto.getName()); @@ -2217,6 +2577,15 @@ public final class Descriptors { file.pool.addSymbol(this); } + /** See {@link FileDescriptor#resolveAllFeatures}. */ + private void resolveAllFeatures() throws DescriptorValidationException { + resolveFeatures(proto.getOptions().getFeatures()); + + for (MethodDescriptor method : methods) { + method.resolveAllFeatures(); + } + } + private void crossLink() throws DescriptorValidationException { for (final MethodDescriptor method : methods) { method.crossLink(); @@ -2224,8 +2593,10 @@ public final class Descriptors { } /** See {@link FileDescriptor#setProto}. */ - private void setProto(final ServiceDescriptorProto proto) { + private void setProto(final ServiceDescriptorProto proto) throws DescriptorValidationException { this.proto = proto; + this.options = null; + resolveFeatures(proto.getOptions().getFeatures()); for (int i = 0; i < methods.length; i++) { methods[i].setProto(proto.getMethod(i)); @@ -2299,11 +2670,26 @@ public final class Descriptors { /** Get the {@code MethodOptions}, defined in {@code descriptor.proto}. */ public MethodOptions getOptions() { - return proto.getOptions(); + if (this.options == null) { + MethodOptions strippedOptions = this.proto.getOptions(); + if (strippedOptions.hasFeatures()) { + // Clients should be using feature accessor methods, not accessing features on the + // options + // proto. + strippedOptions = strippedOptions.toBuilder().clearFeatures().build(); + } + synchronized (this) { + if (this.options == null) { + this.options = strippedOptions; + } + } + } + return this.options; } private final int index; private MethodDescriptorProto proto; + private volatile MethodOptions options; private final String fullName; private final FileDescriptor file; private final ServiceDescriptor service; @@ -2318,6 +2704,7 @@ public final class Descriptors { final ServiceDescriptor parent, final int index) throws DescriptorValidationException { + this.parent = parent; this.index = index; this.proto = proto; this.file = file; @@ -2328,6 +2715,11 @@ public final class Descriptors { file.pool.addSymbol(this); } + /** See {@link FileDescriptor#resolveAllFeatures}. */ + private void resolveAllFeatures() throws DescriptorValidationException { + resolveFeatures(proto.getOptions().getFeatures()); + } + private void crossLink() throws DescriptorValidationException { final GenericDescriptor input = getFile() @@ -2351,8 +2743,10 @@ public final class Descriptors { } /** See {@link FileDescriptor#setProto}. */ - private void setProto(final MethodDescriptorProto proto) { + private void setProto(final MethodDescriptorProto proto) throws DescriptorValidationException { this.proto = proto; + this.options = null; + resolveFeatures(proto.getOptions().getFeatures()); } } @@ -2379,7 +2773,6 @@ public final class Descriptors { * DescriptorPool}. */ public abstract static class GenericDescriptor { - // Private constructor to prevent subclasses outside of com.google.protobuf.Descriptors private GenericDescriptor() {} @@ -2390,6 +2783,79 @@ public final class Descriptors { public abstract String getFullName(); public abstract FileDescriptor getFile(); + + void resolveFeatures(FeatureSet unresolvedFeatures) throws DescriptorValidationException { + if (this.parent != null + && unresolvedFeatures.equals(FeatureSet.getDefaultInstance()) + && !hasInferredLegacyProtoFeatures()) { + this.features = this.parent.features; + validateFeatures(); + return; + } + + // Java features from a custom pool (i.e. buildFrom) may end up in unknown fields or + // use a different descriptor from the generated pool used by the Java runtime. + boolean hasPossibleCustomJavaFeature = false; + for (FieldDescriptor f : unresolvedFeatures.getExtensionFields().keySet()) { + if (f.getNumber() == JavaFeaturesProto.java_.getNumber() + && f != JavaFeaturesProto.java_.getDescriptor()) { + hasPossibleCustomJavaFeature = true; + continue; + } + } + boolean hasPossibleUnknownJavaFeature = + !unresolvedFeatures.getUnknownFields().isEmpty() + && unresolvedFeatures + .getUnknownFields() + .hasField(JavaFeaturesProto.java_.getNumber()); + if (hasPossibleCustomJavaFeature || hasPossibleUnknownJavaFeature) { + ExtensionRegistry registry = ExtensionRegistry.newInstance(); + registry.add(JavaFeaturesProto.java_); + ByteString bytes = unresolvedFeatures.toByteString(); + try { + unresolvedFeatures = FeatureSet.parseFrom(bytes, registry); + } catch (InvalidProtocolBufferException e) { + throw new DescriptorValidationException( + this, "Failed to parse features with Java feature extension registry.", e); + } + } + + FeatureSet.Builder features; + if (this.parent == null) { + Edition edition = getFile().getEdition(); + features = getEditionDefaults(edition).toBuilder(); + } else { + features = this.parent.features.toBuilder(); + } + features.mergeFrom(inferLegacyProtoFeatures()); + features.mergeFrom(unresolvedFeatures); + this.features = internFeatures(features.build()); + validateFeatures(); + } + + FeatureSet inferLegacyProtoFeatures() { + return FeatureSet.getDefaultInstance(); + } + + boolean hasInferredLegacyProtoFeatures() { + return false; + } + + void validateFeatures() throws DescriptorValidationException {} + + FeatureSet getFeatures() { + // TODO: Remove lazy resolution of unresolved features for legacy syntax for + // compatibility with older <4.26.x gencode in the next breaking release. + if (this.features == null + && (getFile().getEdition() == Edition.EDITION_PROTO2 + || getFile().getEdition() == Edition.EDITION_PROTO3)) { + getFile().resolveAllFeaturesImmutable(); + } + return this.features; + } + + GenericDescriptor parent; + volatile FeatureSet features; } /** Thrown when building descriptors fails because the source DescriptorProtos are not valid. */ @@ -2817,7 +3283,21 @@ public final class Descriptors { } public OneofOptions getOptions() { - return proto.getOptions(); + if (this.options == null) { + OneofOptions strippedOptions = this.proto.getOptions(); + if (strippedOptions.hasFeatures()) { + // Clients should be using feature accessor methods, not accessing features on the + // options + // proto. + strippedOptions = strippedOptions.toBuilder().clearFeatures().build(); + } + synchronized (this) { + if (this.options == null) { + this.options = strippedOptions; + } + } + } + return this.options; } /** Get a list of this message type's fields. */ @@ -2834,14 +3314,19 @@ public final class Descriptors { return proto; } - @Deprecated - public boolean isSynthetic() { return fields.length == 1 && fields[0].isProto3Optional; } - private void setProto(final OneofDescriptorProto proto) { + /** See {@link FileDescriptor#resolveAllFeatures}. */ + private void resolveAllFeatures() throws DescriptorValidationException { + resolveFeatures(proto.getOptions().getFeatures()); + } + + private void setProto(final OneofDescriptorProto proto) throws DescriptorValidationException { this.proto = proto; + this.options = null; + resolveFeatures(proto.getOptions().getFeatures()); } private OneofDescriptor( @@ -2849,6 +3334,7 @@ public final class Descriptors { final FileDescriptor file, final Descriptor parent, final int index) { + this.parent = parent; this.proto = proto; fullName = computeFullName(file, parent, proto.getName()); this.file = file; @@ -2860,6 +3346,7 @@ public final class Descriptors { private final int index; private OneofDescriptorProto proto; + private volatile OneofOptions options; private final String fullName; private final FileDescriptor file; diff --git a/java/core/src/main/java/com/google/protobuf/ExtensionRegistryFactory.java b/java/core/src/main/java/com/google/protobuf/ExtensionRegistryFactory.java index 91aae2bc6..b9fa9ed74 100644 --- a/java/core/src/main/java/com/google/protobuf/ExtensionRegistryFactory.java +++ b/java/core/src/main/java/com/google/protobuf/ExtensionRegistryFactory.java @@ -48,7 +48,8 @@ final class ExtensionRegistryFactory { } static boolean isFullRegistry(ExtensionRegistryLite registry) { - return EXTENSION_REGISTRY_CLASS != null + return !Protobuf.assumeLiteRuntime + && EXTENSION_REGISTRY_CLASS != null && EXTENSION_REGISTRY_CLASS.isAssignableFrom(registry.getClass()); } diff --git a/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java b/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java index 18d14bf37..6529071e2 100644 --- a/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java +++ b/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java @@ -53,10 +53,6 @@ public class ExtensionRegistryLite { // applications. Need to support this feature on smaller granularity. private static volatile boolean eagerlyParseMessageSets = false; - // short circuit the ExtensionRegistryFactory via assumevalues trickery - @SuppressWarnings("JavaOptionalSuggestions") - private static boolean doFullRuntimeInheritanceCheck = true; - // Visible for testing. static final String EXTENSION_CLASS_NAME = "com.google.protobuf.Extension"; @@ -88,9 +84,9 @@ public class ExtensionRegistryLite { * available. */ public static ExtensionRegistryLite newInstance() { - return doFullRuntimeInheritanceCheck - ? ExtensionRegistryFactory.create() - : new ExtensionRegistryLite(); + return Protobuf.assumeLiteRuntime + ? new ExtensionRegistryLite() + : ExtensionRegistryFactory.create(); } private static volatile ExtensionRegistryLite emptyRegistry; @@ -100,7 +96,7 @@ public class ExtensionRegistryLite { * ExtensionRegistry} (if the full (non-Lite) proto libraries are available). */ public static ExtensionRegistryLite getEmptyRegistry() { - if (!doFullRuntimeInheritanceCheck) { + if (Protobuf.assumeLiteRuntime) { return EMPTY_REGISTRY_LITE; } ExtensionRegistryLite result = emptyRegistry; @@ -148,7 +144,7 @@ public class ExtensionRegistryLite { if (GeneratedMessageLite.GeneratedExtension.class.isAssignableFrom(extension.getClass())) { add((GeneratedMessageLite.GeneratedExtension) extension); } - if (doFullRuntimeInheritanceCheck && ExtensionRegistryFactory.isFullRegistry(this)) { + if (!Protobuf.assumeLiteRuntime && ExtensionRegistryFactory.isFullRegistry(this)) { try { this.getClass().getMethod("add", ExtensionClassHolder.INSTANCE).invoke(this, extension); } catch (Exception e) { diff --git a/java/core/src/main/java/com/google/protobuf/ExtensionSchemaFull.java b/java/core/src/main/java/com/google/protobuf/ExtensionSchemaFull.java index 9133e7a56..f01884a2c 100644 --- a/java/core/src/main/java/com/google/protobuf/ExtensionSchemaFull.java +++ b/java/core/src/main/java/com/google/protobuf/ExtensionSchemaFull.java @@ -23,7 +23,7 @@ final class ExtensionSchemaFull extends ExtensionSchema { private static long getExtensionsFieldOffset() { try { - Field field = GeneratedMessageV3.ExtendableMessage.class.getDeclaredField("extensions"); + Field field = GeneratedMessage.ExtendableMessage.class.getDeclaredField("extensions"); return UnsafeUtil.objectFieldOffset(field); } catch (Throwable e) { throw new IllegalStateException("Unable to lookup extension field offset"); @@ -32,7 +32,7 @@ final class ExtensionSchemaFull extends ExtensionSchema { @Override boolean hasExtensions(MessageLite prototype) { - return prototype instanceof GeneratedMessageV3.ExtendableMessage; + return prototype instanceof GeneratedMessage.ExtendableMessage; } @Override diff --git a/java/core/src/main/java/com/google/protobuf/ExtensionSchemas.java b/java/core/src/main/java/com/google/protobuf/ExtensionSchemas.java index cd8c852e9..059fdd011 100644 --- a/java/core/src/main/java/com/google/protobuf/ExtensionSchemas.java +++ b/java/core/src/main/java/com/google/protobuf/ExtensionSchemas.java @@ -13,6 +13,9 @@ final class ExtensionSchemas { private static final ExtensionSchema FULL_SCHEMA = loadSchemaForFullRuntime(); private static ExtensionSchema loadSchemaForFullRuntime() { + if (Protobuf.assumeLiteRuntime) { + return null; + } try { Class clazz = Class.forName("com.google.protobuf.ExtensionSchemaFull"); return (ExtensionSchema) clazz.getDeclaredConstructor().newInstance(); @@ -31,4 +34,6 @@ final class ExtensionSchemas { } return FULL_SCHEMA; } + + private ExtensionSchemas() {} } diff --git a/java/core/src/main/java/com/google/protobuf/FieldSet.java b/java/core/src/main/java/com/google/protobuf/FieldSet.java index d2a5d48fb..f536be264 100644 --- a/java/core/src/main/java/com/google/protobuf/FieldSet.java +++ b/java/core/src/main/java/com/google/protobuf/FieldSet.java @@ -49,21 +49,19 @@ final class FieldSet> { MessageLite.Builder internalMergeFrom(MessageLite.Builder to, MessageLite from); } - private static final int DEFAULT_FIELD_MAP_ARRAY_SIZE = 16; - private final SmallSortedMap fields; private boolean isImmutable; private boolean hasLazyField; /** Construct a new FieldSet. */ private FieldSet() { - this.fields = SmallSortedMap.newFieldMap(DEFAULT_FIELD_MAP_ARRAY_SIZE); + this.fields = SmallSortedMap.newFieldMap(); } /** Construct an empty FieldSet. This is only used to initialize DEFAULT_INSTANCE. */ @SuppressWarnings("unused") private FieldSet(final boolean dummy) { - this(SmallSortedMap.newFieldMap(0)); + this(SmallSortedMap.newFieldMap()); makeImmutable(); } @@ -80,7 +78,7 @@ final class FieldSet> { /** Get an immutable empty FieldSet. */ @SuppressWarnings("unchecked") public static > FieldSet emptySet() { - return DEFAULT_INSTANCE; + return (FieldSet) DEFAULT_INSTANCE; } /** Construct a new Builder. */ @@ -88,8 +86,7 @@ final class FieldSet> { return new Builder(); } - @SuppressWarnings("rawtypes") - private static final FieldSet DEFAULT_INSTANCE = new FieldSet(true); + private static final FieldSet DEFAULT_INSTANCE = new FieldSet<>(true); /** Returns {@code true} if empty, {@code false} otherwise. */ boolean isEmpty() { @@ -101,7 +98,8 @@ final class FieldSet> { if (isImmutable) { return; } - for (int i = 0; i < fields.getNumArrayEntries(); ++i) { + int n = fields.getNumArrayEntries(); // Optimisation: hoist out of hot loop. + for (int i = 0; i < n; ++i) { Entry entry = fields.getArrayEntryAt(i); if (entry.getValue() instanceof GeneratedMessageLite) { ((GeneratedMessageLite) entry.getValue()).makeImmutable(); @@ -151,7 +149,8 @@ final class FieldSet> { // We can't just call fields.clone because List objects in the map // should not be shared. FieldSet clone = FieldSet.newFieldSet(); - for (int i = 0; i < fields.getNumArrayEntries(); i++) { + int n = fields.getNumArrayEntries(); // Optimisation: hoist out of hot loop. + for (int i = 0; i < n; i++) { Map.Entry entry = fields.getArrayEntryAt(i); clone.setField(entry.getKey(), entry.getValue()); } @@ -173,7 +172,8 @@ final class FieldSet> { /** Get a simple map containing all the fields. */ public Map getAllFields() { if (hasLazyField) { - SmallSortedMap result = cloneAllFieldsMap(fields, /* copyList */ false); + SmallSortedMap result = + cloneAllFieldsMap(fields, /* copyList= */ false, /* resolveLazyFields= */ true); if (fields.isImmutable()) { result.makeImmutable(); } @@ -183,22 +183,23 @@ final class FieldSet> { } private static > SmallSortedMap cloneAllFieldsMap( - SmallSortedMap fields, boolean copyList) { - SmallSortedMap result = SmallSortedMap.newFieldMap(DEFAULT_FIELD_MAP_ARRAY_SIZE); - for (int i = 0; i < fields.getNumArrayEntries(); i++) { - cloneFieldEntry(result, fields.getArrayEntryAt(i), copyList); + SmallSortedMap fields, boolean copyList, boolean resolveLazyFields) { + SmallSortedMap result = SmallSortedMap.newFieldMap(); + int n = fields.getNumArrayEntries(); // Optimisation: hoist out of hot loop. + for (int i = 0; i < n; i++) { + cloneFieldEntry(result, fields.getArrayEntryAt(i), copyList, resolveLazyFields); } for (Map.Entry entry : fields.getOverflowEntries()) { - cloneFieldEntry(result, entry, copyList); + cloneFieldEntry(result, entry, copyList, resolveLazyFields); } return result; } private static > void cloneFieldEntry( - Map map, Map.Entry entry, boolean copyList) { + Map map, Map.Entry entry, boolean copyList, boolean resolveLazyFields) { T key = entry.getKey(); Object value = entry.getValue(); - if (value instanceof LazyField) { + if (resolveLazyFields && value instanceof LazyField) { map.put(key, ((LazyField) value).getValue()); } else if (copyList && value instanceof List) { map.put(key, new ArrayList<>((List) value)); @@ -212,6 +213,10 @@ final class FieldSet> { * library as it is not protected from mutation when fields is not immutable. */ public Iterator> iterator() { + // Avoid allocation in the common case of empty FieldSet. + if (isEmpty()) { + return Collections.emptyIterator(); + } if (hasLazyField) { return new LazyIterator(fields.entrySet().iterator()); } @@ -224,6 +229,10 @@ final class FieldSet> { * fields is not immutable. */ Iterator> descendingIterator() { + // Avoid an allocation in the common case of empty FieldSet. + if (isEmpty()) { + return Collections.emptyIterator(); + } if (hasLazyField) { return new LazyIterator(fields.descendingEntrySet().iterator()); } @@ -419,7 +428,8 @@ final class FieldSet> { * caller to check that all required fields are present. */ public boolean isInitialized() { - for (int i = 0; i < fields.getNumArrayEntries(); i++) { + int n = fields.getNumArrayEntries(); // Optimisation: hoist out of hot loop. + for (int i = 0; i < n; i++) { if (!isInitialized(fields.getArrayEntryAt(i))) { return false; } @@ -432,12 +442,17 @@ final class FieldSet> { return true; } + // Avoid iterator allocation. + @SuppressWarnings({"ForeachList", "ForeachListWithUserVar"}) private static > boolean isInitialized( final Map.Entry entry) { final T descriptor = entry.getKey(); if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { if (descriptor.isRepeated()) { - for (final Object element : (List) entry.getValue()) { + List list = (List) entry.getValue(); + int listSize = list.size(); + for (int i = 0; i < listSize; i++) { + Object element = list.get(i); if (!isMessageFieldValueInitialized(element)) { return false; } @@ -477,7 +492,8 @@ final class FieldSet> { /** Like {@link Message.Builder#mergeFrom(Message)}, but merges from another {@link FieldSet}. */ public void mergeFrom(final FieldSet other) { - for (int i = 0; i < other.fields.getNumArrayEntries(); i++) { + int n = other.fields.getNumArrayEntries(); // Optimisation: hoist out of hot loop. + for (int i = 0; i < n; i++) { mergeFromField(other.fields.getArrayEntryAt(i)); } for (final Map.Entry entry : other.fields.getOverflowEntries()) { @@ -500,11 +516,12 @@ final class FieldSet> { private void mergeFromField(final Map.Entry entry) { final T descriptor = entry.getKey(); Object otherValue = entry.getValue(); - if (otherValue instanceof LazyField) { - otherValue = ((LazyField) otherValue).getValue(); - } + boolean isLazyField = otherValue instanceof LazyField; if (descriptor.isRepeated()) { + if (isLazyField) { + throw new IllegalStateException("Lazy fields can not be repeated"); + } Object value = getField(descriptor); if (value == null) { value = new ArrayList<>(); @@ -516,9 +533,17 @@ final class FieldSet> { } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { Object value = getField(descriptor); if (value == null) { + // New field. fields.put(descriptor, cloneIfMutable(otherValue)); + if (isLazyField) { + hasLazyField = true; + } } else { - // Merge the messages. + // There is an existing field. Need to merge the messages. + if (otherValue instanceof LazyField) { + // Extract the actual value for lazy fields. + otherValue = ((LazyField) otherValue).getValue(); + } value = descriptor .internalMergeFrom(((MessageLite) value).toBuilder(), (MessageLite) otherValue) @@ -526,6 +551,9 @@ final class FieldSet> { fields.put(descriptor, value); } } else { + if (isLazyField) { + throw new IllegalStateException("Lazy fields must be message-valued"); + } fields.put(descriptor, cloneIfMutable(otherValue)); } } @@ -552,7 +580,8 @@ final class FieldSet> { /** See {@link Message#writeTo(CodedOutputStream)}. */ public void writeTo(final CodedOutputStream output) throws IOException { - for (int i = 0; i < fields.getNumArrayEntries(); i++) { + int n = fields.getNumArrayEntries(); // Optimisation: hoist out of hot loop. + for (int i = 0; i < n; i++) { final Map.Entry entry = fields.getArrayEntryAt(i); writeField(entry.getKey(), entry.getValue(), output); } @@ -563,7 +592,8 @@ final class FieldSet> { /** Like {@link #writeTo} but uses MessageSet wire format. */ public void writeMessageSetTo(final CodedOutputStream output) throws IOException { - for (int i = 0; i < fields.getNumArrayEntries(); i++) { + int n = fields.getNumArrayEntries(); // Optimisation: hoist out of hot loop. + for (int i = 0; i < n; i++) { writeMessageSetTo(fields.getArrayEntryAt(i), output); } for (final Map.Entry entry : fields.getOverflowEntries()) { @@ -697,6 +727,8 @@ final class FieldSet> { } /** Write a single field. */ + // Avoid iterator allocation. + @SuppressWarnings({"ForeachList", "ForeachListWithUserVar"}) public static void writeField( final FieldDescriptorLite descriptor, final Object value, final CodedOutputStream output) throws IOException { @@ -704,6 +736,7 @@ final class FieldSet> { int number = descriptor.getNumber(); if (descriptor.isRepeated()) { final List valueList = (List) value; + int valueListSize = valueList.size(); if (descriptor.isPacked()) { if (valueList.isEmpty()) { // The tag should not be written for empty packed fields. @@ -712,16 +745,19 @@ final class FieldSet> { output.writeTag(number, WireFormat.WIRETYPE_LENGTH_DELIMITED); // Compute the total data size so the length can be written. int dataSize = 0; - for (final Object element : valueList) { + for (int i = 0; i < valueListSize; i++) { + Object element = valueList.get(i); dataSize += computeElementSizeNoTag(type, element); } output.writeUInt32NoTag(dataSize); // Write the data itself, without any tags. - for (final Object element : valueList) { + for (int i = 0; i < valueListSize; i++) { + Object element = valueList.get(i); writeElementNoTag(output, type, element); } } else { - for (final Object element : valueList) { + for (int i = 0; i < valueListSize; i++) { + Object element = valueList.get(i); writeElement(output, type, number, element); } } @@ -740,7 +776,8 @@ final class FieldSet> { */ public int getSerializedSize() { int size = 0; - for (int i = 0; i < fields.getNumArrayEntries(); i++) { + int n = fields.getNumArrayEntries(); // Optimisation: hoist out of hot loop. + for (int i = 0; i < n; i++) { final Map.Entry entry = fields.getArrayEntryAt(i); size += computeFieldSize(entry.getKey(), entry.getValue()); } @@ -753,7 +790,8 @@ final class FieldSet> { /** Like {@link #getSerializedSize} but uses MessageSet wire format. */ public int getMessageSetSerializedSize() { int size = 0; - for (int i = 0; i < fields.getNumArrayEntries(); i++) { + int n = fields.getNumArrayEntries(); // Optimisation: hoist out of hot loop. + for (int i = 0; i < n; i++) { size += getMessageSetSerializedSize(fields.getArrayEntryAt(i)); } for (final Map.Entry entry : fields.getOverflowEntries()) { @@ -870,17 +908,21 @@ final class FieldSet> { } /** Compute the number of bytes needed to encode a particular field. */ + // Avoid iterator allocation. + @SuppressWarnings({"ForeachList", "ForeachListWithUserVar"}) public static int computeFieldSize(final FieldDescriptorLite descriptor, final Object value) { WireFormat.FieldType type = descriptor.getLiteType(); int number = descriptor.getNumber(); if (descriptor.isRepeated()) { List valueList = (List) value; + int valueListSize = valueList.size(); if (descriptor.isPacked()) { if (valueList.isEmpty()) { return 0; } int dataSize = 0; - for (final Object element : valueList) { + for (int i = 0; i < valueListSize; i++) { + Object element = valueList.get(i); dataSize += computeElementSizeNoTag(type, element); } return dataSize @@ -888,7 +930,8 @@ final class FieldSet> { + CodedOutputStream.computeUInt32SizeNoTag(dataSize); } else { int size = 0; - for (final Object element : valueList) { + for (int i = 0; i < valueListSize; i++) { + Object element = valueList.get(i); size += computeElementSize(type, number, element); } return size; @@ -910,7 +953,7 @@ final class FieldSet> { private boolean hasNestedBuilders; private Builder() { - this(SmallSortedMap.newFieldMap(DEFAULT_FIELD_MAP_ARRAY_SIZE)); + this(SmallSortedMap.newFieldMap()); } private Builder(SmallSortedMap fields) { @@ -946,7 +989,8 @@ final class FieldSet> { SmallSortedMap fieldsForBuild = fields; if (hasNestedBuilders) { // Make a copy of the fields map with all Builders replaced by Message. - fieldsForBuild = cloneAllFieldsMap(fields, /* copyList */ false); + fieldsForBuild = + cloneAllFieldsMap(fields, /* copyList= */ false, /* resolveLazyFields= */ false); replaceBuilders(fieldsForBuild, partial); } FieldSet fieldSet = new FieldSet<>(fieldsForBuild); @@ -956,7 +1000,8 @@ final class FieldSet> { private static > void replaceBuilders( SmallSortedMap fieldMap, boolean partial) { - for (int i = 0; i < fieldMap.getNumArrayEntries(); i++) { + int n = fieldMap.getNumArrayEntries(); // Optimisation: hoist out of hot loop. + for (int i = 0; i < n; i++) { replaceBuilders(fieldMap.getArrayEntryAt(i), partial); } for (Map.Entry entry : fieldMap.getOverflowEntries()) { @@ -1018,7 +1063,10 @@ final class FieldSet> { /** Returns a new Builder using the fields from {@code fieldSet}. */ public static > Builder fromFieldSet(FieldSet fieldSet) { - Builder builder = new Builder(cloneAllFieldsMap(fieldSet.fields, /* copyList */ true)); + Builder builder = + new Builder( + cloneAllFieldsMap( + fieldSet.fields, /* copyList= */ true, /* resolveLazyFields= */ false)); builder.hasLazyField = fieldSet.hasLazyField; return builder; } @@ -1028,7 +1076,8 @@ final class FieldSet> { /** Get a simple map containing all the fields. */ public Map getAllFields() { if (hasLazyField) { - SmallSortedMap result = cloneAllFieldsMap(fields, /* copyList */ false); + SmallSortedMap result = + cloneAllFieldsMap(fields, /* copyList= */ false, /* resolveLazyFields= */ true); if (fields.isImmutable()) { result.makeImmutable(); } else { @@ -1069,7 +1118,7 @@ final class FieldSet> { private void ensureIsMutable() { if (!isMutable) { - fields = cloneAllFieldsMap(fields, /* copyList */ true); + fields = cloneAllFieldsMap(fields, /* copyList= */ true, /* resolveLazyFields= */ false); isMutable = true; } } @@ -1078,7 +1127,8 @@ final class FieldSet> { * Useful for implementing {@link Message.Builder#setField(Descriptors.FieldDescriptor, * Object)}. */ - @SuppressWarnings({"unchecked", "rawtypes"}) + // Avoid iterator allocation. + @SuppressWarnings({"unchecked", "ForeachList", "ForeachListWithUserVar"}) public void setField(final T descriptor, Object value) { ensureIsMutable(); if (descriptor.isRepeated()) { @@ -1089,8 +1139,10 @@ final class FieldSet> { // Wrap the contents in a new list so that the caller cannot change // the list's contents after setting it. - final List newList = new ArrayList((List) value); - for (final Object element : newList) { + final List newList = new ArrayList<>((List) value); + int newListSize = newList.size(); + for (int i = 0; i < newListSize; i++) { + Object element = newList.get(i); verifyType(descriptor, element); hasNestedBuilders = hasNestedBuilders || element instanceof MessageLite.Builder; } @@ -1244,7 +1296,8 @@ final class FieldSet> { * caller to check that all required fields are present. */ public boolean isInitialized() { - for (int i = 0; i < fields.getNumArrayEntries(); i++) { + int n = fields.getNumArrayEntries(); // Optimisation: hoist out of hot loop. + for (int i = 0; i < n; i++) { if (!FieldSet.isInitialized(fields.getArrayEntryAt(i))) { return false; } @@ -1262,7 +1315,8 @@ final class FieldSet> { */ public void mergeFrom(final FieldSet other) { ensureIsMutable(); - for (int i = 0; i < other.fields.getNumArrayEntries(); i++) { + int n = other.fields.getNumArrayEntries(); // Optimisation: hoist out of hot loop. + for (int i = 0; i < n; i++) { mergeFromField(other.fields.getArrayEntryAt(i)); } for (final Map.Entry entry : other.fields.getOverflowEntries()) { @@ -1270,29 +1324,42 @@ final class FieldSet> { } } - @SuppressWarnings("unchecked") + // Avoid iterator allocation. + @SuppressWarnings({"unchecked", "ForeachList", "ForeachListWithUserVar"}) private void mergeFromField(final Map.Entry entry) { final T descriptor = entry.getKey(); Object otherValue = entry.getValue(); - if (otherValue instanceof LazyField) { - otherValue = ((LazyField) otherValue).getValue(); - } + boolean isLazyField = otherValue instanceof LazyField; if (descriptor.isRepeated()) { + if (isLazyField) { + throw new IllegalStateException("Lazy fields can not be repeated"); + } List value = (List) getFieldAllowBuilders(descriptor); + List otherList = (List) otherValue; + int otherListSize = otherList.size(); if (value == null) { - value = new ArrayList<>(); + value = new ArrayList<>(otherListSize); fields.put(descriptor, value); } - for (Object element : (List) otherValue) { + for (int i = 0; i < otherListSize; i++) { + Object element = otherList.get(i); value.add(FieldSet.cloneIfMutable(element)); } } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { Object value = getFieldAllowBuilders(descriptor); if (value == null) { + // New field. fields.put(descriptor, FieldSet.cloneIfMutable(otherValue)); + if (isLazyField) { + hasLazyField = true; + } } else { - // Merge the messages. + // There is an existing field. Need to merge the messages. + if (otherValue instanceof LazyField) { + // Extract the actual value for lazy fields. + otherValue = ((LazyField) otherValue).getValue(); + } if (value instanceof MessageLite.Builder) { descriptor.internalMergeFrom((MessageLite.Builder) value, (MessageLite) otherValue); } else { @@ -1304,6 +1371,9 @@ final class FieldSet> { } } } else { + if (isLazyField) { + throw new IllegalStateException("Lazy fields must be message-valued"); + } fields.put(descriptor, cloneIfMutable(otherValue)); } } diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java index d3ff341dc..65f1ca99b 100644 --- a/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java +++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java @@ -7,12 +7,20 @@ package com.google.protobuf; +import static com.google.protobuf.Internal.checkNotNull; + import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.EnumDescriptor; import com.google.protobuf.Descriptors.EnumValueDescriptor; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.Descriptors.FileDescriptor; import com.google.protobuf.Descriptors.OneofDescriptor; +import com.google.protobuf.Internal.BooleanList; +import com.google.protobuf.Internal.DoubleList; +import com.google.protobuf.Internal.FloatList; +import com.google.protobuf.Internal.IntList; +import com.google.protobuf.Internal.LongList; +import com.google.protobuf.Internal.ProtobufList; import java.io.IOException; import java.io.InputStream; import java.io.ObjectStreamException; @@ -20,10 +28,12 @@ import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.SortedMap; import java.util.TreeMap; /** @@ -43,7 +53,11 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial */ protected static boolean alwaysUseFieldBuilders = false; - /** For use by generated code only. */ + /** + * For use by generated code only. + * + *

TODO: mark this private and final (breaking change) + */ protected UnknownFieldSet unknownFields; protected GeneratedMessage() { @@ -54,19 +68,31 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial unknownFields = builder.getUnknownFields(); } + /** TODO: Remove this unnecessary intermediate implementation of this method. */ @Override public Parser getParserForType() { throw new UnsupportedOperationException("This is supposed to be overridden by subclasses."); } /** - * For testing. Allows a test to disable the optimization that avoids using field builders for - * nested messages until they are requested. By disabling this optimization, existing tests can be - * reused to test the field builders. See {@link RepeatedFieldBuilder} and {@link - * SingleFieldBuilder}. + * TODO: Stop using SingleFieldBuilder and remove this setting + * + * @see #setAlwaysUseFieldBuildersForTesting(boolean) */ static void enableAlwaysUseFieldBuildersForTesting() { - alwaysUseFieldBuilders = true; + setAlwaysUseFieldBuildersForTesting(true); + } + + /** + * For testing. Allows a test to disable/re-enable the optimization that avoids using field + * builders for nested messages until they are requested. By disabling this optimization, existing + * tests can be reused to test the field builders. See {@link RepeatedFieldBuilder} and {@link + * SingleFieldBuilder}. + * + *

TODO: Stop using SingleFieldBuilder and remove this setting + */ + static void setAlwaysUseFieldBuildersForTesting(boolean useBuilders) { + alwaysUseFieldBuilders = useBuilders; } /** @@ -80,16 +106,39 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial return internalGetFieldAccessorTable().descriptor; } + /** + * TODO: This method should be removed. It enables parsing directly into an + * "immutable" message. Have to leave it for now to support old gencode. + * + * @deprecated use newBuilder().mergeFrom() instead + */ + @Deprecated + protected void mergeFromAndMakeImmutableInternal( + CodedInputStream input, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + Schema schema = Protobuf.getInstance().schemaFor(this); + try { + schema.mergeFrom(this, CodedInputStreamReader.forCodedInput(input), extensionRegistry); + } catch (InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (IOException e) { + throw new InvalidProtocolBufferException(e).setUnfinishedMessage(this); + } + schema.makeImmutable(this); + } + /** * Internal helper to return a modifiable map containing all the fields. The returned Map is - * modifialbe so that the caller can add additional extension fields to implement {@link + * modifiable so that the caller can add additional extension fields to implement {@link * #getAllFields()}. * * @param getBytesForString whether to generate ByteString for string fields */ private Map getAllFieldsMutable(boolean getBytesForString) { - final TreeMap result = new TreeMap(); - final Descriptor descriptor = internalGetFieldAccessorTable().descriptor; + final TreeMap result = new TreeMap<>(); + final FieldAccessorTable fieldAccessorTable = internalGetFieldAccessorTable(); + + final Descriptor descriptor = fieldAccessorTable.descriptor; final List fields = descriptor.getFields(); for (int i = 0; i < fields.size(); i++) { @@ -132,6 +181,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial return result; } + // TODO: compute this at {@code build()} time in the Builder class. @Override public boolean isInitialized() { for (final FieldDescriptor field : getDescriptorForType().getFields()) { @@ -164,7 +214,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial @Override public Map getAllFields() { - return Collections.unmodifiableMap(getAllFieldsMutable(/* getBytesForString = */ false)); + return Collections.unmodifiableMap(getAllFieldsMutable(/* getBytesForString= */ false)); } /** @@ -176,7 +226,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial * fields in order by field number. */ Map getAllFieldsRaw() { - return Collections.unmodifiableMap(getAllFieldsMutable(/* getBytesForString = */ true)); + return Collections.unmodifiableMap(getAllFieldsMutable(/* getBytesForString= */ true)); } @Override @@ -220,14 +270,23 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial return internalGetFieldAccessorTable().getField(field).getRepeated(this, index); } + // TODO: This method should be final. @Override public UnknownFieldSet getUnknownFields() { - throw new UnsupportedOperationException("This is supposed to be overridden by subclasses."); + return unknownFields; + } + + // TODO: This should go away when Schema classes cannot modify immutable + // GeneratedMessage objects anymore. + void setUnknownFields(UnknownFieldSet unknownFields) { + this.unknownFields = unknownFields; } /** * Called by subclasses to parse an unknown field. * + *

TODO remove this method + * * @return {@code true} unless the tag is an end-group tag. */ protected boolean parseUnknownField( @@ -236,9 +295,29 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial ExtensionRegistryLite extensionRegistry, int tag) throws IOException { + if (input.shouldDiscardUnknownFields()) { + return input.skipField(tag); + } return unknownFields.mergeFieldFrom(tag, input); } + /** + * Delegates to parseUnknownField. This method is obsolete, but we must retain it for + * compatibility with older generated code. + * + *

TODO remove this method + */ + protected boolean parseUnknownFieldProto3( + CodedInputStream input, + UnknownFieldSet.Builder unknownFields, + ExtensionRegistryLite extensionRegistry, + int tag) + throws IOException { + return parseUnknownField(input, unknownFields, extensionRegistry, tag); + } + + /** Used by generated code. */ + @SuppressWarnings("ProtoParseWithRegistry") protected static M parseWithIOException(Parser parser, InputStream input) throws IOException { try { @@ -248,6 +327,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } } + /** Used by generated code. */ protected static M parseWithIOException( Parser parser, InputStream input, ExtensionRegistryLite extensions) throws IOException { try { @@ -257,6 +337,8 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } } + /** Used by generated code. */ + @SuppressWarnings("ProtoParseWithRegistry") protected static M parseWithIOException( Parser parser, CodedInputStream input) throws IOException { try { @@ -266,6 +348,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } } + /** Used by generated code. */ protected static M parseWithIOException( Parser parser, CodedInputStream input, ExtensionRegistryLite extensions) throws IOException { @@ -276,6 +359,8 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } } + /** Used by generated code. */ + @SuppressWarnings("ProtoParseWithRegistry") protected static M parseDelimitedWithIOException( Parser parser, InputStream input) throws IOException { try { @@ -285,6 +370,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } } + /** Used by generated code. */ protected static M parseDelimitedWithIOException( Parser parser, InputStream input, ExtensionRegistryLite extensions) throws IOException { try { @@ -294,6 +380,53 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } } + protected static boolean canUseUnsafe() { + return UnsafeUtil.hasUnsafeArrayOperations() && UnsafeUtil.hasUnsafeByteBufferOperations(); + } + + protected static IntList emptyIntList() { + return IntArrayList.emptyList(); + } + + protected static LongList emptyLongList() { + return LongArrayList.emptyList(); + } + + protected static FloatList emptyFloatList() { + return FloatArrayList.emptyList(); + } + + protected static DoubleList emptyDoubleList() { + return DoubleArrayList.emptyList(); + } + + protected static BooleanList emptyBooleanList() { + return BooleanArrayList.emptyList(); + } + + protected static > ListT makeMutableCopy(ListT list) { + return makeMutableCopy(list, 0); + } + + @SuppressWarnings("unchecked") // Guaranteed by proto runtime. + protected static > ListT makeMutableCopy( + ListT list, int minCapacity) { + int size = list.size(); + if (minCapacity <= size) { + minCapacity = size * 2; + } + if (minCapacity <= 0) { + minCapacity = AbstractProtobufList.DEFAULT_CAPACITY; + } + + return (ListT) list.mutableCopyWithCapacity(minCapacity); + } + + @SuppressWarnings("unchecked") // The empty list can be safely cast + protected static ProtobufList emptyList(Class elementType) { + return (ProtobufList) ProtobufArrayList.emptyList(); + } + @Override public void writeTo(final CodedOutputStream output) throws IOException { MessageReflection.writeMessageTo(this, getAllFieldsRaw(), output, false); @@ -306,39 +439,34 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial return size; } - memoizedSize = MessageReflection.getSerializedSize(this, getAllFieldsRaw()); + memoizedSize = MessageReflection.getSerializedSize( + this, getAllFieldsRaw()); return memoizedSize; } - /** Used by parsing constructors in generated classes. */ - protected void makeExtensionsImmutable() { - // Noop for messages without extensions. - } - /** - * TODO: remove this after b/29368482 is fixed. We need to move this interface to - * AbstractMessage in order to versioning GeneratedMessage but this move breaks binary - * compatibility for AppEngine. After AppEngine is fixed we can exclude this from google3. + * This class is used to make a generated protected method inaccessible from user's code (e.g., + * the {@link #newInstance} method below). When this class is used as a parameter's type in a + * generated protected method, the method is visible to user's code in the same package, but since + * the constructor of this class is private to protobuf runtime, user's code can't obtain an + * instance of this class and as such can't actually make a method call on the protected method. */ - protected interface BuilderParent extends AbstractMessage.BuilderParent {} + protected static final class UnusedPrivateParameter { + static final UnusedPrivateParameter INSTANCE = new UnusedPrivateParameter(); - /** TODO: remove this together with GeneratedMessage.BuilderParent. */ - protected abstract Message.Builder newBuilderForType(BuilderParent parent); + private UnusedPrivateParameter() {} + } - @Override - protected Message.Builder newBuilderForType(final AbstractMessage.BuilderParent parent) { - return newBuilderForType( - new BuilderParent() { - @Override - public void markDirty() { - parent.markDirty(); - } - }); + /** Creates a new instance of this message type. Overridden in the generated code. */ + @SuppressWarnings({"unused"}) + protected Object newInstance(UnusedPrivateParameter unused) { + throw new UnsupportedOperationException("This method must be overridden by the subclass."); } + /** Builder class for {@link GeneratedMessage}. */ @SuppressWarnings("unchecked") - public abstract static class Builder> - extends AbstractMessage.Builder { + public abstract static class Builder> + extends AbstractMessage.Builder { private BuilderParent builderParent; @@ -348,7 +476,18 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener. private boolean isClean; - private UnknownFieldSet unknownFields = UnknownFieldSet.getDefaultInstance(); + /** + * This field holds either an {@link UnknownFieldSet} or {@link UnknownFieldSet.Builder}. + * + *

We use an object because it should only be one or the other of those things at a time and + * Object is the only common base. This also saves space. + * + *

Conversions are lazy: if {@link #setUnknownFields} is called, this will contain {@link + * UnknownFieldSet}. If unknown fields are merged into this builder, the current {@link + * UnknownFieldSet} will be converted to a {@link UnknownFieldSet.Builder} and left that way + * until either {@link #setUnknownFields} or {@link #buildPartial} or {@link #build} is called. + */ + private Object unknownFieldsOrBuilder = UnknownFieldSet.getDefaultInstance(); protected Builder() { this(null); @@ -389,8 +528,8 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public BuilderType clone() { - BuilderType builder = (BuilderType) getDefaultInstanceForType().newBuilderForType(); + public BuilderT clone() { + BuilderT builder = (BuilderT) getDefaultInstanceForType().newBuilderForType(); builder.mergeFrom(buildPartial()); return builder; } @@ -400,10 +539,10 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial * builtin fields back to the initial values. */ @Override - public BuilderType clear() { - unknownFields = UnknownFieldSet.getDefaultInstance(); + public BuilderT clear() { + unknownFieldsOrBuilder = UnknownFieldSet.getDefaultInstance(); onChanged(); - return (BuilderType) this; + return (BuilderT) this; } /** @@ -424,8 +563,9 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial /** Internal helper which returns a mutable map. */ private Map getAllFieldsMutable() { - final TreeMap result = new TreeMap(); - final Descriptor descriptor = internalGetFieldAccessorTable().descriptor; + final TreeMap result = new TreeMap<>(); + final FieldAccessorTable fieldAccessorTable = internalGetFieldAccessorTable(); + final Descriptor descriptor = fieldAccessorTable.descriptor; final List fields = descriptor.getFields(); for (int i = 0; i < fields.size(); i++) { @@ -500,28 +640,28 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial if (field.isRepeated()) { // The underlying list object is still modifiable at this point. // Make sure not to expose the modifiable list to the caller. - return Collections.unmodifiableList((List) object); + return Collections.unmodifiableList((List) object); } else { return object; } } @Override - public BuilderType setField(final FieldDescriptor field, final Object value) { + public BuilderT setField(final FieldDescriptor field, final Object value) { internalGetFieldAccessorTable().getField(field).set(this, value); - return (BuilderType) this; + return (BuilderT) this; } @Override - public BuilderType clearField(final FieldDescriptor field) { + public BuilderT clearField(final FieldDescriptor field) { internalGetFieldAccessorTable().getField(field).clear(this); - return (BuilderType) this; + return (BuilderT) this; } @Override - public BuilderType clearOneof(final OneofDescriptor oneof) { + public BuilderT clearOneof(final OneofDescriptor oneof) { internalGetFieldAccessorTable().getOneof(oneof).clear(this); - return (BuilderType) this; + return (BuilderT) this; } @Override @@ -535,31 +675,51 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public BuilderType setRepeatedField( + public BuilderT setRepeatedField( final FieldDescriptor field, final int index, final Object value) { internalGetFieldAccessorTable().getField(field).setRepeated(this, index, value); - return (BuilderType) this; + return (BuilderT) this; } @Override - public BuilderType addRepeatedField(final FieldDescriptor field, final Object value) { + public BuilderT addRepeatedField(final FieldDescriptor field, final Object value) { internalGetFieldAccessorTable().getField(field).addRepeated(this, value); - return (BuilderType) this; + return (BuilderT) this; } - @Override - public BuilderType setUnknownFields(final UnknownFieldSet unknownFields) { - this.unknownFields = unknownFields; + private BuilderT setUnknownFieldsInternal(final UnknownFieldSet unknownFields) { + unknownFieldsOrBuilder = unknownFields; onChanged(); - return (BuilderType) this; + return (BuilderT) this; + } + + @Override + public BuilderT setUnknownFields(final UnknownFieldSet unknownFields) { + return setUnknownFieldsInternal(unknownFields); + } + + /** + * This method is obsolete, but we must retain it for compatibility with older generated code. + */ + protected BuilderT setUnknownFieldsProto3(final UnknownFieldSet unknownFields) { + return setUnknownFieldsInternal(unknownFields); } @Override - public BuilderType mergeUnknownFields(final UnknownFieldSet unknownFields) { - this.unknownFields = - UnknownFieldSet.newBuilder(this.unknownFields).mergeFrom(unknownFields).build(); + public BuilderT mergeUnknownFields(final UnknownFieldSet unknownFields) { + if (UnknownFieldSet.getDefaultInstance().equals(unknownFields)) { + return (BuilderT) this; + } + + if (UnknownFieldSet.getDefaultInstance().equals(unknownFieldsOrBuilder)) { + unknownFieldsOrBuilder = unknownFields; + onChanged(); + return (BuilderT) this; + } + + getUnknownFieldSetBuilder().mergeFrom(unknownFields); onChanged(); - return (BuilderType) this; + return (BuilderT) this; } @Override @@ -593,21 +753,50 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial @Override public final UnknownFieldSet getUnknownFields() { - return unknownFields; + if (unknownFieldsOrBuilder instanceof UnknownFieldSet) { + return (UnknownFieldSet) unknownFieldsOrBuilder; + } else { + return ((UnknownFieldSet.Builder) unknownFieldsOrBuilder).buildPartial(); + } } /** - * Called by subclasses to parse an unknown field. + * Called by generated subclasses to parse an unknown field. * * @return {@code true} unless the tag is an end-group tag. */ protected boolean parseUnknownField( - final CodedInputStream input, - final UnknownFieldSet.Builder unknownFields, - final ExtensionRegistryLite extensionRegistry, - final int tag) + CodedInputStream input, ExtensionRegistryLite extensionRegistry, int tag) throws IOException { - return unknownFields.mergeFieldFrom(tag, input); + if (input.shouldDiscardUnknownFields()) { + return input.skipField(tag); + } + return getUnknownFieldSetBuilder().mergeFieldFrom(tag, input); + } + + /** Called by generated subclasses to add to the unknown field set. */ + protected final void mergeUnknownLengthDelimitedField(int number, ByteString bytes) { + getUnknownFieldSetBuilder().mergeLengthDelimitedField(number, bytes); + } + + /** Called by generated subclasses to add to the unknown field set. */ + protected final void mergeUnknownVarintField(int number, int value) { + getUnknownFieldSetBuilder().mergeVarintField(number, value); + } + + @Override + protected UnknownFieldSet.Builder getUnknownFieldSetBuilder() { + if (unknownFieldsOrBuilder instanceof UnknownFieldSet) { + unknownFieldsOrBuilder = ((UnknownFieldSet) unknownFieldsOrBuilder).toBuilder(); + } + onChanged(); + return (UnknownFieldSet.Builder) unknownFieldsOrBuilder; + } + + @Override + protected void setUnknownFieldSetBuilder(UnknownFieldSet.Builder builder) { + unknownFieldsOrBuilder = builder; + onChanged(); } /** @@ -635,7 +824,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } /** - * Called when a the builder or one of its nested children has changed and any parent should be + * Called when a builder or one of its nested children has changed and any parent should be * notified of its invalidation. */ protected final void onChanged() { @@ -657,58 +846,56 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial * map field directly and thus enables us to access the map field as a list. */ @SuppressWarnings({"unused", "rawtypes"}) + protected MapFieldReflectionAccessor internalGetMapFieldReflection(int fieldNumber) { + return internalGetMapField(fieldNumber); + } + + /** TODO: Remove, exists for compatibility with generated code. */ + @Deprecated + @SuppressWarnings({"unused", "rawtypes"}) protected MapField internalGetMapField(int fieldNumber) { // Note that we can't use descriptor names here because this method will // be called when descriptor is being initialized. - throw new RuntimeException("No map fields found in " + getClass().getName()); + throw new IllegalArgumentException("No map fields found in " + getClass().getName()); + } + + /** Like {@link #internalGetMapFieldReflection} but return a mutable version. */ + @SuppressWarnings({"unused", "rawtypes"}) + protected MapFieldReflectionAccessor internalGetMutableMapFieldReflection(int fieldNumber) { + return internalGetMutableMapField(fieldNumber); } - /** Like {@link #internalGetMapField} but return a mutable version. */ + /** TODO: Remove, exists for compatibility with generated code. */ + @Deprecated @SuppressWarnings({"unused", "rawtypes"}) protected MapField internalGetMutableMapField(int fieldNumber) { // Note that we can't use descriptor names here because this method will // be called when descriptor is being initialized. - throw new RuntimeException("No map fields found in " + getClass().getName()); + throw new IllegalArgumentException("No map fields found in " + getClass().getName()); } } // ================================================================= // Extensions-related stuff - public interface ExtendableMessageOrBuilder + /** Extends {@link MessageOrBuilder} with extension-related functions. */ + public interface ExtendableMessageOrBuilder> extends MessageOrBuilder { // Re-define for return type covariance. @Override Message getDefaultInstanceForType(); /** Check if a singular extension is present. */ - boolean hasExtension(ExtensionLite extension); + boolean hasExtension(ExtensionLite extension); /** Get the number of elements in a repeated extension. */ - int getExtensionCount(ExtensionLite> extension); + int getExtensionCount(ExtensionLite> extension); /** Get the value of an extension. */ - Type getExtension(ExtensionLite extension); + T getExtension(ExtensionLite extension); /** Get one element of a repeated extension. */ - Type getExtension(ExtensionLite> extension, int index); - - /** Check if a singular extension is present. */ - boolean hasExtension(Extension extension); - /** Check if a singular extension is present. */ - boolean hasExtension(GeneratedExtension extension); - /** Get the number of elements in a repeated extension. */ - int getExtensionCount(Extension> extension); - /** Get the number of elements in a repeated extension. */ - int getExtensionCount(GeneratedExtension> extension); - /** Get the value of an extension. */ - Type getExtension(Extension extension); - /** Get the value of an extension. */ - Type getExtension(GeneratedExtension extension); - /** Get one element of a repeated extension. */ - Type getExtension(Extension> extension, int index); - /** Get one element of a repeated extension. */ - Type getExtension(GeneratedExtension> extension, int index); + T getExtension(ExtensionLite> extension, int index); } /** @@ -743,8 +930,8 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial * *

See also {@link ExtendableBuilder}. */ - public abstract static class ExtendableMessage - extends GeneratedMessage implements ExtendableMessageOrBuilder { + public abstract static class ExtendableMessage> + extends GeneratedMessage implements ExtendableMessageOrBuilder { private static final long serialVersionUID = 1L; @@ -754,12 +941,12 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial this.extensions = FieldSet.newFieldSet(); } - protected ExtendableMessage(ExtendableBuilder builder) { + protected ExtendableMessage(ExtendableBuilder builder) { super(builder); this.extensions = builder.buildExtensions(); } - private void verifyExtensionContainingType(final Extension extension) { + private void verifyExtensionContainingType(final Extension extension) { if (extension.getDescriptor().getContainingType() != getDescriptorForType()) { // This can only happen if someone uses unchecked operations. throw new IllegalArgumentException( @@ -773,9 +960,9 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial /** Check if a singular extension is present. */ @Override - @SuppressWarnings("unchecked") - public final boolean hasExtension(final ExtensionLite extensionLite) { - Extension extension = checkNotLite(extensionLite); + public final boolean hasExtension( + final ExtensionLite extensionLite) { + Extension extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); return extensions.hasField(extension.getDescriptor()); @@ -783,10 +970,9 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial /** Get the number of elements in a repeated extension. */ @Override - @SuppressWarnings("unchecked") - public final int getExtensionCount( - final ExtensionLite> extensionLite) { - Extension> extension = checkNotLite(extensionLite); + public final int getExtensionCount( + final ExtensionLite> extensionLite) { + Extension> extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); final FieldDescriptor descriptor = extension.getDescriptor(); @@ -796,121 +982,73 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial /** Get the value of an extension. */ @Override @SuppressWarnings("unchecked") - public final Type getExtension(final ExtensionLite extensionLite) { - Extension extension = checkNotLite(extensionLite); + public final T getExtension(final ExtensionLite extensionLite) { + Extension extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); FieldDescriptor descriptor = extension.getDescriptor(); final Object value = extensions.getField(descriptor); if (value == null) { if (descriptor.isRepeated()) { - return (Type) Collections.emptyList(); + return (T) Collections.emptyList(); } else if (descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - return (Type) extension.getMessageDefaultInstance(); + return (T) extension.getMessageDefaultInstance(); } else { - return (Type) extension.fromReflectionType(descriptor.getDefaultValue()); + return (T) extension.fromReflectionType(descriptor.getDefaultValue()); } } else { - return (Type) extension.fromReflectionType(value); + return (T) extension.fromReflectionType(value); } } /** Get one element of a repeated extension. */ @Override @SuppressWarnings("unchecked") - public final Type getExtension( - final ExtensionLite> extensionLite, final int index) { - Extension> extension = checkNotLite(extensionLite); + public final T getExtension( + final ExtensionLite> extensionLite, final int index) { + Extension> extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); FieldDescriptor descriptor = extension.getDescriptor(); - return (Type) + return (T) extension.singularFromReflectionType(extensions.getRepeatedField(descriptor, index)); } - /** Check if a singular extension is present. */ - @Override - public final boolean hasExtension(final Extension extension) { - return hasExtension((ExtensionLite) extension); - } - /** Check if a singular extension is present. */ - @Override - public final boolean hasExtension( - final GeneratedExtension extension) { - return hasExtension((ExtensionLite) extension); - } - /** Get the number of elements in a repeated extension. */ - @Override - public final int getExtensionCount(final Extension> extension) { - return getExtensionCount((ExtensionLite>) extension); - } - /** Get the number of elements in a repeated extension. */ - @Override - public final int getExtensionCount( - final GeneratedExtension> extension) { - return getExtensionCount((ExtensionLite>) extension); - } - /** Get the value of an extension. */ - @Override - public final Type getExtension(final Extension extension) { - return getExtension((ExtensionLite) extension); - } - /** Get the value of an extension. */ - @Override - public final Type getExtension(final GeneratedExtension extension) { - return getExtension((ExtensionLite) extension); - } - /** Get one element of a repeated extension. */ - @Override - public final Type getExtension( - final Extension> extension, final int index) { - return getExtension((ExtensionLite>) extension, index); - } - /** Get one element of a repeated extension. */ - @Override - public final Type getExtension( - final GeneratedExtension> extension, final int index) { - return getExtension((ExtensionLite>) extension, index); - } - /** Called by subclasses to check if all extensions are initialized. */ protected boolean extensionsAreInitialized() { return extensions.isInitialized(); } + // TODO: compute this in the builder at {@code build()} time. @Override public boolean isInitialized() { return super.isInitialized() && extensionsAreInitialized(); } - @Override - protected boolean parseUnknownField( - CodedInputStream input, - UnknownFieldSet.Builder unknownFields, - ExtensionRegistryLite extensionRegistry, - int tag) - throws IOException { - return MessageReflection.mergeFieldFrom( - input, - unknownFields, - extensionRegistry, - getDescriptorForType(), - new MessageReflection.ExtensionAdapter(extensions), - tag); + /** + * Used by subclasses to serialize extensions. Extension ranges may be interleaved with field + * numbers, but we must write them in canonical (sorted by field number) order. + * ExtensionSerializer helps us write individual ranges of extensions at once. + */ + protected interface ExtensionSerializer { + public void writeUntil(final int end, final CodedOutputStream output) throws IOException; } - /** Used by parsing constructors in generated classes. */ - @Override - protected void makeExtensionsImmutable() { - extensions.makeImmutable(); + /** No-op implementation that writes nothing, for messages with no extensions. */ + private static final class NoOpExtensionSerializer implements ExtensionSerializer { + // Singleton instance so we can avoid allocating a new one for each message serialization. + private static final NoOpExtensionSerializer INSTANCE = new NoOpExtensionSerializer(); + + @Override + public void writeUntil(final int end, final CodedOutputStream output) { + // no-op + } } /** - * Used by subclasses to serialize extensions. Extension ranges may be interleaved with field - * numbers, but we must write them in canonical (sorted by field number) order. ExtensionWriter - * helps us write individual ranges of extensions at once. + * ExtensionSerializer that writes extensions from the FieldSet, for messages with extensions. */ - protected class ExtensionWriter { + protected class ExtensionWriter implements ExtensionSerializer { // Imagine how much simpler this code would be if Java iterators had // a way to get the next element without advancing the iterator. @@ -918,13 +1056,15 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial private Map.Entry next; private final boolean messageSetWireFormat; - private ExtensionWriter(final boolean messageSetWireFormat) { + // TODO: Should be marked private in v5.x.x once GeneratedMessageV3 is removed. + protected ExtensionWriter(final boolean messageSetWireFormat) { if (iter.hasNext()) { next = iter.next(); } this.messageSetWireFormat = messageSetWireFormat; } + @Override public void writeUntil(final int end, final CodedOutputStream output) throws IOException { while (next != null && next.getKey().getNumber() < end) { FieldDescriptor descriptor = next.getKey(); @@ -957,14 +1097,39 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } } + /** + * For compatibility with older gencode. + * + *

TODO Remove this in the next breaking release. + * + * @deprecated Use {@link newExtensionSerializer()} instead. + */ + @Deprecated protected ExtensionWriter newExtensionWriter() { return new ExtensionWriter(false); } + protected ExtensionSerializer newExtensionSerializer() { + // Avoid allocation in the common case of no extensions. + if (extensions.isEmpty()) { + return NoOpExtensionSerializer.INSTANCE; + } + return new ExtensionWriter(false); + } + + // TODO: Remove, replace with newMessageSetExtensionSerializer(). protected ExtensionWriter newMessageSetExtensionWriter() { return new ExtensionWriter(true); } + protected ExtensionSerializer newMessageSetExtensionSerializer() { + // Avoid allocation in the common case of no extensions. + if (extensions.isEmpty()) { + return NoOpExtensionSerializer.INSTANCE; + } + return new ExtensionWriter(true); + } + /** Called by subclasses to compute the size of extensions. */ protected int extensionsSerializedSize() { return extensions.getSerializedSize(); @@ -984,7 +1149,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial @Override public Map getAllFields() { final Map result = - super.getAllFieldsMutable(/* getBytesForString = */ false); + super.getAllFieldsMutable(/* getBytesForString= */ false); result.putAll(getExtensionFields()); return Collections.unmodifiableMap(result); } @@ -992,7 +1157,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial @Override public Map getAllFieldsRaw() { final Map result = - super.getAllFieldsMutable(/* getBytesForString = */ false); + super.getAllFieldsMutable(/* getBytesForString= */ false); result.putAll(getExtensionFields()); return Collections.unmodifiableMap(result); } @@ -1093,11 +1258,11 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial */ @SuppressWarnings("unchecked") public abstract static class ExtendableBuilder< - MessageType extends ExtendableMessage, - BuilderType extends ExtendableBuilder> - extends Builder implements ExtendableMessageOrBuilder { + MessageT extends ExtendableMessage, + BuilderT extends ExtendableBuilder> + extends Builder implements ExtendableMessageOrBuilder { - private FieldSet extensions = FieldSet.emptySet(); + private FieldSet.Builder extensions; protected ExtendableBuilder() {} @@ -1107,30 +1272,22 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial // For immutable message conversion. void internalSetExtensionSet(FieldSet extensions) { - this.extensions = extensions; + this.extensions = FieldSet.Builder.fromFieldSet(extensions); } @Override - public BuilderType clear() { - extensions = FieldSet.emptySet(); + public BuilderT clear() { + extensions = null; return super.clear(); } - // This is implemented here only to work around an apparent bug in the - // Java compiler and/or build system. See bug #1898463. The mere presence - // of this clone() implementation makes it go away. - @Override - public BuilderType clone() { - return super.clone(); - } - private void ensureExtensionsIsMutable() { - if (extensions.isImmutable()) { - extensions = extensions.clone(); + if (extensions == null) { + extensions = FieldSet.newBuilder(); } } - private void verifyExtensionContainingType(final Extension extension) { + private void verifyExtensionContainingType(final Extension extension) { if (extension.getDescriptor().getContainingType() != getDescriptorForType()) { // This can only happen if someone uses unchecked operations. throw new IllegalArgumentException( @@ -1144,206 +1301,126 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial /** Check if a singular extension is present. */ @Override - public final boolean hasExtension(final ExtensionLite extensionLite) { - Extension extension = checkNotLite(extensionLite); + public final boolean hasExtension( + final ExtensionLite extensionLite) { + Extension extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); - return extensions.hasField(extension.getDescriptor()); + return extensions != null && extensions.hasField(extension.getDescriptor()); } /** Get the number of elements in a repeated extension. */ @Override - public final int getExtensionCount( - final ExtensionLite> extensionLite) { - Extension> extension = checkNotLite(extensionLite); + public final int getExtensionCount( + final ExtensionLite> extensionLite) { + Extension> extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); final FieldDescriptor descriptor = extension.getDescriptor(); - return extensions.getRepeatedFieldCount(descriptor); + return extensions == null ? 0 : extensions.getRepeatedFieldCount(descriptor); } /** Get the value of an extension. */ @Override - public final Type getExtension(final ExtensionLite extensionLite) { - Extension extension = checkNotLite(extensionLite); + public final T getExtension(final ExtensionLite extensionLite) { + Extension extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); FieldDescriptor descriptor = extension.getDescriptor(); - final Object value = extensions.getField(descriptor); + final Object value = extensions == null ? null : extensions.getField(descriptor); if (value == null) { if (descriptor.isRepeated()) { - return (Type) Collections.emptyList(); + return (T) Collections.emptyList(); } else if (descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - return (Type) extension.getMessageDefaultInstance(); + return (T) extension.getMessageDefaultInstance(); } else { - return (Type) extension.fromReflectionType(descriptor.getDefaultValue()); + return (T) extension.fromReflectionType(descriptor.getDefaultValue()); } } else { - return (Type) extension.fromReflectionType(value); + return (T) extension.fromReflectionType(value); } } /** Get one element of a repeated extension. */ @Override - public final Type getExtension( - final ExtensionLite> extensionLite, final int index) { - Extension> extension = checkNotLite(extensionLite); + public final T getExtension( + final ExtensionLite> extensionLite, final int index) { + Extension> extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); FieldDescriptor descriptor = extension.getDescriptor(); - return (Type) + if (extensions == null) { + throw new IndexOutOfBoundsException(); + } + return (T) extension.singularFromReflectionType(extensions.getRepeatedField(descriptor, index)); } /** Set the value of an extension. */ - public final BuilderType setExtension( - final ExtensionLite extensionLite, final Type value) { - Extension extension = checkNotLite(extensionLite); + public final BuilderT setExtension( + final ExtensionLite extensionLite, final T value) { + Extension extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); ensureExtensionsIsMutable(); final FieldDescriptor descriptor = extension.getDescriptor(); extensions.setField(descriptor, extension.toReflectionType(value)); onChanged(); - return (BuilderType) this; + return (BuilderT) this; } /** Set the value of one element of a repeated extension. */ - public final BuilderType setExtension( - final ExtensionLite> extensionLite, + public final BuilderT setExtension( + final ExtensionLite> extensionLite, final int index, - final Type value) { - Extension> extension = checkNotLite(extensionLite); + final T value) { + Extension> extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); ensureExtensionsIsMutable(); final FieldDescriptor descriptor = extension.getDescriptor(); extensions.setRepeatedField(descriptor, index, extension.singularToReflectionType(value)); onChanged(); - return (BuilderType) this; + return (BuilderT) this; } /** Append a value to a repeated extension. */ - public final BuilderType addExtension( - final ExtensionLite> extensionLite, final Type value) { - Extension> extension = checkNotLite(extensionLite); + public final BuilderT addExtension( + final ExtensionLite> extensionLite, final T value) { + Extension> extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); ensureExtensionsIsMutable(); final FieldDescriptor descriptor = extension.getDescriptor(); extensions.addRepeatedField(descriptor, extension.singularToReflectionType(value)); onChanged(); - return (BuilderType) this; + return (BuilderT) this; } /** Clear an extension. */ - public final BuilderType clearExtension( - final ExtensionLite extensionLite) { - Extension extension = checkNotLite(extensionLite); + public final BuilderT clearExtension( + final ExtensionLite extensionLite) { + Extension extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); ensureExtensionsIsMutable(); extensions.clearField(extension.getDescriptor()); onChanged(); - return (BuilderType) this; - } - - /** Check if a singular extension is present. */ - @Override - public final boolean hasExtension(final Extension extension) { - return hasExtension((ExtensionLite) extension); - } - /** Check if a singular extension is present. */ - @Override - public final boolean hasExtension( - final GeneratedExtension extension) { - return hasExtension((ExtensionLite) extension); - } - /** Get the number of elements in a repeated extension. */ - @Override - public final int getExtensionCount(final Extension> extension) { - return getExtensionCount((ExtensionLite>) extension); - } - /** Get the number of elements in a repeated extension. */ - @Override - public final int getExtensionCount( - final GeneratedExtension> extension) { - return getExtensionCount((ExtensionLite>) extension); - } - /** Get the value of an extension. */ - @Override - public final Type getExtension(final Extension extension) { - return getExtension((ExtensionLite) extension); - } - /** Get the value of an extension. */ - @Override - public final Type getExtension(final GeneratedExtension extension) { - return getExtension((ExtensionLite) extension); - } - /** Get the value of an extension. */ - @Override - public final Type getExtension( - final Extension> extension, final int index) { - return getExtension((ExtensionLite>) extension, index); - } - /** Get the value of an extension. */ - @Override - public final Type getExtension( - final GeneratedExtension> extension, final int index) { - return getExtension((ExtensionLite>) extension, index); - } - /** Set the value of an extension. */ - public final BuilderType setExtension( - final Extension extension, final Type value) { - return setExtension((ExtensionLite) extension, value); - } - /** Set the value of an extension. */ - public BuilderType setExtension( - final GeneratedExtension extension, final Type value) { - return setExtension((ExtensionLite) extension, value); - } - /** Set the value of one element of a repeated extension. */ - public final BuilderType setExtension( - final Extension> extension, final int index, final Type value) { - return setExtension((ExtensionLite>) extension, index, value); - } - /** Set the value of one element of a repeated extension. */ - public BuilderType setExtension( - final GeneratedExtension> extension, - final int index, - final Type value) { - return setExtension((ExtensionLite>) extension, index, value); - } - /** Append a value to a repeated extension. */ - public final BuilderType addExtension( - final Extension> extension, final Type value) { - return addExtension((ExtensionLite>) extension, value); - } - /** Append a value to a repeated extension. */ - public BuilderType addExtension( - final GeneratedExtension> extension, final Type value) { - return addExtension((ExtensionLite>) extension, value); - } - /** Clear an extension. */ - public final BuilderType clearExtension(final Extension extension) { - return clearExtension((ExtensionLite) extension); - } - /** Clear an extension. */ - public BuilderType clearExtension(final GeneratedExtension extension) { - return clearExtension((ExtensionLite) extension); + return (BuilderT) this; } /** Called by subclasses to check if all extensions are initialized. */ protected boolean extensionsAreInitialized() { - return extensions.isInitialized(); + return extensions == null || extensions.isInitialized(); } /** * Called by the build code path to create a copy of the extensions for building the message. */ private FieldSet buildExtensions() { - extensions.makeImmutable(); - return extensions; + return extensions == null + ? (FieldSet) FieldSet.emptySet() + : extensions.buildPartial(); } @Override @@ -1351,34 +1428,15 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial return super.isInitialized() && extensionsAreInitialized(); } - /** - * Called by subclasses to parse an unknown field or an extension. - * - * @return {@code true} unless the tag is an end-group tag. - */ - @Override - protected boolean parseUnknownField( - final CodedInputStream input, - final UnknownFieldSet.Builder unknownFields, - final ExtensionRegistryLite extensionRegistry, - final int tag) - throws IOException { - return MessageReflection.mergeFieldFrom( - input, - unknownFields, - extensionRegistry, - getDescriptorForType(), - new MessageReflection.BuilderAdapter(this), - tag); - } - // --------------------------------------------------------------- // Reflection @Override public Map getAllFields() { final Map result = super.getAllFieldsMutable(); - result.putAll(extensions.getAllFields()); + if (extensions != null) { + result.putAll(extensions.getAllFields()); + } return Collections.unmodifiableMap(result); } @@ -1386,7 +1444,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial public Object getField(final FieldDescriptor field) { if (field.isExtension()) { verifyContainingType(field); - final Object value = extensions.getField(field); + final Object value = extensions == null ? null : extensions.getField(field); if (value == null) { if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { // Lacking an ExtensionRegistry, we have no way to determine the @@ -1403,11 +1461,44 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } } + @Override + public Message.Builder getFieldBuilder(final FieldDescriptor field) { + if (field.isExtension()) { + verifyContainingType(field); + if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { + throw new UnsupportedOperationException( + "getFieldBuilder() called on a non-Message type."); + } + ensureExtensionsIsMutable(); + final Object value = extensions.getFieldAllowBuilders(field); + if (value == null) { + Message.Builder builder = DynamicMessage.newBuilder(field.getMessageType()); + extensions.setField(field, builder); + onChanged(); + return builder; + } else { + if (value instanceof Message.Builder) { + return (Message.Builder) value; + } else if (value instanceof Message) { + Message.Builder builder = ((Message) value).toBuilder(); + extensions.setField(field, builder); + onChanged(); + return builder; + } else { + throw new UnsupportedOperationException( + "getRepeatedFieldBuilder() called on a non-Message type."); + } + } + } else { + return super.getFieldBuilder(field); + } + } + @Override public int getRepeatedFieldCount(final FieldDescriptor field) { if (field.isExtension()) { verifyContainingType(field); - return extensions.getRepeatedFieldCount(field); + return extensions == null ? 0 : extensions.getRepeatedFieldCount(field); } else { return super.getRepeatedFieldCount(field); } @@ -1417,79 +1508,134 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial public Object getRepeatedField(final FieldDescriptor field, final int index) { if (field.isExtension()) { verifyContainingType(field); + if (extensions == null) { + throw new IndexOutOfBoundsException(); + } return extensions.getRepeatedField(field, index); } else { return super.getRepeatedField(field, index); } } + @Override + public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field, final int index) { + if (field.isExtension()) { + verifyContainingType(field); + ensureExtensionsIsMutable(); + if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { + throw new UnsupportedOperationException( + "getRepeatedFieldBuilder() called on a non-Message type."); + } + final Object value = extensions.getRepeatedFieldAllowBuilders(field, index); + if (value instanceof Message.Builder) { + return (Message.Builder) value; + } else if (value instanceof Message) { + Message.Builder builder = ((Message) value).toBuilder(); + extensions.setRepeatedField(field, index, builder); + onChanged(); + return builder; + } else { + throw new UnsupportedOperationException( + "getRepeatedFieldBuilder() called on a non-Message type."); + } + } else { + return super.getRepeatedFieldBuilder(field, index); + } + } + @Override public boolean hasField(final FieldDescriptor field) { if (field.isExtension()) { verifyContainingType(field); - return extensions.hasField(field); + return extensions != null && extensions.hasField(field); } else { return super.hasField(field); } } @Override - public BuilderType setField(final FieldDescriptor field, final Object value) { + public BuilderT setField(final FieldDescriptor field, final Object value) { if (field.isExtension()) { verifyContainingType(field); ensureExtensionsIsMutable(); extensions.setField(field, value); onChanged(); - return (BuilderType) this; + return (BuilderT) this; } else { return super.setField(field, value); } } @Override - public BuilderType clearField(final FieldDescriptor field) { + public BuilderT clearField(final FieldDescriptor field) { if (field.isExtension()) { verifyContainingType(field); ensureExtensionsIsMutable(); extensions.clearField(field); onChanged(); - return (BuilderType) this; + return (BuilderT) this; } else { return super.clearField(field); } } @Override - public BuilderType setRepeatedField( + public BuilderT setRepeatedField( final FieldDescriptor field, final int index, final Object value) { if (field.isExtension()) { verifyContainingType(field); ensureExtensionsIsMutable(); extensions.setRepeatedField(field, index, value); onChanged(); - return (BuilderType) this; + return (BuilderT) this; } else { return super.setRepeatedField(field, index, value); } } @Override - public BuilderType addRepeatedField(final FieldDescriptor field, final Object value) { + public BuilderT addRepeatedField(final FieldDescriptor field, final Object value) { if (field.isExtension()) { verifyContainingType(field); ensureExtensionsIsMutable(); extensions.addRepeatedField(field, value); onChanged(); - return (BuilderType) this; + return (BuilderT) this; } else { return super.addRepeatedField(field, value); } } - protected final void mergeExtensionFields(final ExtendableMessage other) { + @Override + public Message.Builder newBuilderForField(final FieldDescriptor field) { + if (field.isExtension()) { + return DynamicMessage.newBuilder(field.getMessageType()); + } else { + return super.newBuilderForField(field); + } + } + + // TODO: Should be marked final in v5.x.x once GeneratedMessageV3 is removed. + protected void mergeExtensionFields(final ExtendableMessage other) { + if (other.extensions != null) { + ensureExtensionsIsMutable(); + extensions.mergeFrom(other.extensions); + onChanged(); + } + } + + @Override + protected boolean parseUnknownField( + CodedInputStream input, ExtensionRegistryLite extensionRegistry, int tag) + throws IOException { ensureExtensionsIsMutable(); - extensions.mergeFrom(other.extensions); - onChanged(); + return MessageReflection.mergeFieldFrom( + input, + input.shouldDiscardUnknownFields() ? null : getUnknownFieldSetBuilder(), + extensionRegistry, + getDescriptorForType(), + new MessageReflection.ExtensionBuilderAdapter(extensions), + tag); } private void verifyContainingType(final FieldDescriptor field) { @@ -1505,21 +1651,21 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial * Gets the descriptor for an extension. The implementation depends on whether the extension is * scoped in the top level of a file or scoped in a Message. */ - static interface ExtensionDescriptorRetriever { + interface ExtensionDescriptorRetriever { FieldDescriptor getDescriptor(); } /** For use by generated code only. */ - public static - GeneratedExtension newMessageScopedGeneratedExtension( + public static + GeneratedExtension newMessageScopedGeneratedExtension( final Message scope, final int descriptorIndex, - final Class singularType, + final Class singularType, final Message defaultInstance) { // For extensions scoped within a Message, we use the Message to resolve // the outer class's descriptor, from which the extension descriptor is // obtained. - return new GeneratedExtension( + return new GeneratedExtension<>( new CachedDescriptorRetriever() { @Override public FieldDescriptor loadDescriptor() { @@ -1532,13 +1678,13 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } /** For use by generated code only. */ - public static - GeneratedExtension newFileScopedGeneratedExtension( - final Class singularType, final Message defaultInstance) { + public static + GeneratedExtension newFileScopedGeneratedExtension( + final Class singularType, final Message defaultInstance) { // For extensions scoped within a file, we rely on the outer class's // static initializer to call internalInit() on the extension when the // descriptor is available. - return new GeneratedExtension( + return new GeneratedExtension<>( null, // ExtensionDescriptorRetriever is initialized in internalInit(); singularType, defaultInstance, @@ -1553,9 +1699,10 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial @Override public FieldDescriptor getDescriptor() { if (descriptor == null) { + FieldDescriptor tmpDescriptor = loadDescriptor(); synchronized (this) { if (descriptor == null) { - descriptor = loadDescriptor(); + descriptor = tmpDescriptor; } } } @@ -1563,72 +1710,6 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } } - /** - * Used in proto1 generated code only. - * - *

After enabling bridge, we can define proto2 extensions (the extended type is a proto2 - * mutable message) in a proto1 .proto file. For these extensions we should generate proto2 - * GeneratedExtensions. - */ - public static - GeneratedExtension newMessageScopedGeneratedExtension( - final Message scope, - final String name, - final Class singularType, - final Message defaultInstance) { - // For extensions scoped within a Message, we use the Message to resolve - // the outer class's descriptor, from which the extension descriptor is - // obtained. - return new GeneratedExtension( - new CachedDescriptorRetriever() { - @Override - protected FieldDescriptor loadDescriptor() { - return scope.getDescriptorForType().findFieldByName(name); - } - }, - singularType, - defaultInstance, - Extension.ExtensionType.MUTABLE); - } - - /** - * Used in proto1 generated code only. - * - *

After enabling bridge, we can define proto2 extensions (the extended type is a proto2 - * mutable message) in a proto1 .proto file. For these extensions we should generate proto2 - * GeneratedExtensions. - */ - public static - GeneratedExtension newFileScopedGeneratedExtension( - final Class singularType, - final Message defaultInstance, - final String descriptorOuterClass, - final String extensionName) { - // For extensions scoped within a file, we load the descriptor outer - // class and rely on it to get the FileDescriptor which then can be - // used to obtain the extension's FieldDescriptor. - return new GeneratedExtension( - new CachedDescriptorRetriever() { - @Override - protected FieldDescriptor loadDescriptor() { - try { - Class clazz = singularType.getClassLoader().loadClass(descriptorOuterClass); - FileDescriptor file = (FileDescriptor) clazz.getField("descriptor").get(null); - return file.findExtensionByName(extensionName); - } catch (Exception e) { - throw new RuntimeException( - "Cannot load descriptors: " - + descriptorOuterClass - + " is not a valid descriptor class name", - e); - } - } - }, - singularType, - defaultInstance, - Extension.ExtensionType.MUTABLE); - } - /** * Type used to represent generated extensions. The protocol compiler generates a static singleton * instance of this class for each extension. @@ -1653,10 +1734,8 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial * singletons as parameters to the extension accessors defined in {@link ExtendableMessage} and * {@link ExtendableBuilder}. */ - public static class GeneratedExtension - extends Extension { - // TODO: Find ways to avoid using Java reflection within this - // class. Also try to avoid suppressing unchecked warnings. + public static class GeneratedExtension + extends Extension { // We can't always initialize the descriptor of a GeneratedExtension when // we first construct it due to initialization order difficulties (namely, @@ -1672,7 +1751,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial // initializer to call internalInit() after the descriptor has been parsed. GeneratedExtension( ExtensionDescriptorRetriever descriptorRetriever, - Class singularType, + Class singularType, Message messageDefaultInstance, ExtensionType extensionType) { if (Message.class.isAssignableFrom(singularType) @@ -1709,7 +1788,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } private ExtensionDescriptorRetriever descriptorRetriever; - private final Class singularType; + private final Class singularType; private final Message messageDefaultInstance; private final Method enumValueOf; private final Method enumGetValueDescriptor; @@ -1743,15 +1822,14 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial * use the generated enum type. */ @Override - @SuppressWarnings("unchecked") protected Object fromReflectionType(final Object value) { FieldDescriptor descriptor = getDescriptor(); if (descriptor.isRepeated()) { if (descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE || descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) { // Must convert the whole list. - final List result = new ArrayList(); - for (final Object element : (List) value) { + final List result = new ArrayList<>(); + for (final Object element : (List) value) { result.add(singularFromReflectionType(element)); } return result; @@ -1778,7 +1856,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial return messageDefaultInstance.newBuilderForType().mergeFrom((Message) value).build(); } case ENUM: - return invokeOrDie(enumValueOf, null, (EnumValueDescriptor) value); + return invokeOrDie(enumValueOf, null, value); default: return value; } @@ -1790,14 +1868,13 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial * use the generated enum type. */ @Override - @SuppressWarnings("unchecked") protected Object toReflectionType(final Object value) { FieldDescriptor descriptor = getDescriptor(); if (descriptor.isRepeated()) { if (descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) { // Must convert the whole list. - final List result = new ArrayList(); - for (final Object element : (List) value) { + final List result = new ArrayList<>(); + for (final Object element : (List) value) { result.add(singularToReflectionType(element)); } return result; @@ -1841,40 +1918,40 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial @Override @SuppressWarnings("unchecked") - public Type getDefaultValue() { + public T getDefaultValue() { if (isRepeated()) { - return (Type) Collections.emptyList(); + return (T) Collections.emptyList(); } if (getDescriptor().getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - return (Type) messageDefaultInstance; + return (T) messageDefaultInstance; } - return (Type) singularFromReflectionType(getDescriptor().getDefaultValue()); + return (T) singularFromReflectionType(getDescriptor().getDefaultValue()); } } // ================================================================= /** Calls Class.getMethod and throws a RuntimeException if it fails. */ - @SuppressWarnings("unchecked") private static Method getMethodOrDie( - final Class clazz, final String name, final Class... params) { + final Class clazz, final String name, final Class... params) { try { return clazz.getMethod(name, params); } catch (NoSuchMethodException e) { - throw new RuntimeException( + throw new IllegalStateException( "Generated message class \"" + clazz.getName() + "\" missing method \"" + name + "\".", e); } } /** Calls invoke and throws a RuntimeException if it fails. */ + @CanIgnoreReturnValue private static Object invokeOrDie( final Method method, final Object object, final Object... params) { try { return method.invoke(object, params); } catch (IllegalAccessException e) { - throw new RuntimeException( - "Couldn't use Java reflection to implement protocol message " + "reflection.", e); + throw new IllegalStateException( + "Couldn't use Java reflection to implement protocol message reflection.", e); } catch (InvocationTargetException e) { final Throwable cause = e.getCause(); if (cause instanceof RuntimeException) { @@ -1882,7 +1959,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } else if (cause instanceof Error) { throw (Error) cause; } else { - throw new RuntimeException( + throw new IllegalStateException( "Unexpected exception thrown by generated accessor method.", cause); } } @@ -1897,18 +1974,26 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial * generated API only allows us to access it as a map. This method returns the underlying map * field directly and thus enables us to access the map field as a list. */ + @SuppressWarnings("unused") + protected MapFieldReflectionAccessor internalGetMapFieldReflection(int fieldNumber) { + return internalGetMapField(fieldNumber); + } + + /** TODO: Remove, exists for compatibility with generated code. */ + @Deprecated @SuppressWarnings({"rawtypes", "unused"}) protected MapField internalGetMapField(int fieldNumber) { // Note that we can't use descriptor names here because this method will // be called when descriptor is being initialized. - throw new RuntimeException("No map fields found in " + getClass().getName()); + throw new IllegalArgumentException("No map fields found in " + getClass().getName()); } /** * Users should ignore this class. This class provides the implementation with access to the * fields of a message object using Java reflection. */ - public static final class FieldAccessorTable { + // TODO: Should be marked final in v5.x.x once GeneratedMessageV3 is removed. + public static class FieldAccessorTable { /** * Construct a FieldAccessorTable for a particular message class. Only one FieldAccessorTable @@ -1924,7 +2009,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial final Descriptor descriptor, final String[] camelCaseNames, final Class messageClass, - final Class builderClass) { + final Class> builderClass) { this(descriptor, camelCaseNames); ensureFieldAccessorsInitialized(messageClass, builderClass); } @@ -1941,11 +2026,6 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial initialized = false; } - private boolean isMapFieldEnabled(FieldDescriptor field) { - boolean result = true; - return result; - } - /** * Ensures the field accessors are initialized. This method is thread-safe. * @@ -1954,7 +2034,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial * @return this */ public FieldAccessorTable ensureFieldAccessorsInitialized( - Class messageClass, Class builderClass) { + Class messageClass, Class> builderClass) { if (initialized) { return this; } @@ -1967,14 +2047,15 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial FieldDescriptor field = descriptor.getFields().get(i); String containingOneofCamelCaseName = null; if (field.getContainingOneof() != null) { - containingOneofCamelCaseName = - camelCaseNames[fieldsSize + field.getContainingOneof().getIndex()]; + int index = fieldsSize + field.getContainingOneof().getIndex(); + if (index < camelCaseNames.length) { + containingOneofCamelCaseName = camelCaseNames[index]; + } } if (field.isRepeated()) { if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - if (field.isMapField() && isMapFieldEnabled(field)) { - fields[i] = - new MapFieldAccessor(field, camelCaseNames[i], messageClass, builderClass); + if (field.isMapField()) { + fields[i] = new MapFieldAccessor(field, messageClass); } else { fields[i] = new RepeatedMessageFieldAccessor( @@ -2025,11 +2106,14 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } } - int oneofsSize = oneofs.length; - for (int i = 0; i < oneofsSize; i++) { - oneofs[i] = - new OneofAccessor( - descriptor, camelCaseNames[i + fieldsSize], messageClass, builderClass); + for (int i = 0; i < descriptor.getOneofs().size(); i++) { + if (i < descriptor.getRealOneofs().size()) { + oneofs[i] = + new RealOneofAccessor( + descriptor, camelCaseNames[i + fieldsSize], messageClass, builderClass); + } else { + oneofs[i] = new SyntheticOneofAccessor(descriptor, i); + } } initialized = true; camelCaseNames = null; @@ -2070,50 +2154,57 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial private interface FieldAccessor { Object get(GeneratedMessage message); - Object get(GeneratedMessage.Builder builder); + Object get(GeneratedMessage.Builder builder); Object getRaw(GeneratedMessage message); - Object getRaw(GeneratedMessage.Builder builder); - - void set(Builder builder, Object value); + void set(Builder builder, Object value); Object getRepeated(GeneratedMessage message, int index); - Object getRepeated(GeneratedMessage.Builder builder, int index); - - Object getRepeatedRaw(GeneratedMessage message, int index); + Object getRepeated(GeneratedMessage.Builder builder, int index); - Object getRepeatedRaw(GeneratedMessage.Builder builder, int index); + void setRepeated(Builder builder, int index, Object value); - void setRepeated(Builder builder, int index, Object value); - - void addRepeated(Builder builder, Object value); + void addRepeated(Builder builder, Object value); boolean has(GeneratedMessage message); - boolean has(GeneratedMessage.Builder builder); + boolean has(GeneratedMessage.Builder builder); int getRepeatedCount(GeneratedMessage message); - int getRepeatedCount(GeneratedMessage.Builder builder); + int getRepeatedCount(GeneratedMessage.Builder builder); - void clear(Builder builder); + void clear(Builder builder); Message.Builder newBuilder(); - Message.Builder getBuilder(GeneratedMessage.Builder builder); + Message.Builder getBuilder(GeneratedMessage.Builder builder); - Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder, int index); + Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder, int index); } /** OneofAccessor provides access to a single oneof. */ - private static class OneofAccessor { - OneofAccessor( + private static interface OneofAccessor { + public boolean has(final GeneratedMessage message); + + public boolean has(GeneratedMessage.Builder builder); + + public FieldDescriptor get(final GeneratedMessage message); + + public FieldDescriptor get(GeneratedMessage.Builder builder); + + public void clear(final Builder builder); + } + + /** RealOneofAccessor provides access to a single real oneof. */ + private static class RealOneofAccessor implements OneofAccessor { + RealOneofAccessor( final Descriptor descriptor, final String camelCaseName, final Class messageClass, - final Class builderClass) { + final Class> builderClass) { this.descriptor = descriptor; caseMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "Case"); caseMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName + "Case"); @@ -2125,20 +2216,17 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial private final Method caseMethodBuilder; private final Method clearMethod; + @Override public boolean has(final GeneratedMessage message) { - if (((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber() == 0) { - return false; - } - return true; + return ((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber() != 0; } - public boolean has(GeneratedMessage.Builder builder) { - if (((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber() == 0) { - return false; - } - return true; + @Override + public boolean has(GeneratedMessage.Builder builder) { + return ((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber() != 0; } + @Override public FieldDescriptor get(final GeneratedMessage message) { int fieldNumber = ((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber(); if (fieldNumber > 0) { @@ -2147,7 +2235,8 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial return null; } - public FieldDescriptor get(GeneratedMessage.Builder builder) { + @Override + public FieldDescriptor get(GeneratedMessage.Builder builder) { int fieldNumber = ((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber(); if (fieldNumber > 0) { return descriptor.findFieldByNumber(fieldNumber); @@ -2155,79 +2244,190 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial return null; } - public void clear(final Builder builder) { - invokeOrDie(clearMethod, builder); + @Override + public void clear(final Builder builder) { + // TODO: remove the unused variable + Object unused = invokeOrDie(clearMethod, builder); } } - private static boolean supportFieldPresence(FileDescriptor file) { - return file.getSyntax() == FileDescriptor.Syntax.PROTO2; + /** SyntheticOneofAccessor provides access to a single synthetic oneof. */ + private static class SyntheticOneofAccessor implements OneofAccessor { + SyntheticOneofAccessor(final Descriptor descriptor, final int oneofIndex) { + OneofDescriptor oneofDescriptor = descriptor.getOneofs().get(oneofIndex); + fieldDescriptor = oneofDescriptor.getFields().get(0); + } + + private final FieldDescriptor fieldDescriptor; + + @Override + public boolean has(final GeneratedMessage message) { + return message.hasField(fieldDescriptor); + } + + @Override + public boolean has(GeneratedMessage.Builder builder) { + return builder.hasField(fieldDescriptor); + } + + @Override + public FieldDescriptor get(final GeneratedMessage message) { + return message.hasField(fieldDescriptor) ? fieldDescriptor : null; + } + + public FieldDescriptor get(GeneratedMessage.Builder builder) { + return builder.hasField(fieldDescriptor) ? fieldDescriptor : null; + } + + @Override + public void clear(final Builder builder) { + builder.clearField(fieldDescriptor); + } } // --------------------------------------------------------------- + @SuppressWarnings("SameNameButDifferent") private static class SingularFieldAccessor implements FieldAccessor { + private interface MethodInvoker { + Object get(final GeneratedMessage message); + + Object get(GeneratedMessage.Builder builder); + + int getOneofFieldNumber(final GeneratedMessage message); + + int getOneofFieldNumber(final GeneratedMessage.Builder builder); + + void set(final GeneratedMessage.Builder builder, final Object value); + + boolean has(final GeneratedMessage message); + + boolean has(GeneratedMessage.Builder builder); + + void clear(final GeneratedMessage.Builder builder); + } + + private static final class ReflectionInvoker implements MethodInvoker { + private final Method getMethod; + private final Method getMethodBuilder; + private final Method setMethod; + private final Method hasMethod; + private final Method hasMethodBuilder; + private final Method clearMethod; + private final Method caseMethod; + private final Method caseMethodBuilder; + + ReflectionInvoker( + final String camelCaseName, + final Class messageClass, + final Class> builderClass, + final String containingOneofCamelCaseName, + boolean isOneofField, + boolean hasHasMethod) { + getMethod = getMethodOrDie(messageClass, "get" + camelCaseName); + getMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName); + Class type = getMethod.getReturnType(); + setMethod = getMethodOrDie(builderClass, "set" + camelCaseName, type); + hasMethod = hasHasMethod ? getMethodOrDie(messageClass, "has" + camelCaseName) : null; + hasMethodBuilder = + hasHasMethod ? getMethodOrDie(builderClass, "has" + camelCaseName) : null; + clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); + caseMethod = + isOneofField + ? getMethodOrDie(messageClass, "get" + containingOneofCamelCaseName + "Case") + : null; + caseMethodBuilder = + isOneofField + ? getMethodOrDie(builderClass, "get" + containingOneofCamelCaseName + "Case") + : null; + } + + @Override + public Object get(final GeneratedMessage message) { + return invokeOrDie(getMethod, message); + } + + @Override + public Object get(GeneratedMessage.Builder builder) { + return invokeOrDie(getMethodBuilder, builder); + } + + @Override + public int getOneofFieldNumber(final GeneratedMessage message) { + return ((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber(); + } + + @Override + public int getOneofFieldNumber(final GeneratedMessage.Builder builder) { + return ((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber(); + } + + @Override + public void set(final GeneratedMessage.Builder builder, final Object value) { + // TODO: remove the unused variable + Object unused = invokeOrDie(setMethod, builder, value); + } + + @Override + public boolean has(final GeneratedMessage message) { + return (Boolean) invokeOrDie(hasMethod, message); + } + + @Override + public boolean has(GeneratedMessage.Builder builder) { + return (Boolean) invokeOrDie(hasMethodBuilder, builder); + } + + @Override + public void clear(final GeneratedMessage.Builder builder) { + // TODO: remove the unused variable + Object unused = invokeOrDie(clearMethod, builder); + } + } + SingularFieldAccessor( final FieldDescriptor descriptor, final String camelCaseName, final Class messageClass, - final Class builderClass, + final Class> builderClass, final String containingOneofCamelCaseName) { + isOneofField = + descriptor.getRealContainingOneof() != null; + hasHasMethod = descriptor.hasPresence(); + ReflectionInvoker reflectionInvoker = + new ReflectionInvoker( + camelCaseName, + messageClass, + builderClass, + containingOneofCamelCaseName, + isOneofField, + hasHasMethod); field = descriptor; - isOneofField = descriptor.getContainingOneof() != null; - hasHasMethod = - supportFieldPresence(descriptor.getFile()) - || (!isOneofField && descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE); - getMethod = getMethodOrDie(messageClass, "get" + camelCaseName); - getMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName); - type = getMethod.getReturnType(); - setMethod = getMethodOrDie(builderClass, "set" + camelCaseName, type); - hasMethod = hasHasMethod ? getMethodOrDie(messageClass, "has" + camelCaseName) : null; - hasMethodBuilder = - hasHasMethod ? getMethodOrDie(builderClass, "has" + camelCaseName) : null; - clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); - caseMethod = - isOneofField - ? getMethodOrDie(messageClass, "get" + containingOneofCamelCaseName + "Case") - : null; - caseMethodBuilder = - isOneofField - ? getMethodOrDie(builderClass, "get" + containingOneofCamelCaseName + "Case") - : null; + type = reflectionInvoker.getMethod.getReturnType(); + invoker = getMethodInvoker(reflectionInvoker); + } + + static MethodInvoker getMethodInvoker(ReflectionInvoker accessor) { + return accessor; } // Note: We use Java reflection to call public methods rather than // access private fields directly as this avoids runtime security // checks. protected final Class type; - protected final Method getMethod; - protected final Method getMethodBuilder; - protected final Method setMethod; - protected final Method hasMethod; - protected final Method hasMethodBuilder; - protected final Method clearMethod; - protected final Method caseMethod; - protected final Method caseMethodBuilder; protected final FieldDescriptor field; protected final boolean isOneofField; protected final boolean hasHasMethod; - - private int getOneofFieldNumber(final GeneratedMessage message) { - return ((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber(); - } - - private int getOneofFieldNumber(final GeneratedMessage.Builder builder) { - return ((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber(); - } + protected final MethodInvoker invoker; @Override public Object get(final GeneratedMessage message) { - return invokeOrDie(getMethod, message); + return invoker.get(message); } @Override - public Object get(GeneratedMessage.Builder builder) { - return invokeOrDie(getMethodBuilder, builder); + public Object get(GeneratedMessage.Builder builder) { + return invoker.get(builder); } @Override @@ -2236,13 +2436,8 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public Object getRaw(GeneratedMessage.Builder builder) { - return get(builder); - } - - @Override - public void set(final Builder builder, final Object value) { - invokeOrDie(setMethod, builder, value); + public void set(final Builder builder, final Object value) { + invoker.set(builder, value); } @Override @@ -2251,29 +2446,17 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public Object getRepeatedRaw(final GeneratedMessage message, final int index) { - throw new UnsupportedOperationException( - "getRepeatedFieldRaw() called on a singular field."); - } - - @Override - public Object getRepeated(GeneratedMessage.Builder builder, int index) { + public Object getRepeated(GeneratedMessage.Builder builder, int index) { throw new UnsupportedOperationException("getRepeatedField() called on a singular field."); } @Override - public Object getRepeatedRaw(GeneratedMessage.Builder builder, int index) { - throw new UnsupportedOperationException( - "getRepeatedFieldRaw() called on a singular field."); - } - - @Override - public void setRepeated(final Builder builder, final int index, final Object value) { + public void setRepeated(final Builder builder, final int index, final Object value) { throw new UnsupportedOperationException("setRepeatedField() called on a singular field."); } @Override - public void addRepeated(final Builder builder, final Object value) { + public void addRepeated(final Builder builder, final Object value) { throw new UnsupportedOperationException("addRepeatedField() called on a singular field."); } @@ -2281,22 +2464,22 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial public boolean has(final GeneratedMessage message) { if (!hasHasMethod) { if (isOneofField) { - return getOneofFieldNumber(message) == field.getNumber(); + return invoker.getOneofFieldNumber(message) == field.getNumber(); } return !get(message).equals(field.getDefaultValue()); } - return (Boolean) invokeOrDie(hasMethod, message); + return invoker.has(message); } @Override - public boolean has(GeneratedMessage.Builder builder) { + public boolean has(GeneratedMessage.Builder builder) { if (!hasHasMethod) { if (isOneofField) { - return getOneofFieldNumber(builder) == field.getNumber(); + return invoker.getOneofFieldNumber(builder) == field.getNumber(); } return !get(builder).equals(field.getDefaultValue()); } - return (Boolean) invokeOrDie(hasMethodBuilder, builder); + return invoker.has(builder); } @Override @@ -2306,14 +2489,14 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public int getRepeatedCount(GeneratedMessage.Builder builder) { + public int getRepeatedCount(GeneratedMessage.Builder builder) { throw new UnsupportedOperationException( "getRepeatedFieldSize() called on a singular field."); } @Override - public void clear(final Builder builder) { - invokeOrDie(clearMethod, builder); + public void clear(final Builder builder) { + invoker.clear(builder); } @Override @@ -2323,56 +2506,145 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public Message.Builder getBuilder(GeneratedMessage.Builder builder) { + public Message.Builder getBuilder(GeneratedMessage.Builder builder) { throw new UnsupportedOperationException("getFieldBuilder() called on a non-Message type."); } @Override - public Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder, int index) { + public Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder, int index) { throw new UnsupportedOperationException( "getRepeatedFieldBuilder() called on a non-Message type."); } } + @SuppressWarnings("SameNameButDifferent") private static class RepeatedFieldAccessor implements FieldAccessor { - protected final Class type; - protected final Method getMethod; - protected final Method getMethodBuilder; - protected final Method getRepeatedMethod; - protected final Method getRepeatedMethodBuilder; - protected final Method setRepeatedMethod; - protected final Method addRepeatedMethod; - protected final Method getCountMethod; - protected final Method getCountMethodBuilder; - protected final Method clearMethod; + interface MethodInvoker { + Object get(final GeneratedMessage message); + + Object get(GeneratedMessage.Builder builder); + + Object getRepeated(final GeneratedMessage message, final int index); + + Object getRepeated(GeneratedMessage.Builder builder, int index); + + void setRepeated( + final GeneratedMessage.Builder builder, final int index, final Object value); + + void addRepeated(final GeneratedMessage.Builder builder, final Object value); + + int getRepeatedCount(final GeneratedMessage message); + + int getRepeatedCount(GeneratedMessage.Builder builder); + + void clear(final GeneratedMessage.Builder builder); + } + + private static final class ReflectionInvoker implements MethodInvoker { + private final Method getMethod; + private final Method getMethodBuilder; + private final Method getRepeatedMethod; + private final Method getRepeatedMethodBuilder; + private final Method setRepeatedMethod; + private final Method addRepeatedMethod; + private final Method getCountMethod; + private final Method getCountMethodBuilder; + private final Method clearMethod; + + ReflectionInvoker( + final String camelCaseName, + final Class messageClass, + final Class> builderClass) { + getMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "List"); + getMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName + "List"); + getRepeatedMethod = getMethodOrDie(messageClass, "get" + camelCaseName, Integer.TYPE); + getRepeatedMethodBuilder = + getMethodOrDie(builderClass, "get" + camelCaseName, Integer.TYPE); + Class type = getRepeatedMethod.getReturnType(); + setRepeatedMethod = + getMethodOrDie(builderClass, "set" + camelCaseName, Integer.TYPE, type); + addRepeatedMethod = getMethodOrDie(builderClass, "add" + camelCaseName, type); + getCountMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "Count"); + getCountMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName + "Count"); + clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); + } + + @Override + public Object get(final GeneratedMessage message) { + return invokeOrDie(getMethod, message); + } + + @Override + public Object get(GeneratedMessage.Builder builder) { + return invokeOrDie(getMethodBuilder, builder); + } + + @Override + public Object getRepeated(final GeneratedMessage message, final int index) { + return invokeOrDie(getRepeatedMethod, message, index); + } + + @Override + public Object getRepeated(GeneratedMessage.Builder builder, int index) { + return invokeOrDie(getRepeatedMethodBuilder, builder, index); + } + + @Override + public void setRepeated( + final GeneratedMessage.Builder builder, final int index, final Object value) { + // TODO: remove the unused variable + Object unused = invokeOrDie(setRepeatedMethod, builder, index, value); + } + + @Override + public void addRepeated(final GeneratedMessage.Builder builder, final Object value) { + // TODO: remove the unused variable + Object unused = invokeOrDie(addRepeatedMethod, builder, value); + } + + @Override + public int getRepeatedCount(final GeneratedMessage message) { + return (Integer) invokeOrDie(getCountMethod, message); + } + + @Override + public int getRepeatedCount(GeneratedMessage.Builder builder) { + return (Integer) invokeOrDie(getCountMethodBuilder, builder); + } + + @Override + public void clear(final GeneratedMessage.Builder builder) { + // TODO: remove the unused variable + Object unused = invokeOrDie(clearMethod, builder); + } + } + + protected final Class type; + protected final MethodInvoker invoker; RepeatedFieldAccessor( final FieldDescriptor descriptor, final String camelCaseName, final Class messageClass, - final Class builderClass) { - getMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "List"); - getMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName + "List"); - getRepeatedMethod = getMethodOrDie(messageClass, "get" + camelCaseName, Integer.TYPE); - getRepeatedMethodBuilder = - getMethodOrDie(builderClass, "get" + camelCaseName, Integer.TYPE); - type = getRepeatedMethod.getReturnType(); - setRepeatedMethod = getMethodOrDie(builderClass, "set" + camelCaseName, Integer.TYPE, type); - addRepeatedMethod = getMethodOrDie(builderClass, "add" + camelCaseName, type); - getCountMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "Count"); - getCountMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName + "Count"); + final Class> builderClass) { + ReflectionInvoker reflectionInvoker = + new ReflectionInvoker(camelCaseName, messageClass, builderClass); + type = reflectionInvoker.getRepeatedMethod.getReturnType(); + invoker = getMethodInvoker(reflectionInvoker); + } - clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); + static MethodInvoker getMethodInvoker(ReflectionInvoker accessor) { + return accessor; } @Override public Object get(final GeneratedMessage message) { - return invokeOrDie(getMethod, message); + return invoker.get(message); } @Override - public Object get(GeneratedMessage.Builder builder) { - return invokeOrDie(getMethodBuilder, builder); + public Object get(GeneratedMessage.Builder builder) { + return invoker.get(builder); } @Override @@ -2381,12 +2653,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public Object getRaw(GeneratedMessage.Builder builder) { - return get(builder); - } - - @Override - public void set(final Builder builder, final Object value) { + public void set(final Builder builder, final Object value) { // Add all the elements individually. This serves two purposes: // 1) Verifies that each element has the correct type. // 2) Insures that the caller cannot modify the list later on and @@ -2399,32 +2666,22 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial @Override public Object getRepeated(final GeneratedMessage message, final int index) { - return invokeOrDie(getRepeatedMethod, message, index); + return invoker.getRepeated(message, index); } @Override - public Object getRepeated(GeneratedMessage.Builder builder, int index) { - return invokeOrDie(getRepeatedMethodBuilder, builder, index); + public Object getRepeated(GeneratedMessage.Builder builder, int index) { + return invoker.getRepeated(builder, index); } @Override - public Object getRepeatedRaw(GeneratedMessage message, int index) { - return getRepeated(message, index); + public void setRepeated(final Builder builder, final int index, final Object value) { + invoker.setRepeated(builder, index, value); } @Override - public Object getRepeatedRaw(GeneratedMessage.Builder builder, int index) { - return getRepeated(builder, index); - } - - @Override - public void setRepeated(final Builder builder, final int index, final Object value) { - invokeOrDie(setRepeatedMethod, builder, index, value); - } - - @Override - public void addRepeated(final Builder builder, final Object value) { - invokeOrDie(addRepeatedMethod, builder, value); + public void addRepeated(final Builder builder, final Object value) { + invoker.addRepeated(builder, value); } @Override @@ -2433,23 +2690,23 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public boolean has(GeneratedMessage.Builder builder) { + public boolean has(GeneratedMessage.Builder builder) { throw new UnsupportedOperationException("hasField() called on a repeated field."); } @Override public int getRepeatedCount(final GeneratedMessage message) { - return (Integer) invokeOrDie(getCountMethod, message); + return invoker.getRepeatedCount(message); } @Override - public int getRepeatedCount(GeneratedMessage.Builder builder) { - return (Integer) invokeOrDie(getCountMethodBuilder, builder); + public int getRepeatedCount(GeneratedMessage.Builder builder) { + return invoker.getRepeatedCount(builder); } @Override - public void clear(final Builder builder) { - invokeOrDie(clearMethod, builder); + public void clear(final Builder builder) { + invoker.clear(builder); } @Override @@ -2459,12 +2716,12 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public Message.Builder getBuilder(GeneratedMessage.Builder builder) { + public Message.Builder getBuilder(GeneratedMessage.Builder builder) { throw new UnsupportedOperationException("getFieldBuilder() called on a non-Message type."); } @Override - public Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder, int index) { + public Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder, int index) { throw new UnsupportedOperationException( "getRepeatedFieldBuilder() called on a non-Message type."); } @@ -2472,13 +2729,10 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial private static class MapFieldAccessor implements FieldAccessor { MapFieldAccessor( - final FieldDescriptor descriptor, - final String camelCaseName, - final Class messageClass, - final Class builderClass) { + final FieldDescriptor descriptor, final Class messageClass) { field = descriptor; Method getDefaultInstanceMethod = getMethodOrDie(messageClass, "getDefaultInstance"); - MapField defaultMapField = + MapFieldReflectionAccessor defaultMapField = getMapField((GeneratedMessage) invokeOrDie(getDefaultInstanceMethod, null)); mapEntryMessageDefaultInstance = defaultMapField.getMapEntryMessageDefaultInstance(); } @@ -2486,22 +2740,35 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial private final FieldDescriptor field; private final Message mapEntryMessageDefaultInstance; - private MapField getMapField(GeneratedMessage message) { - return (MapField) message.internalGetMapField(field.getNumber()); + private MapFieldReflectionAccessor getMapField(GeneratedMessage message) { + return message.internalGetMapFieldReflection(field.getNumber()); } - private MapField getMapField(GeneratedMessage.Builder builder) { - return (MapField) builder.internalGetMapField(field.getNumber()); + private MapFieldReflectionAccessor getMapField(GeneratedMessage.Builder builder) { + return builder.internalGetMapFieldReflection(field.getNumber()); } - private MapField getMutableMapField(GeneratedMessage.Builder builder) { - return (MapField) builder.internalGetMutableMapField(field.getNumber()); + private MapFieldReflectionAccessor getMutableMapField(GeneratedMessage.Builder builder) { + return builder.internalGetMutableMapFieldReflection(field.getNumber()); + } + + private Message coerceType(Message value) { + if (value == null) { + return null; + } + if (mapEntryMessageDefaultInstance.getClass().isInstance(value)) { + return value; + } + // The value is not the exact right message type. However, if it + // is an alternative implementation of the same type -- e.g. a + // DynamicMessage -- we should accept it. In this case we can make + // a copy of the message. + return mapEntryMessageDefaultInstance.toBuilder().mergeFrom(value).build(); } @Override - @SuppressWarnings("unchecked") public Object get(GeneratedMessage message) { - List result = new ArrayList(); + List result = new ArrayList<>(); for (int i = 0; i < getRepeatedCount(message); i++) { result.add(getRepeated(message, i)); } @@ -2509,9 +2776,8 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - @SuppressWarnings("unchecked") - public Object get(Builder builder) { - List result = new ArrayList(); + public Object get(Builder builder) { + List result = new ArrayList<>(); for (int i = 0; i < getRepeatedCount(builder); i++) { result.add(getRepeated(builder, i)); } @@ -2524,14 +2790,9 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public Object getRaw(GeneratedMessage.Builder builder) { - return get(builder); - } - - @Override - public void set(Builder builder, Object value) { + public void set(Builder builder, Object value) { clear(builder); - for (Object entry : (List) value) { + for (Object entry : (List) value) { addRepeated(builder, entry); } } @@ -2542,28 +2803,18 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public Object getRepeated(Builder builder, int index) { + public Object getRepeated(Builder builder, int index) { return getMapField(builder).getList().get(index); } @Override - public Object getRepeatedRaw(GeneratedMessage message, int index) { - return getRepeated(message, index); - } - - @Override - public Object getRepeatedRaw(Builder builder, int index) { - return getRepeated(builder, index); + public void setRepeated(Builder builder, int index, Object value) { + getMutableMapField(builder).getMutableList().set(index, coerceType((Message) value)); } @Override - public void setRepeated(Builder builder, int index, Object value) { - getMutableMapField(builder).getMutableList().set(index, (Message) value); - } - - @Override - public void addRepeated(Builder builder, Object value) { - getMutableMapField(builder).getMutableList().add((Message) value); + public void addRepeated(Builder builder, Object value) { + getMutableMapField(builder).getMutableList().add(coerceType((Message) value)); } @Override @@ -2572,7 +2823,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public boolean has(Builder builder) { + public boolean has(Builder builder) { throw new UnsupportedOperationException("hasField() is not supported for repeated fields."); } @@ -2582,28 +2833,28 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public int getRepeatedCount(Builder builder) { + public int getRepeatedCount(Builder builder) { return getMapField(builder).getList().size(); } @Override - public void clear(Builder builder) { + public void clear(Builder builder) { getMutableMapField(builder).getMutableList().clear(); } @Override - public com.google.protobuf.Message.Builder newBuilder() { + public Message.Builder newBuilder() { return mapEntryMessageDefaultInstance.newBuilderForType(); } @Override - public com.google.protobuf.Message.Builder getBuilder(Builder builder) { + public Message.Builder getBuilder(Builder builder) { throw new UnsupportedOperationException("Nested builder not supported for map fields."); } @Override - public com.google.protobuf.Message.Builder getRepeatedBuilder(Builder builder, int index) { - throw new UnsupportedOperationException("Nested builder not supported for map fields."); + public Message.Builder getRepeatedBuilder(Builder builder, int index) { + throw new UnsupportedOperationException("Map fields cannot be repeated"); } } @@ -2614,7 +2865,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial final FieldDescriptor descriptor, final String camelCaseName, final Class messageClass, - final Class builderClass, + final Class> builderClass, final String containingOneofCamelCaseName) { super(descriptor, camelCaseName, messageClass, builderClass, containingOneofCamelCaseName); @@ -2631,12 +2882,12 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } } - private EnumDescriptor enumDescriptor; + private final EnumDescriptor enumDescriptor; - private Method valueOfMethod; - private Method getValueDescriptorMethod; + private final Method valueOfMethod; + private final Method getValueDescriptorMethod; - private boolean supportUnknownEnumValue; + private final boolean supportUnknownEnumValue; private Method getValueMethod; private Method getValueMethodBuilder; private Method setValueMethod; @@ -2651,7 +2902,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public Object get(final GeneratedMessage.Builder builder) { + public Object get(final GeneratedMessage.Builder builder) { if (supportUnknownEnumValue) { int value = (Integer) invokeOrDie(getValueMethodBuilder, builder); return enumDescriptor.findValueByNumberCreatingIfUnknown(value); @@ -2660,9 +2911,11 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public void set(final Builder builder, final Object value) { + public void set(final Builder builder, final Object value) { if (supportUnknownEnumValue) { - invokeOrDie(setValueMethod, builder, ((EnumValueDescriptor) value).getNumber()); + // TODO: remove the unused variable + Object unused = + invokeOrDie(setValueMethod, builder, ((EnumValueDescriptor) value).getNumber()); return; } super.set(builder, invokeOrDie(valueOfMethod, null, value)); @@ -2674,7 +2927,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial final FieldDescriptor descriptor, final String camelCaseName, final Class messageClass, - final Class builderClass) { + final Class> builderClass) { super(descriptor, camelCaseName, messageClass, builderClass); enumDescriptor = descriptor.getEnumType(); @@ -2695,21 +2948,21 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } } - private EnumDescriptor enumDescriptor; + private final EnumDescriptor enumDescriptor; private final Method valueOfMethod; private final Method getValueDescriptorMethod; - private boolean supportUnknownEnumValue; + private final boolean supportUnknownEnumValue; + private Method getRepeatedValueMethod; private Method getRepeatedValueMethodBuilder; private Method setRepeatedValueMethod; private Method addRepeatedValueMethod; @Override - @SuppressWarnings("unchecked") public Object get(final GeneratedMessage message) { - final List newList = new ArrayList(); + final List newList = new ArrayList<>(); final int size = getRepeatedCount(message); for (int i = 0; i < size; i++) { newList.add(getRepeated(message, i)); @@ -2718,9 +2971,8 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - @SuppressWarnings("unchecked") - public Object get(final GeneratedMessage.Builder builder) { - final List newList = new ArrayList(); + public Object get(final GeneratedMessage.Builder builder) { + final List newList = new ArrayList<>(); final int size = getRepeatedCount(builder); for (int i = 0; i < size; i++) { newList.add(getRepeated(builder, i)); @@ -2738,7 +2990,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public Object getRepeated(final GeneratedMessage.Builder builder, final int index) { + public Object getRepeated(final GeneratedMessage.Builder builder, final int index) { if (supportUnknownEnumValue) { int value = (Integer) invokeOrDie(getRepeatedValueMethodBuilder, builder, index); return enumDescriptor.findValueByNumberCreatingIfUnknown(value); @@ -2747,19 +2999,27 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public void setRepeated(final Builder builder, final int index, final Object value) { + public void setRepeated(final Builder builder, final int index, final Object value) { if (supportUnknownEnumValue) { - invokeOrDie( - setRepeatedValueMethod, builder, index, ((EnumValueDescriptor) value).getNumber()); + // TODO: remove the unused variable + Object unused = + invokeOrDie( + setRepeatedValueMethod, + builder, + index, + ((EnumValueDescriptor) value).getNumber()); return; } super.setRepeated(builder, index, invokeOrDie(valueOfMethod, null, value)); } @Override - public void addRepeated(final Builder builder, final Object value) { + public void addRepeated(final Builder builder, final Object value) { if (supportUnknownEnumValue) { - invokeOrDie(addRepeatedValueMethod, builder, ((EnumValueDescriptor) value).getNumber()); + // TODO: remove the unused variable + Object unused = + invokeOrDie( + addRepeatedValueMethod, builder, ((EnumValueDescriptor) value).getNumber()); return; } super.addRepeated(builder, invokeOrDie(valueOfMethod, null, value)); @@ -2783,17 +3043,15 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial final FieldDescriptor descriptor, final String camelCaseName, final Class messageClass, - final Class builderClass, + final Class> builderClass, final String containingOneofCamelCaseName) { super(descriptor, camelCaseName, messageClass, builderClass, containingOneofCamelCaseName); getBytesMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "Bytes"); - getBytesMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName + "Bytes"); setBytesMethodBuilder = getMethodOrDie(builderClass, "set" + camelCaseName + "Bytes", ByteString.class); } private final Method getBytesMethod; - private final Method getBytesMethodBuilder; private final Method setBytesMethodBuilder; @Override @@ -2802,14 +3060,10 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public Object getRaw(GeneratedMessage.Builder builder) { - return invokeOrDie(getBytesMethodBuilder, builder); - } - - @Override - public void set(GeneratedMessage.Builder builder, Object value) { + public void set(GeneratedMessage.Builder builder, Object value) { if (value instanceof ByteString) { - invokeOrDie(setBytesMethodBuilder, builder, value); + // TODO: remove the unused variable + Object unused = invokeOrDie(setBytesMethodBuilder, builder, value); } else { super.set(builder, value); } @@ -2823,7 +3077,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial final FieldDescriptor descriptor, final String camelCaseName, final Class messageClass, - final Class builderClass, + final Class> builderClass, final String containingOneofCamelCaseName) { super(descriptor, camelCaseName, messageClass, builderClass, containingOneofCamelCaseName); @@ -2849,7 +3103,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public void set(final Builder builder, final Object value) { + public void set(final Builder builder, final Object value) { super.set(builder, coerceType(value)); } @@ -2859,7 +3113,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public Message.Builder getBuilder(GeneratedMessage.Builder builder) { + public Message.Builder getBuilder(GeneratedMessage.Builder builder) { return (Message.Builder) invokeOrDie(getBuilderMethodBuilder, builder); } } @@ -2869,7 +3123,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial final FieldDescriptor descriptor, final String camelCaseName, final Class messageClass, - final Class builderClass) { + final Class> builderClass) { super(descriptor, camelCaseName, messageClass, builderClass); newBuilderMethod = getMethodOrDie(type, "newBuilder"); @@ -2895,12 +3149,12 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial } @Override - public void setRepeated(final Builder builder, final int index, final Object value) { + public void setRepeated(final Builder builder, final int index, final Object value) { super.setRepeated(builder, index, coerceType(value)); } @Override - public void addRepeated(final Builder builder, final Object value) { + public void addRepeated(final Builder builder, final Object value) { super.addRepeated(builder, coerceType(value)); } @@ -2911,7 +3165,7 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial @Override public Message.Builder getRepeatedBuilder( - final GeneratedMessage.Builder builder, final int index) { + final GeneratedMessage.Builder builder, final int index) { return (Message.Builder) invokeOrDie(getBuilderMethodBuilder, builder, index); } } @@ -2931,13 +3185,21 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial /** * Checks that the {@link Extension} is non-Lite and returns it as a {@link GeneratedExtension}. */ - private static , T> - Extension checkNotLite(ExtensionLite extension) { + private static , T> + Extension checkNotLite(ExtensionLite extension) { if (extension.isLite()) { throw new IllegalArgumentException("Expected non-lite extension."); } - return (Extension) extension; + return (Extension) extension; + } + + protected static boolean isStringEmpty(final Object value) { + if (value instanceof String) { + return ((String) value).isEmpty(); + } else { + return ((ByteString) value).isEmpty(); + } } protected static int computeStringSize(final int fieldNumber, final Object value) { @@ -2973,4 +3235,119 @@ public abstract class GeneratedMessage extends AbstractMessage implements Serial output.writeBytesNoTag((ByteString) value); } } + + protected static void serializeIntegerMapTo( + CodedOutputStream out, + MapField field, + MapEntry defaultEntry, + int fieldNumber) + throws IOException { + Map m = field.getMap(); + if (!out.isSerializationDeterministic()) { + serializeMapTo(out, m, defaultEntry, fieldNumber); + return; + } + // Sorting the unboxed keys and then look up the values during serialization is 2x faster + // than sorting map entries with a custom comparator directly. + int[] keys = new int[m.size()]; + int index = 0; + for (int k : m.keySet()) { + keys[index++] = k; + } + Arrays.sort(keys); + for (int key : keys) { + out.writeMessage( + fieldNumber, defaultEntry.newBuilderForType().setKey(key).setValue(m.get(key)).build()); + } + } + + protected static void serializeLongMapTo( + CodedOutputStream out, + MapField field, + MapEntry defaultEntry, + int fieldNumber) + throws IOException { + Map m = field.getMap(); + if (!out.isSerializationDeterministic()) { + serializeMapTo(out, m, defaultEntry, fieldNumber); + return; + } + + long[] keys = new long[m.size()]; + int index = 0; + for (long k : m.keySet()) { + keys[index++] = k; + } + Arrays.sort(keys); + for (long key : keys) { + out.writeMessage( + fieldNumber, defaultEntry.newBuilderForType().setKey(key).setValue(m.get(key)).build()); + } + } + + protected static void serializeStringMapTo( + CodedOutputStream out, + MapField field, + MapEntry defaultEntry, + int fieldNumber) + throws IOException { + Map m = field.getMap(); + if (!out.isSerializationDeterministic()) { + serializeMapTo(out, m, defaultEntry, fieldNumber); + return; + } + + // Sorting the String keys and then look up the values during serialization is 25% faster than + // sorting map entries with a custom comparator directly. + String[] keys = new String[m.size()]; + keys = m.keySet().toArray(keys); + Arrays.sort(keys); + for (String key : keys) { + out.writeMessage( + fieldNumber, defaultEntry.newBuilderForType().setKey(key).setValue(m.get(key)).build()); + } + } + + protected static void serializeBooleanMapTo( + CodedOutputStream out, + MapField field, + MapEntry defaultEntry, + int fieldNumber) + throws IOException { + Map m = field.getMap(); + if (!out.isSerializationDeterministic()) { + serializeMapTo(out, m, defaultEntry, fieldNumber); + return; + } + maybeSerializeBooleanEntryTo(out, m, defaultEntry, fieldNumber, false); + maybeSerializeBooleanEntryTo(out, m, defaultEntry, fieldNumber, true); + } + + private static void maybeSerializeBooleanEntryTo( + CodedOutputStream out, + Map m, + MapEntry defaultEntry, + int fieldNumber, + boolean key) + throws IOException { + if (m.containsKey(key)) { + out.writeMessage( + fieldNumber, defaultEntry.newBuilderForType().setKey(key).setValue(m.get(key)).build()); + } + } + + /** Serialize the map using the iteration order. */ + private static void serializeMapTo( + CodedOutputStream out, Map m, MapEntry defaultEntry, int fieldNumber) + throws IOException { + for (Map.Entry entry : m.entrySet()) { + out.writeMessage( + fieldNumber, + defaultEntry + .newBuilderForType() + .setKey(entry.getKey()) + .setValue(entry.getValue()) + .build()); + } + } } diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java index c153960de..b2045c11c 100644 --- a/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java +++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java @@ -220,7 +220,7 @@ public abstract class GeneratedMessageLite< @Override public final boolean isInitialized() { - return isInitialized((MessageType) this, Boolean.TRUE); + return isInitialized((MessageType) this, /* shouldMemoize= */ true); } @Override @@ -1440,7 +1440,7 @@ public abstract class GeneratedMessageLite< } catch (ClassNotFoundException e) { throw new RuntimeException("Unable to find proto buffer class: " + messageClassName, e); } catch (NoSuchFieldException e) { - return readResolveFallback(); + throw new RuntimeException("Unable to find DEFAULT_INSTANCE in " + messageClassName, e); } catch (SecurityException e) { throw new RuntimeException("Unable to call DEFAULT_INSTANCE in " + messageClassName, e); } catch (IllegalAccessException e) { @@ -1450,33 +1450,6 @@ public abstract class GeneratedMessageLite< } } - /** - * @deprecated from v3.0.0-beta-3+, for compatibility with v2.5.0 and v2.6.1 generated code. - */ - @Deprecated - private Object readResolveFallback() throws ObjectStreamException { - try { - Class messageClass = resolveMessageClass(); - java.lang.reflect.Field defaultInstanceField = - messageClass.getDeclaredField("defaultInstance"); - defaultInstanceField.setAccessible(true); - MessageLite defaultInstance = (MessageLite) defaultInstanceField.get(null); - return defaultInstance.newBuilderForType() - .mergeFrom(asBytes) - .buildPartial(); - } catch (ClassNotFoundException e) { - throw new RuntimeException("Unable to find proto buffer class: " + messageClassName, e); - } catch (NoSuchFieldException e) { - throw new RuntimeException("Unable to find defaultInstance in " + messageClassName, e); - } catch (SecurityException e) { - throw new RuntimeException("Unable to call defaultInstance in " + messageClassName, e); - } catch (IllegalAccessException e) { - throw new RuntimeException("Unable to call parsePartialFrom", e); - } catch (InvalidProtocolBufferException e) { - throw new RuntimeException("Unable to understand proto buffer", e); - } - } - private Class resolveMessageClass() throws ClassNotFoundException { return messageClass != null ? messageClass : Class.forName(messageClassName); } @@ -1648,10 +1621,17 @@ public abstract class GeneratedMessageLite< /** A static helper method for parsing a partial from byte array. */ private static > T parsePartialFrom( - T instance, byte[] input, int offset, int length, ExtensionRegistryLite extensionRegistry) + T defaultInstance, + byte[] input, + int offset, + int length, + ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { + if (length == 0) { + return defaultInstance; + } @SuppressWarnings("unchecked") // Guaranteed by protoc - T result = instance.newMutableInstance(); + T result = defaultInstance.newMutableInstance(); try { Schema schema = Protobuf.getInstance().schemaFor(result); schema.mergeFrom( diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java index 9c4980fcb..3f7104028 100644 --- a/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java +++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java @@ -1,5 +1,5 @@ // Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. +// Copyright 2024 Google LLC. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file or at @@ -7,542 +7,131 @@ package com.google.protobuf; -import static com.google.protobuf.Internal.checkNotNull; - import com.google.protobuf.Descriptors.Descriptor; -import com.google.protobuf.Descriptors.EnumDescriptor; -import com.google.protobuf.Descriptors.EnumValueDescriptor; import com.google.protobuf.Descriptors.FieldDescriptor; -import com.google.protobuf.Descriptors.FileDescriptor; import com.google.protobuf.Descriptors.OneofDescriptor; import com.google.protobuf.Internal.BooleanList; import com.google.protobuf.Internal.DoubleList; import com.google.protobuf.Internal.FloatList; import com.google.protobuf.Internal.IntList; import com.google.protobuf.Internal.LongList; -import com.google.protobuf.Internal.ProtobufList; -// In opensource protobuf, we have versioned this GeneratedMessageV3 class to GeneratedMessageV3 and -// in the future may have GeneratedMessageV4 etc. This allows us to change some aspects of this -// class without breaking binary compatibility with old generated code that still subclasses -// the old GeneratedMessageV3 class. To allow these different GeneratedMessageV? classes to -// interoperate (e.g., a GeneratedMessageV3 object has a message extension field whose class -// type is GeneratedMessageV4), these classes still share a common parent class AbstractMessage -// and are using the same GeneratedMessage.GeneratedExtension class for extension definitions. -// Since this class becomes GeneratedMessageV? in opensource, we have to add an import here -// to be able to use GeneratedMessage.GeneratedExtension. The GeneratedExtension definition in -// this file is also excluded from opensource to avoid conflict. -import com.google.protobuf.GeneratedMessage.GeneratedExtension; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectStreamException; -import java.io.Serializable; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; import java.util.List; -import java.util.Map; -import java.util.TreeMap; /** - * All generated protocol message classes extend this class. This class implements most of the - * Message and Builder interfaces using Java reflection. Users can ignore this class and pretend - * that generated messages implement the Message interface directly. + * Stub for GeneratedMessageV3 wrapping GeneratedMessage for compatibility with older gencode. + * + *

Extends GeneratedMessage.ExtendableMessage instead of GeneratedMessage to allow "multiple + * inheritance" for GeneratedMessageV3.ExtendableMessage subclass. * - * @author kenton@google.com Kenton Varda + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses GeneratedMessage instead. */ -public abstract class GeneratedMessageV3 extends AbstractMessage implements Serializable { +@Deprecated +public abstract class GeneratedMessageV3 + extends GeneratedMessage.ExtendableMessage { private static final long serialVersionUID = 1L; - /** - * For testing. Allows a test to disable the optimization that avoids using field builders for - * nested messages until they are requested. By disabling this optimization, existing tests can be - * reused to test the field builders. - */ - protected static boolean alwaysUseFieldBuilders = false; - - /** - * For use by generated code only. - * - *

TODO: mark this private and final (breaking change) - */ - protected UnknownFieldSet unknownFields; - + @Deprecated protected GeneratedMessageV3() { - unknownFields = UnknownFieldSet.getDefaultInstance(); + super(); } + @Deprecated protected GeneratedMessageV3(Builder builder) { - unknownFields = builder.getUnknownFields(); - } - - /** TODO: Remove this unnecessary intermediate implementation of this method. */ - @Override - public Parser getParserForType() { - throw new UnsupportedOperationException("This is supposed to be overridden by subclasses."); - } - - /** - * TODO: Stop using SingleFieldBuilder and remove this setting - * - * @see #setAlwaysUseFieldBuildersForTesting(boolean) - */ - static void enableAlwaysUseFieldBuildersForTesting() { - setAlwaysUseFieldBuildersForTesting(true); - } - - /** - * For testing. Allows a test to disable/re-enable the optimization that avoids using field - * builders for nested messages until they are requested. By disabling this optimization, existing - * tests can be reused to test the field builders. See {@link RepeatedFieldBuilder} and {@link - * SingleFieldBuilder}. - * - *

TODO: Stop using SingleFieldBuilder and remove this setting - */ - static void setAlwaysUseFieldBuildersForTesting(boolean useBuilders) { - alwaysUseFieldBuilders = useBuilders; + super(builder); } - /** - * Get the FieldAccessorTable for this type. We can't have the message class pass this in to the - * constructor because of bootstrapping trouble with DescriptorProtos. - */ - protected abstract FieldAccessorTable internalGetFieldAccessorTable(); - - @Override - public Descriptor getDescriptorForType() { - return internalGetFieldAccessorTable().descriptor; - } - - /** - * TODO: This method should be removed. It enables parsing directly into an - * "immutable" message. Have to leave it for now to support old gencode. - * - * @deprecated use newBuilder().mergeFrom() instead + /* @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses makeMutableCopy() instead. */ @Deprecated - protected void mergeFromAndMakeImmutableInternal( - CodedInputStream input, ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { - Schema schema = Protobuf.getInstance().schemaFor(this); - try { - schema.mergeFrom(this, CodedInputStreamReader.forCodedInput(input), extensionRegistry); - } catch (InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (IOException e) { - throw new InvalidProtocolBufferException(e).setUnfinishedMessage(this); - } - schema.makeImmutable(this); - } - - /** - * Internal helper to return a modifiable map containing all the fields. The returned Map is - * modifiable so that the caller can add additional extension fields to implement {@link - * #getAllFields()}. - * - * @param getBytesForString whether to generate ByteString for string fields - */ - private Map getAllFieldsMutable(boolean getBytesForString) { - final TreeMap result = new TreeMap<>(); - final Descriptor descriptor = internalGetFieldAccessorTable().descriptor; - final List fields = descriptor.getFields(); - - for (int i = 0; i < fields.size(); i++) { - FieldDescriptor field = fields.get(i); - final OneofDescriptor oneofDescriptor = field.getContainingOneof(); - - /* - * If the field is part of a Oneof, then at maximum one field in the Oneof is set - * and it is not repeated. There is no need to iterate through the others. - */ - if (oneofDescriptor != null) { - // Skip other fields in the Oneof we know are not set - i += oneofDescriptor.getFieldCount() - 1; - if (!hasOneof(oneofDescriptor)) { - // If no field is set in the Oneof, skip all the fields in the Oneof - continue; - } - // Get the pointer to the only field which is set in the Oneof - field = getOneofFieldDescriptor(oneofDescriptor); - } else { - // If we are not in a Oneof, we need to check if the field is set and if it is repeated - if (field.isRepeated()) { - final List value = (List) getField(field); - if (!value.isEmpty()) { - result.put(field, value); - } - continue; - } - if (!hasField(field)) { - continue; - } - } - // Add the field to the map - if (getBytesForString && field.getJavaType() == FieldDescriptor.JavaType.STRING) { - result.put(field, getFieldRaw(field)); - } else { - result.put(field, getField(field)); - } - } - return result; - } - - // TODO: compute this at {@code build()} time in the Builder class. - @Override - public boolean isInitialized() { - for (final FieldDescriptor field : getDescriptorForType().getFields()) { - // Check that all required fields are present. - if (field.isRequired()) { - if (!hasField(field)) { - return false; - } - } - // Check that embedded messages are initialized. - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - if (field.isRepeated()) { - @SuppressWarnings("unchecked") - final List messageList = (List) getField(field); - for (final Message element : messageList) { - if (!element.isInitialized()) { - return false; - } - } - } else { - if (hasField(field) && !((Message) getField(field)).isInitialized()) { - return false; - } - } - } - } - - return true; - } - - @Override - public Map getAllFields() { - return Collections.unmodifiableMap(getAllFieldsMutable(/* getBytesForString= */ false)); - } - - /** - * Returns a collection of all the fields in this message which are set and their corresponding - * values. A singular ("required" or "optional") field is set iff hasField() returns true for that - * field. A "repeated" field is set iff getRepeatedFieldCount() is greater than zero. The values - * are exactly what would be returned by calling {@link #getFieldRaw(Descriptors.FieldDescriptor)} - * for each field. The map is guaranteed to be a sorted map, so iterating over it will return - * fields in order by field number. - */ - Map getAllFieldsRaw() { - return Collections.unmodifiableMap(getAllFieldsMutable(/* getBytesForString= */ true)); - } - - @Override - public boolean hasOneof(final OneofDescriptor oneof) { - return internalGetFieldAccessorTable().getOneof(oneof).has(this); - } - - @Override - public FieldDescriptor getOneofFieldDescriptor(final OneofDescriptor oneof) { - return internalGetFieldAccessorTable().getOneof(oneof).get(this); - } - - @Override - public boolean hasField(final FieldDescriptor field) { - return internalGetFieldAccessorTable().getField(field).has(this); - } - - @Override - public Object getField(final FieldDescriptor field) { - return internalGetFieldAccessorTable().getField(field).get(this); - } - - /** - * Obtains the value of the given field, or the default value if it is not set. For primitive - * fields, the boxed primitive value is returned. For enum fields, the EnumValueDescriptor for the - * value is returned. For embedded message fields, the sub-message is returned. For repeated - * fields, a java.util.List is returned. For present string fields, a ByteString is returned - * representing the bytes that the field contains. - */ - Object getFieldRaw(final FieldDescriptor field) { - return internalGetFieldAccessorTable().getField(field).getRaw(this); - } - - @Override - public int getRepeatedFieldCount(final FieldDescriptor field) { - return internalGetFieldAccessorTable().getField(field).getRepeatedCount(this); - } - - @Override - public Object getRepeatedField(final FieldDescriptor field, final int index) { - return internalGetFieldAccessorTable().getField(field).getRepeated(this, index); - } - - // TODO: This method should be final. - @Override - public UnknownFieldSet getUnknownFields() { - return unknownFields; - } - - // TODO: This should go away when Schema classes cannot modify immutable - // GeneratedMessageV3 objects anymore. - void setUnknownFields(UnknownFieldSet unknownFields) { - this.unknownFields = unknownFields; - } - - /** - * Called by subclasses to parse an unknown field. - * - *

TODO remove this method - * - * @return {@code true} unless the tag is an end-group tag. - */ - protected boolean parseUnknownField( - CodedInputStream input, - UnknownFieldSet.Builder unknownFields, - ExtensionRegistryLite extensionRegistry, - int tag) - throws IOException { - if (input.shouldDiscardUnknownFields()) { - return input.skipField(tag); - } - return unknownFields.mergeFieldFrom(tag, input); - } - - /** - * Delegates to parseUnknownField. This method is obsolete, but we must retain it for - * compatibility with older generated code. - * - *

TODO remove this method - */ - protected boolean parseUnknownFieldProto3( - CodedInputStream input, - UnknownFieldSet.Builder unknownFields, - ExtensionRegistryLite extensionRegistry, - int tag) - throws IOException { - return parseUnknownField(input, unknownFields, extensionRegistry, tag); - } - - /** Used by generated code. */ - @SuppressWarnings("ProtoParseWithRegistry") - protected static M parseWithIOException(Parser parser, InputStream input) - throws IOException { - try { - return parser.parseFrom(input); - } catch (InvalidProtocolBufferException e) { - throw e.unwrapIOException(); - } - } - - /** Used by generated code. */ - protected static M parseWithIOException( - Parser parser, InputStream input, ExtensionRegistryLite extensions) throws IOException { - try { - return parser.parseFrom(input, extensions); - } catch (InvalidProtocolBufferException e) { - throw e.unwrapIOException(); - } - } - - /** Used by generated code. */ - @SuppressWarnings("ProtoParseWithRegistry") - protected static M parseWithIOException( - Parser parser, CodedInputStream input) throws IOException { - try { - return parser.parseFrom(input); - } catch (InvalidProtocolBufferException e) { - throw e.unwrapIOException(); - } - } - - /** Used by generated code. */ - protected static M parseWithIOException( - Parser parser, CodedInputStream input, ExtensionRegistryLite extensions) - throws IOException { - try { - return parser.parseFrom(input, extensions); - } catch (InvalidProtocolBufferException e) { - throw e.unwrapIOException(); - } - } - - /** Used by generated code. */ - @SuppressWarnings("ProtoParseWithRegistry") - protected static M parseDelimitedWithIOException( - Parser parser, InputStream input) throws IOException { - try { - return parser.parseDelimitedFrom(input); - } catch (InvalidProtocolBufferException e) { - throw e.unwrapIOException(); - } - } - - /** Used by generated code. */ - protected static M parseDelimitedWithIOException( - Parser parser, InputStream input, ExtensionRegistryLite extensions) throws IOException { - try { - return parser.parseDelimitedFrom(input, extensions); - } catch (InvalidProtocolBufferException e) { - throw e.unwrapIOException(); - } - } - - protected static boolean canUseUnsafe() { - return UnsafeUtil.hasUnsafeArrayOperations() && UnsafeUtil.hasUnsafeByteBufferOperations(); - } - - protected static IntList emptyIntList() { - return IntArrayList.emptyList(); - } - - // TODO: Unused. Remove. - protected static IntList newIntList() { - return new IntArrayList(); - } - - // TODO: Redundant with makeMutableCopy(). Remove. protected static IntList mutableCopy(IntList list) { return makeMutableCopy(list); } - // TODO: Redundant with makeMutableCopy(). Remove. + /* @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses makeMutableCopy() instead. + */ + @Deprecated protected static LongList mutableCopy(LongList list) { return makeMutableCopy(list); } - // TODO: Redundant with makeMutableCopy(). Remove. + /* @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses makeMutableCopy() instead. + */ + @Deprecated protected static FloatList mutableCopy(FloatList list) { return makeMutableCopy(list); } - // TODO: Redundant with makeMutableCopy(). Remove. + /* @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses makeMutableCopy() instead. + */ + @Deprecated protected static DoubleList mutableCopy(DoubleList list) { return makeMutableCopy(list); } - // TODO: Redundant with makeMutableCopy(). Remove. + /* @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses makeMutableCopy() instead. + */ + @Deprecated protected static BooleanList mutableCopy(BooleanList list) { return makeMutableCopy(list); } - protected static LongList emptyLongList() { - return LongArrayList.emptyList(); - } - - // TODO: Unused. Remove. - protected static LongList newLongList() { - return new LongArrayList(); - } - - protected static FloatList emptyFloatList() { - return FloatArrayList.emptyList(); - } - - // TODO: Unused. Remove. - protected static FloatList newFloatList() { - return new FloatArrayList(); - } - - protected static DoubleList emptyDoubleList() { - return DoubleArrayList.emptyList(); - } - - // TODO: Unused. Remove. - protected static DoubleList newDoubleList() { - return new DoubleArrayList(); - } - - protected static BooleanList emptyBooleanList() { - return BooleanArrayList.emptyList(); - } - - // TODO: Unused. Remove. - protected static BooleanList newBooleanList() { - return new BooleanArrayList(); - } - - protected static > ListT makeMutableCopy(ListT list) { - return makeMutableCopy(list, 0); - } - - @SuppressWarnings("unchecked") // Guaranteed by proto runtime. - protected static > ListT makeMutableCopy( - ListT list, int minCapacity) { - int size = list.size(); - if (minCapacity <= size) { - minCapacity = size * 2; - } - if (minCapacity <= 0) { - minCapacity = AbstractProtobufList.DEFAULT_CAPACITY; - } - - return (ListT) list.mutableCopyWithCapacity(minCapacity); - } - - @SuppressWarnings("unchecked") // The empty list can be safely cast - protected static ProtobufList emptyList(Class elementType) { - return (ProtobufList) ProtobufArrayList.emptyList(); - } - - @Override - public void writeTo(final CodedOutputStream output) throws IOException { - MessageReflection.writeMessageTo(this, getAllFieldsRaw(), output, false); - } - + /* Overrides abstract GeneratedMessage.internalGetFieldAccessorTable(). + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * GeneratedMessage.internalGetFieldAccessorTable() instead. + */ + @Deprecated @Override - public int getSerializedSize() { - int size = memoizedSize; - if (size != -1) { - return size; - } - - memoizedSize = MessageReflection.getSerializedSize( - this, getAllFieldsRaw()); - return memoizedSize; + protected FieldAccessorTable internalGetFieldAccessorTable() { + throw new UnsupportedOperationException("Should be overridden in gencode."); } /** - * This class is used to make a generated protected method inaccessible from user's code (e.g., - * the {@link #newInstance} method below). When this class is used as a parameter's type in a - * generated protected method, the method is visible to user's code in the same package, but since - * the constructor of this class is private to protobuf runtime, user's code can't obtain an - * instance of this class and as such can't actually make a method call on the protected method. + * Stub for GeneratedMessageV3.UnusedPrivateParameter for compatibility with older gencode. + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * GeneratedMessage.UnusedPrivateParameter instead. */ + @Deprecated protected static final class UnusedPrivateParameter { static final UnusedPrivateParameter INSTANCE = new UnusedPrivateParameter(); private UnusedPrivateParameter() {} } - /** Creates a new instance of this message type. Overridden in the generated code. */ + /* Stub for method overridden from old generated code + + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which overrides + * GeneratedMessage.newInstance() instead. + */ + @Deprecated @SuppressWarnings({"unused"}) protected Object newInstance(UnusedPrivateParameter unused) { throw new UnsupportedOperationException("This method must be overridden by the subclass."); } - /** - * Used by parsing constructors in generated classes. - * - *

TODO: remove unused method (extensions should be immutable after build) - */ - protected void makeExtensionsImmutable() { - // Noop for messages without extensions. - } - - /** - * TODO: remove this after b/29368482 is fixed. We need to move this interface to - * AbstractMessage in order to versioning GeneratedMessageV3 but this move breaks binary - * compatibility for AppEngine. After AppEngine is fixed we can exclude this from google3. - * - *

TODO: Remove at breaking change since b/29368482 was fixed in 2020 - */ + @Deprecated protected interface BuilderParent extends AbstractMessage.BuilderParent {} - /** TODO: remove this together with GeneratedMessageV3.BuilderParent. */ + @Deprecated protected abstract Message.Builder newBuilderForType(BuilderParent parent); - /** TODO: generated class should implement this directly */ + /* Removed from GeneratedMessage in + * https://github.com/protocolbuffers/protobuf/commit/787447430fc9a69c071393e85a380b664d261ab4 + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which no longer uses this method. + */ + @Deprecated @Override protected Message.Builder newBuilderForType(final AbstractMessage.BuilderParent parent) { return newBuilderForType( @@ -554,2872 +143,589 @@ public abstract class GeneratedMessageV3 extends AbstractMessage implements Seri }); } - /** Builder class for {@link GeneratedMessageV3}. */ - @SuppressWarnings("unchecked") + /** + * Stub for GeneratedMessageV3.Builder wrapping GeneratedMessage.Builder for compatibility with + * older gencode. + * + *

Extends GeneratedMessage.ExtendableBuilder instead of GeneratedMessage.Builder to allow + * "multiple inheritance" for GeneratedMessageV3.ExtendableBuilder subclass. + * + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses GeneratedMessage.Builder + * instead. + */ + @Deprecated public abstract static class Builder> - extends AbstractMessage.Builder { - - private BuilderParent builderParent; + extends GeneratedMessage.ExtendableBuilder { private BuilderParentImpl meAsParent; - // Indicates that we've built a message and so we are now obligated - // to dispatch dirty invalidations. See GeneratedMessageV3.BuilderListener. - private boolean isClean; - - /** - * This field holds either an {@link UnknownFieldSet} or {@link UnknownFieldSet.Builder}. - * - *

We use an object because it should only be one or the other of those things at a time and - * Object is the only common base. This also saves space. - * - *

Conversions are lazy: if {@link #setUnknownFields} is called, this will contain {@link - * UnknownFieldSet}. If unknown fields are merged into this builder, the current {@link - * UnknownFieldSet} will be converted to a {@link UnknownFieldSet.Builder} and left that way - * until either {@link #setUnknownFields} or {@link #buildPartial} or {@link #build} is called. - */ - private Object unknownFieldsOrBuilder = UnknownFieldSet.getDefaultInstance(); - + @Deprecated protected Builder() { - this(null); + super(null); } + @Deprecated protected Builder(BuilderParent builderParent) { - this.builderParent = builderParent; - } - - @Override - void dispose() { - builderParent = null; - } - - /** Called by the subclass when a message is built. */ - protected void onBuilt() { - if (builderParent != null) { - markClean(); - } - } - - /** - * Called by the subclass or a builder to notify us that a message was built and may be cached - * and therefore invalidations are needed. - */ - @Override - protected void markClean() { - this.isClean = true; + super(builderParent); } - /** - * Gets whether invalidations are needed + /* Stub for method overridden from old generated code removed in + * https://github.com/protocolbuffers/protobuf/commit/7bff169d32710b143951ec6ce2c4ea9a56e2ad24 * - * @return whether invalidations are needed - */ - protected boolean isClean() { - return isClean; - } - + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which overrides + * GeneratedMessage.Builder.clone() instead. */ + @Deprecated @Override public BuilderT clone() { - BuilderT builder = (BuilderT) getDefaultInstanceForType().newBuilderForType(); - builder.mergeFrom(buildPartial()); - return builder; + return super.clone(); } - /** - * Called by the initialization and clear code paths to allow subclasses to reset any of their - * builtin fields back to the initial values. - */ + /* Stub for method overridden from old generated code + * + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which overrides + * GeneratedMessage.Builder.setField() instead. */ + @Deprecated @Override public BuilderT clear() { - unknownFieldsOrBuilder = UnknownFieldSet.getDefaultInstance(); - onChanged(); - return (BuilderT) this; + return super.clear(); } - /** - * Get the FieldAccessorTable for this type. We can't have the message class pass this in to the - * constructor because of bootstrapping trouble with DescriptorProtos. + /* Overrides abstract GeneratedMessage.Builder.internalGetFieldAccessorTable(). + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Users should update gencode to >= 4.26.x which overrides + * GeneratedMessage.Builder.internalGetFieldAccessorTable() instead. */ - protected abstract FieldAccessorTable internalGetFieldAccessorTable(); - + @Deprecated @Override - public Descriptor getDescriptorForType() { - return internalGetFieldAccessorTable().descriptor; + protected FieldAccessorTable internalGetFieldAccessorTable() { + throw new UnsupportedOperationException("Should be overridden in gencode."); } + /* Stub for method overridden from old generated code removed in + * https://github.com/protocolbuffers/protobuf/commit/7bff169d32710b143951ec6ce2c4ea9a56e2ad24 + * + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which overrides + * GeneratedMessage.Builder.setField() instead. */ + @Deprecated @Override - public Map getAllFields() { - return Collections.unmodifiableMap(getAllFieldsMutable()); - } - - /** Internal helper which returns a mutable map. */ - private Map getAllFieldsMutable() { - final TreeMap result = new TreeMap<>(); - final Descriptor descriptor = internalGetFieldAccessorTable().descriptor; - final List fields = descriptor.getFields(); - - for (int i = 0; i < fields.size(); i++) { - FieldDescriptor field = fields.get(i); - final OneofDescriptor oneofDescriptor = field.getContainingOneof(); - - /* - * If the field is part of a Oneof, then at maximum one field in the Oneof is set - * and it is not repeated. There is no need to iterate through the others. - */ - if (oneofDescriptor != null) { - // Skip other fields in the Oneof we know are not set - i += oneofDescriptor.getFieldCount() - 1; - if (!hasOneof(oneofDescriptor)) { - // If no field is set in the Oneof, skip all the fields in the Oneof - continue; - } - // Get the pointer to the only field which is set in the Oneof - field = getOneofFieldDescriptor(oneofDescriptor); - } else { - // If we are not in a Oneof, we need to check if the field is set and if it is repeated - if (field.isRepeated()) { - final List value = (List) getField(field); - if (!value.isEmpty()) { - result.put(field, value); - } - continue; - } - if (!hasField(field)) { - continue; - } - } - // Add the field to the map - result.put(field, getField(field)); - } - return result; + public BuilderT setField(final FieldDescriptor field, final Object value) { + return super.setField(field, value); } + /* Stub for method overridden from old generated code removed in + * https://github.com/protocolbuffers/protobuf/commit/7bff169d32710b143951ec6ce2c4ea9a56e2ad24 + * + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which overrides + * GeneratedMessage.Builder.clearField() instead. */ + @Deprecated @Override - public Message.Builder newBuilderForField(final FieldDescriptor field) { - return internalGetFieldAccessorTable().getField(field).newBuilder(); + public BuilderT clearField(final FieldDescriptor field) { + return super.clearField(field); } + /* Stub for method overridden from old generated code removed in + * https://github.com/protocolbuffers/protobuf/commit/7bff169d32710b143951ec6ce2c4ea9a56e2ad24 + * + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which overrides + * GeneratedMessage.Builder.clearOneof() instead. */ + @Deprecated @Override - public Message.Builder getFieldBuilder(final FieldDescriptor field) { - return internalGetFieldAccessorTable().getField(field).getBuilder(this); + public BuilderT clearOneof(final OneofDescriptor oneof) { + return super.clearOneof(oneof); } + /* Stub for method overridden from old generated code removed in + * https://github.com/protocolbuffers/protobuf/commit/7bff169d32710b143951ec6ce2c4ea9a56e2ad24 + * + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which overrides + * GeneratedMessage.Builder.setRepeatedField() instead. */ + @Deprecated @Override - public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field, int index) { - return internalGetFieldAccessorTable().getField(field).getRepeatedBuilder(this, index); + public BuilderT setRepeatedField( + final FieldDescriptor field, final int index, final Object value) { + return super.setRepeatedField(field, index, value); } + /* Stub for method overridden from old generated code removed in + * https://github.com/protocolbuffers/protobuf/commit/7bff169d32710b143951ec6ce2c4ea9a56e2ad24 + * + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which overrides + * GeneratedMessage.Builder.addRepeatedField() instead. */ + @Deprecated @Override - public boolean hasOneof(final OneofDescriptor oneof) { - return internalGetFieldAccessorTable().getOneof(oneof).has(this); + public BuilderT addRepeatedField(final FieldDescriptor field, final Object value) { + return super.addRepeatedField(field, value); } + /* Stub for method overridden from old generated code removed in + * https://github.com/protocolbuffers/protobuf/commit/7bff169d32710b143951ec6ce2c4ea9a56e2ad24 + * + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which overrides + * GeneratedMessage.Builder.setUnknownFields() instead. */ + @Deprecated @Override - public FieldDescriptor getOneofFieldDescriptor(final OneofDescriptor oneof) { - return internalGetFieldAccessorTable().getOneof(oneof).get(this); + public BuilderT setUnknownFields(final UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); } + /* Stub for method overridden from old generated code removed in + * https://github.com/protocolbuffers/protobuf/commit/7bff169d32710b143951ec6ce2c4ea9a56e2ad24 + * + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which overrides + * GeneratedMessage.Builder.mergeUnknownFields() instead. */ + @Deprecated @Override - public boolean hasField(final FieldDescriptor field) { - return internalGetFieldAccessorTable().getField(field).has(this); + public BuilderT mergeUnknownFields(final UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); } - @Override - public Object getField(final FieldDescriptor field) { - Object object = internalGetFieldAccessorTable().getField(field).get(this); - if (field.isRepeated()) { - // The underlying list object is still modifiable at this point. - // Make sure not to expose the modifiable list to the caller. - return Collections.unmodifiableList((List) object); - } else { - return object; + @Deprecated + private class BuilderParentImpl implements BuilderParent { + @Override + public void markDirty() { + onChanged(); } } - @Override - public BuilderT setField(final FieldDescriptor field, final Object value) { - internalGetFieldAccessorTable().getField(field).set(this, value); - return (BuilderT) this; - } - - @Override - public BuilderT clearField(final FieldDescriptor field) { - internalGetFieldAccessorTable().getField(field).clear(this); - return (BuilderT) this; - } - - @Override - public BuilderT clearOneof(final OneofDescriptor oneof) { - internalGetFieldAccessorTable().getOneof(oneof).clear(this); - return (BuilderT) this; - } - - @Override - public int getRepeatedFieldCount(final FieldDescriptor field) { - return internalGetFieldAccessorTable().getField(field).getRepeatedCount(this); - } - - @Override - public Object getRepeatedField(final FieldDescriptor field, final int index) { - return internalGetFieldAccessorTable().getField(field).getRepeated(this, index); - } - - @Override - public BuilderT setRepeatedField( - final FieldDescriptor field, final int index, final Object value) { - internalGetFieldAccessorTable().getField(field).setRepeated(this, index, value); - return (BuilderT) this; - } - - @Override - public BuilderT addRepeatedField(final FieldDescriptor field, final Object value) { - internalGetFieldAccessorTable().getField(field).addRepeated(this, value); - return (BuilderT) this; - } - - private BuilderT setUnknownFieldsInternal(final UnknownFieldSet unknownFields) { - unknownFieldsOrBuilder = unknownFields; - onChanged(); - return (BuilderT) this; - } - - @Override - public BuilderT setUnknownFields(final UnknownFieldSet unknownFields) { - return setUnknownFieldsInternal(unknownFields); - } - - /** - * This method is obsolete, but we must retain it for compatibility with older generated code. - */ - protected BuilderT setUnknownFieldsProto3(final UnknownFieldSet unknownFields) { - return setUnknownFieldsInternal(unknownFields); - } - - @Override - public BuilderT mergeUnknownFields(final UnknownFieldSet unknownFields) { - if (UnknownFieldSet.getDefaultInstance().equals(unknownFields)) { - return (BuilderT) this; - } - - if (UnknownFieldSet.getDefaultInstance().equals(unknownFieldsOrBuilder)) { - unknownFieldsOrBuilder = unknownFields; - onChanged(); - return (BuilderT) this; - } - - getUnknownFieldSetBuilder().mergeFrom(unknownFields); - onChanged(); - return (BuilderT) this; - } - - @Override - public boolean isInitialized() { - for (final FieldDescriptor field : getDescriptorForType().getFields()) { - // Check that all required fields are present. - if (field.isRequired()) { - if (!hasField(field)) { - return false; - } - } - // Check that embedded messages are initialized. - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - if (field.isRepeated()) { - @SuppressWarnings("unchecked") - final List messageList = (List) getField(field); - for (final Message element : messageList) { - if (!element.isInitialized()) { - return false; - } - } - } else { - if (hasField(field) && !((Message) getField(field)).isInitialized()) { - return false; - } - } - } - } - return true; - } - - @Override - public final UnknownFieldSet getUnknownFields() { - if (unknownFieldsOrBuilder instanceof UnknownFieldSet) { - return (UnknownFieldSet) unknownFieldsOrBuilder; - } else { - return ((UnknownFieldSet.Builder) unknownFieldsOrBuilder).buildPartial(); - } - } - - /** - * Called by generated subclasses to parse an unknown field. + /* Returns GeneratedMessageV3.Builder.BuilderParent instead of + * GeneratedMessage.Builder.BuilderParent. * - * @return {@code true} unless the tag is an end-group tag. + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * GeneratedMessage.Builder.getParentForChildren() instead. */ - protected boolean parseUnknownField( - CodedInputStream input, ExtensionRegistryLite extensionRegistry, int tag) - throws IOException { - if (input.shouldDiscardUnknownFields()) { - return input.skipField(tag); - } - return getUnknownFieldSetBuilder().mergeFieldFrom(tag, input); - } - - /** Called by generated subclasses to add to the unknown field set. */ - protected final void mergeUnknownLengthDelimitedField(int number, ByteString bytes) { - getUnknownFieldSetBuilder().mergeLengthDelimitedField(number, bytes); - } - - /** Called by generated subclasses to add to the unknown field set. */ - protected final void mergeUnknownVarintField(int number, int value) { - getUnknownFieldSetBuilder().mergeVarintField(number, value); - } - - @Override - protected UnknownFieldSet.Builder getUnknownFieldSetBuilder() { - if (unknownFieldsOrBuilder instanceof UnknownFieldSet) { - unknownFieldsOrBuilder = ((UnknownFieldSet) unknownFieldsOrBuilder).toBuilder(); - } - onChanged(); - return (UnknownFieldSet.Builder) unknownFieldsOrBuilder; - } - + @Deprecated @Override - protected void setUnknownFieldSetBuilder(UnknownFieldSet.Builder builder) { - unknownFieldsOrBuilder = builder; - onChanged(); - } - - /** - * Implementation of {@link BuilderParent} for giving to our children. This small inner class - * makes it so we don't publicly expose the BuilderParent methods. - */ - private class BuilderParentImpl implements BuilderParent { - - @Override - public void markDirty() { - onChanged(); - } - } - - /** - * Gets the {@link BuilderParent} for giving to our children. - * - * @return The builder parent for our children. - */ protected BuilderParent getParentForChildren() { if (meAsParent == null) { meAsParent = new BuilderParentImpl(); } return meAsParent; } + } - /** - * Called when a builder or one of its nested children has changed and any parent should be - * notified of its invalidation. - */ - protected final void onChanged() { - if (isClean && builderParent != null) { - builderParent.markDirty(); - - // Don't keep dispatching invalidations until build is called again. - isClean = false; - } - } + /** + * Stub for GeneratedMessageV3.ExtendableMessageOrBuilder wrapping + * GeneratedMessage.ExtendableMessageOrBuilder for compatibility with older gencode. + * + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * GeneratedMessage.ExtendableMessageOrBuilder. + */ + @Deprecated + public interface ExtendableMessageOrBuilder> + extends GeneratedMessage.ExtendableMessageOrBuilder { - /** - * Gets the map field with the given field number. This method should be overridden in the - * generated message class if the message contains map fields. + /* Removed from GeneratedMessage.ExtendableMessageOrBuilder in + * https://github.com/protocolbuffers/protobuf/commit/94a2a448518403341b8aa71335ab1123fbdcccd8 * - *

Unlike other field types, reflection support for map fields can't be implemented based on - * generated public API because we need to access a map field as a list in reflection API but - * the generated API only allows us to access it as a map. This method returns the underlying - * map field directly and thus enables us to access the map field as a list. + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Users should update gencode to >= 4.26.x which no longer overrides this method. */ - @SuppressWarnings({"unused", "rawtypes"}) - protected MapFieldReflectionAccessor internalGetMapFieldReflection(int fieldNumber) { - return internalGetMapField(fieldNumber); - } - - /** TODO: Remove, exists for compatibility with generated code. */ @Deprecated - @SuppressWarnings({"unused", "rawtypes"}) - protected MapField internalGetMapField(int fieldNumber) { - // Note that we can't use descriptor names here because this method will - // be called when descriptor is being initialized. - throw new IllegalArgumentException("No map fields found in " + getClass().getName()); - } - - /** Like {@link #internalGetMapFieldReflection} but return a mutable version. */ - @SuppressWarnings({"unused", "rawtypes"}) - protected MapFieldReflectionAccessor internalGetMutableMapFieldReflection(int fieldNumber) { - return internalGetMutableMapField(fieldNumber); - } + boolean hasExtension(GeneratedExtension extension); - /** TODO: Remove, exists for compatibility with generated code. */ + /* Removed from GeneratedMessage.ExtendableMessageOrBuilder in + * https://github.com/protocolbuffers/protobuf/commit/94a2a448518403341b8aa71335ab1123fbdcccd8 + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Users should update gencode to >= 4.26.x which no longer overrides this method. + */ @Deprecated - @SuppressWarnings({"unused", "rawtypes"}) - protected MapField internalGetMutableMapField(int fieldNumber) { - // Note that we can't use descriptor names here because this method will - // be called when descriptor is being initialized. - throw new IllegalArgumentException("No map fields found in " + getClass().getName()); - } - } - - // ================================================================= - // Extensions-related stuff + int getExtensionCount(GeneratedExtension> extension); - /** Extends {@link MessageOrBuilder} with extension-related functions. */ - public interface ExtendableMessageOrBuilder> - extends MessageOrBuilder { - // Re-define for return type covariance. - @Override - Message getDefaultInstanceForType(); - - /** Check if a singular extension is present. */ - boolean hasExtension(ExtensionLite extension); - - /** Get the number of elements in a repeated extension. */ - int getExtensionCount(ExtensionLite> extension); - - /** Get the value of an extension. */ - T getExtension(ExtensionLite extension); - - /** Get one element of a repeated extension. */ - T getExtension(ExtensionLite> extension, int index); - - /** - * Check if a singular extension is present. - *

TODO: handled by ExtensionLite version - */ - boolean hasExtension( - Extension extension); - /** - * Check if a singular extension is present. - *

TODO: handled by ExtensionLite version - */ - boolean hasExtension( - GeneratedExtension extension); - /** - * Get the number of elements in a repeated extension. - *

TODO: handled by ExtensionLite version - */ - int getExtensionCount( - Extension> extension); - /** - * Get the number of elements in a repeated extension. - *

TODO: handled by ExtensionLite version - */ - int getExtensionCount( - GeneratedExtension> extension); - /** - * Get the value of an extension. - *

TODO: handled by ExtensionLite version - */ - T getExtension( - Extension extension); - /** - * Get the value of an extension. - *

TODO: handled by ExtensionLite version - */ - T getExtension( - GeneratedExtension extension); - /** - * Get one element of a repeated extension. - *

TODO: handled by ExtensionLite version + /* Removed from GeneratedMessage.ExtendableMessageOrBuilder in + * https://github.com/protocolbuffers/protobuf/commit/94a2a448518403341b8aa71335ab1123fbdcccd8 + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Users should update gencode to >= 4.26.x which no longer overrides this method. */ - T getExtension( - Extension> extension, - int index); - /** - * Get one element of a repeated extension. - *

TODO: handled by ExtensionLite version + @Deprecated + T getExtension(GeneratedExtension extension); + + /* Removed from GeneratedMessage.ExtendableMessageOrBuilder in + * https://github.com/protocolbuffers/protobuf/commit/94a2a448518403341b8aa71335ab1123fbdcccd8 + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Users should update gencode to >= 4.26.x which no longer overrides this method. */ - T getExtension( - GeneratedExtension> extension, - int index); + @Deprecated + T getExtension(GeneratedExtension> extension, int index); } /** - * Generated message classes for message types that contain extension ranges subclass this. - * - *

This class implements type-safe accessors for extensions. They implement all the same - * operations that you can do with normal fields -- e.g. "has", "get", and "getCount" -- but for - * extensions. The extensions are identified using instances of the class {@link - * GeneratedExtension}; the protocol compiler generates a static instance of this class for every - * extension in its input. Through the magic of generics, all is made type-safe. - * - *

For example, imagine you have the {@code .proto} file: - * - *

-   * option java_class = "MyProto";
-   *
-   * message Foo {
-   *   extensions 1000 to max;
-   * }
-   *
-   * extend Foo {
-   *   optional int32 bar;
-   * }
-   * 
- * - *

Then you might write code like: + * Stub for GeneratedMessageV3.ExtendableMessage wrapping GeneratedMessage.ExtendableMessage for + * compatibility with older gencode. * - *

-   * MyProto.Foo foo = getFoo();
-   * int i = foo.getExtension(MyProto.bar);
-   * 
- * - *

See also {@link ExtendableBuilder}. + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * GeneratedMessage.ExtendableMessage. */ + @Deprecated public abstract static class ExtendableMessage> - extends GeneratedMessageV3 implements ExtendableMessageOrBuilder { - - private static final long serialVersionUID = 1L; - - private final FieldSet extensions; + extends GeneratedMessageV3 // Extends GeneratedMessage.ExtendableMessage via + // GeneratedMessageV3 + implements ExtendableMessageOrBuilder { + @Deprecated protected ExtendableMessage() { - this.extensions = FieldSet.newFieldSet(); + super(); } + @Deprecated protected ExtendableMessage(ExtendableBuilder builder) { super(builder); - this.extensions = builder.buildExtensions(); - } - - private void verifyExtensionContainingType(final Extension extension) { - if (extension.getDescriptor().getContainingType() != getDescriptorForType()) { - // This can only happen if someone uses unchecked operations. - throw new IllegalArgumentException( - "Extension is for type \"" - + extension.getDescriptor().getContainingType().getFullName() - + "\" which does not match message type \"" - + getDescriptorForType().getFullName() - + "\"."); - } - } - - /** Check if a singular extension is present. */ - @Override - public final boolean hasExtension(final ExtensionLite extensionLite) { - Extension extension = checkNotLite(extensionLite); - - verifyExtensionContainingType(extension); - return extensions.hasField(extension.getDescriptor()); - } - - /** Get the number of elements in a repeated extension. */ - @Override - public final int getExtensionCount(final ExtensionLite> extensionLite) { - Extension> extension = checkNotLite(extensionLite); - - verifyExtensionContainingType(extension); - final FieldDescriptor descriptor = extension.getDescriptor(); - return extensions.getRepeatedFieldCount(descriptor); - } - - /** Get the value of an extension. */ - @Override - @SuppressWarnings("unchecked") - public final T getExtension(final ExtensionLite extensionLite) { - Extension extension = checkNotLite(extensionLite); - - verifyExtensionContainingType(extension); - FieldDescriptor descriptor = extension.getDescriptor(); - final Object value = extensions.getField(descriptor); - if (value == null) { - if (descriptor.isRepeated()) { - return (T) Collections.emptyList(); - } else if (descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - return (T) extension.getMessageDefaultInstance(); - } else { - return (T) extension.fromReflectionType(descriptor.getDefaultValue()); - } - } else { - return (T) extension.fromReflectionType(value); - } - } - - /** Get one element of a repeated extension. */ - @Override - @SuppressWarnings("unchecked") - public final T getExtension( - final ExtensionLite> extensionLite, final int index) { - Extension> extension = checkNotLite(extensionLite); - - verifyExtensionContainingType(extension); - FieldDescriptor descriptor = extension.getDescriptor(); - return (T) - extension.singularFromReflectionType(extensions.getRepeatedField(descriptor, index)); } - /** - * Check if a singular extension is present. - *

TODO: handled by ExtensionLite version - */ - @Override - public final boolean hasExtension(final Extension extension) { - return hasExtension((ExtensionLite) extension); - } - /** - * Check if a singular extension is present. - *

TODO: handled by ExtensionLite version + /* Removed from GeneratedMessage.ExtendableMessage in + * https://github.com/protocolbuffers/protobuf/commit/94a2a448518403341b8aa71335ab1123fbdcccd8 + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Users should update gencode to >= 4.26.x which no longer overrides this method. */ + @Deprecated @Override - public final boolean hasExtension( - final GeneratedExtension extension) { + public final boolean hasExtension(final GeneratedExtension extension) { return hasExtension((ExtensionLite) extension); } - /** - * Get the number of elements in a repeated extension. - *

TODO: handled by ExtensionLite version - */ - @Override - public final int getExtensionCount( - final Extension> extension) { - return getExtensionCount((ExtensionLite>) extension); - } - /** - * Get the number of elements in a repeated extension. - *

TODO: handled by ExtensionLite version + + /* Removed from GeneratedMessage.ExtendableMessage in + * https://github.com/protocolbuffers/protobuf/commit/94a2a448518403341b8aa71335ab1123fbdcccd8 + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Users should update gencode to >= 4.26.x which no longer overrides this method. */ + @Deprecated @Override - public final int getExtensionCount( - final GeneratedExtension> extension) { + public final int getExtensionCount(final GeneratedExtension> extension) { return getExtensionCount((ExtensionLite>) extension); } - /** - * Get the value of an extension. - *

TODO: handled by ExtensionLite version - */ - @Override - public final T getExtension(final Extension extension) { - return getExtension((ExtensionLite) extension); - } - /** - * Get the value of an extension. - *

TODO: handled by ExtensionLite version + + /* Removed from GeneratedMessage.ExtendableMessage in + * https://github.com/protocolbuffers/protobuf/commit/94a2a448518403341b8aa71335ab1123fbdcccd8 + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Users should update gencode to >= 4.26.x which no longer overrides this method. */ + @Deprecated @Override - public final T getExtension( - final GeneratedExtension extension) { + public final T getExtension(final GeneratedExtension extension) { return getExtension((ExtensionLite) extension); } - /** - * Get one element of a repeated extension. - *

TODO: handled by ExtensionLite version - */ - @Override - public final T getExtension( - final Extension> extension, final int index) { - return getExtension((ExtensionLite>) extension, index); - } - /** - * Get one element of a repeated extension. - *

TODO: handled by ExtensionLite version + + /* Removed from GeneratedMessage.ExtendableMessage in + * https://github.com/protocolbuffers/protobuf/commit/94a2a448518403341b8aa71335ab1123fbdcccd8 + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Users should update gencode to >= 4.26.x which no longer overrides this method. */ + @Deprecated @Override public final T getExtension( final GeneratedExtension> extension, final int index) { return getExtension((ExtensionLite>) extension, index); } - /** Called by subclasses to check if all extensions are initialized. */ - protected boolean extensionsAreInitialized() { - return extensions.isInitialized(); - } - - // TODO: compute this in the builder at {@code build()} time. - @Override - public boolean isInitialized() { - return super.isInitialized() && extensionsAreInitialized(); - } - - // TODO: remove mutating method from immutable type - @Override - protected boolean parseUnknownField( - CodedInputStream input, - UnknownFieldSet.Builder unknownFields, - ExtensionRegistryLite extensionRegistry, - int tag) - throws IOException { - return MessageReflection.mergeFieldFrom( - input, - input.shouldDiscardUnknownFields() ? null : unknownFields, - extensionRegistry, - getDescriptorForType(), - new MessageReflection.ExtensionAdapter(extensions), - tag); - } - - /** - * Delegates to parseUnknownField. This method is obsolete, but we must retain it for - * compatibility with older generated code. + /* Overrides abstract GeneratedMessage.ExtendableMessage.internalGetFieldAccessorTable(). * - *

TODO: remove mutating method from immutable type + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Users should update gencode to >= 4.26.x which overrides + * GeneratedMessage.ExtendableMessage.internalGetFieldAccessorTable() instead. */ + @Deprecated @Override - protected boolean parseUnknownFieldProto3( - CodedInputStream input, - UnknownFieldSet.Builder unknownFields, - ExtensionRegistryLite extensionRegistry, - int tag) - throws IOException { - return parseUnknownField(input, unknownFields, extensionRegistry, tag); + protected FieldAccessorTable internalGetFieldAccessorTable() { + throw new UnsupportedOperationException("Should be overridden in gencode."); } /** - * Used by parsing constructors in generated classes. + * Stub for GeneratedMessageV3.ExtendableMessage.ExtensionWriter wrapping + * GeneratedMessage.ExtendableMessage.ExtensionWriter for compatibility with older gencode. * - *

TODO: remove unused method (extensions should be immutable after build) - */ - @Override - protected void makeExtensionsImmutable() { - extensions.makeImmutable(); - } - - /** - * Used by subclasses to serialize extensions. Extension ranges may be interleaved with field - * numbers, but we must write them in canonical (sorted by field number) order. ExtensionWriter - * helps us write individual ranges of extensions at once. + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * GeneratedMessage.ExtendableMessage.ExtensionWriter instead. */ - protected class ExtensionWriter { - // Imagine how much simpler this code would be if Java iterators had - // a way to get the next element without advancing the iterator. - - private final Iterator> iter = extensions.iterator(); - private Map.Entry next; - private final boolean messageSetWireFormat; - + @Deprecated + protected class ExtensionWriter extends GeneratedMessage.ExtendableMessage.ExtensionWriter { private ExtensionWriter(final boolean messageSetWireFormat) { - if (iter.hasNext()) { - next = iter.next(); - } - this.messageSetWireFormat = messageSetWireFormat; - } - - public void writeUntil(final int end, final CodedOutputStream output) throws IOException { - while (next != null && next.getKey().getNumber() < end) { - FieldDescriptor descriptor = next.getKey(); - if (messageSetWireFormat - && descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE - && !descriptor.isRepeated()) { - if (next instanceof LazyField.LazyEntry) { - output.writeRawMessageSetExtension( - descriptor.getNumber(), - ((LazyField.LazyEntry) next).getField().toByteString()); - } else { - output.writeMessageSetExtension(descriptor.getNumber(), (Message) next.getValue()); - } - } else { - // TODO: Taken care of following code, it may cause - // problem when we use LazyField for normal fields/extensions. - // Due to the optional field can be duplicated at the end of - // serialized bytes, which will make the serialized size change - // after lazy field parsed. So when we use LazyField globally, - // we need to change the following write method to write cached - // bytes directly rather than write the parsed message. - FieldSet.writeField(descriptor, next.getValue(), output); - } - if (iter.hasNext()) { - next = iter.next(); - } else { - next = null; - } - } + super(messageSetWireFormat); } } + /* Returns GeneratedMessageV3.ExtendableMessage.ExtensionWriter instead of + * GeneratedMessage.ExtendableMessage.ExtensionWriter. + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Users should update gencode to >= 4.26.x which uses + * GeneratedMessage.ExtendableMessage.newExtensionWriter() instead. + */ + @Deprecated + @Override protected ExtensionWriter newExtensionWriter() { return new ExtensionWriter(false); } + /* Returns GeneratedMessageV3.ExtendableMessage.ExtensionWriter instead of + * GeneratedMessage.ExtendableMessage.ExtensionWriter. + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Users should update gencode to >= 4.26.x which uses + * GeneratedMessage.ExtendableMessage.newMessageSetExtensionWriter() instead. + */ + @Deprecated + @Override protected ExtensionWriter newMessageSetExtensionWriter() { return new ExtensionWriter(true); } + } - /** Called by subclasses to compute the size of extensions. */ - protected int extensionsSerializedSize() { - return extensions.getSerializedSize(); - } + /** + * Stub for GeneratedMessageV3.ExtendableBuilder wrapping GeneratedMessage.ExtendableBuilder for + * compatibility with older gencode. + * + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * GeneratedMessage.ExtendableBuilder instead. + */ + @Deprecated + public abstract static class ExtendableBuilder< + MessageT extends ExtendableMessage, + BuilderT extends ExtendableBuilder> + extends Builder // Extends GeneratedMessage.ExtendableBuilder via Builder + implements ExtendableMessageOrBuilder { - protected int extensionsSerializedSizeAsMessageSet() { - return extensions.getMessageSetSerializedSize(); + @Deprecated + protected ExtendableBuilder() { + super(); } - // --------------------------------------------------------------- - // Reflection - - protected Map getExtensionFields() { - return extensions.getAllFields(); + @Deprecated + protected ExtendableBuilder(BuilderParent parent) { + super(parent); } + /* Removed from GeneratedMessage.ExtendableBuilder in + * https://github.com/protocolbuffers/protobuf/commit/94a2a448518403341b8aa71335ab1123fbdcccd8 + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Users should update gencode to >= 4.26.x which no longer overrides this method. + */ + @Deprecated @Override - public Map getAllFields() { - final Map result = - super.getAllFieldsMutable(/* getBytesForString= */ false); - result.putAll(getExtensionFields()); - return Collections.unmodifiableMap(result); + public final boolean hasExtension(final GeneratedExtension extension) { + return hasExtension((ExtensionLite) extension); } + /* Removed from GeneratedMessage.ExtendableBuilder in + * https://github.com/protocolbuffers/protobuf/commit/94a2a448518403341b8aa71335ab1123fbdcccd8 + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Users should update gencode to >= 4.26.x which no longer overrides this method. + */ + @Deprecated @Override - public Map getAllFieldsRaw() { - final Map result = - super.getAllFieldsMutable(/* getBytesForString= */ false); - result.putAll(getExtensionFields()); - return Collections.unmodifiableMap(result); + public final int getExtensionCount(final GeneratedExtension> extension) { + return getExtensionCount((ExtensionLite>) extension); } + /* Removed from GeneratedMessage.ExtendableBuilder in + * https://github.com/protocolbuffers/protobuf/commit/94a2a448518403341b8aa71335ab1123fbdcccd8 + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Users should update gencode to >= 4.26.x which no longer overrides this method. + */ + @Deprecated @Override - public boolean hasField(final FieldDescriptor field) { - if (field.isExtension()) { - verifyContainingType(field); - return extensions.hasField(field); - } else { - return super.hasField(field); - } + public final T getExtension(final GeneratedExtension extension) { + return getExtension((ExtensionLite) extension); } - @Override - public Object getField(final FieldDescriptor field) { - if (field.isExtension()) { - verifyContainingType(field); - final Object value = extensions.getField(field); - if (value == null) { - if (field.isRepeated()) { - return Collections.emptyList(); - } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - // Lacking an ExtensionRegistry, we have no way to determine the - // extension's real type, so we return a DynamicMessage. - return DynamicMessage.getDefaultInstance(field.getMessageType()); - } else { - return field.getDefaultValue(); - } - } else { - return value; - } - } else { - return super.getField(field); - } - } - - @Override - public int getRepeatedFieldCount(final FieldDescriptor field) { - if (field.isExtension()) { - verifyContainingType(field); - return extensions.getRepeatedFieldCount(field); - } else { - return super.getRepeatedFieldCount(field); - } - } - - @Override - public Object getRepeatedField(final FieldDescriptor field, final int index) { - if (field.isExtension()) { - verifyContainingType(field); - return extensions.getRepeatedField(field, index); - } else { - return super.getRepeatedField(field, index); - } - } - - private void verifyContainingType(final FieldDescriptor field) { - if (field.getContainingType() != getDescriptorForType()) { - throw new IllegalArgumentException("FieldDescriptor does not match message type."); - } - } - } - - /** - * Generated message builders for message types that contain extension ranges subclass this. - * - *

This class implements type-safe accessors for extensions. They implement all the same - * operations that you can do with normal fields -- e.g. "get", "set", and "add" -- but for - * extensions. The extensions are identified using instances of the class {@link - * GeneratedExtension}; the protocol compiler generates a static instance of this class for every - * extension in its input. Through the magic of generics, all is made type-safe. - * - *

For example, imagine you have the {@code .proto} file: - * - *

-   * option java_class = "MyProto";
-   *
-   * message Foo {
-   *   extensions 1000 to max;
-   * }
-   *
-   * extend Foo {
-   *   optional int32 bar;
-   * }
-   * 
- * - *

Then you might write code like: - * - *

-   * MyProto.Foo foo =
-   *   MyProto.Foo.newBuilder()
-   *     .setExtension(MyProto.bar, 123)
-   *     .build();
-   * 
- * - *

See also {@link ExtendableMessage}. - */ - @SuppressWarnings("unchecked") - public abstract static class ExtendableBuilder< - MessageT extends ExtendableMessage, - BuilderT extends ExtendableBuilder> - extends Builder implements ExtendableMessageOrBuilder { - - private FieldSet.Builder extensions; - - protected ExtendableBuilder() {} - - protected ExtendableBuilder(BuilderParent parent) { - super(parent); - } - - // For immutable message conversion. - void internalSetExtensionSet(FieldSet extensions) { - this.extensions = FieldSet.Builder.fromFieldSet(extensions); - } - - @Override - public BuilderT clear() { - extensions = null; - return super.clear(); - } - - private void ensureExtensionsIsMutable() { - if (extensions == null) { - extensions = FieldSet.newBuilder(); - } - } - - private void verifyExtensionContainingType(final Extension extension) { - if (extension.getDescriptor().getContainingType() != getDescriptorForType()) { - // This can only happen if someone uses unchecked operations. - throw new IllegalArgumentException( - "Extension is for type \"" - + extension.getDescriptor().getContainingType().getFullName() - + "\" which does not match message type \"" - + getDescriptorForType().getFullName() - + "\"."); - } - } - - /** Check if a singular extension is present. */ - @Override - public final boolean hasExtension(final ExtensionLite extensionLite) { - Extension extension = checkNotLite(extensionLite); - - verifyExtensionContainingType(extension); - return extensions != null && extensions.hasField(extension.getDescriptor()); - } - - /** Get the number of elements in a repeated extension. */ - @Override - public final int getExtensionCount(final ExtensionLite> extensionLite) { - Extension> extension = checkNotLite(extensionLite); - - verifyExtensionContainingType(extension); - final FieldDescriptor descriptor = extension.getDescriptor(); - return extensions == null ? 0 : extensions.getRepeatedFieldCount(descriptor); - } - - /** Get the value of an extension. */ - @Override - public final T getExtension(final ExtensionLite extensionLite) { - Extension extension = checkNotLite(extensionLite); - - verifyExtensionContainingType(extension); - FieldDescriptor descriptor = extension.getDescriptor(); - final Object value = extensions == null ? null : extensions.getField(descriptor); - if (value == null) { - if (descriptor.isRepeated()) { - return (T) Collections.emptyList(); - } else if (descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - return (T) extension.getMessageDefaultInstance(); - } else { - return (T) extension.fromReflectionType(descriptor.getDefaultValue()); - } - } else { - return (T) extension.fromReflectionType(value); - } - } - - /** Get one element of a repeated extension. */ - @Override - public final T getExtension( - final ExtensionLite> extensionLite, final int index) { - Extension> extension = checkNotLite(extensionLite); - - verifyExtensionContainingType(extension); - FieldDescriptor descriptor = extension.getDescriptor(); - if (extensions == null) { - throw new IndexOutOfBoundsException(); - } - return (T) - extension.singularFromReflectionType(extensions.getRepeatedField(descriptor, index)); - } - - /** Set the value of an extension. */ - public final BuilderT setExtension( - final ExtensionLite extensionLite, final T value) { - Extension extension = checkNotLite(extensionLite); - - verifyExtensionContainingType(extension); - ensureExtensionsIsMutable(); - final FieldDescriptor descriptor = extension.getDescriptor(); - extensions.setField(descriptor, extension.toReflectionType(value)); - onChanged(); - return (BuilderT) this; - } - - /** Set the value of one element of a repeated extension. */ - public final BuilderT setExtension( - final ExtensionLite> extensionLite, final int index, final T value) { - Extension> extension = checkNotLite(extensionLite); - - verifyExtensionContainingType(extension); - ensureExtensionsIsMutable(); - final FieldDescriptor descriptor = extension.getDescriptor(); - extensions.setRepeatedField(descriptor, index, extension.singularToReflectionType(value)); - onChanged(); - return (BuilderT) this; - } - - /** Append a value to a repeated extension. */ - public final BuilderT addExtension( - final ExtensionLite> extensionLite, final T value) { - Extension> extension = checkNotLite(extensionLite); - - verifyExtensionContainingType(extension); - ensureExtensionsIsMutable(); - final FieldDescriptor descriptor = extension.getDescriptor(); - extensions.addRepeatedField(descriptor, extension.singularToReflectionType(value)); - onChanged(); - return (BuilderT) this; - } - - /** Clear an extension. */ - public final BuilderT clearExtension(final ExtensionLite extensionLite) { - Extension extension = checkNotLite(extensionLite); - - verifyExtensionContainingType(extension); - ensureExtensionsIsMutable(); - extensions.clearField(extension.getDescriptor()); - onChanged(); - return (BuilderT) this; - } - - /** - * Check if a singular extension is present. - *

TODO: handled by ExtensionLite version - */ - @Override - public final boolean hasExtension(final Extension extension) { - return hasExtension((ExtensionLite) extension); - } - /** - * Check if a singular extension is present. - *

TODO: handled by ExtensionLite version - */ - @Override - public final boolean hasExtension( - final GeneratedExtension extension) { - return hasExtension((ExtensionLite) extension); - } - /** - * Get the number of elements in a repeated extension. - *

TODO: handled by ExtensionLite version - */ - @Override - public final int getExtensionCount( - final Extension> extension) { - return getExtensionCount((ExtensionLite>) extension); - } - /** - * Get the number of elements in a repeated extension. - *

TODO: handled by ExtensionLite version - */ - @Override - public final int getExtensionCount( - final GeneratedExtension> extension) { - return getExtensionCount((ExtensionLite>) extension); - } - /** - * Get the value of an extension. - *

TODO: handled by ExtensionLite version - */ - @Override - public final T getExtension(final Extension extension) { - return getExtension((ExtensionLite) extension); - } - /** Get the value of an extension. - *

TODO: handled by ExtensionLite version - */ - @Override - public final T getExtension( - final GeneratedExtension extension) { - return getExtension((ExtensionLite) extension); - } - /** - * Get the value of an extension. - *

TODO: handled by ExtensionLite version - */ - @Override - public final T getExtension( - final Extension> extension, final int index) { - return getExtension((ExtensionLite>) extension, index); - } - /** - * Get the value of an extension. - *

TODO: handled by ExtensionLite version + /* Removed from GeneratedMessage.ExtendableBuilder in + * https://github.com/protocolbuffers/protobuf/commit/94a2a448518403341b8aa71335ab1123fbdcccd8 + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Users should update gencode to >= 4.26.x which no longer overrides this method. */ + @Deprecated @Override public final T getExtension( final GeneratedExtension> extension, final int index) { return getExtension((ExtensionLite>) extension, index); } - /** - * Set the value of an extension. - *

TODO: handled by ExtensionLite version - */ - public final BuilderT setExtension( - final Extension extension, final T value) { - return setExtension((ExtensionLite) extension, value); - } - /** - * Set the value of an extension. - *

TODO: handled by ExtensionLite version + + /* Removed from GeneratedMessage.ExtendableBuilder in + * https://github.com/protocolbuffers/protobuf/commit/94a2a448518403341b8aa71335ab1123fbdcccd8 + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which no longer overrides this method. */ + @Deprecated public BuilderT setExtension( - final GeneratedExtension extension, final T value) { + final GeneratedMessage.GeneratedExtension extension, final T value) { return setExtension((ExtensionLite) extension, value); } - /** - * Set the value of one element of a repeated extension. - *

TODO: handled by ExtensionLite version - */ - public final BuilderT setExtension( - final Extension> extension, - final int index, final T value) { - return setExtension((ExtensionLite>) extension, index, value); - } - /** - * Set the value of one element of a repeated extension. - *

TODO: handled by ExtensionLite version + + /* Removed from GeneratedMessage.ExtendableBuilder in + * https://github.com/protocolbuffers/protobuf/commit/94a2a448518403341b8aa71335ab1123fbdcccd8 + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which no longer overrides this method. */ + @Deprecated public BuilderT setExtension( - final GeneratedExtension> extension, - final int index, final T value) { + final GeneratedMessage.GeneratedExtension> extension, + final int index, + final T value) { return setExtension((ExtensionLite>) extension, index, value); } - /** - * Append a value to a repeated extension. - *

TODO: handled by ExtensionLite version - */ - public final BuilderT addExtension( - final Extension> extension, final T value) { - return addExtension((ExtensionLite>) extension, value); - } - /** - * Append a value to a repeated extension. - *

TODO: handled by ExtensionLite version + + /* Removed from GeneratedMessage.ExtendableBuilder in + * https://github.com/protocolbuffers/protobuf/commit/94a2a448518403341b8aa71335ab1123fbdcccd8 + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which no longer overrides this method. */ + @Deprecated public BuilderT addExtension( - final GeneratedExtension> extension, final T value) { + final GeneratedMessage.GeneratedExtension> extension, final T value) { return addExtension((ExtensionLite>) extension, value); } - /** - * Clear an extension. - *

TODO: handled by ExtensionLite version - */ - public final BuilderT clearExtension( - final Extension extension) { - return clearExtension((ExtensionLite) extension); - } - /** - * Clears an extension. - *

TODO: handled by ExtensionLite version + + /* Removed from GeneratedMessage.ExtendableBuilder in + * https://github.com/protocolbuffers/protobuf/commit/94a2a448518403341b8aa71335ab1123fbdcccd8 + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which no longer overrides this method. */ + @Deprecated public BuilderT clearExtension( - final GeneratedExtension extension) { + final GeneratedMessage.GeneratedExtension extension) { return clearExtension((ExtensionLite) extension); } - /** Called by subclasses to check if all extensions are initialized. */ - protected boolean extensionsAreInitialized() { - return extensions == null || extensions.isInitialized(); - } - - /** - * Called by the build code path to create a copy of the extensions for building the message. - */ - private FieldSet buildExtensions() { - return extensions == null - ? (FieldSet) FieldSet.emptySet() - : extensions.buildPartial(); - } - - @Override - public boolean isInitialized() { - return super.isInitialized() && extensionsAreInitialized(); - } - - // --------------------------------------------------------------- - // Reflection - - @Override - public Map getAllFields() { - final Map result = super.getAllFieldsMutable(); - if (extensions != null) { - result.putAll(extensions.getAllFields()); - } - return Collections.unmodifiableMap(result); - } - - @Override - public Object getField(final FieldDescriptor field) { - if (field.isExtension()) { - verifyContainingType(field); - final Object value = extensions == null ? null : extensions.getField(field); - if (value == null) { - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - // Lacking an ExtensionRegistry, we have no way to determine the - // extension's real type, so we return a DynamicMessage. - return DynamicMessage.getDefaultInstance(field.getMessageType()); - } else { - return field.getDefaultValue(); - } - } else { - return value; - } - } else { - return super.getField(field); - } - } - - @Override - public Message.Builder getFieldBuilder(final FieldDescriptor field) { - if (field.isExtension()) { - verifyContainingType(field); - if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { - throw new UnsupportedOperationException( - "getFieldBuilder() called on a non-Message type."); - } - ensureExtensionsIsMutable(); - final Object value = extensions.getFieldAllowBuilders(field); - if (value == null) { - Message.Builder builder = DynamicMessage.newBuilder(field.getMessageType()); - extensions.setField(field, builder); - onChanged(); - return builder; - } else { - if (value instanceof Message.Builder) { - return (Message.Builder) value; - } else if (value instanceof Message) { - Message.Builder builder = ((Message) value).toBuilder(); - extensions.setField(field, builder); - onChanged(); - return builder; - } else { - throw new UnsupportedOperationException( - "getRepeatedFieldBuilder() called on a non-Message type."); - } - } - } else { - return super.getFieldBuilder(field); - } - } - - @Override - public int getRepeatedFieldCount(final FieldDescriptor field) { - if (field.isExtension()) { - verifyContainingType(field); - return extensions == null ? 0 : extensions.getRepeatedFieldCount(field); - } else { - return super.getRepeatedFieldCount(field); - } - } - - @Override - public Object getRepeatedField(final FieldDescriptor field, final int index) { - if (field.isExtension()) { - verifyContainingType(field); - if (extensions == null) { - throw new IndexOutOfBoundsException(); - } - return extensions.getRepeatedField(field, index); - } else { - return super.getRepeatedField(field, index); - } - } - - @Override - public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field, final int index) { - if (field.isExtension()) { - verifyContainingType(field); - ensureExtensionsIsMutable(); - if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { - throw new UnsupportedOperationException( - "getRepeatedFieldBuilder() called on a non-Message type."); - } - final Object value = extensions.getRepeatedFieldAllowBuilders(field, index); - if (value instanceof Message.Builder) { - return (Message.Builder) value; - } else if (value instanceof Message) { - Message.Builder builder = ((Message) value).toBuilder(); - extensions.setRepeatedField(field, index, builder); - onChanged(); - return builder; - } else { - throw new UnsupportedOperationException( - "getRepeatedFieldBuilder() called on a non-Message type."); - } - } else { - return super.getRepeatedFieldBuilder(field, index); - } - } - + /* Stub for method overridden from old generated code removed in + * https://github.com/protocolbuffers/protobuf/commit/7bff169d32710b143951ec6ce2c4ea9a56e2ad24 + * + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which overrides + * GeneratedMessage.ExtendableBuilder.setField() instead. */ + @Deprecated @Override - public boolean hasField(final FieldDescriptor field) { - if (field.isExtension()) { - verifyContainingType(field); - return extensions != null && extensions.hasField(field); - } else { - return super.hasField(field); - } + public BuilderT setField(final FieldDescriptor field, final Object value) { + return super.setField(field, value); } + /* Stub for method overridden from old generated code removed in + * https://github.com/protocolbuffers/protobuf/commit/7bff169d32710b143951ec6ce2c4ea9a56e2ad24 + * + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which overrides + * GeneratedMessage.ExtendableBuilder.clearField() instead. */ + @Deprecated @Override - public BuilderT setField(final FieldDescriptor field, final Object value) { - if (field.isExtension()) { - verifyContainingType(field); - ensureExtensionsIsMutable(); - extensions.setField(field, value); - onChanged(); - return (BuilderT) this; - } else { - return super.setField(field, value); - } + public BuilderT clearField(final FieldDescriptor field) { + return super.clearField(field); } + /* Stub for method overridden from old generated code removed in + * https://github.com/protocolbuffers/protobuf/commit/7bff169d32710b143951ec6ce2c4ea9a56e2ad24 + * + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which overrides + * GeneratedMessage.ExtendableBuilder.clearOneof() instead. */ + @Deprecated @Override - public BuilderT clearField(final FieldDescriptor field) { - if (field.isExtension()) { - verifyContainingType(field); - ensureExtensionsIsMutable(); - extensions.clearField(field); - onChanged(); - return (BuilderT) this; - } else { - return super.clearField(field); - } + public BuilderT clearOneof(final OneofDescriptor oneof) { + return super.clearOneof(oneof); } + /* Stub for method overridden from old generated code removed in + * https://github.com/protocolbuffers/protobuf/commit/7bff169d32710b143951ec6ce2c4ea9a56e2ad24 + * + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which overrides + * GeneratedMessage.ExtendableBuilder.setRepeatedField() instead. */ + @Deprecated @Override public BuilderT setRepeatedField( final FieldDescriptor field, final int index, final Object value) { - if (field.isExtension()) { - verifyContainingType(field); - ensureExtensionsIsMutable(); - extensions.setRepeatedField(field, index, value); - onChanged(); - return (BuilderT) this; - } else { - return super.setRepeatedField(field, index, value); - } + return super.setRepeatedField(field, index, value); } + /* Stub for method overridden from old generated code removed in + * https://github.com/protocolbuffers/protobuf/commit/7bff169d32710b143951ec6ce2c4ea9a56e2ad24 + * + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which overrides + * GeneratedMessage.ExtendableBuilder.addRepeatedField() instead. */ + @Deprecated @Override public BuilderT addRepeatedField(final FieldDescriptor field, final Object value) { - if (field.isExtension()) { - verifyContainingType(field); - ensureExtensionsIsMutable(); - extensions.addRepeatedField(field, value); - onChanged(); - return (BuilderT) this; - } else { - return super.addRepeatedField(field, value); - } - } - - @Override - public Message.Builder newBuilderForField(final FieldDescriptor field) { - if (field.isExtension()) { - return DynamicMessage.newBuilder(field.getMessageType()); - } else { - return super.newBuilderForField(field); - } + return super.addRepeatedField(field, value); } + /* Stub for method called from old generated code. + * @Override not allowed despite inheriting from + * GeneratedMessage.ExtendableBuilder.mergeExtensionFields(). + * + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * GeneratedMessage.ExtendableBuilder.mergeExtensionFields() instead. */ + @Deprecated protected final void mergeExtensionFields(final ExtendableMessage other) { - if (other.extensions != null) { - ensureExtensionsIsMutable(); - extensions.mergeFrom(other.extensions); - onChanged(); - } - } - - @Override - protected boolean parseUnknownField( - CodedInputStream input, ExtensionRegistryLite extensionRegistry, int tag) - throws IOException { - ensureExtensionsIsMutable(); - return MessageReflection.mergeFieldFrom( - input, - input.shouldDiscardUnknownFields() ? null : getUnknownFieldSetBuilder(), - extensionRegistry, - getDescriptorForType(), - new MessageReflection.ExtensionBuilderAdapter(extensions), - tag); - } - - private void verifyContainingType(final FieldDescriptor field) { - if (field.getContainingType() != getDescriptorForType()) { - throw new IllegalArgumentException("FieldDescriptor does not match message type."); - } - } - } - - // ----------------------------------------------------------------- - - /** - * Gets the descriptor for an extension. The implementation depends on whether the extension is - * scoped in the top level of a file or scoped in a Message. - */ - interface ExtensionDescriptorRetriever { - FieldDescriptor getDescriptor(); - } - - // ================================================================= - - /** Calls Class.getMethod and throws a RuntimeException if it fails. */ - private static Method getMethodOrDie( - final Class clazz, final String name, final Class... params) { - try { - return clazz.getMethod(name, params); - } catch (NoSuchMethodException e) { - throw new IllegalStateException( - "Generated message class \"" + clazz.getName() + "\" missing method \"" + name + "\".", - e); - } - } - - /** Calls invoke and throws a RuntimeException if it fails. */ - @CanIgnoreReturnValue - private static Object invokeOrDie( - final Method method, final Object object, final Object... params) { - try { - return method.invoke(object, params); - } catch (IllegalAccessException e) { - throw new IllegalStateException( - "Couldn't use Java reflection to implement protocol message reflection.", e); - } catch (InvocationTargetException e) { - final Throwable cause = e.getCause(); - if (cause instanceof RuntimeException) { - throw (RuntimeException) cause; - } else if (cause instanceof Error) { - throw (Error) cause; - } else { - throw new IllegalStateException( - "Unexpected exception thrown by generated accessor method.", cause); - } + super.mergeExtensionFields(other); } } /** - * Gets the map field with the given field number. This method should be overridden in the - * generated message class if the message contains map fields. + * Stub for GeneratedMessageV3.FieldAccessorTable wrapping GeneratedMessage.FieldAccessorTable for + * compatibility with older gencode. * - *

Unlike other field types, reflection support for map fields can't be implemented based on - * generated public API because we need to access a map field as a list in reflection API but the - * generated API only allows us to access it as a map. This method returns the underlying map - * field directly and thus enables us to access the map field as a list. + * @deprecated This class is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * GeneratedMessage.FieldAccessorTable instead. */ - @SuppressWarnings("unused") - protected MapFieldReflectionAccessor internalGetMapFieldReflection(int fieldNumber) { - return internalGetMapField(fieldNumber); - } - - /** TODO: Remove, exists for compatibility with generated code. */ @Deprecated - @SuppressWarnings({"rawtypes", "unused"}) - protected MapField internalGetMapField(int fieldNumber) { - // Note that we can't use descriptor names here because this method will - // be called when descriptor is being initialized. - throw new IllegalArgumentException("No map fields found in " + getClass().getName()); - } - - /** - * Users should ignore this class. This class provides the implementation with access to the - * fields of a message object using Java reflection. - */ - public static final class FieldAccessorTable { + public static final class FieldAccessorTable extends GeneratedMessage.FieldAccessorTable { - /** - * Construct a FieldAccessorTable for a particular message class. Only one FieldAccessorTable - * should ever be constructed per class. - * - * @param descriptor The type's descriptor. - * @param camelCaseNames The camelcase names of all fields in the message. These are used to - * derive the accessor method names. - * @param messageClass The message type. - * @param builderClass The builder type. - */ + @Deprecated public FieldAccessorTable( final Descriptor descriptor, final String[] camelCaseNames, final Class messageClass, final Class> builderClass) { - this(descriptor, camelCaseNames); - ensureFieldAccessorsInitialized(messageClass, builderClass); + super(descriptor, camelCaseNames, messageClass, builderClass); } - /** - * Construct a FieldAccessorTable for a particular message class without initializing - * FieldAccessors. - */ + @Deprecated public FieldAccessorTable(final Descriptor descriptor, final String[] camelCaseNames) { - this.descriptor = descriptor; - this.camelCaseNames = camelCaseNames; - fields = new FieldAccessor[descriptor.getFields().size()]; - oneofs = new OneofAccessor[descriptor.getOneofs().size()]; - initialized = false; + super(descriptor, camelCaseNames); } - /** - * Ensures the field accessors are initialized. This method is thread-safe. + /* Returns GeneratedMessageV3.FieldAccessorTable instead of GeneratedMessage.FieldAccessorTable. * - * @param messageClass The message type. - * @param builderClass The builder type. - * @return this + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Users should update gencode to >= 4.26.x which uses + * GeneratedMessage.ensureFieldAccessorsInitialized() instead. */ + @Deprecated + @Override public FieldAccessorTable ensureFieldAccessorsInitialized( - Class messageClass, Class> builderClass) { - if (initialized) { - return this; - } - synchronized (this) { - if (initialized) { - return this; - } - int fieldsSize = fields.length; - for (int i = 0; i < fieldsSize; i++) { - FieldDescriptor field = descriptor.getFields().get(i); - String containingOneofCamelCaseName = null; - if (field.getContainingOneof() != null) { - int index = fieldsSize + field.getContainingOneof().getIndex(); - if (index < camelCaseNames.length) { - containingOneofCamelCaseName = camelCaseNames[index]; - } - } - if (field.isRepeated()) { - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - if (field.isMapField()) { - fields[i] = new MapFieldAccessor(field, messageClass); - } else { - fields[i] = - new RepeatedMessageFieldAccessor( - field, camelCaseNames[i], messageClass, builderClass); - } - } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM) { - fields[i] = - new RepeatedEnumFieldAccessor( - field, camelCaseNames[i], messageClass, builderClass); - } else { - fields[i] = - new RepeatedFieldAccessor(field, camelCaseNames[i], messageClass, builderClass); - } - } else { - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - fields[i] = - new SingularMessageFieldAccessor( - field, - camelCaseNames[i], - messageClass, - builderClass, - containingOneofCamelCaseName); - } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM) { - fields[i] = - new SingularEnumFieldAccessor( - field, - camelCaseNames[i], - messageClass, - builderClass, - containingOneofCamelCaseName); - } else if (field.getJavaType() == FieldDescriptor.JavaType.STRING) { - fields[i] = - new SingularStringFieldAccessor( - field, - camelCaseNames[i], - messageClass, - builderClass, - containingOneofCamelCaseName); - } else { - fields[i] = - new SingularFieldAccessor( - field, - camelCaseNames[i], - messageClass, - builderClass, - containingOneofCamelCaseName); - } - } - } - - for (int i = 0; i < descriptor.getOneofs().size(); i++) { - if (i < descriptor.getRealOneofs().size()) { - oneofs[i] = - new RealOneofAccessor( - descriptor, i, camelCaseNames[i + fieldsSize], messageClass, builderClass); - } else { - oneofs[i] = new SyntheticOneofAccessor(descriptor, i); - } - } - - initialized = true; - camelCaseNames = null; - return this; - } - } - - private final Descriptor descriptor; - private final FieldAccessor[] fields; - private String[] camelCaseNames; - private final OneofAccessor[] oneofs; - private volatile boolean initialized; - - /** Get the FieldAccessor for a particular field. */ - private FieldAccessor getField(final FieldDescriptor field) { - if (field.getContainingType() != descriptor) { - throw new IllegalArgumentException("FieldDescriptor does not match message type."); - } else if (field.isExtension()) { - // If this type had extensions, it would subclass ExtendableMessage, - // which overrides the reflection interface to handle extensions. - throw new IllegalArgumentException("This type does not have extensions."); - } - return fields[field.getIndex()]; - } - - /** Get the OneofAccessor for a particular oneof. */ - private OneofAccessor getOneof(final OneofDescriptor oneof) { - if (oneof.getContainingType() != descriptor) { - throw new IllegalArgumentException("OneofDescriptor does not match message type."); - } - return oneofs[oneof.getIndex()]; - } - - /** - * Abstract interface that provides access to a single field. This is implemented differently - * depending on the field type and cardinality. - */ - private interface FieldAccessor { - Object get(GeneratedMessageV3 message); - - Object get(GeneratedMessageV3.Builder builder); - - Object getRaw(GeneratedMessageV3 message); - - void set(Builder builder, Object value); - - Object getRepeated(GeneratedMessageV3 message, int index); - - Object getRepeated(GeneratedMessageV3.Builder builder, int index); - - void setRepeated(Builder builder, int index, Object value); - - void addRepeated(Builder builder, Object value); - - boolean has(GeneratedMessageV3 message); - - boolean has(GeneratedMessageV3.Builder builder); - - int getRepeatedCount(GeneratedMessageV3 message); - - int getRepeatedCount(GeneratedMessageV3.Builder builder); - - void clear(Builder builder); - - Message.Builder newBuilder(); - - Message.Builder getBuilder(GeneratedMessageV3.Builder builder); - - Message.Builder getRepeatedBuilder(GeneratedMessageV3.Builder builder, int index); - } - - /** OneofAccessor provides access to a single oneof. */ - private static interface OneofAccessor { - public boolean has(final GeneratedMessageV3 message); - - public boolean has(GeneratedMessageV3.Builder builder); - - public FieldDescriptor get(final GeneratedMessageV3 message); - - public FieldDescriptor get(GeneratedMessageV3.Builder builder); - - public void clear(final Builder builder); - } - - /** RealOneofAccessor provides access to a single real oneof. */ - private static class RealOneofAccessor implements OneofAccessor { - RealOneofAccessor( - final Descriptor descriptor, - final int oneofIndex, - final String camelCaseName, - final Class messageClass, - final Class> builderClass) { - this.descriptor = descriptor; - caseMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "Case"); - caseMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName + "Case"); - clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); - } - - private final Descriptor descriptor; - private final Method caseMethod; - private final Method caseMethodBuilder; - private final Method clearMethod; - - @Override - public boolean has(final GeneratedMessageV3 message) { - return ((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber() != 0; - } - - @Override - public boolean has(GeneratedMessageV3.Builder builder) { - return ((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber() != 0; - } - - @Override - public FieldDescriptor get(final GeneratedMessageV3 message) { - int fieldNumber = ((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber(); - if (fieldNumber > 0) { - return descriptor.findFieldByNumber(fieldNumber); - } - return null; - } - - @Override - public FieldDescriptor get(GeneratedMessageV3.Builder builder) { - int fieldNumber = ((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber(); - if (fieldNumber > 0) { - return descriptor.findFieldByNumber(fieldNumber); - } - return null; - } - - @Override - public void clear(final Builder builder) { - // TODO: remove the unused variable - Object unused = invokeOrDie(clearMethod, builder); - } - } - - /** SyntheticOneofAccessor provides access to a single synthetic oneof. */ - private static class SyntheticOneofAccessor implements OneofAccessor { - SyntheticOneofAccessor(final Descriptor descriptor, final int oneofIndex) { - OneofDescriptor oneofDescriptor = descriptor.getOneofs().get(oneofIndex); - fieldDescriptor = oneofDescriptor.getFields().get(0); - } - - private final FieldDescriptor fieldDescriptor; - - @Override - public boolean has(final GeneratedMessageV3 message) { - return message.hasField(fieldDescriptor); - } - - @Override - public boolean has(GeneratedMessageV3.Builder builder) { - return builder.hasField(fieldDescriptor); - } - - @Override - public FieldDescriptor get(final GeneratedMessageV3 message) { - return message.hasField(fieldDescriptor) ? fieldDescriptor : null; - } - - public FieldDescriptor get(GeneratedMessageV3.Builder builder) { - return builder.hasField(fieldDescriptor) ? fieldDescriptor : null; - } - - @Override - public void clear(final Builder builder) { - builder.clearField(fieldDescriptor); - } - } - - // --------------------------------------------------------------- - - @SuppressWarnings("SameNameButDifferent") - private static class SingularFieldAccessor implements FieldAccessor { - private interface MethodInvoker { - Object get(final GeneratedMessageV3 message); - - Object get(GeneratedMessageV3.Builder builder); - - int getOneofFieldNumber(final GeneratedMessageV3 message); - - int getOneofFieldNumber(final GeneratedMessageV3.Builder builder); - - void set(final GeneratedMessageV3.Builder builder, final Object value); - - boolean has(final GeneratedMessageV3 message); - - boolean has(GeneratedMessageV3.Builder builder); - - void clear(final GeneratedMessageV3.Builder builder); - } - - private static final class ReflectionInvoker implements MethodInvoker { - private final Method getMethod; - private final Method getMethodBuilder; - private final Method setMethod; - private final Method hasMethod; - private final Method hasMethodBuilder; - private final Method clearMethod; - private final Method caseMethod; - private final Method caseMethodBuilder; - - ReflectionInvoker( - final FieldDescriptor descriptor, - final String camelCaseName, - final Class messageClass, - final Class> builderClass, - final String containingOneofCamelCaseName, - boolean isOneofField, - boolean hasHasMethod) { - getMethod = getMethodOrDie(messageClass, "get" + camelCaseName); - getMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName); - Class type = getMethod.getReturnType(); - setMethod = getMethodOrDie(builderClass, "set" + camelCaseName, type); - hasMethod = hasHasMethod ? getMethodOrDie(messageClass, "has" + camelCaseName) : null; - hasMethodBuilder = - hasHasMethod ? getMethodOrDie(builderClass, "has" + camelCaseName) : null; - clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); - caseMethod = - isOneofField - ? getMethodOrDie(messageClass, "get" + containingOneofCamelCaseName + "Case") - : null; - caseMethodBuilder = - isOneofField - ? getMethodOrDie(builderClass, "get" + containingOneofCamelCaseName + "Case") - : null; - } - - @Override - public Object get(final GeneratedMessageV3 message) { - return invokeOrDie(getMethod, message); - } - - @Override - public Object get(GeneratedMessageV3.Builder builder) { - return invokeOrDie(getMethodBuilder, builder); - } - - @Override - public int getOneofFieldNumber(final GeneratedMessageV3 message) { - return ((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber(); - } - - @Override - public int getOneofFieldNumber(final GeneratedMessageV3.Builder builder) { - return ((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber(); - } - - @Override - public void set(final GeneratedMessageV3.Builder builder, final Object value) { - // TODO: remove the unused variable - Object unused = invokeOrDie(setMethod, builder, value); - } - - @Override - public boolean has(final GeneratedMessageV3 message) { - return (Boolean) invokeOrDie(hasMethod, message); - } - - @Override - public boolean has(GeneratedMessageV3.Builder builder) { - return (Boolean) invokeOrDie(hasMethodBuilder, builder); - } - - @Override - public void clear(final GeneratedMessageV3.Builder builder) { - // TODO: remove the unused variable - Object unused = invokeOrDie(clearMethod, builder); - } - } - - SingularFieldAccessor( - final FieldDescriptor descriptor, - final String camelCaseName, - final Class messageClass, - final Class> builderClass, - final String containingOneofCamelCaseName) { - isOneofField = - descriptor.getRealContainingOneof() != null; - hasHasMethod = - descriptor.getFile().getSyntax() == FileDescriptor.Syntax.EDITIONS && descriptor.hasPresence() - || descriptor.getFile().getSyntax() == FileDescriptor.Syntax.PROTO2 - || descriptor.hasOptionalKeyword() - || (!isOneofField && descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE); - ReflectionInvoker reflectionInvoker = - new ReflectionInvoker( - descriptor, - camelCaseName, - messageClass, - builderClass, - containingOneofCamelCaseName, - isOneofField, - hasHasMethod); - field = descriptor; - type = reflectionInvoker.getMethod.getReturnType(); - invoker = getMethodInvoker(reflectionInvoker); - } - - static MethodInvoker getMethodInvoker(ReflectionInvoker accessor) { - return accessor; - } - - // Note: We use Java reflection to call public methods rather than - // access private fields directly as this avoids runtime security - // checks. - protected final Class type; - protected final FieldDescriptor field; - protected final boolean isOneofField; - protected final boolean hasHasMethod; - protected final MethodInvoker invoker; - - @Override - public Object get(final GeneratedMessageV3 message) { - return invoker.get(message); - } - - @Override - public Object get(GeneratedMessageV3.Builder builder) { - return invoker.get(builder); - } - - @Override - public Object getRaw(final GeneratedMessageV3 message) { - return get(message); - } - - @Override - public void set(final Builder builder, final Object value) { - invoker.set(builder, value); - } - - @Override - public Object getRepeated(final GeneratedMessageV3 message, final int index) { - throw new UnsupportedOperationException("getRepeatedField() called on a singular field."); - } - - @Override - public Object getRepeated(GeneratedMessageV3.Builder builder, int index) { - throw new UnsupportedOperationException("getRepeatedField() called on a singular field."); - } - - @Override - public void setRepeated(final Builder builder, final int index, final Object value) { - throw new UnsupportedOperationException("setRepeatedField() called on a singular field."); - } - - @Override - public void addRepeated(final Builder builder, final Object value) { - throw new UnsupportedOperationException("addRepeatedField() called on a singular field."); - } - - @Override - public boolean has(final GeneratedMessageV3 message) { - if (!hasHasMethod) { - if (isOneofField) { - return invoker.getOneofFieldNumber(message) == field.getNumber(); - } - return !get(message).equals(field.getDefaultValue()); - } - return invoker.has(message); - } - - @Override - public boolean has(GeneratedMessageV3.Builder builder) { - if (!hasHasMethod) { - if (isOneofField) { - return invoker.getOneofFieldNumber(builder) == field.getNumber(); - } - return !get(builder).equals(field.getDefaultValue()); - } - return invoker.has(builder); - } - - @Override - public int getRepeatedCount(final GeneratedMessageV3 message) { - throw new UnsupportedOperationException( - "getRepeatedFieldSize() called on a singular field."); - } - - @Override - public int getRepeatedCount(GeneratedMessageV3.Builder builder) { - throw new UnsupportedOperationException( - "getRepeatedFieldSize() called on a singular field."); - } - - @Override - public void clear(final Builder builder) { - invoker.clear(builder); - } - - @Override - public Message.Builder newBuilder() { - throw new UnsupportedOperationException( - "newBuilderForField() called on a non-Message type."); - } - - @Override - public Message.Builder getBuilder(GeneratedMessageV3.Builder builder) { - throw new UnsupportedOperationException("getFieldBuilder() called on a non-Message type."); - } - - @Override - public Message.Builder getRepeatedBuilder(GeneratedMessageV3.Builder builder, int index) { - throw new UnsupportedOperationException( - "getRepeatedFieldBuilder() called on a non-Message type."); - } - } - - @SuppressWarnings("SameNameButDifferent") - private static class RepeatedFieldAccessor implements FieldAccessor { - interface MethodInvoker { - Object get(final GeneratedMessageV3 message); - - Object get(GeneratedMessageV3.Builder builder); - - Object getRepeated(final GeneratedMessageV3 message, final int index); - - Object getRepeated(GeneratedMessageV3.Builder builder, int index); - - void setRepeated( - final GeneratedMessageV3.Builder builder, final int index, final Object value); - - void addRepeated(final GeneratedMessageV3.Builder builder, final Object value); - - int getRepeatedCount(final GeneratedMessageV3 message); - - int getRepeatedCount(GeneratedMessageV3.Builder builder); - - void clear(final GeneratedMessageV3.Builder builder); - } - - private static final class ReflectionInvoker implements MethodInvoker { - private final Method getMethod; - private final Method getMethodBuilder; - private final Method getRepeatedMethod; - private final Method getRepeatedMethodBuilder; - private final Method setRepeatedMethod; - private final Method addRepeatedMethod; - private final Method getCountMethod; - private final Method getCountMethodBuilder; - private final Method clearMethod; - - ReflectionInvoker( - final FieldDescriptor descriptor, - final String camelCaseName, - final Class messageClass, - final Class> builderClass) { - getMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "List"); - getMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName + "List"); - getRepeatedMethod = getMethodOrDie(messageClass, "get" + camelCaseName, Integer.TYPE); - getRepeatedMethodBuilder = - getMethodOrDie(builderClass, "get" + camelCaseName, Integer.TYPE); - Class type = getRepeatedMethod.getReturnType(); - setRepeatedMethod = - getMethodOrDie(builderClass, "set" + camelCaseName, Integer.TYPE, type); - addRepeatedMethod = getMethodOrDie(builderClass, "add" + camelCaseName, type); - getCountMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "Count"); - getCountMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName + "Count"); - clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); - } - - @Override - public Object get(final GeneratedMessageV3 message) { - return invokeOrDie(getMethod, message); - } - - @Override - public Object get(GeneratedMessageV3.Builder builder) { - return invokeOrDie(getMethodBuilder, builder); - } - - @Override - public Object getRepeated(final GeneratedMessageV3 message, final int index) { - return invokeOrDie(getRepeatedMethod, message, index); - } - - @Override - public Object getRepeated(GeneratedMessageV3.Builder builder, int index) { - return invokeOrDie(getRepeatedMethodBuilder, builder, index); - } - - @Override - public void setRepeated( - final GeneratedMessageV3.Builder builder, final int index, final Object value) { - // TODO: remove the unused variable - Object unused = invokeOrDie(setRepeatedMethod, builder, index, value); - } - - @Override - public void addRepeated(final GeneratedMessageV3.Builder builder, final Object value) { - // TODO: remove the unused variable - Object unused = invokeOrDie(addRepeatedMethod, builder, value); - } - - @Override - public int getRepeatedCount(final GeneratedMessageV3 message) { - return (Integer) invokeOrDie(getCountMethod, message); - } - - @Override - public int getRepeatedCount(GeneratedMessageV3.Builder builder) { - return (Integer) invokeOrDie(getCountMethodBuilder, builder); - } - - @Override - public void clear(final GeneratedMessageV3.Builder builder) { - // TODO: remove the unused variable - Object unused = invokeOrDie(clearMethod, builder); - } - } - - protected final Class type; - protected final MethodInvoker invoker; - - RepeatedFieldAccessor( - final FieldDescriptor descriptor, - final String camelCaseName, - final Class messageClass, - final Class> builderClass) { - ReflectionInvoker reflectionInvoker = - new ReflectionInvoker(descriptor, camelCaseName, messageClass, builderClass); - type = reflectionInvoker.getRepeatedMethod.getReturnType(); - invoker = getMethodInvoker(reflectionInvoker); - } - - static MethodInvoker getMethodInvoker(ReflectionInvoker accessor) { - return accessor; - } - - @Override - public Object get(final GeneratedMessageV3 message) { - return invoker.get(message); - } - - @Override - public Object get(GeneratedMessageV3.Builder builder) { - return invoker.get(builder); - } - - @Override - public Object getRaw(final GeneratedMessageV3 message) { - return get(message); - } - - @Override - public void set(final Builder builder, final Object value) { - // Add all the elements individually. This serves two purposes: - // 1) Verifies that each element has the correct type. - // 2) Insures that the caller cannot modify the list later on and - // have the modifications be reflected in the message. - clear(builder); - for (final Object element : (List) value) { - addRepeated(builder, element); - } - } - - @Override - public Object getRepeated(final GeneratedMessageV3 message, final int index) { - return invoker.getRepeated(message, index); - } - - @Override - public Object getRepeated(GeneratedMessageV3.Builder builder, int index) { - return invoker.getRepeated(builder, index); - } - - @Override - public void setRepeated(final Builder builder, final int index, final Object value) { - invoker.setRepeated(builder, index, value); - } - - @Override - public void addRepeated(final Builder builder, final Object value) { - invoker.addRepeated(builder, value); - } - - @Override - public boolean has(final GeneratedMessageV3 message) { - throw new UnsupportedOperationException("hasField() called on a repeated field."); - } - - @Override - public boolean has(GeneratedMessageV3.Builder builder) { - throw new UnsupportedOperationException("hasField() called on a repeated field."); - } - - @Override - public int getRepeatedCount(final GeneratedMessageV3 message) { - return invoker.getRepeatedCount(message); - } - - @Override - public int getRepeatedCount(GeneratedMessageV3.Builder builder) { - return invoker.getRepeatedCount(builder); - } - - @Override - public void clear(final Builder builder) { - invoker.clear(builder); - } - - @Override - public Message.Builder newBuilder() { - throw new UnsupportedOperationException( - "newBuilderForField() called on a non-Message type."); - } - - @Override - public Message.Builder getBuilder(GeneratedMessageV3.Builder builder) { - throw new UnsupportedOperationException("getFieldBuilder() called on a non-Message type."); - } - - @Override - public Message.Builder getRepeatedBuilder(GeneratedMessageV3.Builder builder, int index) { - throw new UnsupportedOperationException( - "getRepeatedFieldBuilder() called on a non-Message type."); - } - } - - private static class MapFieldAccessor implements FieldAccessor { - MapFieldAccessor( - final FieldDescriptor descriptor, final Class messageClass) { - field = descriptor; - Method getDefaultInstanceMethod = getMethodOrDie(messageClass, "getDefaultInstance"); - MapFieldReflectionAccessor defaultMapField = - getMapField((GeneratedMessageV3) invokeOrDie(getDefaultInstanceMethod, null)); - mapEntryMessageDefaultInstance = defaultMapField.getMapEntryMessageDefaultInstance(); - } - - private final FieldDescriptor field; - private final Message mapEntryMessageDefaultInstance; - - private MapFieldReflectionAccessor getMapField(GeneratedMessageV3 message) { - return message.internalGetMapFieldReflection(field.getNumber()); - } - - private MapFieldReflectionAccessor getMapField(GeneratedMessageV3.Builder builder) { - return builder.internalGetMapFieldReflection(field.getNumber()); - } - - private MapFieldReflectionAccessor getMutableMapField(GeneratedMessageV3.Builder builder) { - return builder.internalGetMutableMapFieldReflection(field.getNumber()); - } - - private Message coerceType(Message value) { - if (value == null) { - return null; - } - if (mapEntryMessageDefaultInstance.getClass().isInstance(value)) { - return value; - } - // The value is not the exact right message type. However, if it - // is an alternative implementation of the same type -- e.g. a - // DynamicMessage -- we should accept it. In this case we can make - // a copy of the message. - return mapEntryMessageDefaultInstance.toBuilder().mergeFrom(value).build(); - } - - @Override - public Object get(GeneratedMessageV3 message) { - List result = new ArrayList<>(); - for (int i = 0; i < getRepeatedCount(message); i++) { - result.add(getRepeated(message, i)); - } - return Collections.unmodifiableList(result); - } - - @Override - public Object get(Builder builder) { - List result = new ArrayList<>(); - for (int i = 0; i < getRepeatedCount(builder); i++) { - result.add(getRepeated(builder, i)); - } - return Collections.unmodifiableList(result); - } - - @Override - public Object getRaw(GeneratedMessageV3 message) { - return get(message); - } - - @Override - public void set(Builder builder, Object value) { - clear(builder); - for (Object entry : (List) value) { - addRepeated(builder, entry); - } - } - - @Override - public Object getRepeated(GeneratedMessageV3 message, int index) { - return getMapField(message).getList().get(index); - } - - @Override - public Object getRepeated(Builder builder, int index) { - return getMapField(builder).getList().get(index); - } - - @Override - public void setRepeated(Builder builder, int index, Object value) { - getMutableMapField(builder).getMutableList().set(index, coerceType((Message) value)); - } - - @Override - public void addRepeated(Builder builder, Object value) { - getMutableMapField(builder).getMutableList().add(coerceType((Message) value)); - } - - @Override - public boolean has(GeneratedMessageV3 message) { - throw new UnsupportedOperationException("hasField() is not supported for repeated fields."); - } - - @Override - public boolean has(Builder builder) { - throw new UnsupportedOperationException("hasField() is not supported for repeated fields."); - } - - @Override - public int getRepeatedCount(GeneratedMessageV3 message) { - return getMapField(message).getList().size(); - } - - @Override - public int getRepeatedCount(Builder builder) { - return getMapField(builder).getList().size(); - } - - @Override - public void clear(Builder builder) { - getMutableMapField(builder).getMutableList().clear(); - } - - @Override - public Message.Builder newBuilder() { - return mapEntryMessageDefaultInstance.newBuilderForType(); - } - - @Override - public Message.Builder getBuilder(Builder builder) { - throw new UnsupportedOperationException("Nested builder not supported for map fields."); - } - - @Override - public Message.Builder getRepeatedBuilder(Builder builder, int index) { - throw new UnsupportedOperationException("Map fields cannot be repeated"); - } - } - - // --------------------------------------------------------------- - - private static final class SingularEnumFieldAccessor extends SingularFieldAccessor { - SingularEnumFieldAccessor( - final FieldDescriptor descriptor, - final String camelCaseName, - final Class messageClass, - final Class> builderClass, - final String containingOneofCamelCaseName) { - super(descriptor, camelCaseName, messageClass, builderClass, containingOneofCamelCaseName); - - enumDescriptor = descriptor.getEnumType(); - - valueOfMethod = getMethodOrDie(type, "valueOf", EnumValueDescriptor.class); - getValueDescriptorMethod = getMethodOrDie(type, "getValueDescriptor"); - - supportUnknownEnumValue = !descriptor.legacyEnumFieldTreatedAsClosed(); - if (supportUnknownEnumValue) { - getValueMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "Value"); - getValueMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName + "Value"); - setValueMethod = getMethodOrDie(builderClass, "set" + camelCaseName + "Value", int.class); - } - } - - private final EnumDescriptor enumDescriptor; - - private final Method valueOfMethod; - private final Method getValueDescriptorMethod; - - private final boolean supportUnknownEnumValue; - private Method getValueMethod; - private Method getValueMethodBuilder; - private Method setValueMethod; - - @Override - public Object get(final GeneratedMessageV3 message) { - if (supportUnknownEnumValue) { - int value = (Integer) invokeOrDie(getValueMethod, message); - return enumDescriptor.findValueByNumberCreatingIfUnknown(value); - } - return invokeOrDie(getValueDescriptorMethod, super.get(message)); - } - - @Override - public Object get(final GeneratedMessageV3.Builder builder) { - if (supportUnknownEnumValue) { - int value = (Integer) invokeOrDie(getValueMethodBuilder, builder); - return enumDescriptor.findValueByNumberCreatingIfUnknown(value); - } - return invokeOrDie(getValueDescriptorMethod, super.get(builder)); - } - - @Override - public void set(final Builder builder, final Object value) { - if (supportUnknownEnumValue) { - // TODO: remove the unused variable - Object unused = - invokeOrDie(setValueMethod, builder, ((EnumValueDescriptor) value).getNumber()); - return; - } - super.set(builder, invokeOrDie(valueOfMethod, null, value)); - } - } - - private static final class RepeatedEnumFieldAccessor extends RepeatedFieldAccessor { - RepeatedEnumFieldAccessor( - final FieldDescriptor descriptor, - final String camelCaseName, - final Class messageClass, - final Class> builderClass) { - super(descriptor, camelCaseName, messageClass, builderClass); - - enumDescriptor = descriptor.getEnumType(); - - valueOfMethod = getMethodOrDie(type, "valueOf", EnumValueDescriptor.class); - getValueDescriptorMethod = getMethodOrDie(type, "getValueDescriptor"); - - supportUnknownEnumValue = !descriptor.legacyEnumFieldTreatedAsClosed(); - if (supportUnknownEnumValue) { - getRepeatedValueMethod = - getMethodOrDie(messageClass, "get" + camelCaseName + "Value", int.class); - getRepeatedValueMethodBuilder = - getMethodOrDie(builderClass, "get" + camelCaseName + "Value", int.class); - setRepeatedValueMethod = - getMethodOrDie(builderClass, "set" + camelCaseName + "Value", int.class, int.class); - addRepeatedValueMethod = - getMethodOrDie(builderClass, "add" + camelCaseName + "Value", int.class); - } - } - - private final EnumDescriptor enumDescriptor; - - private final Method valueOfMethod; - private final Method getValueDescriptorMethod; - - private final boolean supportUnknownEnumValue; - - private Method getRepeatedValueMethod; - private Method getRepeatedValueMethodBuilder; - private Method setRepeatedValueMethod; - private Method addRepeatedValueMethod; - - @Override - public Object get(final GeneratedMessageV3 message) { - final List newList = new ArrayList<>(); - final int size = getRepeatedCount(message); - for (int i = 0; i < size; i++) { - newList.add(getRepeated(message, i)); - } - return Collections.unmodifiableList(newList); - } - - @Override - public Object get(final GeneratedMessageV3.Builder builder) { - final List newList = new ArrayList<>(); - final int size = getRepeatedCount(builder); - for (int i = 0; i < size; i++) { - newList.add(getRepeated(builder, i)); - } - return Collections.unmodifiableList(newList); - } - - @Override - public Object getRepeated(final GeneratedMessageV3 message, final int index) { - if (supportUnknownEnumValue) { - int value = (Integer) invokeOrDie(getRepeatedValueMethod, message, index); - return enumDescriptor.findValueByNumberCreatingIfUnknown(value); - } - return invokeOrDie(getValueDescriptorMethod, super.getRepeated(message, index)); - } - - @Override - public Object getRepeated(final GeneratedMessageV3.Builder builder, final int index) { - if (supportUnknownEnumValue) { - int value = (Integer) invokeOrDie(getRepeatedValueMethodBuilder, builder, index); - return enumDescriptor.findValueByNumberCreatingIfUnknown(value); - } - return invokeOrDie(getValueDescriptorMethod, super.getRepeated(builder, index)); - } - - @Override - public void setRepeated(final Builder builder, final int index, final Object value) { - if (supportUnknownEnumValue) { - // TODO: remove the unused variable - Object unused = - invokeOrDie( - setRepeatedValueMethod, - builder, - index, - ((EnumValueDescriptor) value).getNumber()); - return; - } - super.setRepeated(builder, index, invokeOrDie(valueOfMethod, null, value)); - } - - @Override - public void addRepeated(final Builder builder, final Object value) { - if (supportUnknownEnumValue) { - // TODO: remove the unused variable - Object unused = - invokeOrDie( - addRepeatedValueMethod, builder, ((EnumValueDescriptor) value).getNumber()); - return; - } - super.addRepeated(builder, invokeOrDie(valueOfMethod, null, value)); - } - } - - // --------------------------------------------------------------- - - /** - * Field accessor for string fields. - * - *

This class makes getFooBytes() and setFooBytes() available for reflection API so that - * reflection based serialize/parse functions can access the raw bytes of the field to preserve - * non-UTF8 bytes in the string. - * - *

This ensures the serialize/parse round-trip safety, which is important for servers which - * forward messages. - */ - private static final class SingularStringFieldAccessor extends SingularFieldAccessor { - SingularStringFieldAccessor( - final FieldDescriptor descriptor, - final String camelCaseName, - final Class messageClass, - final Class> builderClass, - final String containingOneofCamelCaseName) { - super(descriptor, camelCaseName, messageClass, builderClass, containingOneofCamelCaseName); - getBytesMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "Bytes"); - setBytesMethodBuilder = - getMethodOrDie(builderClass, "set" + camelCaseName + "Bytes", ByteString.class); - } - - private final Method getBytesMethod; - private final Method setBytesMethodBuilder; - - @Override - public Object getRaw(final GeneratedMessageV3 message) { - return invokeOrDie(getBytesMethod, message); - } - - @Override - public void set(GeneratedMessageV3.Builder builder, Object value) { - if (value instanceof ByteString) { - // TODO: remove the unused variable - Object unused = invokeOrDie(setBytesMethodBuilder, builder, value); - } else { - super.set(builder, value); - } - } - } - - // --------------------------------------------------------------- - - private static final class SingularMessageFieldAccessor extends SingularFieldAccessor { - SingularMessageFieldAccessor( - final FieldDescriptor descriptor, - final String camelCaseName, - final Class messageClass, - final Class> builderClass, - final String containingOneofCamelCaseName) { - super(descriptor, camelCaseName, messageClass, builderClass, containingOneofCamelCaseName); - - newBuilderMethod = getMethodOrDie(type, "newBuilder"); - getBuilderMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName + "Builder"); - } - - private final Method newBuilderMethod; - private final Method getBuilderMethodBuilder; - - private Object coerceType(final Object value) { - if (type.isInstance(value)) { - return value; - } else { - // The value is not the exact right message type. However, if it - // is an alternative implementation of the same type -- e.g. a - // DynamicMessage -- we should accept it. In this case we can make - // a copy of the message. - return ((Message.Builder) invokeOrDie(newBuilderMethod, null)) - .mergeFrom((Message) value) - .buildPartial(); - } - } - - @Override - public void set(final Builder builder, final Object value) { - super.set(builder, coerceType(value)); - } - - @Override - public Message.Builder newBuilder() { - return (Message.Builder) invokeOrDie(newBuilderMethod, null); - } - - @Override - public Message.Builder getBuilder(GeneratedMessageV3.Builder builder) { - return (Message.Builder) invokeOrDie(getBuilderMethodBuilder, builder); - } - } - - private static final class RepeatedMessageFieldAccessor extends RepeatedFieldAccessor { - RepeatedMessageFieldAccessor( - final FieldDescriptor descriptor, - final String camelCaseName, - final Class messageClass, - final Class> builderClass) { - super(descriptor, camelCaseName, messageClass, builderClass); - - newBuilderMethod = getMethodOrDie(type, "newBuilder"); - getBuilderMethodBuilder = - getMethodOrDie(builderClass, "get" + camelCaseName + "Builder", Integer.TYPE); - } - - private final Method newBuilderMethod; - private final Method getBuilderMethodBuilder; - - private Object coerceType(final Object value) { - if (type.isInstance(value)) { - return value; - } else { - // The value is not the exact right message type. However, if it - // is an alternative implementation of the same type -- e.g. a - // DynamicMessage -- we should accept it. In this case we can make - // a copy of the message. - return ((Message.Builder) invokeOrDie(newBuilderMethod, null)) - .mergeFrom((Message) value) - .build(); - } - } - - @Override - public void setRepeated(final Builder builder, final int index, final Object value) { - super.setRepeated(builder, index, coerceType(value)); - } - - @Override - public void addRepeated(final Builder builder, final Object value) { - super.addRepeated(builder, coerceType(value)); - } - - @Override - public Message.Builder newBuilder() { - return (Message.Builder) invokeOrDie(newBuilderMethod, null); - } - - @Override - public Message.Builder getRepeatedBuilder( - final GeneratedMessageV3.Builder builder, final int index) { - return (Message.Builder) invokeOrDie(getBuilderMethodBuilder, builder, index); - } - } - } - - /** - * Replaces this object in the output stream with a serialized form. Part of Java's serialization - * magic. Generated sub-classes must override this method by calling {@code return - * super.writeReplace();} - * - * @return a SerializedForm of this message - */ - protected Object writeReplace() throws ObjectStreamException { - return new GeneratedMessageLite.SerializedForm(this); - } - - /** - * Checks that the {@link Extension} is non-Lite and returns it as a {@link GeneratedExtension}. - */ - private static , T> - Extension checkNotLite(ExtensionLite extension) { - if (extension.isLite()) { - throw new IllegalArgumentException("Expected non-lite extension."); - } - - return (Extension) extension; - } - - protected static boolean isStringEmpty(final Object value) { - if (value instanceof String) { - return ((String) value).isEmpty(); - } else { - return ((ByteString) value).isEmpty(); - } - } - - protected static int computeStringSize(final int fieldNumber, final Object value) { - if (value instanceof String) { - return CodedOutputStream.computeStringSize(fieldNumber, (String) value); - } else { - return CodedOutputStream.computeBytesSize(fieldNumber, (ByteString) value); - } - } - - protected static int computeStringSizeNoTag(final Object value) { - if (value instanceof String) { - return CodedOutputStream.computeStringSizeNoTag((String) value); - } else { - return CodedOutputStream.computeBytesSizeNoTag((ByteString) value); - } - } - - protected static void writeString( - CodedOutputStream output, final int fieldNumber, final Object value) throws IOException { - if (value instanceof String) { - output.writeString(fieldNumber, (String) value); - } else { - output.writeBytes(fieldNumber, (ByteString) value); - } - } - - protected static void writeStringNoTag(CodedOutputStream output, final Object value) - throws IOException { - if (value instanceof String) { - output.writeStringNoTag((String) value); - } else { - output.writeBytesNoTag((ByteString) value); - } - } - - protected static void serializeIntegerMapTo( - CodedOutputStream out, - MapField field, - MapEntry defaultEntry, - int fieldNumber) - throws IOException { - Map m = field.getMap(); - if (!out.isSerializationDeterministic()) { - serializeMapTo(out, m, defaultEntry, fieldNumber); - return; - } - // Sorting the unboxed keys and then look up the values during serialization is 2x faster - // than sorting map entries with a custom comparator directly. - int[] keys = new int[m.size()]; - int index = 0; - for (int k : m.keySet()) { - keys[index++] = k; - } - Arrays.sort(keys); - for (int key : keys) { - out.writeMessage( - fieldNumber, defaultEntry.newBuilderForType().setKey(key).setValue(m.get(key)).build()); - } - } - - protected static void serializeLongMapTo( - CodedOutputStream out, - MapField field, - MapEntry defaultEntry, - int fieldNumber) - throws IOException { - Map m = field.getMap(); - if (!out.isSerializationDeterministic()) { - serializeMapTo(out, m, defaultEntry, fieldNumber); - return; - } - - long[] keys = new long[m.size()]; - int index = 0; - for (long k : m.keySet()) { - keys[index++] = k; - } - Arrays.sort(keys); - for (long key : keys) { - out.writeMessage( - fieldNumber, defaultEntry.newBuilderForType().setKey(key).setValue(m.get(key)).build()); - } - } - - protected static void serializeStringMapTo( - CodedOutputStream out, - MapField field, - MapEntry defaultEntry, - int fieldNumber) - throws IOException { - Map m = field.getMap(); - if (!out.isSerializationDeterministic()) { - serializeMapTo(out, m, defaultEntry, fieldNumber); - return; - } - - // Sorting the String keys and then look up the values during serialization is 25% faster than - // sorting map entries with a custom comparator directly. - String[] keys = new String[m.size()]; - keys = m.keySet().toArray(keys); - Arrays.sort(keys); - for (String key : keys) { - out.writeMessage( - fieldNumber, defaultEntry.newBuilderForType().setKey(key).setValue(m.get(key)).build()); - } - } - - protected static void serializeBooleanMapTo( - CodedOutputStream out, - MapField field, - MapEntry defaultEntry, - int fieldNumber) - throws IOException { - Map m = field.getMap(); - if (!out.isSerializationDeterministic()) { - serializeMapTo(out, m, defaultEntry, fieldNumber); - return; - } - maybeSerializeBooleanEntryTo(out, m, defaultEntry, fieldNumber, false); - maybeSerializeBooleanEntryTo(out, m, defaultEntry, fieldNumber, true); - } - - private static void maybeSerializeBooleanEntryTo( - CodedOutputStream out, - Map m, - MapEntry defaultEntry, - int fieldNumber, - boolean key) - throws IOException { - if (m.containsKey(key)) { - out.writeMessage( - fieldNumber, defaultEntry.newBuilderForType().setKey(key).setValue(m.get(key)).build()); - } - } - - /** Serialize the map using the iteration order. */ - private static void serializeMapTo( - CodedOutputStream out, Map m, MapEntry defaultEntry, int fieldNumber) - throws IOException { - for (Map.Entry entry : m.entrySet()) { - out.writeMessage( - fieldNumber, - defaultEntry - .newBuilderForType() - .setKey(entry.getKey()) - .setValue(entry.getValue()) - .build()); + Class messageClass, + Class> builderClass) { + return (FieldAccessorTable) super.ensureFieldAccessorsInitialized(messageClass, builderClass); } } } diff --git a/java/core/src/main/java/com/google/protobuf/IntArrayList.java b/java/core/src/main/java/com/google/protobuf/IntArrayList.java index eea507ae8..e6a1aca1e 100644 --- a/java/core/src/main/java/com/google/protobuf/IntArrayList.java +++ b/java/core/src/main/java/com/google/protobuf/IntArrayList.java @@ -47,7 +47,7 @@ final class IntArrayList extends AbstractProtobufList */ private IntArrayList(int[] other, int size, boolean isMutable) { super(isMutable); - array = other; + this.array = other; this.size = size; } diff --git a/java/core/src/main/java/com/google/protobuf/Internal.java b/java/core/src/main/java/com/google/protobuf/Internal.java index 3024aa9df..07bec5ef2 100644 --- a/java/core/src/main/java/com/google/protobuf/Internal.java +++ b/java/core/src/main/java/com/google/protobuf/Internal.java @@ -371,6 +371,36 @@ public final class Internal { return ((MessageLite) destination).toBuilder().mergeFrom((MessageLite) source).buildPartial(); } + /** + * Provides an immutable view of {@code List} around an {@code IntList}. + * + *

Protobuf internal. Used in protobuf generated code only. + */ + public static class IntListAdapter extends AbstractList { + /** Convert individual elements of the List from int to T. */ + public interface IntConverter { + T convert(int from); + } + + private final IntList fromList; + private final IntConverter converter; + + public IntListAdapter(IntList fromList, IntConverter converter) { + this.fromList = fromList; + this.converter = converter; + } + + @Override + public T get(int index) { + return converter.convert(fromList.getInt(index)); + } + + @Override + public int size() { + return fromList.size(); + } + } + /** * Provides an immutable view of {@code List} around a {@code List}. * diff --git a/java/core/src/main/java/com/google/protobuf/JavaEditionDefaults.java b/java/core/src/main/java/com/google/protobuf/JavaEditionDefaults.java new file mode 100644 index 000000000..6c5a077e9 --- /dev/null +++ b/java/core/src/main/java/com/google/protobuf/JavaEditionDefaults.java @@ -0,0 +1,10 @@ +// This file contains the serialized FeatureSetDefaults object corresponding to +// the Java runtime. This is used for feature resolution under Editions. + +package com.google.protobuf; + +public final class JavaEditionDefaults { + public static final String PROTOBUF_INTERNAL_JAVA_EDITION_DEFAULTS = "\n\035\030\204\007\"\003\312>\000*\023\010\001\020\002\030\002 \003(\0010\002\312>\004\010\001\020\001\n\035\030\347\007\"\003\312>\000*\023\010\002\020\001\030\001 \002(\0010\001\312>\004\010\000\020\001\n\035\030\350\007\"\023\010\001\020\001\030\001 \002(\0010\001\312>\004\010\000\020\001*\003\312>\000 \346\007(\350\007"; + + private JavaEditionDefaults() {} +} diff --git a/java/core/src/main/java/com/google/protobuf/JavaEditionDefaults.java.template b/java/core/src/main/java/com/google/protobuf/JavaEditionDefaults.java.template new file mode 100644 index 000000000..d50b2ccbe --- /dev/null +++ b/java/core/src/main/java/com/google/protobuf/JavaEditionDefaults.java.template @@ -0,0 +1,10 @@ +// This file contains the serialized FeatureSetDefaults object corresponding to +// the Java runtime. This is used for feature resolution under Editions. + +package com.google.protobuf; + +public final class JavaEditionDefaults { + public static final String PROTOBUF_INTERNAL_JAVA_EDITION_DEFAULTS = "DEFAULTS_VALUE"; + + private JavaEditionDefaults() {} +} diff --git a/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java b/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java index d5579fe28..d52fc0a70 100644 --- a/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java +++ b/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java @@ -31,8 +31,6 @@ import java.io.IOException; * @author xiangl@google.com (Xiang Li) */ public class LazyFieldLite { - private static final ExtensionRegistryLite EMPTY_REGISTRY = - ExtensionRegistryLite.getEmptyRegistry(); /* * The value associated with the LazyFieldLite object is stored in one or more of the following diff --git a/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java b/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java index cb9b62acc..e01cd6bc5 100644 --- a/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java +++ b/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java @@ -145,6 +145,29 @@ public class LazyStringArrayList extends AbstractProtobufList modCount++; } + @Override + @CanIgnoreReturnValue + public boolean add(String element) { + ensureIsMutable(); + list.add(element); + modCount++; + return true; + } + + @Override + public void add(ByteString element) { + ensureIsMutable(); + list.add(element); + modCount++; + } + + @Override + public void add(byte[] element) { + ensureIsMutable(); + list.add(element); + modCount++; + } + @Override public boolean addAll(Collection c) { // The default implementation of AbstractCollection.addAll(Collection) @@ -197,20 +220,6 @@ public class LazyStringArrayList extends AbstractProtobufList modCount++; } - @Override - public void add(ByteString element) { - ensureIsMutable(); - list.add(element); - modCount++; - } - - @Override - public void add(byte[] element) { - ensureIsMutable(); - list.add(element); - modCount++; - } - @Override public Object getRaw(int index) { return list.get(index); diff --git a/java/core/src/main/java/com/google/protobuf/LegacyDescriptorsUtil.java b/java/core/src/main/java/com/google/protobuf/LegacyDescriptorsUtil.java deleted file mode 100644 index 7b2351caf..000000000 --- a/java/core/src/main/java/com/google/protobuf/LegacyDescriptorsUtil.java +++ /dev/null @@ -1,72 +0,0 @@ -// Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file or at -// https://developers.google.com/open-source/licenses/bsd - -package com.google.protobuf; - -import com.google.protobuf.Descriptors.FieldDescriptor; -import com.google.protobuf.Descriptors.FileDescriptor; -import com.google.protobuf.Descriptors.OneofDescriptor; - -/** - * This file is meant to be a temporary housing for legacy descriptor APIs we want to deprecate and - * remove. This will help prevent backslide by allowing us to control visibility. - */ -public final class LegacyDescriptorsUtil { - - /** Wraps FileDescriptor */ - public static final class LegacyFileDescriptor { - - /** The syntax of the .proto file. */ - public static enum Syntax { - UNKNOWN("unknown"), - PROTO2("proto2"), - PROTO3("proto3"); - - Syntax(String name) { - this.name = name; - } - - final String name; - } - - public static Syntax getSyntax(FileDescriptor descriptor) { - switch (descriptor.getSyntax()) { - case UNKNOWN: - return Syntax.UNKNOWN; - case PROTO2: - return Syntax.PROTO2; - case PROTO3: - return Syntax.PROTO3; - } - throw new IllegalArgumentException("Unexpected syntax"); - } - - private LegacyFileDescriptor() {} - } - - /** Wraps FieldDescriptor */ - public static final class LegacyFieldDescriptor { - - public static boolean hasOptionalKeyword(FieldDescriptor descriptor) { - return descriptor.hasOptionalKeyword(); - } - - private LegacyFieldDescriptor() {} - } - - /** Wraps OneofDescriptor */ - public static final class LegacyOneofDescriptor { - - public static boolean isSynthetic(OneofDescriptor descriptor) { - return descriptor.isSynthetic(); - } - - private LegacyOneofDescriptor() {} - } - - private LegacyDescriptorsUtil() {} -} diff --git a/java/core/src/main/java/com/google/protobuf/LegacyUnredactedTextFormat.java b/java/core/src/main/java/com/google/protobuf/LegacyUnredactedTextFormat.java new file mode 100644 index 000000000..bcab5c2ed --- /dev/null +++ b/java/core/src/main/java/com/google/protobuf/LegacyUnredactedTextFormat.java @@ -0,0 +1,38 @@ +package com.google.protobuf; + +/** + * The legacy APIs preserve the existing toString() behavior (output TextFormat), which allows us to + * migrate toString callers that expect TextFormat output off toString. Eventually, we will make + * toString output DebugFormat, which is randomized and redacts SPII fields (incompatible with + * TextFormat). + */ +public final class LegacyUnredactedTextFormat { + + private LegacyUnredactedTextFormat() {} + + /** Like {@code TextFormat.printer().printToString(message)}, but for legacy purposes. */ + static String legacyUnredactedMultilineString(MessageOrBuilder message) { + return TextFormat.printer().printToString(message); + } + + /** Like {@code TextFormat.printer().printToString(fields)}, but for legacy purposes. */ + static String legacyUnredactedMultilineString(UnknownFieldSet fields) { + return TextFormat.printer().printToString(fields); + } + + /** + * Like {@code TextFormat.printer().emittingSingleLine(true).printToString(message)}, but for + * legacy purposes. + */ + static String legacyUnredactedSingleLineString(MessageOrBuilder message) { + return TextFormat.printer().emittingSingleLine(true).printToString(message); + } + + /** + * Like {@code TextFormat.printer().emittingSingleLine(true).printToString(fields)}, but for + * legacy purposes. + */ + static String legacyUnredactedSingleLineString(UnknownFieldSet fields) { + return TextFormat.printer().emittingSingleLine(true).printToString(fields); + } +} diff --git a/java/core/src/main/java/com/google/protobuf/ListFieldSchema.java b/java/core/src/main/java/com/google/protobuf/ListFieldSchema.java index bc55eb23c..eaab68b8d 100644 --- a/java/core/src/main/java/com/google/protobuf/ListFieldSchema.java +++ b/java/core/src/main/java/com/google/protobuf/ListFieldSchema.java @@ -7,162 +7,17 @@ package com.google.protobuf; -import com.google.protobuf.Internal.ProtobufList; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; /** * Utility class that aids in properly manipulating list fields for either the lite or full runtime. */ @CheckReturnValue -abstract class ListFieldSchema { - // Disallow construction. - private ListFieldSchema() {} +interface ListFieldSchema { - private static final ListFieldSchema FULL_INSTANCE = new ListFieldSchemaFull(); - private static final ListFieldSchema LITE_INSTANCE = new ListFieldSchemaLite(); + List mutableListAt(Object msg, long offset); - abstract List mutableListAt(Object msg, long offset); + void makeImmutableListAt(Object msg, long offset); - abstract void makeImmutableListAt(Object msg, long offset); - - abstract void mergeListsAt(Object msg, Object otherMsg, long offset); - - static ListFieldSchema full() { - return FULL_INSTANCE; - } - - static ListFieldSchema lite() { - return LITE_INSTANCE; - } - - /** Implementation for the full runtime. */ - private static final class ListFieldSchemaFull extends ListFieldSchema { - - private static final Class UNMODIFIABLE_LIST_CLASS = - Collections.unmodifiableList(Collections.emptyList()).getClass(); - - @Override - List mutableListAt(Object message, long offset) { - return mutableListAt(message, offset, AbstractProtobufList.DEFAULT_CAPACITY); - } - - @Override - void makeImmutableListAt(Object message, long offset) { - List list = (List) UnsafeUtil.getObject(message, offset); - Object immutable = null; - if (list instanceof LazyStringList) { - immutable = ((LazyStringList) list).getUnmodifiableView(); - } else if (UNMODIFIABLE_LIST_CLASS.isAssignableFrom(list.getClass())) { - // already immutable - return; - } else if (list instanceof PrimitiveNonBoxingCollection && list instanceof ProtobufList) { - if (((ProtobufList) list).isModifiable()) { - ((ProtobufList) list).makeImmutable(); - } - return; - } else { - immutable = Collections.unmodifiableList((List) list); - } - UnsafeUtil.putObject(message, offset, immutable); - } - - @SuppressWarnings("unchecked") - private static List mutableListAt(Object message, long offset, int additionalCapacity) { - List list = getList(message, offset); - if (list.isEmpty()) { - if (list instanceof LazyStringList) { - list = (List) new LazyStringArrayList(additionalCapacity); - } else if (list instanceof PrimitiveNonBoxingCollection && list instanceof ProtobufList) { - list = ((ProtobufList) list).mutableCopyWithCapacity(additionalCapacity); - } else { - list = new ArrayList(additionalCapacity); - } - UnsafeUtil.putObject(message, offset, list); - } else if (UNMODIFIABLE_LIST_CLASS.isAssignableFrom(list.getClass())) { - ArrayList newList = new ArrayList(list.size() + additionalCapacity); - newList.addAll(list); - list = newList; - UnsafeUtil.putObject(message, offset, list); - } else if (list instanceof UnmodifiableLazyStringList) { - LazyStringArrayList newList = new LazyStringArrayList(list.size() + additionalCapacity); - newList.addAll((UnmodifiableLazyStringList) list); - list = (List) newList; - UnsafeUtil.putObject(message, offset, list); - } else if (list instanceof PrimitiveNonBoxingCollection - && list instanceof ProtobufList - && !((ProtobufList) list).isModifiable()) { - list = ((ProtobufList) list).mutableCopyWithCapacity(list.size() + additionalCapacity); - UnsafeUtil.putObject(message, offset, list); - } - return list; - } - - @Override - void mergeListsAt(Object msg, Object otherMsg, long offset) { - List other = getList(otherMsg, offset); - List mine = mutableListAt(msg, offset, other.size()); - - int size = mine.size(); - int otherSize = other.size(); - if (size > 0 && otherSize > 0) { - mine.addAll(other); - } - - List merged = size > 0 ? mine : other; - UnsafeUtil.putObject(msg, offset, merged); - } - - @SuppressWarnings("unchecked") - static List getList(Object message, long offset) { - return (List) UnsafeUtil.getObject(message, offset); - } - } - - /** Implementation for the lite runtime. */ - private static final class ListFieldSchemaLite extends ListFieldSchema { - - @Override - List mutableListAt(Object message, long offset) { - ProtobufList list = getProtobufList(message, offset); - if (!list.isModifiable()) { - int size = list.size(); - list = - list.mutableCopyWithCapacity( - size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2); - UnsafeUtil.putObject(message, offset, list); - } - return list; - } - - @Override - void makeImmutableListAt(Object message, long offset) { - ProtobufList list = getProtobufList(message, offset); - list.makeImmutable(); - } - - @Override - void mergeListsAt(Object msg, Object otherMsg, long offset) { - ProtobufList mine = getProtobufList(msg, offset); - ProtobufList other = getProtobufList(otherMsg, offset); - - int size = mine.size(); - int otherSize = other.size(); - if (size > 0 && otherSize > 0) { - if (!mine.isModifiable()) { - mine = mine.mutableCopyWithCapacity(size + otherSize); - } - mine.addAll(other); - } - - ProtobufList merged = size > 0 ? mine : other; - UnsafeUtil.putObject(msg, offset, merged); - } - - @SuppressWarnings("unchecked") - static ProtobufList getProtobufList(Object message, long offset) { - return (ProtobufList) UnsafeUtil.getObject(message, offset); - } - } + void mergeListsAt(Object msg, Object otherMsg, long offset); } diff --git a/java/core/src/main/java/com/google/protobuf/ListFieldSchemaFull.java b/java/core/src/main/java/com/google/protobuf/ListFieldSchemaFull.java new file mode 100644 index 000000000..8f0308ab6 --- /dev/null +++ b/java/core/src/main/java/com/google/protobuf/ListFieldSchemaFull.java @@ -0,0 +1,99 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +package com.google.protobuf; + +import com.google.protobuf.Internal.ProtobufList; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Utility class that aids in properly manipulating list fields for either the lite or full runtime. + */ +@CheckReturnValue +final class ListFieldSchemaFull implements ListFieldSchema { + + private static final Class UNMODIFIABLE_LIST_CLASS = + Collections.unmodifiableList(Collections.emptyList()).getClass(); + + @Override + public List mutableListAt(Object message, long offset) { + return mutableListAt(message, offset, AbstractProtobufList.DEFAULT_CAPACITY); + } + + @SuppressWarnings("unchecked") + private static List mutableListAt(Object message, long offset, int additionalCapacity) { + List list = getList(message, offset); + if (list.isEmpty()) { + if (list instanceof LazyStringList) { + list = (List) new LazyStringArrayList(additionalCapacity); + } else if (list instanceof PrimitiveNonBoxingCollection && list instanceof ProtobufList) { + list = ((ProtobufList) list).mutableCopyWithCapacity(additionalCapacity); + } else { + list = new ArrayList(additionalCapacity); + } + UnsafeUtil.putObject(message, offset, list); + } else if (UNMODIFIABLE_LIST_CLASS.isAssignableFrom(list.getClass())) { + ArrayList newList = new ArrayList(list.size() + additionalCapacity); + newList.addAll(list); + list = newList; + UnsafeUtil.putObject(message, offset, list); + } else if (list instanceof UnmodifiableLazyStringList) { + LazyStringArrayList newList = new LazyStringArrayList(list.size() + additionalCapacity); + newList.addAll((UnmodifiableLazyStringList) list); + list = (List) newList; + UnsafeUtil.putObject(message, offset, list); + } else if (list instanceof PrimitiveNonBoxingCollection + && list instanceof ProtobufList + && !((ProtobufList) list).isModifiable()) { + list = ((ProtobufList) list).mutableCopyWithCapacity(list.size() + additionalCapacity); + UnsafeUtil.putObject(message, offset, list); + } + return list; + } + + @Override + public void makeImmutableListAt(Object message, long offset) { + List list = (List) UnsafeUtil.getObject(message, offset); + Object immutable = null; + if (list instanceof LazyStringList) { + immutable = ((LazyStringList) list).getUnmodifiableView(); + } else if (UNMODIFIABLE_LIST_CLASS.isAssignableFrom(list.getClass())) { + // already immutable + return; + } else if (list instanceof PrimitiveNonBoxingCollection && list instanceof ProtobufList) { + if (((ProtobufList) list).isModifiable()) { + ((ProtobufList) list).makeImmutable(); + } + return; + } else { + immutable = Collections.unmodifiableList((List) list); + } + UnsafeUtil.putObject(message, offset, immutable); + } + + @Override + public void mergeListsAt(Object msg, Object otherMsg, long offset) { + List other = getList(otherMsg, offset); + List mine = mutableListAt(msg, offset, other.size()); + + int size = mine.size(); + int otherSize = other.size(); + if (size > 0 && otherSize > 0) { + mine.addAll(other); + } + + List merged = size > 0 ? mine : other; + UnsafeUtil.putObject(msg, offset, merged); + } + + @SuppressWarnings("unchecked") + static List getList(Object message, long offset) { + return (List) UnsafeUtil.getObject(message, offset); + } +} diff --git a/java/core/src/main/java/com/google/protobuf/ListFieldSchemaLite.java b/java/core/src/main/java/com/google/protobuf/ListFieldSchemaLite.java new file mode 100644 index 000000000..f0cf71ecc --- /dev/null +++ b/java/core/src/main/java/com/google/protobuf/ListFieldSchemaLite.java @@ -0,0 +1,59 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +package com.google.protobuf; + +import com.google.protobuf.Internal.ProtobufList; +import java.util.List; + +/** + * Utility class that aids in properly manipulating list fields for either the lite or full runtime. + */ +final class ListFieldSchemaLite implements ListFieldSchema { + + @Override + public List mutableListAt(Object message, long offset) { + ProtobufList list = getProtobufList(message, offset); + if (!list.isModifiable()) { + int size = list.size(); + list = + list.mutableCopyWithCapacity( + size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2); + UnsafeUtil.putObject(message, offset, list); + } + return list; + } + + @Override + public void makeImmutableListAt(Object message, long offset) { + ProtobufList list = getProtobufList(message, offset); + list.makeImmutable(); + } + + @Override + public void mergeListsAt(Object msg, Object otherMsg, long offset) { + ProtobufList mine = getProtobufList(msg, offset); + ProtobufList other = getProtobufList(otherMsg, offset); + + int size = mine.size(); + int otherSize = other.size(); + if (size > 0 && otherSize > 0) { + if (!mine.isModifiable()) { + mine = mine.mutableCopyWithCapacity(size + otherSize); + } + mine.addAll(other); + } + + ProtobufList merged = size > 0 ? mine : other; + UnsafeUtil.putObject(msg, offset, merged); + } + + @SuppressWarnings("unchecked") + static ProtobufList getProtobufList(Object message, long offset) { + return (ProtobufList) UnsafeUtil.getObject(message, offset); + } +} diff --git a/java/core/src/main/java/com/google/protobuf/ListFieldSchemas.java b/java/core/src/main/java/com/google/protobuf/ListFieldSchemas.java new file mode 100644 index 000000000..536b2177a --- /dev/null +++ b/java/core/src/main/java/com/google/protobuf/ListFieldSchemas.java @@ -0,0 +1,36 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +package com.google.protobuf; + +@CheckReturnValue +final class ListFieldSchemas { + private static final ListFieldSchema FULL_SCHEMA = loadSchemaForFullRuntime(); + private static final ListFieldSchema LITE_SCHEMA = new ListFieldSchemaLite(); + + static ListFieldSchema full() { + return FULL_SCHEMA; + } + + static ListFieldSchema lite() { + return LITE_SCHEMA; + } + + private static ListFieldSchema loadSchemaForFullRuntime() { + if (Protobuf.assumeLiteRuntime) { + return null; + } + try { + Class clazz = Class.forName("com.google.protobuf.ListFieldSchemaFull"); + return (ListFieldSchema) clazz.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + return null; + } + } + + private ListFieldSchemas() {} +} diff --git a/java/core/src/main/java/com/google/protobuf/ManifestSchemaFactory.java b/java/core/src/main/java/com/google/protobuf/ManifestSchemaFactory.java index c923b129d..c3adcab14 100644 --- a/java/core/src/main/java/com/google/protobuf/ManifestSchemaFactory.java +++ b/java/core/src/main/java/com/google/protobuf/ManifestSchemaFactory.java @@ -34,57 +34,37 @@ final class ManifestSchemaFactory implements SchemaFactory { // MessageSet has a special schema. if (messageInfo.isMessageSetWireFormat()) { - if (GeneratedMessageLite.class.isAssignableFrom(messageType)) { - return MessageSetSchema.newSchema( - SchemaUtil.unknownFieldSetLiteSchema(), - ExtensionSchemas.lite(), - messageInfo.getDefaultInstance()); - } - return MessageSetSchema.newSchema( - SchemaUtil.unknownFieldSetFullSchema(), - ExtensionSchemas.full(), - messageInfo.getDefaultInstance()); + return useLiteRuntime(messageType) + ? MessageSetSchema.newSchema( + SchemaUtil.unknownFieldSetLiteSchema(), + ExtensionSchemas.lite(), + messageInfo.getDefaultInstance()) + : MessageSetSchema.newSchema( + SchemaUtil.unknownFieldSetFullSchema(), + ExtensionSchemas.full(), + messageInfo.getDefaultInstance()); } return newSchema(messageType, messageInfo); } private static Schema newSchema(Class messageType, MessageInfo messageInfo) { - if (GeneratedMessageLite.class.isAssignableFrom(messageType)) { - return allowExtensions(messageInfo) - ? MessageSchema.newSchema( - messageType, - messageInfo, - NewInstanceSchemas.lite(), - ListFieldSchema.lite(), - SchemaUtil.unknownFieldSetLiteSchema(), - ExtensionSchemas.lite(), - MapFieldSchemas.lite()) - : MessageSchema.newSchema( - messageType, - messageInfo, - NewInstanceSchemas.lite(), - ListFieldSchema.lite(), - SchemaUtil.unknownFieldSetLiteSchema(), - /* extensionSchema= */ null, - MapFieldSchemas.lite()); - } - return allowExtensions(messageInfo) + return useLiteRuntime(messageType) ? MessageSchema.newSchema( messageType, messageInfo, - NewInstanceSchemas.full(), - ListFieldSchema.full(), - SchemaUtil.unknownFieldSetFullSchema(), - ExtensionSchemas.full(), - MapFieldSchemas.full()) + NewInstanceSchemas.lite(), + ListFieldSchemas.lite(), + SchemaUtil.unknownFieldSetLiteSchema(), + allowExtensions(messageInfo) ? ExtensionSchemas.lite() : null, + MapFieldSchemas.lite()) : MessageSchema.newSchema( messageType, messageInfo, NewInstanceSchemas.full(), - ListFieldSchema.full(), + ListFieldSchemas.full(), SchemaUtil.unknownFieldSetFullSchema(), - /* extensionSchema= */ null, + allowExtensions(messageInfo) ? ExtensionSchemas.full() : null, MapFieldSchemas.full()); } @@ -145,6 +125,9 @@ final class ManifestSchemaFactory implements SchemaFactory { }; private static MessageInfoFactory getDescriptorMessageInfoFactory() { + if (Protobuf.assumeLiteRuntime) { + return EMPTY_FACTORY; + } try { Class clazz = Class.forName("com.google.protobuf.DescriptorMessageInfoFactory"); return (MessageInfoFactory) clazz.getDeclaredMethod("getInstance").invoke(null); @@ -152,4 +135,8 @@ final class ManifestSchemaFactory implements SchemaFactory { return EMPTY_FACTORY; } } + + private static boolean useLiteRuntime(Class messageType) { + return Protobuf.assumeLiteRuntime || GeneratedMessageLite.class.isAssignableFrom(messageType); + } } diff --git a/java/core/src/main/java/com/google/protobuf/MapFieldReflectionAccessor.java b/java/core/src/main/java/com/google/protobuf/MapFieldReflectionAccessor.java index f51423816..64928e3cd 100644 --- a/java/core/src/main/java/com/google/protobuf/MapFieldReflectionAccessor.java +++ b/java/core/src/main/java/com/google/protobuf/MapFieldReflectionAccessor.java @@ -14,7 +14,7 @@ import java.util.List; * reflection to access both. */ public abstract class MapFieldReflectionAccessor { - /** Gets the content of this MapField as a read-only List. */ + /** Gets the content of this MapField as a list of read-only values. */ abstract List getList(); /** Gets a mutable List view of this MapField. */ diff --git a/java/core/src/main/java/com/google/protobuf/MapFieldSchemaFull.java b/java/core/src/main/java/com/google/protobuf/MapFieldSchemaFull.java index d41144917..624c258d4 100644 --- a/java/core/src/main/java/com/google/protobuf/MapFieldSchemaFull.java +++ b/java/core/src/main/java/com/google/protobuf/MapFieldSchemaFull.java @@ -10,7 +10,7 @@ package com.google.protobuf; import com.google.protobuf.MapEntryLite.Metadata; import java.util.Map; -class MapFieldSchemaFull implements MapFieldSchema { +final class MapFieldSchemaFull implements MapFieldSchema { @Override public Map forMutableMapData(Object mapField) { return ((MapField) mapField).getMutableMap(); diff --git a/java/core/src/main/java/com/google/protobuf/MapFieldSchemaLite.java b/java/core/src/main/java/com/google/protobuf/MapFieldSchemaLite.java index f7c5d85f3..c21678d28 100644 --- a/java/core/src/main/java/com/google/protobuf/MapFieldSchemaLite.java +++ b/java/core/src/main/java/com/google/protobuf/MapFieldSchemaLite.java @@ -11,7 +11,7 @@ import com.google.protobuf.MapEntryLite.Metadata; import java.util.Map; @CheckReturnValue -class MapFieldSchemaLite implements MapFieldSchema { +final class MapFieldSchemaLite implements MapFieldSchema { @Override public Map forMutableMapData(Object mapField) { diff --git a/java/core/src/main/java/com/google/protobuf/MapFieldSchemas.java b/java/core/src/main/java/com/google/protobuf/MapFieldSchemas.java index d7eeccd8c..dc762b994 100644 --- a/java/core/src/main/java/com/google/protobuf/MapFieldSchemas.java +++ b/java/core/src/main/java/com/google/protobuf/MapFieldSchemas.java @@ -21,6 +21,9 @@ final class MapFieldSchemas { } private static MapFieldSchema loadSchemaForFullRuntime() { + if (Protobuf.assumeLiteRuntime) { + return null; + } try { Class clazz = Class.forName("com.google.protobuf.MapFieldSchemaFull"); return (MapFieldSchema) clazz.getDeclaredConstructor().newInstance(); @@ -28,4 +31,6 @@ final class MapFieldSchemas { return null; } } + + private MapFieldSchemas() {} } diff --git a/java/core/src/main/java/com/google/protobuf/NewInstanceSchemaFull.java b/java/core/src/main/java/com/google/protobuf/NewInstanceSchemaFull.java index aacec0a55..b48c304c7 100644 --- a/java/core/src/main/java/com/google/protobuf/NewInstanceSchemaFull.java +++ b/java/core/src/main/java/com/google/protobuf/NewInstanceSchemaFull.java @@ -10,7 +10,6 @@ package com.google.protobuf; final class NewInstanceSchemaFull implements NewInstanceSchema { @Override public Object newInstance(Object defaultInstance) { - return ((GeneratedMessageV3) defaultInstance) - .newInstance(GeneratedMessageV3.UnusedPrivateParameter.INSTANCE); + return ((Message) defaultInstance).toBuilder().buildPartial(); } } diff --git a/java/core/src/main/java/com/google/protobuf/NewInstanceSchemas.java b/java/core/src/main/java/com/google/protobuf/NewInstanceSchemas.java index 800f44891..3f89b34a3 100644 --- a/java/core/src/main/java/com/google/protobuf/NewInstanceSchemas.java +++ b/java/core/src/main/java/com/google/protobuf/NewInstanceSchemas.java @@ -21,6 +21,9 @@ final class NewInstanceSchemas { } private static NewInstanceSchema loadSchemaForFullRuntime() { + if (Protobuf.assumeLiteRuntime) { + return null; + } try { Class clazz = Class.forName("com.google.protobuf.NewInstanceSchemaFull"); return (NewInstanceSchema) clazz.getDeclaredConstructor().newInstance(); @@ -28,4 +31,6 @@ final class NewInstanceSchemas { return null; } } + + private NewInstanceSchemas() {} } diff --git a/java/core/src/main/java/com/google/protobuf/NioByteString.java b/java/core/src/main/java/com/google/protobuf/NioByteString.java deleted file mode 100644 index 56c0de9e8..000000000 --- a/java/core/src/main/java/com/google/protobuf/NioByteString.java +++ /dev/null @@ -1,269 +0,0 @@ -// Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file or at -// https://developers.google.com/open-source/licenses/bsd - -package com.google.protobuf; - -import static com.google.protobuf.Internal.checkNotNull; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InvalidObjectException; -import java.io.ObjectInputStream; -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.InvalidMarkException; -import java.nio.charset.Charset; -import java.util.Collections; -import java.util.List; - -/** A {@link ByteString} that wraps around a {@link ByteBuffer}. */ -final class NioByteString extends ByteString.LeafByteString { - private final ByteBuffer buffer; - - NioByteString(ByteBuffer buffer) { - checkNotNull(buffer, "buffer"); - - // Use native byte order for fast fixed32/64 operations. - this.buffer = buffer.slice().order(ByteOrder.nativeOrder()); - } - - // ================================================================= - // Serializable - - /** Magic method that lets us override serialization behavior. */ - private Object writeReplace() { - return ByteString.copyFrom(buffer.slice()); - } - - /** Magic method that lets us override deserialization behavior. */ - private void readObject(@SuppressWarnings("unused") ObjectInputStream in) throws IOException { - throw new InvalidObjectException("NioByteString instances are not to be serialized directly"); - } - - // ================================================================= - - @Override - public byte byteAt(int index) { - try { - return buffer.get(index); - } catch (ArrayIndexOutOfBoundsException e) { - throw e; - } catch (IndexOutOfBoundsException e) { - throw new ArrayIndexOutOfBoundsException(e.getMessage()); - } - } - - @Override - public byte internalByteAt(int index) { - // it isn't possible to avoid the bounds checking inside of ByteBuffer, so just use the default - // implementation. - return byteAt(index); - } - - @Override - public int size() { - return buffer.remaining(); - } - - @Override - public ByteString substring(int beginIndex, int endIndex) { - try { - ByteBuffer slice = slice(beginIndex, endIndex); - return new NioByteString(slice); - } catch (ArrayIndexOutOfBoundsException e) { - throw e; - } catch (IndexOutOfBoundsException e) { - throw new ArrayIndexOutOfBoundsException(e.getMessage()); - } - } - - @Override - protected void copyToInternal( - byte[] target, int sourceOffset, int targetOffset, int numberToCopy) { - ByteBuffer slice = buffer.slice(); - Java8Compatibility.position(slice, sourceOffset); - slice.get(target, targetOffset, numberToCopy); - } - - @Override - public void copyTo(ByteBuffer target) { - target.put(buffer.slice()); - } - - @Override - public void writeTo(OutputStream out) throws IOException { - out.write(toByteArray()); - } - - @Override - boolean equalsRange(ByteString other, int offset, int length) { - return substring(0, length).equals(other.substring(offset, offset + length)); - } - - @Override - void writeToInternal(OutputStream out, int sourceOffset, int numberToWrite) throws IOException { - if (buffer.hasArray()) { - // Optimized write for array-backed buffers. - // Note that we're taking the risk that a malicious OutputStream could modify the array. - int bufferOffset = buffer.arrayOffset() + buffer.position() + sourceOffset; - out.write(buffer.array(), bufferOffset, numberToWrite); - return; - } - - ByteBufferWriter.write(slice(sourceOffset, sourceOffset + numberToWrite), out); - } - - @Override - void writeTo(ByteOutput output) throws IOException { - output.writeLazy(buffer.slice()); - } - - @Override - public ByteBuffer asReadOnlyByteBuffer() { - return buffer.asReadOnlyBuffer(); - } - - @Override - public List asReadOnlyByteBufferList() { - return Collections.singletonList(asReadOnlyByteBuffer()); - } - - @Override - protected String toStringInternal(Charset charset) { - final byte[] bytes; - final int offset; - final int length; - if (buffer.hasArray()) { - bytes = buffer.array(); - offset = buffer.arrayOffset() + buffer.position(); - length = buffer.remaining(); - } else { - // TODO: Can we optimize this? - bytes = toByteArray(); - offset = 0; - length = bytes.length; - } - return new String(bytes, offset, length, charset); - } - - @Override - public boolean isValidUtf8() { - return Utf8.isValidUtf8(buffer); - } - - @Override - protected int partialIsValidUtf8(int state, int offset, int length) { - return Utf8.partialIsValidUtf8(state, buffer, offset, offset + length); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - if (!(other instanceof ByteString)) { - return false; - } - ByteString otherString = ((ByteString) other); - if (size() != otherString.size()) { - return false; - } - if (size() == 0) { - return true; - } - if (other instanceof NioByteString) { - return buffer.equals(((NioByteString) other).buffer); - } - if (other instanceof RopeByteString) { - return other.equals(this); - } - return buffer.equals(otherString.asReadOnlyByteBuffer()); - } - - @Override - protected int partialHash(int h, int offset, int length) { - for (int i = offset; i < offset + length; i++) { - h = h * 31 + buffer.get(i); - } - return h; - } - - @Override - public InputStream newInput() { - return new InputStream() { - private final ByteBuffer buf = buffer.slice(); - - @Override - public void mark(int readlimit) { - Java8Compatibility.mark(buf); - } - - @Override - public boolean markSupported() { - return true; - } - - @Override - public void reset() throws IOException { - try { - Java8Compatibility.reset(buf); - } catch (InvalidMarkException e) { - throw new IOException(e); - } - } - - @Override - public int available() throws IOException { - return buf.remaining(); - } - - @Override - public int read() throws IOException { - if (!buf.hasRemaining()) { - return -1; - } - return buf.get() & 0xFF; - } - - @Override - public int read(byte[] bytes, int off, int len) throws IOException { - if (!buf.hasRemaining()) { - return -1; - } - - len = Math.min(len, buf.remaining()); - buf.get(bytes, off, len); - return len; - } - }; - } - - @Override - public CodedInputStream newCodedInput() { - return CodedInputStream.newInstance(buffer, true); - } - - /** - * Creates a slice of a range of this buffer. - * - * @param beginIndex the beginning index of the slice (inclusive). - * @param endIndex the end index of the slice (exclusive). - * @return the requested slice. - */ - private ByteBuffer slice(int beginIndex, int endIndex) { - if (beginIndex < buffer.position() || endIndex > buffer.limit() || beginIndex > endIndex) { - throw new IllegalArgumentException( - String.format("Invalid indices [%d, %d]", beginIndex, endIndex)); - } - - ByteBuffer slice = buffer.slice(); - Java8Compatibility.position(slice, beginIndex - buffer.position()); - Java8Compatibility.limit(slice, endIndex - buffer.position()); - return slice; - } -} diff --git a/java/core/src/main/java/com/google/protobuf/Protobuf.java b/java/core/src/main/java/com/google/protobuf/Protobuf.java index 82bb66b78..fafd18d87 100644 --- a/java/core/src/main/java/com/google/protobuf/Protobuf.java +++ b/java/core/src/main/java/com/google/protobuf/Protobuf.java @@ -22,6 +22,13 @@ import java.util.concurrent.ConcurrentMap; final class Protobuf { private static final Protobuf INSTANCE = new Protobuf(); + // short circuit the full runtime support via assumevalues trickery + // this value should only be set to true via ProGuard -assumevalues + // directives when doing whole program optimization of Android applications + // to enable discarding of full runtime support. + @SuppressWarnings({"JavaOptionalSuggestions", "NonFinalStaticField"}) + static boolean assumeLiteRuntime = false; + private final SchemaFactory schemaFactory; // TODO: Consider using ClassValue instead. diff --git a/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilderV3.java b/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilderV3.java index b36794ffa..dc3326190 100644 --- a/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilderV3.java +++ b/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilderV3.java @@ -1,5 +1,5 @@ // Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. +// Copyright 2024 Google LLC. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file or at @@ -17,24 +17,13 @@ import java.util.List; import java.util.RandomAccess; /** - * {@code RepeatedFieldBuilderV3} implements a structure that a protocol message uses to hold a - * repeated field of other protocol messages. It supports the classical use case of adding immutable - * {@link Message}'s to the repeated field and is highly optimized around this (no extra memory - * allocations and sharing of immutable arrays).
- * It also supports the additional use case of adding a {@link Message.Builder} to the repeated - * field and deferring conversion of that {@code Builder} to an immutable {@code Message}. In this - * way, it's possible to maintain a tree of {@code Builder}'s that acts as a fully read/write data - * structure.
- * Logically, one can think of a tree of builders as converting the entire tree to messages when - * build is called on the root or when any method is called that desires a Message instead of a - * Builder. In terms of the implementation, the {@code SingleFieldBuilderV3} and {@code - * RepeatedFieldBuilderV3} classes cache messages that were created so that messages only need to be - * created when some change occurred in its builder or a builder for one of its descendants. + * Stub for RepeatedFieldBuilderV3 matching RepeatedFieldBuilder for compatibility with older + * gencode. This cannot wrap RepeatedFieldBuilder directly due to RepeatedFieldBuilder having more + * restrictive extends GeneratedMessage for MType and BType. * - * @param the type of message for the field - * @param the type of builder for the field - * @param the common interface for the message and the builder - * @author jonp@google.com (Jon Perlow) + * @deprecated This class is deprecated, and slated for removal in the next breaking change. Users + * should update gencode to >= 4.26.x which replaces RepeatedFieldBuilderV3 with + * RepeatedFieldBuilder. */ public class RepeatedFieldBuilderV3< MType extends AbstractMessage, @@ -42,65 +31,23 @@ public class RepeatedFieldBuilderV3< IType extends MessageOrBuilder> implements AbstractMessage.BuilderParent { - // Parent to send changes to. private AbstractMessage.BuilderParent parent; - // List of messages. Never null. It may be immutable, in which case - // isMessagesListMutable will be false. See note below. private List messages; - // Whether messages is an mutable array that can be modified. private boolean isMessagesListMutable; - // List of builders. May be null, in which case, no nested builders were - // created. If not null, entries represent the builder for that index. private List> builders; - // Here are the invariants for messages and builders: - // 1. messages is never null and its count corresponds to the number of items - // in the repeated field. - // 2. If builders is non-null, messages and builders MUST always - // contain the same number of items. - // 3. Entries in either array can be null, but for any index, there MUST be - // either a Message in messages or a builder in builders. - // 4. If the builder at an index is non-null, the builder is - // authoritative. This is the case where a Builder was set on the index. - // Any message in the messages array MUST be ignored. - // t. If the builder at an index is null, the message in the messages - // list is authoritative. This is the case where a Message (not a Builder) - // was set directly for an index. - - // Indicates that we've built a message and so we are now obligated - // to dispatch dirty invalidations. See AbstractMessage.BuilderListener. private boolean isClean; - // A view of this builder that exposes a List interface of messages. This is - // initialized on demand. This is fully backed by this object and all changes - // are reflected in it. Access to any item converts it to a message if it - // was a builder. private MessageExternalList externalMessageList; - // A view of this builder that exposes a List interface of builders. This is - // initialized on demand. This is fully backed by this object and all changes - // are reflected in it. Access to any item converts it to a builder if it - // was a message. private BuilderExternalList externalBuilderList; - // A view of this builder that exposes a List interface of the interface - // implemented by messages and builders. This is initialized on demand. This - // is fully backed by this object and all changes are reflected in it. - // Access to any item returns either a builder or message depending on - // what is most efficient. private MessageOrBuilderExternalList externalMessageOrBuilderList; - /** - * Constructs a new builder with an empty list of messages. - * - * @param messages the current list of messages - * @param isMessagesListMutable Whether the messages list is mutable - * @param parent a listener to notify of changes - * @param isClean whether the builder is initially marked clean - */ + @Deprecated public RepeatedFieldBuilderV3( List messages, boolean isMessagesListMutable, @@ -112,15 +59,16 @@ public class RepeatedFieldBuilderV3< this.isClean = isClean; } + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.dispose() instead. + */ + @Deprecated public void dispose() { - // Null out parent so we stop sending it invalidations. parent = null; } - /** - * Ensures that the list of messages is mutable so it can be updated. If it's immutable, a copy is - * made. - */ private void ensureMutableMessageList() { if (!isMessagesListMutable) { messages = new ArrayList(messages); @@ -128,10 +76,6 @@ public class RepeatedFieldBuilderV3< } } - /** - * Ensures that the list of builders is not null. If it's null, the list is created and - * initialized to be the same size as the messages list with null entries. - */ private void ensureBuilders() { if (this.builders == null) { this.builders = new ArrayList>(messages.size()); @@ -141,59 +85,43 @@ public class RepeatedFieldBuilderV3< } } - /** - * Gets the count of items in the list. - * - * @return the count of items in the list. + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.getCount() instead. */ + @Deprecated public int getCount() { return messages.size(); } - /** - * Gets whether the list is empty. - * - * @return whether the list is empty + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.isEmpty() instead. */ + @Deprecated public boolean isEmpty() { return messages.isEmpty(); } - /** - * Get the message at the specified index. If the message is currently stored as a {@code - * Builder}, it is converted to a {@code Message} by calling {@link Message.Builder#buildPartial} - * on it. - * - * @param index the index of the message to get - * @return the message for the specified index + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.getMessage() instead. */ + @Deprecated public MType getMessage(int index) { return getMessage(index, false); } - /** - * Get the message at the specified index. If the message is currently stored as a {@code - * Builder}, it is converted to a {@code Message} by calling {@link Message.Builder#buildPartial} - * on it. - * - * @param index the index of the message to get - * @param forBuild this is being called for build so we want to make sure we - * SingleFieldBuilderV3.build to send dirty invalidations - * @return the message for the specified index - */ private MType getMessage(int index, boolean forBuild) { if (this.builders == null) { - // We don't have any builders -- return the current Message. - // This is the case where no builder was created, so we MUST have a - // Message. return messages.get(index); } SingleFieldBuilderV3 builder = builders.get(index); if (builder == null) { - // We don't have a builder -- return the current message. - // This is the case where no builder was created for the entry at index, - // so we MUST have a message. return messages.get(index); } else { @@ -201,13 +129,12 @@ public class RepeatedFieldBuilderV3< } } - /** - * Gets a builder for the specified index. If no builder has been created for that index, a - * builder is created on demand by calling {@link Message#toBuilder}. - * - * @param index the index of the message to get - * @return The builder for that index + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.getBuilder() instead. */ + @Deprecated public BType getBuilder(int index) { ensureBuilders(); SingleFieldBuilderV3 builder = builders.get(index); @@ -219,27 +146,20 @@ public class RepeatedFieldBuilderV3< return builder.getBuilder(); } - /** - * Gets the base class interface for the specified index. This may either be a builder or a - * message. It will return whatever is more efficient. - * - * @param index the index of the message to get - * @return the message or builder for the index as the base class interface + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.getMessageOrBuilder() instead. */ + @Deprecated @SuppressWarnings("unchecked") public IType getMessageOrBuilder(int index) { if (this.builders == null) { - // We don't have any builders -- return the current Message. - // This is the case where no builder was created, so we MUST have a - // Message. return (IType) messages.get(index); } SingleFieldBuilderV3 builder = builders.get(index); if (builder == null) { - // We don't have a builder -- return the current message. - // This is the case where no builder was created for the entry at index, - // so we MUST have a message. return (IType) messages.get(index); } else { @@ -247,13 +167,12 @@ public class RepeatedFieldBuilderV3< } } - /** - * Sets a message at the specified index replacing the existing item at that index. - * - * @param index the index to set. - * @param message the message to set - * @return the builder + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.setMessage() instead. */ + @Deprecated @CanIgnoreReturnValue public RepeatedFieldBuilderV3 setMessage(int index, MType message) { checkNotNull(message); @@ -270,12 +189,12 @@ public class RepeatedFieldBuilderV3< return this; } - /** - * Appends the specified element to the end of this list. - * - * @param message the message to add - * @return the builder + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.addMessage() instead. */ + @Deprecated @CanIgnoreReturnValue public RepeatedFieldBuilderV3 addMessage(MType message) { checkNotNull(message); @@ -289,15 +208,12 @@ public class RepeatedFieldBuilderV3< return this; } - /** - * Inserts the specified message at the specified position in this list. Shifts the element - * currently at that position (if any) and any subsequent elements to the right (adds one to their - * indices). - * - * @param index the index at which to insert the message - * @param message the message to add - * @return the builder + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.addMessage() instead. */ + @Deprecated @CanIgnoreReturnValue public RepeatedFieldBuilderV3 addMessage(int index, MType message) { checkNotNull(message); @@ -311,13 +227,12 @@ public class RepeatedFieldBuilderV3< return this; } - /** - * Appends all of the messages in the specified collection to the end of this list, in the order - * that they are returned by the specified collection's iterator. - * - * @param values the messages to add - * @return the builder + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.addAllMessages() instead. */ + @Deprecated @CanIgnoreReturnValue public RepeatedFieldBuilderV3 addAllMessages( Iterable values) { @@ -325,7 +240,6 @@ public class RepeatedFieldBuilderV3< checkNotNull(value); } - // If we can inspect the size, we can more efficiently add messages. int size = -1; if (values instanceof Collection) { final Collection collection = (Collection) values; @@ -349,12 +263,12 @@ public class RepeatedFieldBuilderV3< return this; } - /** - * Appends a new builder to the end of this list and returns the builder. - * - * @param message the message to add which is the basis of the builder - * @return the new builder + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.addBuilder() instead. */ + @Deprecated public BType addBuilder(MType message) { ensureMutableMessageList(); ensureBuilders(); @@ -367,14 +281,12 @@ public class RepeatedFieldBuilderV3< return builder.getBuilder(); } - /** - * Inserts a new builder at the specified position in this list. Shifts the element currently at - * that position (if any) and any subsequent elements to the right (adds one to their indices). - * - * @param index the index at which to insert the builder - * @param message the message to add which is the basis of the builder - * @return the builder + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.addBuilder() instead. */ + @Deprecated public BType addBuilder(int index, MType message) { ensureMutableMessageList(); ensureBuilders(); @@ -387,12 +299,12 @@ public class RepeatedFieldBuilderV3< return builder.getBuilder(); } - /** - * Removes the element at the specified position in this list. Shifts any subsequent elements to - * the left (subtracts one from their indices). - * - * @param index the index at which to remove the message + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.remove() instead. */ + @Deprecated public void remove(int index) { ensureMutableMessageList(); messages.remove(index); @@ -406,7 +318,12 @@ public class RepeatedFieldBuilderV3< incrementModCounts(); } - /** Removes all of the elements from this list. The list will be empty after this call returns. */ + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.clear() instead. + */ + @Deprecated public void clear() { messages = Collections.emptyList(); isMessagesListMutable = false; @@ -422,25 +339,21 @@ public class RepeatedFieldBuilderV3< incrementModCounts(); } - /** - * Builds the list of messages from the builder and returns them. - * - * @return an immutable list of messages + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.build() instead. */ + @Deprecated public List build() { - // Now that build has been called, we are required to dispatch - // invalidations. isClean = true; if (!isMessagesListMutable && builders == null) { - // We still have an immutable list and we never created a builder. return messages; } boolean allMessagesInSync = true; if (!isMessagesListMutable) { - // We still have an immutable list. Let's see if any of them are out - // of sync with their builders. for (int i = 0; i < messages.size(); i++) { Message message = messages.get(i); SingleFieldBuilderV3 builder = builders.get(i); @@ -452,30 +365,24 @@ public class RepeatedFieldBuilderV3< } } if (allMessagesInSync) { - // Immutable list is still in sync. return messages; } } - - // Need to make sure messages is up to date ensureMutableMessageList(); for (int i = 0; i < messages.size(); i++) { messages.set(i, getMessage(i, true)); } - - // We're going to return our list as immutable so we mark that we can - // no longer update it. messages = Collections.unmodifiableList(messages); isMessagesListMutable = false; return messages; } - /** - * Gets a view of the builder as a list of messages. The returned list is live and will reflect - * any changes to the underlying builder. - * - * @return the messages in the list + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.getMessageList() instead. */ + @Deprecated public List getMessageList() { if (externalMessageList == null) { externalMessageList = new MessageExternalList(this); @@ -483,12 +390,12 @@ public class RepeatedFieldBuilderV3< return externalMessageList; } - /** - * Gets a view of the builder as a list of builders. This returned list is live and will reflect - * any changes to the underlying builder. - * - * @return the builders in the list + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.getBuilderList() instead. */ + @Deprecated public List getBuilderList() { if (externalBuilderList == null) { externalBuilderList = new BuilderExternalList(this); @@ -496,12 +403,12 @@ public class RepeatedFieldBuilderV3< return externalBuilderList; } - /** - * Gets a view of the builder as a list of MessageOrBuilders. This returned list is live and will - * reflect any changes to the underlying builder. - * - * @return the builders in the list + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.getMessageOrBuilderList() instead. */ + @Deprecated public List getMessageOrBuilderList() { if (externalMessageOrBuilderList == null) { externalMessageOrBuilderList = new MessageOrBuilderExternalList(this); @@ -509,28 +416,24 @@ public class RepeatedFieldBuilderV3< return externalMessageOrBuilderList; } - /** - * Called when a the builder or one of its nested children has changed and any parent should be - * notified of its invalidation. - */ private void onChanged() { if (isClean && parent != null) { parent.markDirty(); - - // Don't keep dispatching invalidations until build is called again. isClean = false; } } + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.markDirty() instead. + */ + @Deprecated @Override public void markDirty() { onChanged(); } - /** - * Increments the mod counts so that an ConcurrentModificationException can be thrown if calling - * code tries to modify the builder while its iterating the list. - */ private void incrementModCounts() { if (externalMessageList != null) { externalMessageList.incrementModCount(); @@ -543,13 +446,6 @@ public class RepeatedFieldBuilderV3< } } - /** - * Provides a live view of the builder as a list of messages. - * - * @param the type of message for the field - * @param the type of builder for the field - * @param the common interface for the message and the builder - */ private static class MessageExternalList< MType extends AbstractMessage, BType extends AbstractMessage.Builder, @@ -558,32 +454,44 @@ public class RepeatedFieldBuilderV3< RepeatedFieldBuilderV3 builder; + @Deprecated MessageExternalList(RepeatedFieldBuilderV3 builder) { this.builder = builder; } + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.MessageExternalList.size() instead. + */ + @Deprecated @Override public int size() { return this.builder.getCount(); } + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.MessageExternalList.get() instead. + */ + @Deprecated @Override public MType get(int index) { return builder.getMessage(index); } + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.MessageExternalList.incrementModCount() instead. + */ + @Deprecated void incrementModCount() { modCount++; } } - /** - * Provides a live view of the builder as a list of builders. - * - * @param the type of message for the field - * @param the type of builder for the field - * @param the common interface for the message and the builder - */ private static class BuilderExternalList< MType extends AbstractMessage, BType extends AbstractMessage.Builder, @@ -592,32 +500,44 @@ public class RepeatedFieldBuilderV3< RepeatedFieldBuilderV3 builder; + @Deprecated BuilderExternalList(RepeatedFieldBuilderV3 builder) { this.builder = builder; } + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.BuilderExternalList.size() instead. + */ + @Deprecated @Override public int size() { return this.builder.getCount(); } + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.BuilderExternalList.get() instead. + */ + @Deprecated @Override public BType get(int index) { return builder.getBuilder(index); } + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.BuilderExternalList.incrementModCount() instead. + */ + @Deprecated void incrementModCount() { modCount++; } } - /** - * Provides a live view of the builder as a list of builders. - * - * @param the type of message for the field - * @param the type of builder for the field - * @param the common interface for the message and the builder - */ private static class MessageOrBuilderExternalList< MType extends AbstractMessage, BType extends AbstractMessage.Builder, @@ -630,16 +550,34 @@ public class RepeatedFieldBuilderV3< this.builder = builder; } + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.MessageOrBuilderExternalList.size() instead. + */ + @Deprecated @Override public int size() { return this.builder.getCount(); } + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.MessageOrBuilderExternalList.get() instead. + */ + @Deprecated @Override public IType get(int index) { return builder.getMessageOrBuilder(index); } + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * RepeatedFieldBuilder.MessageOrBuilderExternalList.incrementModCount() instead. + */ + @Deprecated void incrementModCount() { modCount++; } diff --git a/java/core/src/main/java/com/google/protobuf/RuntimeVersion.java b/java/core/src/main/java/com/google/protobuf/RuntimeVersion.java new file mode 100644 index 000000000..c0ee66197 --- /dev/null +++ b/java/core/src/main/java/com/google/protobuf/RuntimeVersion.java @@ -0,0 +1,150 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +package com.google.protobuf; + +import java.util.logging.Logger; + +/** + * Provides the version of this Protobuf Java runtime, and methods for Protobuf Java gencode to + * validate that versions are compatible. Fields and methods in this class should be only accessed + * by related unit tests and Protobuf Java gencode, and should not be used elsewhere. + */ +public final class RuntimeVersion { + + /** Indicates the domain of the Protobuf artifact. */ + public enum RuntimeDomain { + GOOGLE_INTERNAL, + PUBLIC, + } + + // The version of this runtime. + // Automatically updated by Protobuf release process. Do not edit manually. + // These OSS versions are not stripped to avoid merging conflicts. + public static final RuntimeDomain OSS_DOMAIN = RuntimeDomain.PUBLIC; + public static final int OSS_MAJOR = 4; + public static final int OSS_MINOR = 28; + public static final int OSS_PATCH = 3; + public static final String OSS_SUFFIX = ""; + + public static final RuntimeDomain DOMAIN = OSS_DOMAIN; + public static final int MAJOR = OSS_MAJOR; + public static final int MINOR = OSS_MINOR; + public static final int PATCH = OSS_PATCH; + public static final String SUFFIX = OSS_SUFFIX; + + private static final String VERSION_STRING = versionString(MAJOR, MINOR, PATCH, SUFFIX); + private static final Logger logger = Logger.getLogger(RuntimeVersion.class.getName()); + + /** + * Validates that the gencode version is compatible with this runtime version according to + * https://protobuf.dev/support/cross-version-runtime-guarantee/. + * + *

This method is currently only used by Protobuf Java **full version** gencode. Do not call it + * elsewhere. + * + * @param domain the domain where Protobuf Java code was generated. + * @param major the major version of Protobuf Java gencode. + * @param minor the minor version of Protobuf Java gencode. + * @param patch the micro/patch version of Protobuf Java gencode. + * @param suffix the version suffix e.g. "-rc2", "-dev", etc. + * @param location the debugging location e.g. generated Java class to put in the error messages. + * @throws ProtobufRuntimeVersionException if versions are incompatible. + */ + public static void validateProtobufGencodeVersion( + RuntimeDomain domain, int major, int minor, int patch, String suffix, String location) { + if (checkDisabled()) { + return; + } + validateProtobufGencodeVersionImpl(domain, major, minor, patch, suffix, location); + } + + /** The actual implementation of version validation. */ + private static void validateProtobufGencodeVersionImpl( + RuntimeDomain domain, int major, int minor, int patch, String suffix, String location) { + if (checkDisabled()) { + return; + } + String gencodeVersionString = versionString(major, minor, patch, suffix); + // Check that version numbers are valid. + if (major < 0 || minor < 0 || patch < 0) { + throw new ProtobufRuntimeVersionException("Invalid gencode version: " + gencodeVersionString); + } + + // Check that runtime domain is the same as the gencode domain. + if (domain != DOMAIN) { + throw new ProtobufRuntimeVersionException( + String.format( + "Detected mismatched Protobuf Gencode/Runtime domains when loading %s: gencode %s," + + " runtime %s. Cross-domain usage of Protobuf is not supported.", + location, domain, DOMAIN)); + } + + // Check that runtime major version is the same as the gencode major version. + if (major != MAJOR) { + if (major == MAJOR - 1) { + logger.warning( + String.format( + " Protobuf gencode version %s is exactly one major version older than the runtime" + + " version %s at %s. Please update the gencode to avoid compatibility" + + " violations in the next runtime release.", + gencodeVersionString, VERSION_STRING, location)); + } else { + throw new ProtobufRuntimeVersionException( + String.format( + "Detected mismatched Protobuf Gencode/Runtime major versions when loading %s:" + + " gencode %s, runtime %s. Same major version is required.", + location, gencodeVersionString, VERSION_STRING)); + } + } + + // Check that runtime version is newer than the gencode version. + if (MINOR < minor || (minor == MINOR && PATCH < patch)) { + throw new ProtobufRuntimeVersionException( + String.format( + "Detected incompatible Protobuf Gencode/Runtime versions when loading %s: gencode %s," + + " runtime %s. Runtime version cannot be older than the linked gencode version.", + location, gencodeVersionString, VERSION_STRING)); + } + + // Check that runtime version suffix is the same as the gencode version suffix. + if (!suffix.equals(SUFFIX)) { + throw new ProtobufRuntimeVersionException( + String.format( + "Detected mismatched Protobuf Gencode/Runtime version suffixes when loading %s:" + + " gencode %s, runtime %s. Version suffixes must be the same.", + location, gencodeVersionString, VERSION_STRING)); + } + } + + /** + * A runtime exception to be thrown by the version validator if version is not well defined or + * versions mismatch. + */ + public static final class ProtobufRuntimeVersionException extends RuntimeException { + public ProtobufRuntimeVersionException(String message) { + super(message); + } + } + + /** Gets the version string given the version segments. */ + private static String versionString(int major, int minor, int patch, String suffix) { + return String.format("%d.%d.%d%s", major, minor, patch, suffix); + } + + private static boolean checkDisabled() { + // Check the environmental variable, and temporarily disable validation if it's set to true. + String disableFlag = java.lang.System.getenv("TEMORARILY_DISABLE_PROTOBUF_VERSION_CHECK"); + if ((disableFlag != null && disableFlag.equals("true"))) { + return true; + } + + return false; + } + + private RuntimeVersion() {} +} diff --git a/java/core/src/main/java/com/google/protobuf/SchemaUtil.java b/java/core/src/main/java/com/google/protobuf/SchemaUtil.java index ecc9b2a32..c08076ab6 100644 --- a/java/core/src/main/java/com/google/protobuf/SchemaUtil.java +++ b/java/core/src/main/java/com/google/protobuf/SchemaUtil.java @@ -31,17 +31,18 @@ final class SchemaUtil { private SchemaUtil() {} /** - * Requires that the given message extend {@link com.google.protobuf.GeneratedMessageV3} or {@link + * Requires that the given message extend {@link com.google.protobuf.GeneratedMessage} or {@link * GeneratedMessageLite}. */ public static void requireGeneratedMessage(Class messageType) { // TODO decide if we're keeping support for Full in schema classes and handle this // better. if (!GeneratedMessageLite.class.isAssignableFrom(messageType) + && !Protobuf.assumeLiteRuntime && GENERATED_MESSAGE_CLASS != null && !GENERATED_MESSAGE_CLASS.isAssignableFrom(messageType)) { throw new IllegalArgumentException( - "Message classes must extend GeneratedMessageV3 or GeneratedMessageLite"); + "Message classes must extend GeneratedMessage or GeneratedMessageLite"); } } @@ -781,16 +782,22 @@ final class SchemaUtil { } private static Class getGeneratedMessageClass() { + if (Protobuf.assumeLiteRuntime) { + return null; + } try { // TODO decide if we're keeping support for Full in schema classes and handle // this better. - return Class.forName("com.google.protobuf.GeneratedMessageV3"); + return Class.forName("com.google.protobuf.GeneratedMessage"); } catch (Throwable e) { return null; } } private static Class getUnknownFieldSetSchemaClass() { + if (Protobuf.assumeLiteRuntime) { + return null; + } try { return Class.forName("com.google.protobuf.UnknownFieldSetSchema"); } catch (Throwable e) { diff --git a/java/core/src/main/java/com/google/protobuf/SingleFieldBuilderV3.java b/java/core/src/main/java/com/google/protobuf/SingleFieldBuilderV3.java index 0aff2b82a..3b074aece 100644 --- a/java/core/src/main/java/com/google/protobuf/SingleFieldBuilderV3.java +++ b/java/core/src/main/java/com/google/protobuf/SingleFieldBuilderV3.java @@ -1,5 +1,5 @@ // Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. +// Copyright 2024 Google LLC. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file or at @@ -10,114 +10,94 @@ package com.google.protobuf; import static com.google.protobuf.Internal.checkNotNull; /** - * {@code SingleFieldBuilderV3} implements a structure that a protocol message uses to hold a single - * field of another protocol message. It supports the classical use case of setting an immutable - * {@link Message} as the value of the field and is highly optimized around this. + * Stub for SingleFieldBuilderV3 matching SingleFieldBuilder for compatibility with older gencode. + * This cannot wrap SingleFieldBuilder directly due to SingleFieldBuilder having more restrictive + * extends GeneratedMessage for MType and BType. * - *

It also supports the additional use case of setting a {@link Message.Builder} as the field and - * deferring conversion of that {@code Builder} to an immutable {@code Message}. In this way, it's - * possible to maintain a tree of {@code Builder}'s that acts as a fully read/write data structure. - *
- * Logically, one can think of a tree of builders as converting the entire tree to messages when - * build is called on the root or when any method is called that desires a Message instead of a - * Builder. In terms of the implementation, the {@code SingleFieldBuilderV3} and {@code - * RepeatedFieldBuilderV3} classes cache messages that were created so that messages only need to be - * created when some change occurred in its builder or a builder for one of its descendants. - * - * @param the type of message for the field - * @param the type of builder for the field - * @param the common interface for the message and the builder - * @author jonp@google.com (Jon Perlow) + * @deprecated This class is deprecated, and slated for removal in the next breaking change. Users + * should update gencode to >= 4.26.x which replaces SingleFieldBuilderV3 with + * SingleFieldBuilder. */ +@Deprecated public class SingleFieldBuilderV3< MType extends AbstractMessage, BType extends AbstractMessage.Builder, IType extends MessageOrBuilder> implements AbstractMessage.BuilderParent { - // Parent to send changes to. private AbstractMessage.BuilderParent parent; - // Invariant: one of builder or message fields must be non-null. - - // If set, this is the case where we are backed by a builder. In this case, - // message field represents a cached message for the builder (or null if - // there is no cached message). private BType builder; - // If builder is non-null, this represents a cached message from the builder. - // If builder is null, this is the authoritative message for the field. private MType message; - // Indicates that we've built a message and so we are now obligated - // to dispatch dirty invalidations. See AbstractMessage.BuilderListener. private boolean isClean; - public SingleFieldBuilderV3(MType message, AbstractMessage.BuilderParent parent, boolean isClean) { + @Deprecated + public SingleFieldBuilderV3( + MType message, AbstractMessage.BuilderParent parent, boolean isClean) { this.message = checkNotNull(message); this.parent = parent; this.isClean = isClean; } + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * SingleFieldBuilder.dispose() instead. + */ + @Deprecated public void dispose() { - // Null out parent so we stop sending it invalidations. parent = null; } - /** - * Get the message for the field. If the message is currently stored as a {@code Builder}, it is - * converted to a {@code Message} by calling {@link Message.Builder#buildPartial} on it. If no - * message has been set, returns the default instance of the message. - * - * @return the message for the field + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * SingleFieldBuilder.getMessage() instead. */ + @Deprecated @SuppressWarnings("unchecked") public MType getMessage() { if (message == null) { - // If message is null, the invariant is that we must be have a builder. message = (MType) builder.buildPartial(); } return message; } - /** - * Builds the message and returns it. - * - * @return the message + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * SingleFieldBuilder.build() instead. */ + @Deprecated public MType build() { - // Now that build has been called, we are required to dispatch - // invalidations. isClean = true; return getMessage(); } - /** - * Gets a builder for the field. If no builder has been created yet, a builder is created on - * demand by calling {@link Message#toBuilder}. - * - * @return The builder for the field + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * SingleFieldBuilder.getbuilder() instead. */ + @Deprecated @SuppressWarnings("unchecked") public BType getBuilder() { if (builder == null) { - // builder.mergeFrom() on a fresh builder - // does not create any sub-objects with independent clean/dirty states, - // therefore setting the builder itself to clean without actually calling - // build() cannot break any invariants. builder = (BType) message.newBuilderForType(this); - builder.mergeFrom(message); // no-op if message is the default message + builder.mergeFrom(message); builder.markClean(); } return builder; } - /** - * Gets the base class interface for the field. This may either be a builder or a message. It will - * return whatever is more efficient. - * - * @return the message or builder for the field as the base class interface + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * SingleFieldBuilder.getMessageOrBuilder() instead. */ + @Deprecated @SuppressWarnings("unchecked") public IType getMessageOrBuilder() { if (builder != null) { @@ -127,12 +107,12 @@ public class SingleFieldBuilderV3< } } - /** - * Sets a message for the field replacing any existing value. - * - * @param message the message to set - * @return the builder + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * SingleFieldBuilder.setMessage() instead. */ + @Deprecated @CanIgnoreReturnValue public SingleFieldBuilderV3 setMessage(MType message) { this.message = checkNotNull(message); @@ -144,12 +124,12 @@ public class SingleFieldBuilderV3< return this; } - /** - * Merges the field from another field. - * - * @param value the value to merge from - * @return the builder + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * SingleFieldBuilder.mergeFrom() instead. */ + @Deprecated @CanIgnoreReturnValue public SingleFieldBuilderV3 mergeFrom(MType value) { if (builder == null && message == message.getDefaultInstanceForType()) { @@ -161,11 +141,12 @@ public class SingleFieldBuilderV3< return this; } - /** - * Clears the value of the field. - * - * @return the builder + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * SingleFieldBuilder.clear() instead. */ + @Deprecated @SuppressWarnings("unchecked") @CanIgnoreReturnValue public SingleFieldBuilderV3 clear() { @@ -179,30 +160,33 @@ public class SingleFieldBuilderV3< builder = null; } onChanged(); - // After clearing, parent is dirty, but this field builder is now clean and any changes should - // trickle up. isClean = true; return this; } - /** - * Called when a the builder or one of its nested children has changed and any parent should be - * notified of its invalidation. + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * SingleFieldBuilder.onChanged() instead. */ + @Deprecated private void onChanged() { - // If builder is null, this is the case where onChanged is being called - // from setMessage or clear. if (builder != null) { message = null; } if (isClean && parent != null) { parent.markDirty(); - // Don't keep dispatching invalidations until build is called again. isClean = false; } } + /* + * @deprecated This method is deprecated, and slated for removal in the next Java breaking change + * (5.x). Users should update gencode to >= 4.26.x which uses + * SingleFieldBuilder.markDirty() instead. + */ + @Deprecated @Override public void markDirty() { onChanged(); diff --git a/java/core/src/main/java/com/google/protobuf/SmallSortedMap.java b/java/core/src/main/java/com/google/protobuf/SmallSortedMap.java index 17e8d77db..2066558a6 100644 --- a/java/core/src/main/java/com/google/protobuf/SmallSortedMap.java +++ b/java/core/src/main/java/com/google/protobuf/SmallSortedMap.java @@ -14,7 +14,6 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; @@ -59,6 +58,8 @@ import java.util.TreeMap; // a subclass to aid testability of the core logic. class SmallSortedMap, V> extends AbstractMap { + static final int DEFAULT_FIELD_MAP_ARRAY_SIZE = 16; + /** * Creates a new instance for mapping FieldDescriptors to their values. The {@link * #makeImmutable()} implementation will convert the List values of any repeated fields to @@ -68,8 +69,8 @@ class SmallSortedMap, V> extends AbstractMap { * mappings. */ static > - SmallSortedMap newFieldMap(int arraySize) { - return new SmallSortedMap(arraySize) { + SmallSortedMap newFieldMap() { + return new SmallSortedMap() { @Override @SuppressWarnings("unchecked") public void makeImmutable() { @@ -77,13 +78,13 @@ class SmallSortedMap, V> extends AbstractMap { for (int i = 0; i < getNumArrayEntries(); i++) { final Map.Entry entry = getArrayEntryAt(i); if (entry.getKey().isRepeated()) { - final List value = (List) entry.getValue(); + final List value = (List) entry.getValue(); entry.setValue(Collections.unmodifiableList(value)); } } for (Map.Entry entry : getOverflowEntries()) { if (entry.getKey().isRepeated()) { - final List value = (List) entry.getValue(); + final List value = (List) entry.getValue(); entry.setValue(Collections.unmodifiableList(value)); } } @@ -93,17 +94,11 @@ class SmallSortedMap, V> extends AbstractMap { }; } - /** - * Creates a new instance for testing. - * - * @param arraySize The size of the entry array containing the lexicographically smallest - * mappings. - */ - static , V> SmallSortedMap newInstanceForTest(int arraySize) { - return new SmallSortedMap(arraySize); + /** Creates a new instance for testing. */ + static , V> SmallSortedMap newInstanceForTest() { + return new SmallSortedMap<>(); } - private final int maxArraySize; // The "entry array" is actually a List because generic arrays are not // allowed. ArrayList also nicely handles the entry shifting on inserts and // removes. @@ -116,12 +111,7 @@ class SmallSortedMap, V> extends AbstractMap { private Map overflowEntriesDescending; private volatile DescendingEntrySet lazyDescendingEntrySet; - /** - * @code arraySize Size of the array in which the lexicographically smallest mappings are stored. - * (i.e. the {@code k} referred to in the class documentation). - */ - private SmallSortedMap(int arraySize) { - this.maxArraySize = arraySize; + private SmallSortedMap() { this.entryList = Collections.emptyList(); this.overflowEntries = Collections.emptyMap(); this.overflowEntriesDescending = Collections.emptyMap(); @@ -169,16 +159,10 @@ class SmallSortedMap, V> extends AbstractMap { /** @return An iterable over the overflow entries. */ public Iterable> getOverflowEntries() { return overflowEntries.isEmpty() - ? EmptySet.>iterable() + ? Collections.emptySet() : overflowEntries.entrySet(); } - Iterable> getOverflowEntriesDescending() { - return overflowEntriesDescending.isEmpty() - ? EmptySet.>iterable() - : overflowEntriesDescending.entrySet(); - } - @Override public int size() { return entryList.size() + overflowEntries.size(); @@ -222,14 +206,14 @@ class SmallSortedMap, V> extends AbstractMap { } ensureEntryArrayMutable(); final int insertionPoint = -(index + 1); - if (insertionPoint >= maxArraySize) { + if (insertionPoint >= DEFAULT_FIELD_MAP_ARRAY_SIZE) { // Put directly in overflow. return getOverflowEntriesMutable().put(key, value); } // Insert new Entry in array. - if (entryList.size() == maxArraySize) { + if (entryList.size() == DEFAULT_FIELD_MAP_ARRAY_SIZE) { // Shift the last array entry into overflow. - final Entry lastEntryInArray = entryList.remove(maxArraySize - 1); + final Entry lastEntryInArray = entryList.remove(DEFAULT_FIELD_MAP_ARRAY_SIZE - 1); getOverflowEntriesMutable().put(lastEntryInArray.getKey(), lastEntryInArray.getValue()); } entryList.add(insertionPoint, new Entry(key, value)); @@ -364,7 +348,7 @@ class SmallSortedMap, V> extends AbstractMap { private void ensureEntryArrayMutable() { checkMutable(); if (entryList.isEmpty() && !(entryList instanceof ArrayList)) { - entryList = new ArrayList(maxArraySize); + entryList = new ArrayList<>(DEFAULT_FIELD_MAP_ARRAY_SIZE); } } @@ -597,45 +581,6 @@ class SmallSortedMap, V> extends AbstractMap { } } - /** - * Helper class that holds immutable instances of an Iterable/Iterator that we return when the - * overflow entries is empty. This eliminates the creation of an Iterator object when there is - * nothing to iterate over. - */ - private static class EmptySet { - - private static final Iterator ITERATOR = - new Iterator() { - @Override - public boolean hasNext() { - return false; - } - - @Override - public Object next() { - throw new NoSuchElementException(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - }; - - private static final Iterable ITERABLE = - new Iterable() { - @Override - public Iterator iterator() { - return ITERATOR; - } - }; - - @SuppressWarnings("unchecked") - static Iterable iterable() { - return (Iterable) ITERABLE; - } - } - @Override public boolean equals(Object o) { if (this == o) { diff --git a/java/core/src/main/java/com/google/protobuf/TextFormat.java b/java/core/src/main/java/com/google/protobuf/TextFormat.java index a49919ef0..bfae1a17a 100644 --- a/java/core/src/main/java/com/google/protobuf/TextFormat.java +++ b/java/core/src/main/java/com/google/protobuf/TextFormat.java @@ -38,189 +38,21 @@ public final class TextFormat { private static final String DEBUG_STRING_SILENT_MARKER = "\t "; - /** - * Outputs a textual representation of the Protocol Message supplied into the parameter output. - * (This representation is the new version of the classic "ProtocolPrinter" output from the - * original Protocol Buffer system) - * - * @deprecated Use {@code printer().print(MessageOrBuilder, Appendable)} - */ - @Deprecated - @InlineMe( - replacement = "TextFormat.printer().print(message, output)", - imports = "com.google.protobuf.TextFormat") - public static void print(final MessageOrBuilder message, final Appendable output) - throws IOException { - printer().print(message, output); - } - - /** - * Outputs a textual representation of {@code fields} to {@code output}. - * - * @deprecated Use {@code printer().print(UnknownFieldSet, Appendable)} - */ - @Deprecated - public static void print(final UnknownFieldSet fields, final Appendable output) - throws IOException { - printer().print(fields, output); - } - - /** - * Same as {@code print()}, except that non-ASCII characters are not escaped. - * - * @deprecated Use {@code printer().escapingNonAscii(false).print(MessageOrBuilder, Appendable)} - */ - @Deprecated - @InlineMe( - replacement = "TextFormat.printer().escapingNonAscii(false).print(message, output)", - imports = "com.google.protobuf.TextFormat") - public static void printUnicode(final MessageOrBuilder message, final Appendable output) - throws IOException { - printer().escapingNonAscii(false).print(message, output); - } - - /** - * Same as {@code print()}, except that non-ASCII characters are not escaped. - * - * @deprecated Use {@code printer().escapingNonAscii(false).print(UnknownFieldSet, Appendable)} - */ - @Deprecated - public static void printUnicode(final UnknownFieldSet fields, final Appendable output) - throws IOException { - printer().escapingNonAscii(false).print(fields, output); - } + private static final String REDACTED_MARKER = "[REDACTED]"; /** * Generates a human readable form of this message, useful for debugging and other purposes, with * no newline characters. This is just a trivial wrapper around {@link * TextFormat.Printer#shortDebugString(MessageOrBuilder)}. - */ - public static String shortDebugString(final MessageOrBuilder message) { - return printer().shortDebugString(message); - } - - /** - * Generates a human readable form of the field, useful for debugging and other purposes, with - * no newline characters. - * - * @deprecated Use {@code printer().shortDebugString(FieldDescriptor, Object)} - */ - @Deprecated - public static String shortDebugString(final FieldDescriptor field, final Object value) { - return printer().shortDebugString(field, value); - } - - /** - * Generates a human readable form of the unknown fields, useful for debugging and other - * purposes, with no newline characters. - * - * @deprecated Use {@code printer().shortDebugString(UnknownFieldSet)} - */ - @Deprecated - public static String shortDebugString(final UnknownFieldSet fields) { - return printer().shortDebugString(fields); - } - - /** - * Like {@code print()}, but writes directly to a {@code String} and returns it. * - * @deprecated Use {@code message.toString()} - */ - @Deprecated - @InlineMe( - replacement = "TextFormat.printer().printToString(message)", - imports = "com.google.protobuf.TextFormat") - public static String printToString(final MessageOrBuilder message) { - return printer().printToString(message); - } - - /** - * Like {@code print()}, but writes directly to a {@code String} and returns it. - * - * @deprecated Use {@link UnknownFieldSet#toString()} - */ - @Deprecated - public static String printToString(final UnknownFieldSet fields) { - return printer().printToString(fields); - } - - /** - * Same as {@code printToString()}, except that non-ASCII characters in string type fields are not - * escaped in backslash+octals. - * - * @deprecated Use {@code printer().escapingNonAscii(false).printToString(MessageOrBuilder)} - */ - @Deprecated - @InlineMe( - replacement = "TextFormat.printer().escapingNonAscii(false).printToString(message)", - imports = "com.google.protobuf.TextFormat") - public static String printToUnicodeString(final MessageOrBuilder message) { - return printer().escapingNonAscii(false).printToString(message); - } - - /** - * Same as {@code printToString()}, except that non-ASCII characters in string type fields are - * not escaped in backslash+octals. - * - * @deprecated Use {@code printer().escapingNonAscii(false).printToString(UnknownFieldSet)} - */ - @Deprecated - public static String printToUnicodeString(final UnknownFieldSet fields) { - return printer().escapingNonAscii(false).printToString(fields); - } - - /** @deprecated Use {@code printer().printField(FieldDescriptor, Object, Appendable)} */ - @Deprecated - public static void printField( - final FieldDescriptor field, final Object value, final Appendable output) - throws IOException { - printer().printField(field, value, output); - } - - /** @deprecated Use {@code printer().printFieldToString(FieldDescriptor, Object)} */ - @Deprecated - public static String printFieldToString(final FieldDescriptor field, final Object value) { - return printer().printFieldToString(field, value); - } - - /** - * Outputs a unicode textual representation of the value of given field value. - * - *

Same as {@code printFieldValue()}, except that non-ASCII characters in string type fields - * are not escaped in backslash+octals. - * - * @deprecated Use {@code printer().escapingNonAscii(false).printFieldValue(FieldDescriptor, - * Object, Appendable)} - * @param field the descriptor of the field - * @param value the value of the field - * @param output the output to which to append the formatted value - * @throws ClassCastException if the value is not appropriate for the given field descriptor - * @throws IOException if there is an exception writing to the output - */ - @Deprecated - public static void printUnicodeFieldValue( - final FieldDescriptor field, final Object value, final Appendable output) - throws IOException { - printer().escapingNonAscii(false).printFieldValue(field, value, output); - } - - /** - * Outputs a textual representation of the value of given field value. - * - * @deprecated Use {@code printer().printFieldValue(FieldDescriptor, Object, Appendable)} - * @param field the descriptor of the field - * @param value the value of the field - * @param output the output to which to append the formatted value - * @throws ClassCastException if the value is not appropriate for the given field descriptor - * @throws IOException if there is an exception writing to the output + * @deprecated Use {@code printer().emittingSingleLine(true).printToString(MessageOrBuilder)} */ @Deprecated @InlineMe( - replacement = "TextFormat.printer().printFieldValue(field, value, output)", + replacement = "TextFormat.printer().emittingSingleLine(true).printToString(message)", imports = "com.google.protobuf.TextFormat") - public static void printFieldValue( - final FieldDescriptor field, final Object value, final Appendable output) throws IOException { - printer().printFieldValue(field, value, output); + public static String shortDebugString(final MessageOrBuilder message) { + return printer().emittingSingleLine(true).printToString(message); } /** @@ -234,11 +66,12 @@ public final class TextFormat { */ public static void printUnknownFieldValue( final int tag, final Object value, final Appendable output) throws IOException { - printUnknownFieldValue(tag, value, multiLineOutput(output)); + printUnknownFieldValue(tag, value, setSingleLineOutput(output, false), false); } private static void printUnknownFieldValue( - final int tag, final Object value, final TextGenerator generator) throws IOException { + final int tag, final Object value, final TextGenerator generator, boolean redact) + throws IOException { switch (WireFormat.getTagWireType(tag)) { case WireFormat.WIRETYPE_VARINT: generator.print(unsignedToString((Long) value)); @@ -256,7 +89,7 @@ public final class TextFormat { generator.print("{"); generator.eol(); generator.indent(); - Printer.printUnknownFields(message, generator); + Printer.printUnknownFields(message, generator, redact); generator.outdent(); generator.print("}"); } catch (InvalidProtocolBufferException e) { @@ -267,7 +100,7 @@ public final class TextFormat { } break; case WireFormat.WIRETYPE_START_GROUP: - Printer.printUnknownFields((UnknownFieldSet) value, generator); + Printer.printUnknownFields((UnknownFieldSet) value, generator, redact); break; default: throw new IllegalArgumentException("Bad tag: " + tag); @@ -283,16 +116,39 @@ public final class TextFormat { public static final class Printer { // Printer instance which escapes non-ASCII characters. - private static final Printer DEFAULT = new Printer(true, TypeRegistry.getEmptyTypeRegistry()); + private static final Printer DEFAULT = + new Printer( + true, + TypeRegistry.getEmptyTypeRegistry(), + ExtensionRegistryLite.getEmptyRegistry(), + false, + false); /** Whether to escape non ASCII characters with backslash and octal. */ private final boolean escapeNonAscii; private final TypeRegistry typeRegistry; + private final ExtensionRegistryLite extensionRegistry; + + /** + * Whether to enable redaction of sensitive fields and introduce randomization. Note that when + * this is enabled, the output will no longer be deserializable. + */ + private final boolean enablingSafeDebugFormat; - private Printer(boolean escapeNonAscii, TypeRegistry typeRegistry) { + private final boolean singleLine; + + private Printer( + boolean escapeNonAscii, + TypeRegistry typeRegistry, + ExtensionRegistryLite extensionRegistry, + boolean enablingSafeDebugFormat, + boolean singleLine) { this.escapeNonAscii = escapeNonAscii; this.typeRegistry = typeRegistry; + this.extensionRegistry = extensionRegistry; + this.enablingSafeDebugFormat = enablingSafeDebugFormat; + this.singleLine = singleLine; } /** @@ -305,7 +161,8 @@ public final class TextFormat { * with the escape mode set to the given parameter. */ public Printer escapingNonAscii(boolean escapeNonAscii) { - return new Printer(escapeNonAscii, typeRegistry); + return new Printer( + escapeNonAscii, typeRegistry, extensionRegistry, enablingSafeDebugFormat, singleLine); } /** @@ -318,7 +175,48 @@ public final class TextFormat { if (this.typeRegistry != TypeRegistry.getEmptyTypeRegistry()) { throw new IllegalArgumentException("Only one typeRegistry is allowed."); } - return new Printer(escapeNonAscii, typeRegistry); + return new Printer( + escapeNonAscii, typeRegistry, extensionRegistry, enablingSafeDebugFormat, singleLine); + } + + /** + * Creates a new {@link Printer} using the given extensionRegistry. The new Printer clones all + * other configurations from the current {@link Printer}. + * + * @throws IllegalArgumentException if a registry is already set. + */ + public Printer usingExtensionRegistry(ExtensionRegistryLite extensionRegistry) { + if (this.extensionRegistry != ExtensionRegistryLite.getEmptyRegistry()) { + throw new IllegalArgumentException("Only one extensionRegistry is allowed."); + } + return new Printer( + escapeNonAscii, typeRegistry, extensionRegistry, enablingSafeDebugFormat, singleLine); + } + + /** + * Return a new Printer instance that outputs a redacted and unstable format suitable for + * debugging. + * + * @param enablingSafeDebugFormat If true, the new Printer will redact all proto fields that are + * marked by a debug_redact=true option, and apply an unstable prefix to the output. + * @return a new Printer that clones all other configurations from the current {@link Printer}, + * with the enablingSafeDebugFormat mode set to the given parameter. + */ + Printer enablingSafeDebugFormat(boolean enablingSafeDebugFormat) { + return new Printer( + escapeNonAscii, typeRegistry, extensionRegistry, enablingSafeDebugFormat, singleLine); + } + + /** + * Return a new Printer instance with the specified line formatting status. + * + * @param singleLine If true, the new Printer will output no newline characters. + * @return a new Printer that clones all other configurations from the current {@link Printer}, + * with the singleLine mode set to the given parameter. + */ + public Printer emittingSingleLine(boolean singleLine) { + return new Printer( + escapeNonAscii, typeRegistry, extensionRegistry, enablingSafeDebugFormat, singleLine); } /** @@ -327,12 +225,13 @@ public final class TextFormat { * original Protocol Buffer system) */ public void print(final MessageOrBuilder message, final Appendable output) throws IOException { - print(message, multiLineOutput(output)); + print(message, setSingleLineOutput(output, this.singleLine)); } /** Outputs a textual representation of {@code fields} to {@code output}. */ public void print(final UnknownFieldSet fields, final Appendable output) throws IOException { - printUnknownFields(fields, multiLineOutput(output)); + printUnknownFields( + fields, setSingleLineOutput(output, this.singleLine), this.enablingSafeDebugFormat); } private void print(final MessageOrBuilder message, final TextGenerator generator) @@ -344,6 +243,14 @@ public final class TextFormat { printMessage(message, generator); } + private void applyUnstablePrefix(final Appendable output) { + try { + output.append(""); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + /** * Attempt to print the 'google.protobuf.Any' message in a human-friendly format. Returns false * if the message isn't a valid 'google.protobuf.Any' message (in which case the message should @@ -377,7 +284,7 @@ public final class TextFormat { return false; } contentBuilder = DynamicMessage.getDefaultInstance(contentType).newBuilderForType(); - contentBuilder.mergeFrom((ByteString) value); + contentBuilder.mergeFrom((ByteString) value, extensionRegistry); } catch (InvalidProtocolBufferException e) { // The value of Any is malformed. We cannot print it out nicely, so fallback to printing out // the type_url and value as bytes. Note that we fail open here to be consistent with @@ -400,6 +307,9 @@ public final class TextFormat { public String printFieldToString(final FieldDescriptor field, final Object value) { try { final StringBuilder text = new StringBuilder(); + if (enablingSafeDebugFormat) { + applyUnstablePrefix(text); + } printField(field, value, text); return text.toString(); } catch (IOException e) { @@ -409,7 +319,7 @@ public final class TextFormat { public void printField(final FieldDescriptor field, final Object value, final Appendable output) throws IOException { - printField(field, value, multiLineOutput(output)); + printField(field, value, setSingleLineOutput(output, this.singleLine)); } private void printField( @@ -514,12 +424,19 @@ public final class TextFormat { public void printFieldValue( final FieldDescriptor field, final Object value, final Appendable output) throws IOException { - printFieldValue(field, value, multiLineOutput(output)); + printFieldValue(field, value, setSingleLineOutput(output, this.singleLine)); } private void printFieldValue( final FieldDescriptor field, final Object value, final TextGenerator generator) throws IOException { + if (shouldRedact(field)) { + generator.print(REDACTED_MARKER); + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + generator.eol(); + } + return; + } switch (field.getType()) { case INT32: case SINT32: @@ -585,10 +502,54 @@ public final class TextFormat { } } + private boolean shouldRedactOptionValue(EnumValueDescriptor optionValue) { + if (optionValue.getOptions().hasDebugRedact()) { + return optionValue.getOptions().getDebugRedact(); + } + return false; + } + + // The criteria for redacting a field is as follows: 1) The enablingSafeDebugFormat printer + // option + // must be on. 2) The field must be marked by a debug_redact=true option, or is marked by an + // option with an enum value that is marked by a debug_redact=true option. + private boolean shouldRedact(final FieldDescriptor field) { + if (!this.enablingSafeDebugFormat) { + return false; + } + if (field.getOptions().hasDebugRedact()) { + return field.getOptions().getDebugRedact(); + } + // Iterate through every option; if it's an enum, we check each enum value for debug_redact. + for (Map.Entry entry : + field.getOptions().getAllFields().entrySet()) { + Descriptors.FieldDescriptor option = entry.getKey(); + if (option.getType() != Descriptors.FieldDescriptor.Type.ENUM) { + continue; + } + if (option.isRepeated()) { + for (EnumValueDescriptor value : (List) entry.getValue()) { + if (shouldRedactOptionValue(value)) { + return true; + } + } + } else { + EnumValueDescriptor optionValue = (EnumValueDescriptor) entry.getValue(); + if (shouldRedactOptionValue(optionValue)) { + return true; + } + } + } + return false; + } + /** Like {@code print()}, but writes directly to a {@code String} and returns it. */ public String printToString(final MessageOrBuilder message) { try { final StringBuilder text = new StringBuilder(); + if (enablingSafeDebugFormat) { + applyUnstablePrefix(text); + } print(message, text); return text.toString(); } catch (IOException e) { @@ -599,6 +560,9 @@ public final class TextFormat { public String printToString(final UnknownFieldSet fields) { try { final StringBuilder text = new StringBuilder(); + if (enablingSafeDebugFormat) { + applyUnstablePrefix(text); + } print(fields, text); return text.toString(); } catch (IOException e) { @@ -609,56 +573,62 @@ public final class TextFormat { /** * Generates a human readable form of this message, useful for debugging and other purposes, * with no newline characters. + * + * @deprecated Use {@code + * this.printer().emittingSingleLine(true).printToString(MessageOrBuilder)} */ + @Deprecated + @InlineMe(replacement = "this.emittingSingleLine(true).printToString(message)") public String shortDebugString(final MessageOrBuilder message) { - try { - final StringBuilder text = new StringBuilder(); - print(message, singleLineOutput(text)); - return text.toString(); - } catch (IOException e) { - throw new IllegalStateException(e); - } + return this.emittingSingleLine(true).printToString(message); } /** * Generates a human readable form of the field, useful for debugging and other purposes, with * no newline characters. + * + * @deprecated Use {@code this.emittingSingleLine(true).printFieldToString(FieldDescriptor, + * Object)} */ + @Deprecated + @InlineMe(replacement = "this.emittingSingleLine(true).printFieldToString(field, value)") public String shortDebugString(final FieldDescriptor field, final Object value) { - try { - final StringBuilder text = new StringBuilder(); - printField(field, value, singleLineOutput(text)); - return text.toString(); - } catch (IOException e) { - throw new IllegalStateException(e); - } + return this.emittingSingleLine(true).printFieldToString(field, value); } /** * Generates a human readable form of the unknown fields, useful for debugging and other * purposes, with no newline characters. + * + * @deprecated Use {@code this.emittingSingleLine(true).printToString(UnknownFieldSet)} */ + @Deprecated + @InlineMe(replacement = "this.emittingSingleLine(true).printToString(fields)") public String shortDebugString(final UnknownFieldSet fields) { - try { - final StringBuilder text = new StringBuilder(); - printUnknownFields(fields, singleLineOutput(text)); - return text.toString(); - } catch (IOException e) { - throw new IllegalStateException(e); - } + return this.emittingSingleLine(true).printToString(fields); } private static void printUnknownFieldValue( - final int tag, final Object value, final TextGenerator generator) throws IOException { + final int tag, final Object value, final TextGenerator generator, boolean redact) + throws IOException { switch (WireFormat.getTagWireType(tag)) { case WireFormat.WIRETYPE_VARINT: - generator.print(unsignedToString((Long) value)); + generator.print( + redact + ? String.format("UNKNOWN_VARINT %s", REDACTED_MARKER) + : unsignedToString((Long) value)); break; case WireFormat.WIRETYPE_FIXED32: - generator.print(String.format((Locale) null, "0x%08x", (Integer) value)); + generator.print( + redact + ? String.format("UNKNOWN_FIXED32 %s", REDACTED_MARKER) + : String.format((Locale) null, "0x%08x", (Integer) value)); break; case WireFormat.WIRETYPE_FIXED64: - generator.print(String.format((Locale) null, "0x%016x", (Long) value)); + generator.print( + redact + ? String.format("UNKNOWN_FIXED64 %s", REDACTED_MARKER) + : String.format((Locale) null, "0x%016x", (Long) value)); break; case WireFormat.WIRETYPE_LENGTH_DELIMITED: try { @@ -667,18 +637,22 @@ public final class TextFormat { generator.print("{"); generator.eol(); generator.indent(); - printUnknownFields(message, generator); + printUnknownFields(message, generator, redact); generator.outdent(); generator.print("}"); } catch (InvalidProtocolBufferException e) { // If not parseable as a message, print as a String + if (redact) { + generator.print(String.format("UNKNOWN_STRING %s", REDACTED_MARKER)); + break; + } generator.print("\""); generator.print(escapeBytes((ByteString) value)); generator.print("\""); } break; case WireFormat.WIRETYPE_START_GROUP: - printUnknownFields((UnknownFieldSet) value, generator); + printUnknownFields((UnknownFieldSet) value, generator, redact); break; default: throw new IllegalArgumentException("Bad tag: " + tag); @@ -690,7 +664,7 @@ public final class TextFormat { for (Map.Entry field : message.getAllFields().entrySet()) { printField(field.getKey(), field.getValue(), generator); } - printUnknownFields(message.getUnknownFields(), generator); + printUnknownFields(message.getUnknownFields(), generator, this.enablingSafeDebugFormat); } private void printSingleField( @@ -710,7 +684,7 @@ public final class TextFormat { } generator.print("]"); } else { - if (field.getType() == FieldDescriptor.Type.GROUP) { + if (field.isGroupLike()) { // Groups must be serialized with their original capitalization. generator.print(field.getMessageType().getName()); } else { @@ -736,24 +710,32 @@ public final class TextFormat { } private static void printUnknownFields( - final UnknownFieldSet unknownFields, final TextGenerator generator) throws IOException { + final UnknownFieldSet unknownFields, final TextGenerator generator, boolean redact) + throws IOException { + if (unknownFields.isEmpty()) { + return; + } for (Map.Entry entry : unknownFields.asMap().entrySet()) { final int number = entry.getKey(); final UnknownFieldSet.Field field = entry.getValue(); - printUnknownField(number, WireFormat.WIRETYPE_VARINT, field.getVarintList(), generator); - printUnknownField(number, WireFormat.WIRETYPE_FIXED32, field.getFixed32List(), generator); - printUnknownField(number, WireFormat.WIRETYPE_FIXED64, field.getFixed64List(), generator); + printUnknownField( + number, WireFormat.WIRETYPE_VARINT, field.getVarintList(), generator, redact); + printUnknownField( + number, WireFormat.WIRETYPE_FIXED32, field.getFixed32List(), generator, redact); + printUnknownField( + number, WireFormat.WIRETYPE_FIXED64, field.getFixed64List(), generator, redact); printUnknownField( number, WireFormat.WIRETYPE_LENGTH_DELIMITED, field.getLengthDelimitedList(), - generator); + generator, + redact); for (final UnknownFieldSet value : field.getGroupList()) { generator.print(entry.getKey().toString()); generator.print(" {"); generator.eol(); generator.indent(); - printUnknownFields(value, generator); + printUnknownFields(value, generator, redact); generator.outdent(); generator.print("}"); generator.eol(); @@ -762,12 +744,16 @@ public final class TextFormat { } private static void printUnknownField( - final int number, final int wireType, final List values, final TextGenerator generator) + final int number, + final int wireType, + final List values, + final TextGenerator generator, + boolean redact) throws IOException { for (final Object value : values) { generator.print(String.valueOf(number)); generator.print(": "); - printUnknownFieldValue(wireType, value, generator); + printUnknownFieldValue(wireType, value, generator, redact); generator.eol(); } } @@ -793,12 +779,8 @@ public final class TextFormat { } } - private static TextGenerator multiLineOutput(Appendable output) { - return new TextGenerator(output, false); - } - - private static TextGenerator singleLineOutput(Appendable output) { - return new TextGenerator(output, true); + private static TextGenerator setSingleLineOutput(Appendable output, boolean singleLine) { + return new TextGenerator(output, singleLine); } /** An inner class for writing text to the output stream. */ @@ -1396,7 +1378,13 @@ public final class TextFormat { } } - /** Thrown when encountering an unknown field while parsing a text format message. */ + /** Obsolete exception, once thrown when encountering an unknown field while parsing a text + format message. + * + * @deprecated This exception is unused and will be removed in the next breaking release + (v5.x.x). + */ + @Deprecated public static class UnknownFieldParseException extends ParseException { private final String unknownField; @@ -1876,15 +1864,12 @@ public final class TextFormat { final String lowerName = name.toLowerCase(Locale.US); field = type.findFieldByName(lowerName); // If the case-insensitive match worked but the field is NOT a group, - if (field != null && field.getType() != FieldDescriptor.Type.GROUP) { + if (field != null && !field.isGroupLike()) { + field = null; + } + if (field != null && !field.getMessageType().getName().equals(name)) { field = null; } - } - // Again, special-case group names as described above. - if (field != null - && field.getType() == FieldDescriptor.Type.GROUP - && !field.getMessageType().getName().equals(name)) { - field = null; } if (field == null) { diff --git a/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java b/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java index 10fd7a7f6..40fc62596 100644 --- a/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java +++ b/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java @@ -15,7 +15,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.ListIterator; import java.util.Map; import java.util.TreeMap; @@ -83,8 +82,17 @@ public final class UnknownFieldSet implements MessageLite { return fields.hashCode(); } + /** Whether the field set has no fields. */ + public boolean isEmpty() { + return fields.isEmpty(); + } + /** Get a map of fields in the set by number. */ public Map asMap() { + // Avoid an allocation for the common case of an empty map. + if (fields.isEmpty()) { + return Collections.emptyMap(); + } return (Map) fields.clone(); } @@ -102,6 +110,10 @@ public final class UnknownFieldSet implements MessageLite { /** Serializes the set and writes it to {@code output}. */ @Override public void writeTo(CodedOutputStream output) throws IOException { + if (fields.isEmpty()) { + // Avoid allocating an iterator. + return; + } for (Map.Entry entry : fields.entrySet()) { Field field = entry.getValue(); field.writeTo(entry.getKey(), output); @@ -174,16 +186,22 @@ public final class UnknownFieldSet implements MessageLite { @Override public int getSerializedSize() { int result = 0; - if (!fields.isEmpty()) { - for (Map.Entry entry : fields.entrySet()) { - result += entry.getValue().getSerializedSize(entry.getKey()); - } + if (fields.isEmpty()) { + // Avoid allocating an iterator. + return result; + } + for (Map.Entry entry : fields.entrySet()) { + result += entry.getValue().getSerializedSize(entry.getKey()); } return result; } /** Serializes the set and writes it to {@code output} using {@code MessageSet} wire format. */ public void writeAsMessageSetTo(CodedOutputStream output) throws IOException { + if (fields.isEmpty()) { + // Avoid allocating an iterator. + return; + } for (Map.Entry entry : fields.entrySet()) { entry.getValue().writeAsMessageSetExtensionTo(entry.getKey(), output); } @@ -191,6 +209,10 @@ public final class UnknownFieldSet implements MessageLite { /** Serializes the set and writes it to {@code writer}. */ void writeTo(Writer writer) throws IOException { + if (fields.isEmpty()) { + // Avoid allocating an iterator. + return; + } if (writer.fieldOrder() == Writer.FieldOrder.DESCENDING) { // Write fields in descending order. for (Map.Entry entry : fields.descendingMap().entrySet()) { @@ -206,6 +228,10 @@ public final class UnknownFieldSet implements MessageLite { /** Serializes the set and writes it to {@code writer} using {@code MessageSet} wire format. */ void writeAsMessageSetTo(Writer writer) throws IOException { + if (fields.isEmpty()) { + // Avoid allocating an iterator. + return; + } if (writer.fieldOrder() == Writer.FieldOrder.DESCENDING) { // Write fields in descending order. for (Map.Entry entry : fields.descendingMap().entrySet()) { @@ -222,6 +248,10 @@ public final class UnknownFieldSet implements MessageLite { /** Get the number of bytes required to encode this set using {@code MessageSet} wire format. */ public int getSerializedSizeAsMessageSet() { int result = 0; + if (fields.isEmpty()) { + // Avoid allocating an iterator. + return result; + } for (Map.Entry entry : fields.entrySet()) { result += entry.getValue().getSerializedSizeAsMessageSetExtension(entry.getKey()); } @@ -452,6 +482,11 @@ public final class UnknownFieldSet implements MessageLite { * changes may or may not be reflected in this map. */ public Map asMap() { + // Avoid an allocation for the common case of an empty map. + if (fieldBuilders.isEmpty()) { + return Collections.emptyMap(); + } + TreeMap fields = new TreeMap<>(); for (Map.Entry entry : fieldBuilders.entrySet()) { fields.put(entry.getKey(), entry.getValue().build()); @@ -742,40 +777,52 @@ public final class UnknownFieldSet implements MessageLite { } /** Serializes the field, including field number, and writes it to {@code output}. */ + @SuppressWarnings({"ForeachList", "ForeachListWithUserVar"}) // No iterator allocation. public void writeTo(int fieldNumber, CodedOutputStream output) throws IOException { - for (long value : varint) { + for (int i = 0; i < varint.size(); i++) { + long value = varint.get(i); output.writeUInt64(fieldNumber, value); } - for (int value : fixed32) { + for (int i = 0; i < fixed32.size(); i++) { + int value = fixed32.get(i); output.writeFixed32(fieldNumber, value); } - for (long value : fixed64) { + for (int i = 0; i < fixed64.size(); i++) { + long value = fixed64.get(i); output.writeFixed64(fieldNumber, value); } - for (ByteString value : lengthDelimited) { + for (int i = 0; i < lengthDelimited.size(); i++) { + ByteString value = lengthDelimited.get(i); output.writeBytes(fieldNumber, value); } - for (UnknownFieldSet value : group) { + for (int i = 0; i < group.size(); i++) { + UnknownFieldSet value = group.get(i); output.writeGroup(fieldNumber, value); } } /** Get the number of bytes required to encode this field, including field number. */ + @SuppressWarnings({"ForeachList", "ForeachListWithUserVar"}) // No iterator allocation. public int getSerializedSize(int fieldNumber) { int result = 0; - for (long value : varint) { + for (int i = 0; i < varint.size(); i++) { + long value = varint.get(i); result += CodedOutputStream.computeUInt64Size(fieldNumber, value); } - for (int value : fixed32) { + for (int i = 0; i < fixed32.size(); i++) { + int value = fixed32.get(i); result += CodedOutputStream.computeFixed32Size(fieldNumber, value); } - for (long value : fixed64) { + for (int i = 0; i < fixed64.size(); i++) { + long value = fixed64.get(i); result += CodedOutputStream.computeFixed64Size(fieldNumber, value); } - for (ByteString value : lengthDelimited) { + for (int i = 0; i < lengthDelimited.size(); i++) { + ByteString value = lengthDelimited.get(i); result += CodedOutputStream.computeBytesSize(fieldNumber, value); } - for (UnknownFieldSet value : group) { + for (int i = 0; i < group.size(); i++) { + UnknownFieldSet value = group.get(i); result += CodedOutputStream.computeGroupSize(fieldNumber, value); } return result; @@ -785,9 +832,11 @@ public final class UnknownFieldSet implements MessageLite { * Serializes the field, including field number, and writes it to {@code output}, using {@code * MessageSet} wire format. */ + @SuppressWarnings({"ForeachList", "ForeachListWithUserVar"}) // No iterator allocation. public void writeAsMessageSetExtensionTo(int fieldNumber, CodedOutputStream output) throws IOException { - for (ByteString value : lengthDelimited) { + for (int i = 0; i < lengthDelimited.size(); i++) { + ByteString value = lengthDelimited.get(i); output.writeRawMessageSetExtension(fieldNumber, value); } } @@ -818,17 +867,18 @@ public final class UnknownFieldSet implements MessageLite { * Serializes the field, including field number, and writes it to {@code writer}, using {@code * MessageSet} wire format. */ - private void writeAsMessageSetExtensionTo(int fieldNumber, Writer writer) - throws IOException { + @SuppressWarnings({"ForeachList", "ForeachListWithUserVar"}) // No iterator allocation. + private void writeAsMessageSetExtensionTo(int fieldNumber, Writer writer) throws IOException { if (writer.fieldOrder() == Writer.FieldOrder.DESCENDING) { // Write in descending field order. - ListIterator iter = lengthDelimited.listIterator(lengthDelimited.size()); - while (iter.hasPrevious()) { - writer.writeMessageSetItem(fieldNumber, iter.previous()); + for (int i = lengthDelimited.size() - 1; i >= 0; i--) { + ByteString value = lengthDelimited.get(i); + writer.writeMessageSetItem(fieldNumber, value); } } else { // Write in ascending field order. - for (ByteString value : lengthDelimited) { + for (int i = 0; i < lengthDelimited.size(); i++) { + ByteString value = lengthDelimited.get(i); writer.writeMessageSetItem(fieldNumber, value); } } @@ -838,9 +888,11 @@ public final class UnknownFieldSet implements MessageLite { * Get the number of bytes required to encode this field, including field number, using {@code * MessageSet} wire format. */ + @SuppressWarnings({"ForeachList", "ForeachListWithUserVar"}) // No iterator allocation. public int getSerializedSizeAsMessageSetExtension(int fieldNumber) { int result = 0; - for (ByteString value : lengthDelimited) { + for (int i = 0; i < lengthDelimited.size(); i++) { + ByteString value = lengthDelimited.get(i); result += CodedOutputStream.computeRawMessageSetExtensionSize(fieldNumber, value); } return result; diff --git a/java/core/src/main/java/com/google/protobuf/UnknownFieldSetSchema.java b/java/core/src/main/java/com/google/protobuf/UnknownFieldSetSchema.java index e1eccccea..d60deba18 100644 --- a/java/core/src/main/java/com/google/protobuf/UnknownFieldSetSchema.java +++ b/java/core/src/main/java/com/google/protobuf/UnknownFieldSetSchema.java @@ -65,22 +65,22 @@ class UnknownFieldSetSchema extends UnknownFieldSchemaDISCLAIMER: The methods in this class should only be called if it is * guaranteed that the buffer backing the {@link ByteString} will never change! Mutation of a {@link @@ -38,7 +38,6 @@ import java.nio.ByteBuffer; * parts of the code base modifying the buffer. In fact, both parts of the code base may be correct * - it is the bridging with the unsafe operations that was in error! */ -@ExperimentalApi public final class UnsafeByteOperations { private UnsafeByteOperations() {} diff --git a/java/core/src/main/java/com/google/protobuf/Utf8.java b/java/core/src/main/java/com/google/protobuf/Utf8.java index f71820ef9..d52006754 100644 --- a/java/core/src/main/java/com/google/protobuf/Utf8.java +++ b/java/core/src/main/java/com/google/protobuf/Utf8.java @@ -214,24 +214,24 @@ final class Utf8 { * @throws IllegalArgumentException if {@code sequence} contains ill-formed UTF-16 (unpaired * surrogates) */ - static int encodedLength(CharSequence sequence) { + static int encodedLength(String string) { // Warning to maintainers: this implementation is highly optimized. - int utf16Length = sequence.length(); + int utf16Length = string.length(); int utf8Length = utf16Length; int i = 0; // This loop optimizes for pure ASCII. - while (i < utf16Length && sequence.charAt(i) < 0x80) { + while (i < utf16Length && string.charAt(i) < 0x80) { i++; } // This loop optimizes for chars less than 0x800. for (; i < utf16Length; i++) { - char c = sequence.charAt(i); + char c = string.charAt(i); if (c < 0x800) { utf8Length += ((0x7f - c) >>> 31); // branch free! } else { - utf8Length += encodedLengthGeneral(sequence, i); + utf8Length += encodedLengthGeneral(string, i); break; } } @@ -244,11 +244,11 @@ final class Utf8 { return utf8Length; } - private static int encodedLengthGeneral(CharSequence sequence, int start) { - int utf16Length = sequence.length(); + private static int encodedLengthGeneral(String string, int start) { + int utf16Length = string.length(); int utf8Length = 0; for (int i = start; i < utf16Length; i++) { - char c = sequence.charAt(i); + char c = string.charAt(i); if (c < 0x800) { utf8Length += (0x7f - c) >>> 31; // branch free! } else { @@ -256,7 +256,7 @@ final class Utf8 { // jdk7+: if (Character.isSurrogate(c)) { if (Character.MIN_SURROGATE <= c && c <= Character.MAX_SURROGATE) { // Check that we have a well-formed surrogate pair. - int cp = Character.codePointAt(sequence, i); + int cp = Character.codePointAt(string, i); if (cp < MIN_SUPPLEMENTARY_CODE_POINT) { throw new UnpairedSurrogateException(i, utf16Length); } @@ -267,7 +267,7 @@ final class Utf8 { return utf8Length; } - static int encode(CharSequence in, byte[] out, int offset, int length) { + static int encode(String in, byte[] out, int offset, int length) { return processor.encodeUtf8(in, out, offset, length); } // End Guava UTF-8 methods. @@ -326,9 +326,9 @@ final class Utf8 { * * @param in the source string to be encoded * @param out the target buffer to receive the encoded string. - * @see Utf8#encode(CharSequence, byte[], int, int) + * @see Utf8#encode(String, byte[], int, int) */ - static void encodeUtf8(CharSequence in, ByteBuffer out) { + static void encodeUtf8(String in, ByteBuffer out) { processor.encodeUtf8(in, out); } @@ -724,7 +724,7 @@ final class Utf8 { * {@code bytes.length - offset} * @return the new offset, equivalent to {@code offset + Utf8.encodedLength(sequence)} */ - abstract int encodeUtf8(CharSequence in, byte[] out, int offset, int length); + abstract int encodeUtf8(String in, byte[] out, int offset, int length); /** * Encodes an input character sequence ({@code in}) to UTF-8 in the target buffer ({@code out}). @@ -743,7 +743,7 @@ final class Utf8 { * @throws ArrayIndexOutOfBoundsException if {@code in} encoded in UTF-8 is longer than {@code * out.remaining()} */ - final void encodeUtf8(CharSequence in, ByteBuffer out) { + final void encodeUtf8(String in, ByteBuffer out) { if (out.hasArray()) { final int offset = out.arrayOffset(); int endIndex = Utf8.encode(in, out.array(), offset + out.position(), out.remaining()); @@ -756,13 +756,13 @@ final class Utf8 { } /** Encodes the input character sequence to a direct {@link ByteBuffer} instance. */ - abstract void encodeUtf8Direct(CharSequence in, ByteBuffer out); + abstract void encodeUtf8Direct(String in, ByteBuffer out); /** * Encodes the input character sequence to a {@link ByteBuffer} instance using the {@link * ByteBuffer} API, rather than potentially faster approaches. */ - final void encodeUtf8Default(CharSequence in, ByteBuffer out) { + final void encodeUtf8Default(String in, ByteBuffer out) { final int inLength = in.length(); int outIx = out.position(); int inIx = 0; @@ -1013,7 +1013,7 @@ final class Utf8 { } @Override - int encodeUtf8(CharSequence in, byte[] out, int offset, int length) { + int encodeUtf8(String in, byte[] out, int offset, int length) { int utf16Length = in.length(); int j = offset; int i = 0; @@ -1065,7 +1065,7 @@ final class Utf8 { } @Override - void encodeUtf8Direct(CharSequence in, ByteBuffer out) { + void encodeUtf8Direct(String in, ByteBuffer out) { // For safe processing, we have to use the ByteBuffer API. encodeUtf8Default(in, out); } @@ -1346,13 +1346,13 @@ final class Utf8 { String decodeUtf8(byte[] bytes, int index, int size) throws InvalidProtocolBufferException { String s = new String(bytes, index, size, Internal.UTF_8); - // "\uFFFD" is UTF-8 default replacement string, which illegal byte sequences get replaced + // '\uFFFD' is the UTF-8 default replacement char, which illegal byte sequences get replaced // with. - if (!s.contains("\uFFFD")) { + if (s.indexOf('\uFFFD') < 0) { return s; } - // Since s contains "\uFFFD" there are 2 options: + // Since s contains '\uFFFD' there are 2 options: // 1) The byte array slice is invalid UTF-8. // 2) The byte array slice is valid UTF-8 and contains encodings for "\uFFFD". // To rule out (1), we encode s and compare it to the byte array slice. @@ -1442,7 +1442,7 @@ final class Utf8 { } @Override - int encodeUtf8(final CharSequence in, final byte[] out, final int offset, final int length) { + int encodeUtf8(final String in, final byte[] out, final int offset, final int length) { long outIx = offset; final long outLimit = outIx + length; final int inLimit = in.length(); @@ -1503,7 +1503,7 @@ final class Utf8 { } @Override - void encodeUtf8Direct(CharSequence in, ByteBuffer out) { + void encodeUtf8Direct(String in, ByteBuffer out) { final long address = addressOffset(out); long outIx = address + out.position(); final long outLimit = address + out.limit(); diff --git a/java/core/src/main/java/com/google/protobuf/java_features.proto b/java/core/src/main/resources/google/protobuf/java_features.proto similarity index 62% rename from java/core/src/main/java/com/google/protobuf/java_features.proto rename to java/core/src/main/resources/google/protobuf/java_features.proto index d02d067a3..bbf441c06 100644 --- a/java/core/src/main/java/com/google/protobuf/java_features.proto +++ b/java/core/src/main/resources/google/protobuf/java_features.proto @@ -27,7 +27,15 @@ message JavaFeatures { retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FIELD, targets = TARGET_TYPE_FILE, - edition_defaults = { edition: EDITION_PROTO2, value: "true" }, + feature_support = { + edition_introduced: EDITION_2023, + edition_deprecated: EDITION_2023, + deprecation_warning: "The legacy closed enum treatment in Java is " + "deprecated and is scheduled to be removed in " + "edition 2025. Mark enum type on the enum " + "definitions themselves rather than on fields.", + }, + edition_defaults = { edition: EDITION_LEGACY, value: "true" }, edition_defaults = { edition: EDITION_PROTO3, value: "false" } ]; @@ -47,6 +55,15 @@ message JavaFeatures { retention = RETENTION_RUNTIME, targets = TARGET_TYPE_FIELD, targets = TARGET_TYPE_FILE, - edition_defaults = { edition: EDITION_PROTO2, value: "DEFAULT" } + feature_support = { + edition_introduced: EDITION_2023, + edition_deprecated: EDITION_2024, + deprecation_warning: "The Java-specific utf8 validation feature is " + "deprecated and is scheduled to be removed in " + "edition 2025. Utf8 validation behavior should " + "use the global cross-language utf8_validation " + "feature.", + }, + edition_defaults = { edition: EDITION_LEGACY, value: "DEFAULT" } ]; } diff --git a/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java b/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java index 14c4bbeea..f73cb3b0e 100644 --- a/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java +++ b/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java @@ -494,7 +494,7 @@ public class CodedInputStreamTest { /** Skipping a huge blob should not allocate excessive memory, so there should be no limit */ @Test public void testSkipMaliciouslyHugeBlob() throws Exception { - InputStream is = new RepeatingInputStream(new byte[]{1}, Integer.MAX_VALUE); + InputStream is = new RepeatingInputStream(new byte[] {1}, Integer.MAX_VALUE); CodedInputStream.newInstance(is).skipRawBytes(Integer.MAX_VALUE); } @@ -1502,16 +1502,17 @@ public class CodedInputStreamTest { public void testMaliciousInputStream() throws Exception { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(outputStream); - codedOutputStream.writeByteArrayNoTag(new byte[] { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5 }); + codedOutputStream.writeByteArrayNoTag(new byte[] {0x0, 0x1, 0x2, 0x3, 0x4, 0x5}); codedOutputStream.flush(); final List maliciousCapture = new ArrayList<>(); - InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()) { - @Override - public synchronized int read(byte[] b, int off, int len) { - maliciousCapture.add(b); - return super.read(b, off, len); - } - }; + InputStream inputStream = + new ByteArrayInputStream(outputStream.toByteArray()) { + @Override + public synchronized int read(byte[] b, int off, int len) { + maliciousCapture.add(b); + return super.read(b, off, len); + } + }; // test ByteString diff --git a/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java b/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java index c68166daa..1f17f24fe 100644 --- a/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java +++ b/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java @@ -326,6 +326,81 @@ public class CodedOutputStreamTest { .isEqualTo(-75123905439571256L); } + @Test + public void computeIntSize() { + assertThat(CodedOutputStream.computeUInt32SizeNoTag(0)).isEqualTo(1); + assertThat(CodedOutputStream.computeUInt64SizeNoTag(0)).isEqualTo(1); + int i; + for (i = 0; i < 7; i++) { + assertThat(CodedOutputStream.computeInt32SizeNoTag(1 << i)).isEqualTo(1); + assertThat(CodedOutputStream.computeUInt32SizeNoTag(1 << i)).isEqualTo(1); + assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(1); + } + for (; i < 14; i++) { + assertThat(CodedOutputStream.computeInt32SizeNoTag(1 << i)).isEqualTo(2); + assertThat(CodedOutputStream.computeUInt32SizeNoTag(1 << i)).isEqualTo(2); + assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(2); + } + for (; i < 21; i++) { + assertThat(CodedOutputStream.computeInt32SizeNoTag(1 << i)).isEqualTo(3); + assertThat(CodedOutputStream.computeUInt32SizeNoTag(1 << i)).isEqualTo(3); + assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(3); + } + for (; i < 28; i++) { + assertThat(CodedOutputStream.computeInt32SizeNoTag(1 << i)).isEqualTo(4); + assertThat(CodedOutputStream.computeUInt32SizeNoTag(1 << i)).isEqualTo(4); + assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(4); + } + for (; i < 31; i++) { + assertThat(CodedOutputStream.computeInt32SizeNoTag(1 << i)).isEqualTo(5); + assertThat(CodedOutputStream.computeUInt32SizeNoTag(1 << i)).isEqualTo(5); + assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(5); + } + for (; i < 32; i++) { + assertThat(CodedOutputStream.computeInt32SizeNoTag(1 << i)).isEqualTo(10); + assertThat(CodedOutputStream.computeUInt32SizeNoTag(1 << i)).isEqualTo(5); + assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(5); + } + for (; i < 35; i++) { + assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(5); + } + for (; i < 42; i++) { + assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(6); + } + for (; i < 49; i++) { + assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(7); + } + for (; i < 56; i++) { + assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(8); + } + for (; i < 63; i++) { + assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(9); + } + } + + @Test + public void computeTagSize() { + assertThat(CodedOutputStream.computeTagSize(0)).isEqualTo(1); + int i; + for (i = 0; i < 4; i++) { + assertThat(CodedOutputStream.computeTagSize(1 << i)).isEqualTo(1); + } + for (; i < 11; i++) { + assertThat(CodedOutputStream.computeTagSize(1 << i)).isEqualTo(2); + } + for (; i < 18; i++) { + assertThat(CodedOutputStream.computeTagSize(1 << i)).isEqualTo(3); + } + for (; i < 25; i++) { + assertThat(CodedOutputStream.computeTagSize(1 << i)).isEqualTo(4); + } + for (; i < 29; i++) { + assertThat(CodedOutputStream.computeTagSize(1 << i)).isEqualTo(5); + } + // Invalid tags + assertThat(CodedOutputStream.computeTagSize((1 << 30) + 1)).isEqualTo(1); + } + /** * Test writing a message containing a negative enum value. This used to fail because the size was * not properly computed as a sign-extended varint. diff --git a/java/core/src/test/java/com/google/protobuf/DebugFormatTest.java b/java/core/src/test/java/com/google/protobuf/DebugFormatTest.java new file mode 100644 index 000000000..daf2924d9 --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/DebugFormatTest.java @@ -0,0 +1,210 @@ +package com.google.protobuf; + +import static com.google.common.truth.Truth.assertThat; +import static protobuf_unittest.UnittestProto.redactedExtension; + +import com.google.protobuf.Descriptors.FieldDescriptor; +import protobuf_unittest.UnittestProto.RedactedFields; +import protobuf_unittest.UnittestProto.TestEmptyMessage; +import protobuf_unittest.UnittestProto.TestNestedMessageRedaction; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public final class DebugFormatTest { + + private static final String REDACTED_REGEX = "\\[REDACTED\\]"; + private static final String UNSTABLE_PREFIX_SINGLE_LINE = getUnstablePrefix(true); + private static final String UNSTABLE_PREFIX_MULTILINE = getUnstablePrefix(false); + + private static String getUnstablePrefix(boolean singleLine) { + return ""; + } + + @Test + public void multilineMessageFormat_returnsMultiline() { + RedactedFields message = RedactedFields.newBuilder().setOptionalUnredactedString("foo").build(); + + String result = DebugFormat.multiline().toString(message); + assertThat(result) + .matches( + String.format("%soptional_unredacted_string: \"foo\"\n", UNSTABLE_PREFIX_MULTILINE)); + } + + @Test + public void singleLineMessageFormat_returnsSingleLine() { + RedactedFields message = RedactedFields.newBuilder().setOptionalUnredactedString("foo").build(); + + String result = DebugFormat.singleLine().toString(message); + assertThat(result) + .matches( + String.format("%soptional_unredacted_string: \"foo\"", UNSTABLE_PREFIX_SINGLE_LINE)); + } + + @Test + public void messageFormat_debugRedactFieldIsRedacted() { + RedactedFields message = RedactedFields.newBuilder().setOptionalRedactedString("foo").build(); + + String result = DebugFormat.multiline().toString(message); + + assertThat(result) + .matches( + String.format( + "%soptional_redacted_string: %s\n", UNSTABLE_PREFIX_MULTILINE, REDACTED_REGEX)); + } + + @Test + public void messageFormat_debugRedactMessageIsRedacted() { + RedactedFields message = + RedactedFields.newBuilder() + .setOptionalRedactedMessage( + TestNestedMessageRedaction.newBuilder().setOptionalUnredactedNestedString("foo")) + .build(); + + String result = DebugFormat.multiline().toString(message); + + assertThat(result) + .matches( + String.format( + "%soptional_redacted_message \\{\n %s\n\\}\n", + UNSTABLE_PREFIX_MULTILINE, REDACTED_REGEX)); + } + + @Test + public void messageFormat_debugRedactMapIsRedacted() { + RedactedFields message = RedactedFields.newBuilder().putMapRedactedString("foo", "bar").build(); + + String result = DebugFormat.multiline().toString(message); + + assertThat(result) + .matches( + String.format( + "%smap_redacted_string \\{\\n %s\n\\}\n", + UNSTABLE_PREFIX_MULTILINE, REDACTED_REGEX)); + } + + @Test + public void messageFormat_debugRedactExtensionIsRedacted() { + RedactedFields message = + RedactedFields.newBuilder().setExtension(redactedExtension, "foo").build(); + + String result = DebugFormat.multiline().toString(message); + + assertThat(result) + .matches( + String.format( + "%s\\[protobuf_unittest\\.redacted_extension\\]: %s\n", + UNSTABLE_PREFIX_MULTILINE, REDACTED_REGEX)); + } + + @Test + public void messageFormat_redactFalseIsNotRedacted() { + RedactedFields message = + RedactedFields.newBuilder().setOptionalRedactedFalseString("foo").build(); + + String result = DebugFormat.multiline().toString(message); + assertThat(result) + .matches( + String.format( + "%soptional_redacted_false_string: \"foo\"\n", UNSTABLE_PREFIX_MULTILINE)); + } + + @Test + public void messageFormat_nonSensitiveFieldIsNotRedacted() { + RedactedFields message = RedactedFields.newBuilder().setOptionalUnredactedString("foo").build(); + + String result = DebugFormat.multiline().toString(message); + + assertThat(result) + .matches( + String.format("%soptional_unredacted_string: \"foo\"\n", UNSTABLE_PREFIX_MULTILINE)); + } + + @Test + public void descriptorDebugFormat_returnsExpectedFormat() { + FieldDescriptor field = + RedactedFields.getDescriptor().findFieldByName("optional_redacted_string"); + String result = DebugFormat.multiline().toString(field, "foo"); + assertThat(result) + .matches( + String.format( + "%soptional_redacted_string: %s\n", UNSTABLE_PREFIX_MULTILINE, REDACTED_REGEX)); + } + + @Test + public void unstableFormat_isStablePerProcess() { + RedactedFields message1 = + RedactedFields.newBuilder().setOptionalUnredactedString("foo").build(); + RedactedFields message2 = + RedactedFields.newBuilder().setOptionalUnredactedString("foo").build(); + for (int i = 0; i < 5; i++) { + String result1 = DebugFormat.multiline().toString(message1); + String result2 = DebugFormat.multiline().toString(message2); + assertThat(result1).isEqualTo(result2); + } + } + + @Test + public void lazyDebugFormatMessage_supportsImplicitFormatting() { + RedactedFields message = RedactedFields.newBuilder().setOptionalUnredactedString("foo").build(); + + Object lazyDebug = DebugFormat.singleLine().lazyToString(message); + + assertThat(String.format("%s", lazyDebug)) + .matches( + String.format("%soptional_unredacted_string: \"foo\"", UNSTABLE_PREFIX_SINGLE_LINE)); + } + + private UnknownFieldSet makeUnknownFieldSet() { + return UnknownFieldSet.newBuilder() + .addField( + 5, + UnknownFieldSet.Field.newBuilder() + .addVarint(1) + .addFixed32(2) + .addFixed64(3) + .addLengthDelimited(ByteString.copyFromUtf8("4")) + .addLengthDelimited( + UnknownFieldSet.newBuilder() + .addField(12, UnknownFieldSet.Field.newBuilder().addVarint(6).build()) + .build() + .toByteString()) + .addGroup( + UnknownFieldSet.newBuilder() + .addField(10, UnknownFieldSet.Field.newBuilder().addVarint(5).build()) + .build()) + .build()) + .addField( + 8, UnknownFieldSet.Field.newBuilder().addVarint(1).addVarint(2).addVarint(3).build()) + .addField( + 15, + UnknownFieldSet.Field.newBuilder() + .addVarint(0xABCDEF1234567890L) + .addFixed32(0xABCD1234) + .addFixed64(0xABCDEF1234567890L) + .build()) + .build(); + } + + @Test + public void unknownFieldsDebugFormat_returnsExpectedFormat() { + TestEmptyMessage unknownFields = + TestEmptyMessage.newBuilder().setUnknownFields(makeUnknownFieldSet()).build(); + + assertThat(DebugFormat.multiline().toString(unknownFields)) + .matches( + String.format("%s5: UNKNOWN_VARINT %s\n", UNSTABLE_PREFIX_MULTILINE, REDACTED_REGEX) + + String.format("5: UNKNOWN_FIXED32 %s\n", REDACTED_REGEX) + + String.format("5: UNKNOWN_FIXED64 %s\n", REDACTED_REGEX) + + String.format("5: UNKNOWN_STRING %s\n", REDACTED_REGEX) + + String.format("5: \\{\n 12: UNKNOWN_VARINT %s\n\\}\n", REDACTED_REGEX) + + String.format("5 \\{\n 10: UNKNOWN_VARINT %s\n\\}\n", REDACTED_REGEX) + + String.format("8: UNKNOWN_VARINT %s\n", REDACTED_REGEX) + + String.format("8: UNKNOWN_VARINT %s\n", REDACTED_REGEX) + + String.format("8: UNKNOWN_VARINT %s\n", REDACTED_REGEX) + + String.format("15: UNKNOWN_VARINT %s\n", REDACTED_REGEX) + + String.format("15: UNKNOWN_FIXED32 %s\n", REDACTED_REGEX) + + String.format("15: UNKNOWN_FIXED64 %s\n", REDACTED_REGEX)); + } +} diff --git a/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java b/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java index aae4a4dd1..903e1158d 100644 --- a/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java +++ b/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java @@ -9,14 +9,22 @@ package com.google.protobuf; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static org.junit.Assert.assertThrows; import com.google.protobuf.DescriptorProtos.DescriptorProto; +import com.google.protobuf.DescriptorProtos.DescriptorProto.ExtensionRange; import com.google.protobuf.DescriptorProtos.Edition; import com.google.protobuf.DescriptorProtos.EnumDescriptorProto; import com.google.protobuf.DescriptorProtos.EnumValueDescriptorProto; +import com.google.protobuf.DescriptorProtos.FeatureSetDefaults; +import com.google.protobuf.DescriptorProtos.FeatureSetDefaults.FeatureSetEditionDefault; import com.google.protobuf.DescriptorProtos.FieldDescriptorProto; +import com.google.protobuf.DescriptorProtos.FieldOptions; import com.google.protobuf.DescriptorProtos.FileDescriptorProto; import com.google.protobuf.DescriptorProtos.FileOptions; +import com.google.protobuf.DescriptorProtos.MethodDescriptorProto; +import com.google.protobuf.DescriptorProtos.OneofDescriptorProto; +import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto; import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.DescriptorValidationException; import com.google.protobuf.Descriptors.EnumDescriptor; @@ -29,6 +37,8 @@ import com.google.protobuf.Descriptors.ServiceDescriptor; import com.google.protobuf.test.UnittestImport; import com.google.protobuf.test.UnittestImport.ImportEnum; import com.google.protobuf.test.UnittestImport.ImportEnumForMap; +import legacy_features_unittest.UnittestLegacyFeatures; +import pb.UnittestFeatures; import protobuf_unittest.TestCustomOptions; import protobuf_unittest.UnittestCustomOptions; import protobuf_unittest.UnittestProto; @@ -44,1004 +54,1734 @@ import protobuf_unittest.UnittestProto.TestReservedEnumFields; import protobuf_unittest.UnittestProto.TestReservedFields; import protobuf_unittest.UnittestProto.TestService; import protobuf_unittest.UnittestRetention; +import protobuf_unittest.UnittestProto3Extensions.Proto3FileExtensions; import java.util.Collections; import java.util.List; +import org.junit.Before; import org.junit.Test; +import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; + import protobuf_unittest.NestedExtension; import protobuf_unittest.NonNestedExtension; /** Unit test for {@link Descriptors}. */ -@RunWith(JUnit4.class) +@RunWith(Enclosed.class) public class DescriptorsTest { - // Regression test for bug where referencing a FieldDescriptor.Type value - // before a FieldDescriptorProto.Type value would yield a - // ExceptionInInitializerError. - @SuppressWarnings("unused") - private static final Object STATIC_INIT_TEST = FieldDescriptor.Type.BOOL; + public static class GeneralDescriptorsTest { + // Regression test for bug where referencing a FieldDescriptor.Type value + // before a FieldDescriptorProto.Type value would yield a + // ExceptionInInitializerError. + @SuppressWarnings("unused") + private static final Object STATIC_INIT_TEST = FieldDescriptor.Type.BOOL; + + @Test + public void testFieldTypeEnumMapping() throws Exception { + assertThat(FieldDescriptor.Type.values()) + .hasLength(FieldDescriptorProto.Type.values().length); + for (FieldDescriptor.Type type : FieldDescriptor.Type.values()) { + FieldDescriptorProto.Type protoType = type.toProto(); + assertThat(protoType.name()).isEqualTo("TYPE_" + type.name()); + assertThat(FieldDescriptor.Type.valueOf(protoType)).isEqualTo(type); + } + } + + @Test + public void testFileDescriptor() throws Exception { + FileDescriptor file = UnittestProto.getDescriptor(); + + assertThat(file.getName()).isEqualTo("google/protobuf/unittest.proto"); + assertThat(file.getPackage()).isEqualTo("protobuf_unittest"); + assertThat(file.getOptions().getJavaOuterClassname()).isEqualTo("UnittestProto"); + assertThat(file.toProto().getName()).isEqualTo("google/protobuf/unittest.proto"); + + assertThat(file.getDependencies()).containsExactly(UnittestImport.getDescriptor()); + + Descriptor messageType = TestAllTypes.getDescriptor(); + assertThat(file.getMessageTypes().get(0)).isEqualTo(messageType); + assertThat(file.findMessageTypeByName("TestAllTypes")).isEqualTo(messageType); + assertThat(file.findMessageTypeByName("NoSuchType")).isNull(); + assertThat(file.findMessageTypeByName("protobuf_unittest.TestAllTypes")).isNull(); + for (int i = 0; i < file.getMessageTypes().size(); i++) { + assertThat(file.getMessageTypes().get(i).getIndex()).isEqualTo(i); + } + + EnumDescriptor enumType = ForeignEnum.getDescriptor(); + assertThat(file.getEnumTypes().get(0)).isEqualTo(enumType); + assertThat(file.findEnumTypeByName("ForeignEnum")).isEqualTo(enumType); + assertThat(file.findEnumTypeByName("NoSuchType")).isNull(); + assertThat(file.findEnumTypeByName("protobuf_unittest.ForeignEnum")).isNull(); + assertThat(UnittestImport.getDescriptor().getEnumTypes()) + .containsExactly(ImportEnum.getDescriptor(), ImportEnumForMap.getDescriptor()) + .inOrder(); + for (int i = 0; i < file.getEnumTypes().size(); i++) { + assertThat(file.getEnumTypes().get(i).getIndex()).isEqualTo(i); + } + + ServiceDescriptor service = TestService.getDescriptor(); + assertThat(file.getServices().get(0)).isEqualTo(service); + assertThat(file.findServiceByName("TestService")).isEqualTo(service); + assertThat(file.findServiceByName("NoSuchType")).isNull(); + assertThat(file.findServiceByName("protobuf_unittest.TestService")).isNull(); + assertThat(UnittestImport.getDescriptor().getServices()).isEqualTo(Collections.emptyList()); + for (int i = 0; i < file.getServices().size(); i++) { + assertThat(file.getServices().get(i).getIndex()).isEqualTo(i); + } - @Test - public void testFieldTypeEnumMapping() throws Exception { - assertThat(FieldDescriptor.Type.values()).hasLength(FieldDescriptorProto.Type.values().length); - for (FieldDescriptor.Type type : FieldDescriptor.Type.values()) { - FieldDescriptorProto.Type protoType = type.toProto(); - assertThat(protoType.name()).isEqualTo("TYPE_" + type.name()); - assertThat(FieldDescriptor.Type.valueOf(protoType)).isEqualTo(type); + FieldDescriptor extension = UnittestProto.optionalInt32Extension.getDescriptor(); + assertThat(file.getExtensions().get(0)).isEqualTo(extension); + assertThat(file.findExtensionByName("optional_int32_extension")).isEqualTo(extension); + assertThat(file.findExtensionByName("no_such_ext")).isNull(); + assertThat(file.findExtensionByName("protobuf_unittest.optional_int32_extension")).isNull(); + assertThat(UnittestImport.getDescriptor().getExtensions()).isEqualTo(Collections.emptyList()); + for (int i = 0; i < file.getExtensions().size(); i++) { + assertThat(file.getExtensions().get(i).getIndex()).isEqualTo(i); + } } - } - @Test - public void testFileDescriptor() throws Exception { - FileDescriptor file = UnittestProto.getDescriptor(); - - assertThat(file.getName()).isEqualTo("google/protobuf/unittest.proto"); - assertThat(file.getSyntax()).isEqualTo(Descriptors.FileDescriptor.Syntax.PROTO2); - assertThat(file.getPackage()).isEqualTo("protobuf_unittest"); - assertThat(file.getOptions().getJavaOuterClassname()).isEqualTo("UnittestProto"); - assertThat(file.toProto().getName()).isEqualTo("google/protobuf/unittest.proto"); - - assertThat(file.getDependencies()).containsExactly(UnittestImport.getDescriptor()); - - Descriptor messageType = TestAllTypes.getDescriptor(); - assertThat(file.getMessageTypes().get(0)).isEqualTo(messageType); - assertThat(file.findMessageTypeByName("TestAllTypes")).isEqualTo(messageType); - assertThat(file.findMessageTypeByName("NoSuchType")).isNull(); - assertThat(file.findMessageTypeByName("protobuf_unittest.TestAllTypes")).isNull(); - for (int i = 0; i < file.getMessageTypes().size(); i++) { - assertThat(file.getMessageTypes().get(i).getIndex()).isEqualTo(i); - } - - EnumDescriptor enumType = ForeignEnum.getDescriptor(); - assertThat(file.getEnumTypes().get(0)).isEqualTo(enumType); - assertThat(file.findEnumTypeByName("ForeignEnum")).isEqualTo(enumType); - assertThat(file.findEnumTypeByName("NoSuchType")).isNull(); - assertThat(file.findEnumTypeByName("protobuf_unittest.ForeignEnum")).isNull(); - assertThat(UnittestImport.getDescriptor().getEnumTypes()) - .containsExactly(ImportEnum.getDescriptor(), ImportEnumForMap.getDescriptor()) - .inOrder(); - for (int i = 0; i < file.getEnumTypes().size(); i++) { - assertThat(file.getEnumTypes().get(i).getIndex()).isEqualTo(i); - } - - ServiceDescriptor service = TestService.getDescriptor(); - assertThat(file.getServices().get(0)).isEqualTo(service); - assertThat(file.findServiceByName("TestService")).isEqualTo(service); - assertThat(file.findServiceByName("NoSuchType")).isNull(); - assertThat(file.findServiceByName("protobuf_unittest.TestService")).isNull(); - assertThat(UnittestImport.getDescriptor().getServices()).isEqualTo(Collections.emptyList()); - for (int i = 0; i < file.getServices().size(); i++) { - assertThat(file.getServices().get(i).getIndex()).isEqualTo(i); - } - - FieldDescriptor extension = UnittestProto.optionalInt32Extension.getDescriptor(); - assertThat(file.getExtensions().get(0)).isEqualTo(extension); - assertThat(file.findExtensionByName("optional_int32_extension")).isEqualTo(extension); - assertThat(file.findExtensionByName("no_such_ext")).isNull(); - assertThat(file.findExtensionByName("protobuf_unittest.optional_int32_extension")).isNull(); - assertThat(UnittestImport.getDescriptor().getExtensions()).isEqualTo(Collections.emptyList()); - for (int i = 0; i < file.getExtensions().size(); i++) { - assertThat(file.getExtensions().get(i).getIndex()).isEqualTo(i); + @Test + public void testFileDescriptorGetEdition() throws Exception { + FileDescriptorProto proto2 = FileDescriptorProto.newBuilder().setSyntax("proto2").build(); + FileDescriptor file2 = Descriptors.FileDescriptor.buildFrom(proto2, new FileDescriptor[0]); + assertThat(file2.getEdition()).isEqualTo(Edition.EDITION_PROTO2); + + FileDescriptorProto proto3 = FileDescriptorProto.newBuilder().setSyntax("proto3").build(); + FileDescriptor file3 = Descriptors.FileDescriptor.buildFrom(proto3, new FileDescriptor[0]); + assertThat(file3.getEdition()).isEqualTo(Edition.EDITION_PROTO3); + + FileDescriptorProto protoEdition = + FileDescriptorProto.newBuilder() + .setSyntax("editions") + .setEdition(Edition.EDITION_2023) + .build(); + FileDescriptor fileEdition = + Descriptors.FileDescriptor.buildFrom(protoEdition, new FileDescriptor[0]); + assertThat(fileEdition.getEdition()).isEqualTo(Edition.EDITION_2023); + + FileDescriptorProto protoMissingEdition = + FileDescriptorProto.newBuilder().setSyntax("editions").build(); + IllegalArgumentException exception = + assertThrows( + IllegalArgumentException.class, + () -> + Descriptors.FileDescriptor.buildFrom(protoMissingEdition, new FileDescriptor[0])); + assertThat(exception) + .hasMessageThat() + .contains("Edition EDITION_UNKNOWN is lower than the minimum supported edition"); } - } - @Test - public void testFileDescriptorGetSyntax() throws Exception { - FileDescriptorProto proto2 = FileDescriptorProto.newBuilder().setSyntax("proto2").build(); - FileDescriptor file2 = Descriptors.FileDescriptor.buildFrom(proto2, new FileDescriptor[0]); - assertThat(file2.getSyntax()).isEqualTo(Descriptors.FileDescriptor.Syntax.PROTO2); + @Test + public void testFileDescriptorCopyHeadingTo() throws Exception { + FileDescriptorProto.Builder protoBuilder = + FileDescriptorProto.newBuilder() + .setName("foo.proto") + .setPackage("foo.bar.baz") + .setSyntax("proto2") + .setOptions(FileOptions.newBuilder().setJavaPackage("foo.bar.baz").build()) + // Won't be copied. + .addMessageType(DescriptorProto.newBuilder().setName("Foo").build()); + FileDescriptor file2 = + Descriptors.FileDescriptor.buildFrom(protoBuilder.build(), new FileDescriptor[0]); + FileDescriptorProto.Builder protoBuilder2 = FileDescriptorProto.newBuilder(); + file2.copyHeadingTo(protoBuilder2); + FileDescriptorProto toProto2 = protoBuilder2.build(); + assertThat(toProto2.getName()).isEqualTo("foo.proto"); + assertThat(toProto2.getPackage()).isEqualTo("foo.bar.baz"); + assertThat(toProto2.getSyntax()).isEqualTo("proto2"); + assertThat(toProto2.getOptions().getJavaPackage()).isEqualTo("foo.bar.baz"); + assertThat(toProto2.getMessageTypeList()).isEmpty(); + + protoBuilder.setSyntax("proto3"); + FileDescriptor file3 = + Descriptors.FileDescriptor.buildFrom(protoBuilder.build(), new FileDescriptor[0]); + FileDescriptorProto.Builder protoBuilder3 = FileDescriptorProto.newBuilder(); + file3.copyHeadingTo(protoBuilder3); + FileDescriptorProto toProto3 = protoBuilder3.build(); + assertThat(toProto3.getName()).isEqualTo("foo.proto"); + assertThat(toProto3.getPackage()).isEqualTo("foo.bar.baz"); + assertThat(toProto3.getSyntax()).isEqualTo("proto3"); + assertThat(toProto2.getOptions().getJavaPackage()).isEqualTo("foo.bar.baz"); + assertThat(toProto3.getMessageTypeList()).isEmpty(); + } - FileDescriptorProto proto3 = FileDescriptorProto.newBuilder().setSyntax("proto3").build(); - FileDescriptor file3 = Descriptors.FileDescriptor.buildFrom(proto3, new FileDescriptor[0]); - assertThat(file3.getSyntax()).isEqualTo(Descriptors.FileDescriptor.Syntax.PROTO3); - } + @Test + public void testDescriptor() throws Exception { + Descriptor messageType = TestAllTypes.getDescriptor(); + Descriptor nestedType = TestAllTypes.NestedMessage.getDescriptor(); + + assertThat(messageType.getName()).isEqualTo("TestAllTypes"); + assertThat(messageType.getFullName()).isEqualTo("protobuf_unittest.TestAllTypes"); + assertThat(messageType.getFile()).isEqualTo(UnittestProto.getDescriptor()); + assertThat(messageType.getContainingType()).isNull(); + assertThat(messageType.getOptions()) + .isEqualTo(DescriptorProtos.MessageOptions.getDefaultInstance()); + assertThat(messageType.toProto().getName()).isEqualTo("TestAllTypes"); + + assertThat(nestedType.getName()).isEqualTo("NestedMessage"); + assertThat(nestedType.getFullName()).isEqualTo("protobuf_unittest.TestAllTypes.NestedMessage"); + assertThat(nestedType.getFile()).isEqualTo(UnittestProto.getDescriptor()); + assertThat(nestedType.getContainingType()).isEqualTo(messageType); + + FieldDescriptor field = messageType.getFields().get(0); + assertThat(field.getName()).isEqualTo("optional_int32"); + assertThat(messageType.findFieldByName("optional_int32")).isEqualTo(field); + assertThat(messageType.findFieldByName("no_such_field")).isNull(); + assertThat(messageType.findFieldByNumber(1)).isEqualTo(field); + assertThat(messageType.findFieldByNumber(571283)).isNull(); + for (int i = 0; i < messageType.getFields().size(); i++) { + assertThat(messageType.getFields().get(i).getIndex()).isEqualTo(i); + } - @Test - public void testFileDescriptorCopyHeadingTo() throws Exception { - FileDescriptorProto.Builder protoBuilder = - FileDescriptorProto.newBuilder() - .setName("foo.proto") - .setPackage("foo.bar.baz") - .setSyntax("proto2") - .setOptions(FileOptions.newBuilder().setJavaPackage("foo.bar.baz").build()) - // Won't be copied. - .addMessageType(DescriptorProto.newBuilder().setName("Foo").build()); - FileDescriptor file2 = - Descriptors.FileDescriptor.buildFrom(protoBuilder.build(), new FileDescriptor[0]); - FileDescriptorProto.Builder protoBuilder2 = FileDescriptorProto.newBuilder(); - file2.copyHeadingTo(protoBuilder2); - FileDescriptorProto toProto2 = protoBuilder2.build(); - assertThat(toProto2.getName()).isEqualTo("foo.proto"); - assertThat(toProto2.getPackage()).isEqualTo("foo.bar.baz"); - assertThat(toProto2.getSyntax()).isEqualTo("proto2"); - assertThat(toProto2.getOptions().getJavaPackage()).isEqualTo("foo.bar.baz"); - assertThat(toProto2.getMessageTypeList()).isEmpty(); - - protoBuilder.setSyntax("proto3"); - FileDescriptor file3 = - Descriptors.FileDescriptor.buildFrom(protoBuilder.build(), new FileDescriptor[0]); - FileDescriptorProto.Builder protoBuilder3 = FileDescriptorProto.newBuilder(); - file3.copyHeadingTo(protoBuilder3); - FileDescriptorProto toProto3 = protoBuilder3.build(); - assertThat(toProto3.getName()).isEqualTo("foo.proto"); - assertThat(toProto3.getPackage()).isEqualTo("foo.bar.baz"); - assertThat(toProto3.getSyntax()).isEqualTo("proto3"); - assertThat(toProto2.getOptions().getJavaPackage()).isEqualTo("foo.bar.baz"); - assertThat(toProto3.getMessageTypeList()).isEmpty(); - } + assertThat(messageType.getNestedTypes().get(0)).isEqualTo(nestedType); + assertThat(messageType.findNestedTypeByName("NestedMessage")).isEqualTo(nestedType); + assertThat(messageType.findNestedTypeByName("NoSuchType")).isNull(); + for (int i = 0; i < messageType.getNestedTypes().size(); i++) { + assertThat(messageType.getNestedTypes().get(i).getIndex()).isEqualTo(i); + } - @Test - public void testDescriptor() throws Exception { - Descriptor messageType = TestAllTypes.getDescriptor(); - Descriptor nestedType = TestAllTypes.NestedMessage.getDescriptor(); - - assertThat(messageType.getName()).isEqualTo("TestAllTypes"); - assertThat(messageType.getFullName()).isEqualTo("protobuf_unittest.TestAllTypes"); - assertThat(messageType.getFile()).isEqualTo(UnittestProto.getDescriptor()); - assertThat(messageType.getContainingType()).isNull(); - assertThat(messageType.getOptions()) - .isEqualTo(DescriptorProtos.MessageOptions.getDefaultInstance()); - assertThat(messageType.toProto().getName()).isEqualTo("TestAllTypes"); - - assertThat(nestedType.getName()).isEqualTo("NestedMessage"); - assertThat(nestedType.getFullName()).isEqualTo("protobuf_unittest.TestAllTypes.NestedMessage"); - assertThat(nestedType.getFile()).isEqualTo(UnittestProto.getDescriptor()); - assertThat(nestedType.getContainingType()).isEqualTo(messageType); - - FieldDescriptor field = messageType.getFields().get(0); - assertThat(field.getName()).isEqualTo("optional_int32"); - assertThat(messageType.findFieldByName("optional_int32")).isEqualTo(field); - assertThat(messageType.findFieldByName("no_such_field")).isNull(); - assertThat(messageType.findFieldByNumber(1)).isEqualTo(field); - assertThat(messageType.findFieldByNumber(571283)).isNull(); - for (int i = 0; i < messageType.getFields().size(); i++) { - assertThat(messageType.getFields().get(i).getIndex()).isEqualTo(i); - } - - assertThat(messageType.getNestedTypes().get(0)).isEqualTo(nestedType); - assertThat(messageType.findNestedTypeByName("NestedMessage")).isEqualTo(nestedType); - assertThat(messageType.findNestedTypeByName("NoSuchType")).isNull(); - for (int i = 0; i < messageType.getNestedTypes().size(); i++) { - assertThat(messageType.getNestedTypes().get(i).getIndex()).isEqualTo(i); - } - - EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor(); - assertThat(messageType.getEnumTypes().get(0)).isEqualTo(enumType); - assertThat(messageType.findEnumTypeByName("NestedEnum")).isEqualTo(enumType); - assertThat(messageType.findEnumTypeByName("NoSuchType")).isNull(); - for (int i = 0; i < messageType.getEnumTypes().size(); i++) { - assertThat(messageType.getEnumTypes().get(i).getIndex()).isEqualTo(i); + EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor(); + assertThat(messageType.getEnumTypes().get(0)).isEqualTo(enumType); + assertThat(messageType.findEnumTypeByName("NestedEnum")).isEqualTo(enumType); + assertThat(messageType.findEnumTypeByName("NoSuchType")).isNull(); + for (int i = 0; i < messageType.getEnumTypes().size(); i++) { + assertThat(messageType.getEnumTypes().get(i).getIndex()).isEqualTo(i); + } } - } - @Test - public void testFieldDescriptor() throws Exception { - Descriptor messageType = TestAllTypes.getDescriptor(); - FieldDescriptor primitiveField = messageType.findFieldByName("optional_int32"); - FieldDescriptor enumField = messageType.findFieldByName("optional_nested_enum"); - FieldDescriptor messageField = messageType.findFieldByName("optional_foreign_message"); - FieldDescriptor cordField = messageType.findFieldByName("optional_cord"); - FieldDescriptor extension = UnittestProto.optionalInt32Extension.getDescriptor(); - FieldDescriptor nestedExtension = TestRequired.single.getDescriptor(); - - assertThat(primitiveField.getName()).isEqualTo("optional_int32"); - assertThat(primitiveField.getFullName()) - .isEqualTo("protobuf_unittest.TestAllTypes.optional_int32"); - assertThat(primitiveField.getNumber()).isEqualTo(1); - assertThat(primitiveField.getContainingType()).isEqualTo(messageType); - assertThat(primitiveField.getFile()).isEqualTo(UnittestProto.getDescriptor()); - assertThat(primitiveField.getType()).isEqualTo(FieldDescriptor.Type.INT32); - assertThat(primitiveField.getJavaType()).isEqualTo(FieldDescriptor.JavaType.INT); - assertThat(primitiveField.getOptions()) - .isEqualTo(DescriptorProtos.FieldOptions.getDefaultInstance()); - assertThat(primitiveField.isExtension()).isFalse(); - assertThat(primitiveField.toProto().getName()).isEqualTo("optional_int32"); - - assertThat(enumField.getName()).isEqualTo("optional_nested_enum"); - assertThat(enumField.getType()).isEqualTo(FieldDescriptor.Type.ENUM); - assertThat(enumField.getJavaType()).isEqualTo(FieldDescriptor.JavaType.ENUM); - assertThat(enumField.getEnumType()).isEqualTo(TestAllTypes.NestedEnum.getDescriptor()); - - assertThat(messageField.getName()).isEqualTo("optional_foreign_message"); - assertThat(messageField.getType()).isEqualTo(FieldDescriptor.Type.MESSAGE); - assertThat(messageField.getJavaType()).isEqualTo(FieldDescriptor.JavaType.MESSAGE); - assertThat(messageField.getMessageType()).isEqualTo(ForeignMessage.getDescriptor()); - - assertThat(cordField.getName()).isEqualTo("optional_cord"); - assertThat(cordField.getType()).isEqualTo(FieldDescriptor.Type.STRING); - assertThat(cordField.getJavaType()).isEqualTo(FieldDescriptor.JavaType.STRING); - assertThat(cordField.getOptions().getCtype()) - .isEqualTo(DescriptorProtos.FieldOptions.CType.CORD); - - assertThat(extension.getName()).isEqualTo("optional_int32_extension"); - assertThat(extension.getFullName()).isEqualTo("protobuf_unittest.optional_int32_extension"); - assertThat(extension.getNumber()).isEqualTo(1); - assertThat(extension.getContainingType()).isEqualTo(TestAllExtensions.getDescriptor()); - assertThat(extension.getFile()).isEqualTo(UnittestProto.getDescriptor()); - assertThat(extension.getType()).isEqualTo(FieldDescriptor.Type.INT32); - assertThat(extension.getJavaType()).isEqualTo(FieldDescriptor.JavaType.INT); - assertThat(extension.getOptions()) - .isEqualTo(DescriptorProtos.FieldOptions.getDefaultInstance()); - assertThat(extension.isExtension()).isTrue(); - assertThat(extension.getExtensionScope()).isNull(); - assertThat(extension.toProto().getName()).isEqualTo("optional_int32_extension"); - - assertThat(nestedExtension.getName()).isEqualTo("single"); - assertThat(nestedExtension.getFullName()).isEqualTo("protobuf_unittest.TestRequired.single"); - assertThat(nestedExtension.getExtensionScope()).isEqualTo(TestRequired.getDescriptor()); - } + @Test + public void testFieldDescriptor() throws Exception { + Descriptor messageType = TestAllTypes.getDescriptor(); + FieldDescriptor primitiveField = messageType.findFieldByName("optional_int32"); + FieldDescriptor enumField = messageType.findFieldByName("optional_nested_enum"); + FieldDescriptor messageField = messageType.findFieldByName("optional_foreign_message"); + FieldDescriptor cordField = messageType.findFieldByName("optional_cord"); + FieldDescriptor extension = UnittestProto.optionalInt32Extension.getDescriptor(); + FieldDescriptor nestedExtension = TestRequired.single.getDescriptor(); + + assertThat(primitiveField.getName()).isEqualTo("optional_int32"); + assertThat(primitiveField.getFullName()) + .isEqualTo("protobuf_unittest.TestAllTypes.optional_int32"); + assertThat(primitiveField.getNumber()).isEqualTo(1); + assertThat(primitiveField.getContainingType()).isEqualTo(messageType); + assertThat(primitiveField.getFile()).isEqualTo(UnittestProto.getDescriptor()); + assertThat(primitiveField.getType()).isEqualTo(FieldDescriptor.Type.INT32); + assertThat(primitiveField.getJavaType()).isEqualTo(FieldDescriptor.JavaType.INT); + assertThat(primitiveField.getOptions()) + .isEqualTo(DescriptorProtos.FieldOptions.getDefaultInstance()); + assertThat(primitiveField.isExtension()).isFalse(); + assertThat(primitiveField.toProto().getName()).isEqualTo("optional_int32"); + + assertThat(enumField.getName()).isEqualTo("optional_nested_enum"); + assertThat(enumField.getType()).isEqualTo(FieldDescriptor.Type.ENUM); + assertThat(enumField.getJavaType()).isEqualTo(FieldDescriptor.JavaType.ENUM); + assertThat(enumField.getEnumType()).isEqualTo(TestAllTypes.NestedEnum.getDescriptor()); + + assertThat(messageField.getName()).isEqualTo("optional_foreign_message"); + assertThat(messageField.getType()).isEqualTo(FieldDescriptor.Type.MESSAGE); + assertThat(messageField.getJavaType()).isEqualTo(FieldDescriptor.JavaType.MESSAGE); + assertThat(messageField.getMessageType()).isEqualTo(ForeignMessage.getDescriptor()); + + assertThat(cordField.getName()).isEqualTo("optional_cord"); + assertThat(cordField.getType()).isEqualTo(FieldDescriptor.Type.STRING); + assertThat(cordField.getJavaType()).isEqualTo(FieldDescriptor.JavaType.STRING); + assertThat(cordField.getOptions().getCtype()) + .isEqualTo(DescriptorProtos.FieldOptions.CType.CORD); + + assertThat(extension.getName()).isEqualTo("optional_int32_extension"); + assertThat(extension.getFullName()).isEqualTo("protobuf_unittest.optional_int32_extension"); + assertThat(extension.getNumber()).isEqualTo(1); + assertThat(extension.getContainingType()).isEqualTo(TestAllExtensions.getDescriptor()); + assertThat(extension.getFile()).isEqualTo(UnittestProto.getDescriptor()); + assertThat(extension.getType()).isEqualTo(FieldDescriptor.Type.INT32); + assertThat(extension.getJavaType()).isEqualTo(FieldDescriptor.JavaType.INT); + assertThat(extension.getOptions()) + .isEqualTo(DescriptorProtos.FieldOptions.getDefaultInstance()); + assertThat(extension.isExtension()).isTrue(); + assertThat(extension.getExtensionScope()).isNull(); + assertThat(extension.toProto().getName()).isEqualTo("optional_int32_extension"); + + assertThat(nestedExtension.getName()).isEqualTo("single"); + assertThat(nestedExtension.getFullName()).isEqualTo("protobuf_unittest.TestRequired.single"); + assertThat(nestedExtension.getExtensionScope()).isEqualTo(TestRequired.getDescriptor()); + } - @Test - public void testFieldDescriptorLabel() throws Exception { - FieldDescriptor requiredField = TestRequired.getDescriptor().findFieldByName("a"); - FieldDescriptor optionalField = TestAllTypes.getDescriptor().findFieldByName("optional_int32"); - FieldDescriptor repeatedField = TestAllTypes.getDescriptor().findFieldByName("repeated_int32"); - - assertThat(requiredField.isRequired()).isTrue(); - assertThat(requiredField.isRepeated()).isFalse(); - assertThat(optionalField.isRequired()).isFalse(); - assertThat(optionalField.isRepeated()).isFalse(); - assertThat(repeatedField.isRequired()).isFalse(); - assertThat(repeatedField.isRepeated()).isTrue(); - } + @Test + public void testFieldDescriptorLabel() throws Exception { + FieldDescriptor requiredField = TestRequired.getDescriptor().findFieldByName("a"); + FieldDescriptor optionalField = + TestAllTypes.getDescriptor().findFieldByName("optional_int32"); + FieldDescriptor repeatedField = + TestAllTypes.getDescriptor().findFieldByName("repeated_int32"); + + assertThat(requiredField.isRequired()).isTrue(); + assertThat(requiredField.isRepeated()).isFalse(); + assertThat(optionalField.isRequired()).isFalse(); + assertThat(optionalField.isRepeated()).isFalse(); + assertThat(repeatedField.isRequired()).isFalse(); + assertThat(repeatedField.isRepeated()).isTrue(); + } - @Test - public void testFieldDescriptorJsonName() throws Exception { - FieldDescriptor requiredField = TestRequired.getDescriptor().findFieldByName("a"); - FieldDescriptor optionalField = TestAllTypes.getDescriptor().findFieldByName("optional_int32"); - FieldDescriptor repeatedField = TestAllTypes.getDescriptor().findFieldByName("repeated_int32"); - assertThat(requiredField.getJsonName()).isEqualTo("a"); - assertThat(optionalField.getJsonName()).isEqualTo("optionalInt32"); - assertThat(repeatedField.getJsonName()).isEqualTo("repeatedInt32"); - } + @Test + public void testFieldDescriptorJsonName() throws Exception { + FieldDescriptor requiredField = TestRequired.getDescriptor().findFieldByName("a"); + FieldDescriptor optionalField = + TestAllTypes.getDescriptor().findFieldByName("optional_int32"); + FieldDescriptor repeatedField = + TestAllTypes.getDescriptor().findFieldByName("repeated_int32"); + assertThat(requiredField.getJsonName()).isEqualTo("a"); + assertThat(optionalField.getJsonName()).isEqualTo("optionalInt32"); + assertThat(repeatedField.getJsonName()).isEqualTo("repeatedInt32"); + } - @Test - public void testFieldDescriptorDefault() throws Exception { - Descriptor d = TestAllTypes.getDescriptor(); - assertThat(d.findFieldByName("optional_int32").hasDefaultValue()).isFalse(); - assertThat(d.findFieldByName("optional_int32").getDefaultValue()).isEqualTo(0); - assertThat(d.findFieldByName("default_int32").hasDefaultValue()).isTrue(); - assertThat(d.findFieldByName("default_int32").getDefaultValue()).isEqualTo(41); - - d = TestExtremeDefaultValues.getDescriptor(); - assertThat(d.findFieldByName("escaped_bytes").getDefaultValue()) - .isEqualTo( - ByteString.copyFrom( - "\0\001\007\b\f\n\r\t\013\\\'\"\u00fe".getBytes(Internal.ISO_8859_1))); - assertThat(d.findFieldByName("large_uint32").getDefaultValue()).isEqualTo(-1); - assertThat(d.findFieldByName("large_uint64").getDefaultValue()).isEqualTo(-1L); - } + @Test + public void testFieldDescriptorDefault() throws Exception { + Descriptor d = TestAllTypes.getDescriptor(); + assertThat(d.findFieldByName("optional_int32").hasDefaultValue()).isFalse(); + assertThat(d.findFieldByName("optional_int32").getDefaultValue()).isEqualTo(0); + assertThat(d.findFieldByName("default_int32").hasDefaultValue()).isTrue(); + assertThat(d.findFieldByName("default_int32").getDefaultValue()).isEqualTo(41); + + d = TestExtremeDefaultValues.getDescriptor(); + assertThat(d.findFieldByName("escaped_bytes").getDefaultValue()) + .isEqualTo( + ByteString.copyFrom( + "\0\001\007\b\f\n\r\t\013\\\'\"\u00fe".getBytes(Internal.ISO_8859_1))); + assertThat(d.findFieldByName("large_uint32").getDefaultValue()).isEqualTo(-1); + assertThat(d.findFieldByName("large_uint64").getDefaultValue()).isEqualTo(-1L); + } - @Test - public void testFieldDescriptorLegacyEnumFieldTreatedAsClosed() throws Exception { - // Make an open enum definition. - FileDescriptorProto openEnumFile = - FileDescriptorProto.newBuilder() - .setName("open_enum.proto") - .setSyntax("proto3") - .addEnumType( - EnumDescriptorProto.newBuilder() - .setName("TestEnumOpen") - .addValue( - EnumValueDescriptorProto.newBuilder() - .setName("TestEnumOpen_VALUE0") - .setNumber(0) - .build()) - .build()) - .build(); - FileDescriptor openFileDescriptor = - Descriptors.FileDescriptor.buildFrom(openEnumFile, new FileDescriptor[0]); - EnumDescriptor openEnum = openFileDescriptor.getEnumTypes().get(0); - assertThat(openEnum.isClosed()).isFalse(); - - // Create a message that treats enum fields as closed. - FileDescriptorProto closedEnumFile = - FileDescriptorProto.newBuilder() - .setName("closed_enum_field.proto") - .addDependency("open_enum.proto") - .setSyntax("proto2") - .addEnumType( - EnumDescriptorProto.newBuilder() - .setName("TestEnum") - .addValue( - EnumValueDescriptorProto.newBuilder() - .setName("TestEnum_VALUE0") - .setNumber(0) - .build()) - .build()) - .addMessageType( - DescriptorProto.newBuilder() - .setName("TestClosedEnumField") - .addField( - FieldDescriptorProto.newBuilder() - .setName("int_field") - .setNumber(1) - .setType(FieldDescriptorProto.Type.TYPE_INT32) - .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) - .build()) - .addField( - FieldDescriptorProto.newBuilder() - .setName("open_enum") - .setNumber(2) - .setType(FieldDescriptorProto.Type.TYPE_ENUM) - .setTypeName("TestEnumOpen") - .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) - .build()) - .addField( - FieldDescriptorProto.newBuilder() - .setName("closed_enum") - .setNumber(3) - .setType(FieldDescriptorProto.Type.TYPE_ENUM) - .setTypeName("TestEnum") - .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) - .build()) - .build()) - .build(); - Descriptor closedMessage = - Descriptors.FileDescriptor.buildFrom( - closedEnumFile, new FileDescriptor[] {openFileDescriptor}) - .getMessageTypes() - .get(0); - assertThat(closedMessage.findFieldByName("int_field").legacyEnumFieldTreatedAsClosed()) - .isFalse(); - - assertThat(closedMessage.findFieldByName("closed_enum").legacyEnumFieldTreatedAsClosed()) - .isTrue(); - assertThat(closedMessage.findFieldByName("open_enum").legacyEnumFieldTreatedAsClosed()) - .isTrue(); - } + @Test + public void testFieldDescriptorLegacyEnumFieldTreatedAsOpen() throws Exception { + // Make an open enum definition and message that treats enum fields as open. + + FileDescriptorProto openEnumFile = + FileDescriptorProto.newBuilder() + .setName("open_enum.proto") + .setSyntax("proto3") + .addEnumType( + EnumDescriptorProto.newBuilder() + .setName("TestEnumOpen") + .addValue( + EnumValueDescriptorProto.newBuilder() + .setName("TestEnumOpen_VALUE0") + .setNumber(0) + .build()) + .build()) + .addMessageType( + DescriptorProto.newBuilder() + .setName("TestOpenEnumField") + .addField( + FieldDescriptorProto.newBuilder() + .setName("int_field") + .setNumber(1) + .setType(FieldDescriptorProto.Type.TYPE_INT32) + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .build()) + .addField( + FieldDescriptorProto.newBuilder() + .setName("open_enum") + .setNumber(2) + .setType(FieldDescriptorProto.Type.TYPE_ENUM) + .setTypeName("TestEnumOpen") + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .build()) + .build()) + .build(); + FileDescriptor openEnumFileDescriptor = + Descriptors.FileDescriptor.buildFrom(openEnumFile, new FileDescriptor[0]); + Descriptor openMessage = openEnumFileDescriptor.getMessageTypes().get(0); + EnumDescriptor openEnum = openEnumFileDescriptor.findEnumTypeByName("TestEnumOpen"); + assertThat(openEnum.isClosed()).isFalse(); + assertThat(openMessage.findFieldByName("int_field").legacyEnumFieldTreatedAsClosed()) + .isFalse(); + assertThat(openMessage.findFieldByName("open_enum").legacyEnumFieldTreatedAsClosed()) + .isFalse(); + } - @Test - public void testFieldDescriptorLegacyEnumFieldTreatedAsOpen() throws Exception { - // Make an open enum definition and message that treats enum fields as open. - FileDescriptorProto openEnumFile = - FileDescriptorProto.newBuilder() - .setName("open_enum.proto") - .setSyntax("proto3") - .addEnumType( - EnumDescriptorProto.newBuilder() - .setName("TestEnumOpen") - .addValue( - EnumValueDescriptorProto.newBuilder() - .setName("TestEnumOpen_VALUE0") - .setNumber(0) - .build()) - .build()) - .addMessageType( - DescriptorProto.newBuilder() - .setName("TestOpenEnumField") - .addField( - FieldDescriptorProto.newBuilder() - .setName("int_field") - .setNumber(1) - .setType(FieldDescriptorProto.Type.TYPE_INT32) - .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) - .build()) - .addField( - FieldDescriptorProto.newBuilder() - .setName("open_enum") - .setNumber(2) - .setType(FieldDescriptorProto.Type.TYPE_ENUM) - .setTypeName("TestEnumOpen") - .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) - .build()) - .build()) - .build(); - FileDescriptor openEnumFileDescriptor = - Descriptors.FileDescriptor.buildFrom(openEnumFile, new FileDescriptor[0]); - Descriptor openMessage = openEnumFileDescriptor.getMessageTypes().get(0); - EnumDescriptor openEnum = openEnumFileDescriptor.findEnumTypeByName("TestEnumOpen"); - assertThat(openEnum.isClosed()).isFalse(); - assertThat(openMessage.findFieldByName("int_field").legacyEnumFieldTreatedAsClosed()).isFalse(); - assertThat(openMessage.findFieldByName("open_enum").legacyEnumFieldTreatedAsClosed()).isFalse(); - } + @Test + public void testEditionFieldDescriptorLegacyEnumFieldTreatedAsClosedUnknown() throws Exception { + // Make an open enum definition. + FileDescriptorProto openEnumFile = + FileDescriptorProto.newBuilder() + .setName("open_enum.proto") + .setSyntax("editions") + .setEdition(Edition.EDITION_2023) + .addEnumType( + EnumDescriptorProto.newBuilder() + .setName("TestEnumOpen") + .addValue( + EnumValueDescriptorProto.newBuilder() + .setName("TestEnumOpen_VALUE0") + .setNumber(0) + .build()) + .build()) + .build(); + FileDescriptor openFileDescriptor = + Descriptors.FileDescriptor.buildFrom(openEnumFile, new FileDescriptor[0]); + EnumDescriptor openEnum = openFileDescriptor.getEnumTypes().get(0); + assertThat(openEnum.isClosed()).isFalse(); + + // Create a message that treats enum fields as closed. + FileDescriptorProto editionsClosedEnumFile = + FileDescriptorProto.newBuilder() + .setName("editions_closed_enum_field.proto") + .addDependency("open_enum.proto") + .setSyntax("editions") + .setEdition(Edition.EDITION_2023) + .setOptions( + FileOptions.newBuilder() + .setFeatures( + DescriptorProtos.FeatureSet.newBuilder() + .setEnumType(DescriptorProtos.FeatureSet.EnumType.CLOSED) + .build()) + .build()) + .addEnumType( + EnumDescriptorProto.newBuilder() + .setName("TestEnum") + .addValue( + EnumValueDescriptorProto.newBuilder() + .setName("TestEnum_VALUE0") + .setNumber(0) + .build()) + .build()) + .addMessageType( + DescriptorProto.newBuilder() + .setName("TestClosedEnumField") + .addField( + FieldDescriptorProto.newBuilder() + .setName("int_field") + .setNumber(1) + .setType(FieldDescriptorProto.Type.TYPE_INT32) + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .build()) + .addField( + FieldDescriptorProto.newBuilder() + .setName("open_enum") + .setNumber(2) + .setType(FieldDescriptorProto.Type.TYPE_ENUM) + .setTypeName("TestEnumOpen") + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setOptions( + DescriptorProtos.FieldOptions.newBuilder() + .setFeatures( + DescriptorProtos.FeatureSet.newBuilder() + .setExtension( + JavaFeaturesProto.java_, + JavaFeaturesProto.JavaFeatures.newBuilder() + .setLegacyClosedEnum(true) + .build()) + .build()) + .build()) + .build()) + .addField( + FieldDescriptorProto.newBuilder() + .setName("closed_enum") + .setNumber(3) + .setType(FieldDescriptorProto.Type.TYPE_ENUM) + .setTypeName("TestEnum") + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .build()) + .build()) + .build(); + // Ensure Java features are in unknown fields. + editionsClosedEnumFile = + FileDescriptorProto.parseFrom( + editionsClosedEnumFile.toByteString(), ExtensionRegistry.getEmptyRegistry()); + Descriptor editionsClosedMessage = + Descriptors.FileDescriptor.buildFrom( + editionsClosedEnumFile, new FileDescriptor[] {openFileDescriptor}) + .getMessageTypes() + .get(0); + assertThat( + editionsClosedMessage.findFieldByName("int_field").legacyEnumFieldTreatedAsClosed()) + .isFalse(); + assertThat( + editionsClosedMessage.findFieldByName("closed_enum").legacyEnumFieldTreatedAsClosed()) + .isTrue(); + assertThat( + editionsClosedMessage.findFieldByName("open_enum").legacyEnumFieldTreatedAsClosed()) + .isTrue(); + } - @Test - public void testEnumDescriptor() throws Exception { - EnumDescriptor enumType = ForeignEnum.getDescriptor(); - EnumDescriptor nestedType = TestAllTypes.NestedEnum.getDescriptor(); - - assertThat(enumType.getName()).isEqualTo("ForeignEnum"); - assertThat(enumType.getFullName()).isEqualTo("protobuf_unittest.ForeignEnum"); - assertThat(enumType.getFile()).isEqualTo(UnittestProto.getDescriptor()); - assertThat(enumType.isClosed()).isTrue(); - assertThat(enumType.getContainingType()).isNull(); - assertThat(enumType.getOptions()).isEqualTo(DescriptorProtos.EnumOptions.getDefaultInstance()); - - assertThat(nestedType.getName()).isEqualTo("NestedEnum"); - assertThat(nestedType.getFullName()).isEqualTo("protobuf_unittest.TestAllTypes.NestedEnum"); - assertThat(nestedType.getFile()).isEqualTo(UnittestProto.getDescriptor()); - assertThat(nestedType.getContainingType()).isEqualTo(TestAllTypes.getDescriptor()); - - EnumValueDescriptor value = ForeignEnum.FOREIGN_FOO.getValueDescriptor(); - assertThat(enumType.getValues().get(0)).isEqualTo(value); - assertThat(value.getName()).isEqualTo("FOREIGN_FOO"); - assertThat(value.toString()).isEqualTo("FOREIGN_FOO"); - assertThat(value.getNumber()).isEqualTo(4); - assertThat(enumType.findValueByName("FOREIGN_FOO")).isEqualTo(value); - assertThat(enumType.findValueByNumber(4)).isEqualTo(value); - assertThat(enumType.findValueByName("NO_SUCH_VALUE")).isNull(); - for (int i = 0; i < enumType.getValues().size(); i++) { - assertThat(enumType.getValues().get(i).getIndex()).isEqualTo(i); + @Test + public void testEditionFieldDescriptorLegacyEnumFieldTreatedAsClosedCustomPool() + throws Exception { + + FileDescriptor javaFeaturesDescriptor = + Descriptors.FileDescriptor.buildFrom( + JavaFeaturesProto.getDescriptor().toProto(), + new FileDescriptor[] {DescriptorProtos.getDescriptor()}); + // Make an open enum definition. + FileDescriptorProto openEnumFile = + FileDescriptorProto.newBuilder() + .setName("open_enum.proto") + .setSyntax("editions") + .setEdition(Edition.EDITION_2023) + .addEnumType( + EnumDescriptorProto.newBuilder() + .setName("TestEnumOpen") + .addValue( + EnumValueDescriptorProto.newBuilder() + .setName("TestEnumOpen_VALUE0") + .setNumber(0) + .build()) + .build()) + .build(); + FileDescriptor openFileDescriptor = + Descriptors.FileDescriptor.buildFrom(openEnumFile, new FileDescriptor[0]); + EnumDescriptor openEnum = openFileDescriptor.getEnumTypes().get(0); + assertThat(openEnum.isClosed()).isFalse(); + + // Create a message that treats enum fields as closed. + FileDescriptorProto editionsClosedEnumFile = + FileDescriptorProto.newBuilder() + .setName("editions_closed_enum_field.proto") + .addDependency("open_enum.proto") + .setSyntax("editions") + .setEdition(Edition.EDITION_2023) + .setOptions( + FileOptions.newBuilder() + .setFeatures( + DescriptorProtos.FeatureSet.newBuilder() + .setEnumType(DescriptorProtos.FeatureSet.EnumType.CLOSED) + .build()) + .build()) + .addEnumType( + EnumDescriptorProto.newBuilder() + .setName("TestEnum") + .addValue( + EnumValueDescriptorProto.newBuilder() + .setName("TestEnum_VALUE0") + .setNumber(0) + .build()) + .build()) + .addMessageType( + DescriptorProto.newBuilder() + .setName("TestClosedEnumField") + .addField( + FieldDescriptorProto.newBuilder() + .setName("int_field") + .setNumber(1) + .setType(FieldDescriptorProto.Type.TYPE_INT32) + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .build()) + .addField( + FieldDescriptorProto.newBuilder() + .setName("open_enum") + .setNumber(2) + .setType(FieldDescriptorProto.Type.TYPE_ENUM) + .setTypeName("TestEnumOpen") + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setOptions( + DescriptorProtos.FieldOptions.newBuilder() + .setFeatures( + DescriptorProtos.FeatureSet.newBuilder() + .setField( + // Set extension using custom descriptor + javaFeaturesDescriptor.findExtensionByName( + JavaFeaturesProto.java_ + .getDescriptor() + .getName()), + JavaFeaturesProto.JavaFeatures.newBuilder() + .setLegacyClosedEnum(true) + .build()) + .build()) + .build()) + .build()) + .addField( + FieldDescriptorProto.newBuilder() + .setName("closed_enum") + .setNumber(3) + .setType(FieldDescriptorProto.Type.TYPE_ENUM) + .setTypeName("TestEnum") + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .build()) + .build()) + .build(); + Descriptor editionsClosedMessage = + Descriptors.FileDescriptor.buildFrom( + editionsClosedEnumFile, + new FileDescriptor[] {openFileDescriptor, javaFeaturesDescriptor}) + .getMessageTypes() + .get(0); + assertThat( + editionsClosedMessage.findFieldByName("int_field").legacyEnumFieldTreatedAsClosed()) + .isFalse(); + assertThat( + editionsClosedMessage.findFieldByName("closed_enum").legacyEnumFieldTreatedAsClosed()) + .isTrue(); + assertThat( + editionsClosedMessage.findFieldByName("open_enum").legacyEnumFieldTreatedAsClosed()) + .isTrue(); } - } - @Test - public void testServiceDescriptor() throws Exception { - ServiceDescriptor service = TestService.getDescriptor(); + @Test + public void testEnumDescriptor() throws Exception { + EnumDescriptor enumType = ForeignEnum.getDescriptor(); + EnumDescriptor nestedType = TestAllTypes.NestedEnum.getDescriptor(); + + assertThat(enumType.getName()).isEqualTo("ForeignEnum"); + assertThat(enumType.getFullName()).isEqualTo("protobuf_unittest.ForeignEnum"); + assertThat(enumType.getFile()).isEqualTo(UnittestProto.getDescriptor()); + assertThat(enumType.isClosed()).isTrue(); + assertThat(enumType.getContainingType()).isNull(); + assertThat(enumType.getOptions()) + .isEqualTo(DescriptorProtos.EnumOptions.getDefaultInstance()); + + assertThat(nestedType.getName()).isEqualTo("NestedEnum"); + assertThat(nestedType.getFullName()).isEqualTo("protobuf_unittest.TestAllTypes.NestedEnum"); + assertThat(nestedType.getFile()).isEqualTo(UnittestProto.getDescriptor()); + assertThat(nestedType.getContainingType()).isEqualTo(TestAllTypes.getDescriptor()); + + EnumValueDescriptor value = ForeignEnum.FOREIGN_FOO.getValueDescriptor(); + assertThat(enumType.getValues().get(0)).isEqualTo(value); + assertThat(value.getName()).isEqualTo("FOREIGN_FOO"); + assertThat(value.toString()).isEqualTo("FOREIGN_FOO"); + assertThat(value.getNumber()).isEqualTo(4); + assertThat(enumType.findValueByName("FOREIGN_FOO")).isEqualTo(value); + assertThat(enumType.findValueByNumber(4)).isEqualTo(value); + assertThat(enumType.findValueByName("NO_SUCH_VALUE")).isNull(); + for (int i = 0; i < enumType.getValues().size(); i++) { + assertThat(enumType.getValues().get(i).getIndex()).isEqualTo(i); + } + } + + @Test + public void testServiceDescriptor() throws Exception { + ServiceDescriptor service = TestService.getDescriptor(); - assertThat(service.getName()).isEqualTo("TestService"); - assertThat(service.getFullName()).isEqualTo("protobuf_unittest.TestService"); - assertThat(service.getFile()).isEqualTo(UnittestProto.getDescriptor()); + assertThat(service.getName()).isEqualTo("TestService"); + assertThat(service.getFullName()).isEqualTo("protobuf_unittest.TestService"); + assertThat(service.getFile()).isEqualTo(UnittestProto.getDescriptor()); - MethodDescriptor fooMethod = service.getMethods().get(0); - assertThat(fooMethod.getName()).isEqualTo("Foo"); - assertThat(fooMethod.getInputType()).isEqualTo(UnittestProto.FooRequest.getDescriptor()); - assertThat(fooMethod.getOutputType()).isEqualTo(UnittestProto.FooResponse.getDescriptor()); - assertThat(service.findMethodByName("Foo")).isEqualTo(fooMethod); + MethodDescriptor fooMethod = service.getMethods().get(0); + assertThat(fooMethod.getName()).isEqualTo("Foo"); + assertThat(fooMethod.getInputType()).isEqualTo(UnittestProto.FooRequest.getDescriptor()); + assertThat(fooMethod.getOutputType()).isEqualTo(UnittestProto.FooResponse.getDescriptor()); + assertThat(service.findMethodByName("Foo")).isEqualTo(fooMethod); - MethodDescriptor barMethod = service.getMethods().get(1); - assertThat(barMethod.getName()).isEqualTo("Bar"); - assertThat(barMethod.getInputType()).isEqualTo(UnittestProto.BarRequest.getDescriptor()); - assertThat(barMethod.getOutputType()).isEqualTo(UnittestProto.BarResponse.getDescriptor()); - assertThat(service.findMethodByName("Bar")).isEqualTo(barMethod); + MethodDescriptor barMethod = service.getMethods().get(1); + assertThat(barMethod.getName()).isEqualTo("Bar"); + assertThat(barMethod.getInputType()).isEqualTo(UnittestProto.BarRequest.getDescriptor()); + assertThat(barMethod.getOutputType()).isEqualTo(UnittestProto.BarResponse.getDescriptor()); + assertThat(service.findMethodByName("Bar")).isEqualTo(barMethod); - assertThat(service.findMethodByName("NoSuchMethod")).isNull(); + assertThat(service.findMethodByName("NoSuchMethod")).isNull(); - for (int i = 0; i < service.getMethods().size(); i++) { - assertThat(service.getMethods().get(i).getIndex()).isEqualTo(i); + for (int i = 0; i < service.getMethods().size(); i++) { + assertThat(service.getMethods().get(i).getIndex()).isEqualTo(i); + } } - } - @Test - public void testCustomOptions() throws Exception { - // Get the descriptor indirectly from a dependent proto class. This is to - // ensure that when a proto class is loaded, custom options defined in its - // dependencies are also properly initialized. - Descriptor descriptor = - TestCustomOptions.TestMessageWithCustomOptionsContainer.getDescriptor() - .findFieldByName("field") - .getMessageType(); + @Test + public void testCustomOptions() throws Exception { + // Get the descriptor indirectly from a dependent proto class. This is to + // ensure that when a proto class is loaded, custom options defined in its + // dependencies are also properly initialized. + Descriptor descriptor = + TestCustomOptions.TestMessageWithCustomOptionsContainer.getDescriptor() + .findFieldByName("field") + .getMessageType(); - assertThat(descriptor.getOptions().hasExtension(UnittestCustomOptions.messageOpt1)).isTrue(); - assertThat(descriptor.getOptions().getExtension(UnittestCustomOptions.messageOpt1)) - .isEqualTo(Integer.valueOf(-56)); + assertThat(descriptor.getOptions().hasExtension(UnittestCustomOptions.messageOpt1)).isTrue(); + assertThat(descriptor.getOptions().getExtension(UnittestCustomOptions.messageOpt1)) + .isEqualTo(Integer.valueOf(-56)); - FieldDescriptor field = descriptor.findFieldByName("field1"); - assertThat(field).isNotNull(); + FieldDescriptor field = descriptor.findFieldByName("field1"); + assertThat(field).isNotNull(); - assertThat(field.getOptions().hasExtension(UnittestCustomOptions.fieldOpt1)).isTrue(); - assertThat(field.getOptions().getExtension(UnittestCustomOptions.fieldOpt1)) - .isEqualTo(Long.valueOf(8765432109L)); + assertThat(field.getOptions().hasExtension(UnittestCustomOptions.fieldOpt1)).isTrue(); + assertThat(field.getOptions().getExtension(UnittestCustomOptions.fieldOpt1)) + .isEqualTo(Long.valueOf(8765432109L)); - OneofDescriptor oneof = descriptor.getOneofs().get(0); - assertThat(oneof).isNotNull(); + OneofDescriptor oneof = descriptor.getOneofs().get(0); + assertThat(oneof).isNotNull(); - assertThat(oneof.getOptions().hasExtension(UnittestCustomOptions.oneofOpt1)).isTrue(); - assertThat(oneof.getOptions().getExtension(UnittestCustomOptions.oneofOpt1)) - .isEqualTo(Integer.valueOf(-99)); + assertThat(oneof.getOptions().hasExtension(UnittestCustomOptions.oneofOpt1)).isTrue(); + assertThat(oneof.getOptions().getExtension(UnittestCustomOptions.oneofOpt1)) + .isEqualTo(Integer.valueOf(-99)); - EnumDescriptor enumType = - UnittestCustomOptions.TestMessageWithCustomOptions.AnEnum.getDescriptor(); + EnumDescriptor enumType = + UnittestCustomOptions.TestMessageWithCustomOptions.AnEnum.getDescriptor(); - assertThat(enumType.getOptions().hasExtension(UnittestCustomOptions.enumOpt1)).isTrue(); - assertThat(enumType.getOptions().getExtension(UnittestCustomOptions.enumOpt1)) - .isEqualTo(Integer.valueOf(-789)); + assertThat(enumType.getOptions().hasExtension(UnittestCustomOptions.enumOpt1)).isTrue(); + assertThat(enumType.getOptions().getExtension(UnittestCustomOptions.enumOpt1)) + .isEqualTo(Integer.valueOf(-789)); - ServiceDescriptor service = UnittestCustomOptions.TestServiceWithCustomOptions.getDescriptor(); + ServiceDescriptor service = + UnittestCustomOptions.TestServiceWithCustomOptions.getDescriptor(); - assertThat(service.getOptions().hasExtension(UnittestCustomOptions.serviceOpt1)).isTrue(); - assertThat(service.getOptions().getExtension(UnittestCustomOptions.serviceOpt1)) - .isEqualTo(Long.valueOf(-9876543210L)); + assertThat(service.getOptions().hasExtension(UnittestCustomOptions.serviceOpt1)).isTrue(); + assertThat(service.getOptions().getExtension(UnittestCustomOptions.serviceOpt1)) + .isEqualTo(Long.valueOf(-9876543210L)); - MethodDescriptor method = service.findMethodByName("Foo"); - assertThat(method).isNotNull(); + MethodDescriptor method = service.findMethodByName("Foo"); + assertThat(method).isNotNull(); - assertThat(method.getOptions().hasExtension(UnittestCustomOptions.methodOpt1)).isTrue(); - assertThat(method.getOptions().getExtension(UnittestCustomOptions.methodOpt1)) - .isEqualTo(UnittestCustomOptions.MethodOpt1.METHODOPT1_VAL2); - } + assertThat(method.getOptions().hasExtension(UnittestCustomOptions.methodOpt1)).isTrue(); + assertThat(method.getOptions().getExtension(UnittestCustomOptions.methodOpt1)) + .isEqualTo(UnittestCustomOptions.MethodOpt1.METHODOPT1_VAL2); + } - @Test - public void testOptionRetention() throws Exception { - // Verify that options with RETENTION_SOURCE are stripped from the - // generated descriptors. - FileOptions options = UnittestRetention.getDescriptor().getOptions(); - assertThat(options.hasExtension(UnittestRetention.plainOption)).isTrue(); - assertThat(options.hasExtension(UnittestRetention.runtimeRetentionOption)).isTrue(); - assertThat(options.hasExtension(UnittestRetention.sourceRetentionOption)).isFalse(); - } + @Test + public void testOptionRetention() throws Exception { + // Verify that options with RETENTION_SOURCE are stripped from the + // generated descriptors. + FileOptions options = UnittestRetention.getDescriptor().getOptions(); + assertThat(options.hasExtension(UnittestRetention.plainOption)).isTrue(); + assertThat(options.hasExtension(UnittestRetention.runtimeRetentionOption)).isTrue(); + assertThat(options.hasExtension(UnittestRetention.sourceRetentionOption)).isFalse(); + } - /** Test that the FieldDescriptor.Type enum is the same as the WireFormat.FieldType enum. */ - @Test - public void testFieldTypeTablesMatch() throws Exception { - FieldDescriptor.Type[] values1 = FieldDescriptor.Type.values(); - WireFormat.FieldType[] values2 = WireFormat.FieldType.values(); + /** Test that the FieldDescriptor.Type enum is the same as the WireFormat.FieldType enum. */ + @Test + public void testFieldTypeTablesMatch() throws Exception { + FieldDescriptor.Type[] values1 = FieldDescriptor.Type.values(); + WireFormat.FieldType[] values2 = WireFormat.FieldType.values(); - assertThat(values1).hasLength(values2.length); + assertThat(values1).hasLength(values2.length); - for (int i = 0; i < values1.length; i++) { - assertThat(values1[i].toString()).isEqualTo(values2[i].toString()); + for (int i = 0; i < values1.length; i++) { + assertThat(values1[i].toString()).isEqualTo(values2[i].toString()); + } } - } - /** Test that the FieldDescriptor.JavaType enum is the same as the WireFormat.JavaType enum. */ - @Test - public void testJavaTypeTablesMatch() throws Exception { - FieldDescriptor.JavaType[] values1 = FieldDescriptor.JavaType.values(); - WireFormat.JavaType[] values2 = WireFormat.JavaType.values(); + /** Test that the FieldDescriptor.JavaType enum is the same as the WireFormat.JavaType enum. */ + @Test + public void testJavaTypeTablesMatch() throws Exception { + FieldDescriptor.JavaType[] values1 = FieldDescriptor.JavaType.values(); + WireFormat.JavaType[] values2 = WireFormat.JavaType.values(); - assertThat(values1).hasLength(values2.length); + assertThat(values1).hasLength(values2.length); - for (int i = 0; i < values1.length; i++) { - assertThat(values1[i].toString()).isEqualTo(values2[i].toString()); + for (int i = 0; i < values1.length; i++) { + assertThat(values1[i].toString()).isEqualTo(values2[i].toString()); + } } - } - @Test - public void testEnormousDescriptor() throws Exception { - // The descriptor for this file is larger than 64k, yet it did not cause - // a compiler error due to an over-long string literal. - assertThat(UnittestEnormousDescriptor.getDescriptor().toProto().getSerializedSize()) - .isGreaterThan(65536); - } + @Test + public void testEnormousDescriptor() throws Exception { + // The descriptor for this file is larger than 64k, yet it did not cause + // a compiler error due to an over-long string literal. + assertThat(UnittestEnormousDescriptor.getDescriptor().toProto().getSerializedSize()) + .isGreaterThan(65536); + } - /** Tests that the DescriptorValidationException works as intended. */ - @Test - public void testDescriptorValidatorException() throws Exception { - FileDescriptorProto fileDescriptorProto = - FileDescriptorProto.newBuilder() - .setName("foo.proto") - .addMessageType( - DescriptorProto.newBuilder() - .setName("Foo") - .addField( - FieldDescriptorProto.newBuilder() - .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) - .setType(FieldDescriptorProto.Type.TYPE_INT32) - .setName("foo") - .setNumber(1) - .setDefaultValue("invalid") - .build()) - .build()) - .build(); - try { - Descriptors.FileDescriptor.buildFrom(fileDescriptorProto, new FileDescriptor[0]); - assertWithMessage("DescriptorValidationException expected").fail(); - } catch (DescriptorValidationException e) { - // Expected; check that the error message contains some useful hints - assertThat(e).hasMessageThat().contains("foo"); - assertThat(e).hasMessageThat().contains("Foo"); - assertThat(e).hasMessageThat().contains("invalid"); - assertThat(e).hasCauseThat().isInstanceOf(NumberFormatException.class); - assertThat(e).hasCauseThat().hasMessageThat().contains("invalid"); + /** Tests that the DescriptorValidationException works as intended. */ + @Test + public void testDescriptorValidatorException() throws Exception { + FileDescriptorProto fileDescriptorProto = + FileDescriptorProto.newBuilder() + .setName("foo.proto") + .addMessageType( + DescriptorProto.newBuilder() + .setName("Foo") + .addField( + FieldDescriptorProto.newBuilder() + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setType(FieldDescriptorProto.Type.TYPE_INT32) + .setName("foo") + .setNumber(1) + .setDefaultValue("invalid") + .build()) + .build()) + .build(); + try { + Descriptors.FileDescriptor.buildFrom(fileDescriptorProto, new FileDescriptor[0]); + assertWithMessage("DescriptorValidationException expected").fail(); + } catch (DescriptorValidationException e) { + // Expected; check that the error message contains some useful hints + assertThat(e).hasMessageThat().contains("foo"); + assertThat(e).hasMessageThat().contains("Foo"); + assertThat(e).hasMessageThat().contains("invalid"); + assertThat(e).hasCauseThat().isInstanceOf(NumberFormatException.class); + assertThat(e).hasCauseThat().hasMessageThat().contains("invalid"); + } } - } - /** Tests that parsing an unknown enum throws an exception */ - @Test - public void testParseUnknownEnum() { - FieldDescriptorProto.Builder field = - FieldDescriptorProto.newBuilder() - .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) - .setTypeName("UnknownEnum") - .setType(FieldDescriptorProto.Type.TYPE_ENUM) - .setName("bar") - .setNumber(1); - DescriptorProto.Builder messageType = - DescriptorProto.newBuilder().setName("Foo").addField(field); - FileDescriptorProto fooProto = - FileDescriptorProto.newBuilder() - .setName("foo.proto") - .addDependency("bar.proto") - .addMessageType(messageType) - .build(); - try { - Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0], true); - assertWithMessage("DescriptorValidationException expected").fail(); - } catch (DescriptorValidationException expected) { - assertThat(expected.getMessage()).contains("\"UnknownEnum\" is not an enum type."); + /** Tests that parsing an unknown enum throws an exception */ + @Test + public void testParseUnknownEnum() { + FieldDescriptorProto.Builder field = + FieldDescriptorProto.newBuilder() + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setTypeName("UnknownEnum") + .setType(FieldDescriptorProto.Type.TYPE_ENUM) + .setName("bar") + .setNumber(1); + DescriptorProto.Builder messageType = + DescriptorProto.newBuilder().setName("Foo").addField(field); + FileDescriptorProto fooProto = + FileDescriptorProto.newBuilder() + .setName("foo.proto") + .addDependency("bar.proto") + .addMessageType(messageType) + .build(); + try { + Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0], true); + assertWithMessage("DescriptorValidationException expected").fail(); + } catch (DescriptorValidationException expected) { + assertThat(expected.getMessage()).contains("\"UnknownEnum\" is not an enum type."); + } } - } - /** - * Tests the translate/crosslink for an example where a message field's name and type name are the - * same. - */ - @Test - public void testDescriptorComplexCrosslink() throws Exception { - FileDescriptorProto fileDescriptorProto = - FileDescriptorProto.newBuilder() - .setName("foo.proto") - .addMessageType( - DescriptorProto.newBuilder() - .setName("Foo") - .addField( - FieldDescriptorProto.newBuilder() - .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) - .setType(FieldDescriptorProto.Type.TYPE_INT32) - .setName("foo") - .setNumber(1) - .build()) - .build()) - .addMessageType( - DescriptorProto.newBuilder() - .setName("Bar") - .addField( - FieldDescriptorProto.newBuilder() - .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) - .setTypeName("Foo") - .setName("Foo") - .setNumber(1) - .build()) - .build()) - .build(); - // translate and crosslink - FileDescriptor file = - Descriptors.FileDescriptor.buildFrom(fileDescriptorProto, new FileDescriptor[0]); - // verify resulting descriptors - assertThat(file).isNotNull(); - List msglist = file.getMessageTypes(); - assertThat(msglist).isNotNull(); - assertThat(msglist).hasSize(2); - boolean barFound = false; - for (Descriptor desc : msglist) { - if (desc.getName().equals("Bar")) { - barFound = true; + /** + * Tests the translate/crosslink for an example where a message field's name and type name are + * the same. + */ + @Test + public void testDescriptorComplexCrosslink() throws Exception { + FileDescriptorProto fileDescriptorProto = + FileDescriptorProto.newBuilder() + .setName("foo.proto") + .addMessageType( + DescriptorProto.newBuilder() + .setName("Foo") + .addField( + FieldDescriptorProto.newBuilder() + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setType(FieldDescriptorProto.Type.TYPE_INT32) + .setName("foo") + .setNumber(1) + .build()) + .build()) + .addMessageType( + DescriptorProto.newBuilder() + .setName("Bar") + .addField( + FieldDescriptorProto.newBuilder() + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setTypeName("Foo") + .setName("Foo") + .setNumber(1) + .build()) + .build()) + .build(); + // translate and crosslink + FileDescriptor file = + Descriptors.FileDescriptor.buildFrom(fileDescriptorProto, new FileDescriptor[0]); + // verify resulting descriptors + assertThat(file).isNotNull(); + List msglist = file.getMessageTypes(); + assertThat(msglist).isNotNull(); + assertThat(msglist).hasSize(2); + boolean barFound = false; + for (Descriptor desc : msglist) { + if (desc.getName().equals("Bar")) { + barFound = true; + assertThat(desc.getFields()).isNotNull(); + List fieldlist = desc.getFields(); + assertThat(fieldlist).isNotNull(); + assertThat(fieldlist).hasSize(1); + assertThat(fieldlist.get(0).getType()).isSameInstanceAs(FieldDescriptor.Type.MESSAGE); + assertThat(fieldlist.get(0).getMessageType().getName().equals("Foo")).isTrue(); + } + } + assertThat(barFound).isTrue(); + } + + @Test + public void testDependencyOrder() throws Exception { + FileDescriptorProto fooProto = FileDescriptorProto.newBuilder().setName("foo.proto").build(); + FileDescriptorProto barProto = + FileDescriptorProto.newBuilder().setName("bar.proto").addDependency("foo.proto").build(); + FileDescriptorProto bazProto = + FileDescriptorProto.newBuilder() + .setName("baz.proto") + .addDependency("foo.proto") + .addDependency("bar.proto") + .addPublicDependency(0) + .addPublicDependency(1) + .build(); + FileDescriptor fooFile = + Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0]); + FileDescriptor barFile = + Descriptors.FileDescriptor.buildFrom(barProto, new FileDescriptor[] {fooFile}); + + // Items in the FileDescriptor array can be in any order. + FileDescriptor unused1 = + Descriptors.FileDescriptor.buildFrom(bazProto, new FileDescriptor[] {fooFile, barFile}); + FileDescriptor unused2 = + Descriptors.FileDescriptor.buildFrom(bazProto, new FileDescriptor[] {barFile, fooFile}); + } + + @Test + public void testInvalidPublicDependency() throws Exception { + FileDescriptorProto fooProto = FileDescriptorProto.newBuilder().setName("foo.proto").build(); + FileDescriptorProto barProto = + FileDescriptorProto.newBuilder() + .setName("boo.proto") + .addDependency("foo.proto") + .addPublicDependency(1) // Error, should be 0. + .build(); + FileDescriptor fooFile = + Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0]); + try { + Descriptors.FileDescriptor.buildFrom(barProto, new FileDescriptor[] {fooFile}); + assertWithMessage("DescriptorValidationException expected").fail(); + } catch (DescriptorValidationException e) { + assertThat(e).hasMessageThat().contains("Invalid public dependency index."); + } + } + + @Test + public void testUnknownFieldsDenied() throws Exception { + FileDescriptorProto fooProto = + FileDescriptorProto.newBuilder() + .setName("foo.proto") + .addMessageType( + DescriptorProto.newBuilder() + .setName("Foo") + .addField( + FieldDescriptorProto.newBuilder() + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setTypeName("Bar") + .setName("bar") + .setNumber(1))) + .build(); + + try { + Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0]); + assertWithMessage("DescriptorValidationException expected").fail(); + } catch (DescriptorValidationException e) { + assertThat(e).hasMessageThat().contains("Bar"); + assertThat(e).hasMessageThat().contains("is not defined"); + } + } + + @Test + public void testUnknownFieldsAllowed() throws Exception { + FileDescriptorProto fooProto = + FileDescriptorProto.newBuilder() + .setName("foo.proto") + .addDependency("bar.proto") + .addMessageType( + DescriptorProto.newBuilder() + .setName("Foo") + .addField( + FieldDescriptorProto.newBuilder() + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setTypeName("Bar") + .setName("bar") + .setNumber(1))) + .build(); + FileDescriptor unused = + Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0], true); + } + + @Test + public void testHiddenDependency() throws Exception { + FileDescriptorProto barProto = + FileDescriptorProto.newBuilder() + .setName("bar.proto") + .addMessageType(DescriptorProto.newBuilder().setName("Bar")) + .build(); + FileDescriptorProto forwardProto = + FileDescriptorProto.newBuilder() + .setName("forward.proto") + .addDependency("bar.proto") + .build(); + FileDescriptorProto fooProto = + FileDescriptorProto.newBuilder() + .setName("foo.proto") + .addDependency("forward.proto") + .addMessageType( + DescriptorProto.newBuilder() + .setName("Foo") + .addField( + FieldDescriptorProto.newBuilder() + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setTypeName("Bar") + .setName("bar") + .setNumber(1))) + .build(); + FileDescriptor barFile = + Descriptors.FileDescriptor.buildFrom(barProto, new FileDescriptor[0]); + FileDescriptor forwardFile = + Descriptors.FileDescriptor.buildFrom(forwardProto, new FileDescriptor[] {barFile}); + + try { + FileDescriptor unused = + Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[] {forwardFile}); + assertWithMessage("DescriptorValidationException expected").fail(); + } catch (DescriptorValidationException e) { + assertThat(e).hasMessageThat().contains("Bar"); + assertThat(e).hasMessageThat().contains("is not defined"); + } + } + + @Test + public void testPublicDependency() throws Exception { + FileDescriptorProto barProto = + FileDescriptorProto.newBuilder() + .setName("bar.proto") + .addMessageType(DescriptorProto.newBuilder().setName("Bar")) + .build(); + FileDescriptorProto forwardProto = + FileDescriptorProto.newBuilder() + .setName("forward.proto") + .addDependency("bar.proto") + .addPublicDependency(0) + .build(); + FileDescriptorProto fooProto = + FileDescriptorProto.newBuilder() + .setName("foo.proto") + .addDependency("forward.proto") + .addMessageType( + DescriptorProto.newBuilder() + .setName("Foo") + .addField( + FieldDescriptorProto.newBuilder() + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setTypeName("Bar") + .setName("bar") + .setNumber(1))) + .build(); + FileDescriptor barFile = + Descriptors.FileDescriptor.buildFrom(barProto, new FileDescriptor[0]); + FileDescriptor forwardFile = + Descriptors.FileDescriptor.buildFrom(forwardProto, new FileDescriptor[] {barFile}); + FileDescriptor unused = + Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[] {forwardFile}); + } + + /** Tests the translate/crosslink for an example with a more complex namespace referencing. */ + @Test + public void testComplexNamespacePublicDependency() throws Exception { + FileDescriptorProto fooProto = + FileDescriptorProto.newBuilder() + .setName("bar.proto") + .setPackage("a.b.c.d.bar.shared") + .addEnumType( + EnumDescriptorProto.newBuilder() + .setName("MyEnum") + .addValue(EnumValueDescriptorProto.newBuilder().setName("BLAH").setNumber(1))) + .build(); + FileDescriptorProto barProto = + FileDescriptorProto.newBuilder() + .setName("foo.proto") + .addDependency("bar.proto") + .setPackage("a.b.c.d.foo.shared") + .addMessageType( + DescriptorProto.newBuilder() + .setName("MyMessage") + .addField( + FieldDescriptorProto.newBuilder() + .setLabel(FieldDescriptorProto.Label.LABEL_REPEATED) + .setTypeName("bar.shared.MyEnum") + .setName("MyField") + .setNumber(1))) + .build(); + // translate and crosslink + FileDescriptor fooFile = + Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0]); + FileDescriptor barFile = + Descriptors.FileDescriptor.buildFrom(barProto, new FileDescriptor[] {fooFile}); + // verify resulting descriptors + assertThat(barFile).isNotNull(); + List msglist = barFile.getMessageTypes(); + assertThat(msglist).isNotNull(); + assertThat(msglist).hasSize(1); + Descriptor desc = msglist.get(0); + if (desc.getName().equals("MyMessage")) { assertThat(desc.getFields()).isNotNull(); List fieldlist = desc.getFields(); assertThat(fieldlist).isNotNull(); assertThat(fieldlist).hasSize(1); - assertThat(fieldlist.get(0).getType()).isSameInstanceAs(FieldDescriptor.Type.MESSAGE); - assertThat(fieldlist.get(0).getMessageType().getName().equals("Foo")).isTrue(); + FieldDescriptor field = fieldlist.get(0); + assertThat(field.getType()).isSameInstanceAs(FieldDescriptor.Type.ENUM); + assertThat(field.getEnumType().getName().equals("MyEnum")).isTrue(); + assertThat(field.getEnumType().getFile().getName().equals("bar.proto")).isTrue(); + assertThat(field.getEnumType().getFile().getPackage().equals("a.b.c.d.bar.shared")) + .isTrue(); } } - assertThat(barFound).isTrue(); - } - @Test - public void testDependencyOrder() throws Exception { - FileDescriptorProto fooProto = FileDescriptorProto.newBuilder().setName("foo.proto").build(); - FileDescriptorProto barProto = - FileDescriptorProto.newBuilder().setName("bar.proto").addDependency("foo.proto").build(); - FileDescriptorProto bazProto = - FileDescriptorProto.newBuilder() - .setName("baz.proto") - .addDependency("foo.proto") - .addDependency("bar.proto") - .addPublicDependency(0) - .addPublicDependency(1) - .build(); - FileDescriptor fooFile = Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0]); - FileDescriptor barFile = - Descriptors.FileDescriptor.buildFrom(barProto, new FileDescriptor[] {fooFile}); + @Test + public void testOneofDescriptor() throws Exception { + Descriptor messageType = TestAllTypes.getDescriptor(); + FieldDescriptor field = messageType.findFieldByName("oneof_nested_message"); + OneofDescriptor oneofDescriptor = field.getContainingOneof(); + assertThat(oneofDescriptor).isNotNull(); + assertThat(messageType.getOneofs().get(0)).isSameInstanceAs(oneofDescriptor); + assertThat(oneofDescriptor.getName()).isEqualTo("oneof_field"); - // Items in the FileDescriptor array can be in any order. - FileDescriptor unused1 = - Descriptors.FileDescriptor.buildFrom(bazProto, new FileDescriptor[] {fooFile, barFile}); - FileDescriptor unused2 = - Descriptors.FileDescriptor.buildFrom(bazProto, new FileDescriptor[] {barFile, fooFile}); - } + assertThat(oneofDescriptor.getFieldCount()).isEqualTo(7); + assertThat(field).isSameInstanceAs(oneofDescriptor.getField(1)); - @Test - public void testInvalidPublicDependency() throws Exception { - FileDescriptorProto fooProto = FileDescriptorProto.newBuilder().setName("foo.proto").build(); - FileDescriptorProto barProto = - FileDescriptorProto.newBuilder() - .setName("boo.proto") - .addDependency("foo.proto") - .addPublicDependency(1) // Error, should be 0. - .build(); - FileDescriptor fooFile = Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0]); - try { - Descriptors.FileDescriptor.buildFrom(barProto, new FileDescriptor[] {fooFile}); - assertWithMessage("DescriptorValidationException expected").fail(); - } catch (DescriptorValidationException e) { - assertThat(e).hasMessageThat().contains("Invalid public dependency index."); + assertThat(oneofDescriptor.getFields()).hasSize(7); + assertThat(field).isEqualTo(oneofDescriptor.getFields().get(1)); } - } - @Test - public void testUnknownFieldsDenied() throws Exception { - FileDescriptorProto fooProto = - FileDescriptorProto.newBuilder() - .setName("foo.proto") - .addMessageType( - DescriptorProto.newBuilder() - .setName("Foo") - .addField( - FieldDescriptorProto.newBuilder() - .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) - .setTypeName("Bar") - .setName("bar") - .setNumber(1))) - .build(); - - try { - Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0]); - assertWithMessage("DescriptorValidationException expected").fail(); - } catch (DescriptorValidationException e) { - assertThat(e).hasMessageThat().contains("Bar"); - assertThat(e).hasMessageThat().contains("is not defined"); + @Test + public void testMessageDescriptorExtensions() throws Exception { + assertThat(TestAllTypes.getDescriptor().isExtendable()).isFalse(); + assertThat(TestAllExtensions.getDescriptor().isExtendable()).isTrue(); + assertThat(TestMultipleExtensionRanges.getDescriptor().isExtendable()).isTrue(); + + assertThat(TestAllTypes.getDescriptor().isExtensionNumber(3)).isFalse(); + assertThat(TestAllExtensions.getDescriptor().isExtensionNumber(3)).isTrue(); + assertThat(TestMultipleExtensionRanges.getDescriptor().isExtensionNumber(42)).isTrue(); + assertThat(TestMultipleExtensionRanges.getDescriptor().isExtensionNumber(43)).isFalse(); + assertThat(TestMultipleExtensionRanges.getDescriptor().isExtensionNumber(4142)).isFalse(); + assertThat(TestMultipleExtensionRanges.getDescriptor().isExtensionNumber(4143)).isTrue(); } - } - @Test - public void testUnknownFieldsAllowed() throws Exception { - FileDescriptorProto fooProto = - FileDescriptorProto.newBuilder() - .setName("foo.proto") - .addDependency("bar.proto") - .addMessageType( - DescriptorProto.newBuilder() - .setName("Foo") - .addField( - FieldDescriptorProto.newBuilder() - .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) - .setTypeName("Bar") - .setName("bar") - .setNumber(1))) - .build(); - FileDescriptor unused = - Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0], true); - } + @Test + public void testReservedFields() { + Descriptor d = TestReservedFields.getDescriptor(); + assertThat(d.isReservedNumber(2)).isTrue(); + assertThat(d.isReservedNumber(8)).isFalse(); + assertThat(d.isReservedNumber(9)).isTrue(); + assertThat(d.isReservedNumber(10)).isTrue(); + assertThat(d.isReservedNumber(11)).isTrue(); + assertThat(d.isReservedNumber(12)).isFalse(); + assertThat(d.isReservedName("foo")).isFalse(); + assertThat(d.isReservedName("bar")).isTrue(); + assertThat(d.isReservedName("baz")).isTrue(); + } - @Test - public void testHiddenDependency() throws Exception { - FileDescriptorProto barProto = - FileDescriptorProto.newBuilder() - .setName("bar.proto") - .addMessageType(DescriptorProto.newBuilder().setName("Bar")) - .build(); - FileDescriptorProto forwardProto = - FileDescriptorProto.newBuilder() - .setName("forward.proto") - .addDependency("bar.proto") - .build(); - FileDescriptorProto fooProto = - FileDescriptorProto.newBuilder() - .setName("foo.proto") - .addDependency("forward.proto") - .addMessageType( - DescriptorProto.newBuilder() - .setName("Foo") - .addField( - FieldDescriptorProto.newBuilder() - .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) - .setTypeName("Bar") - .setName("bar") - .setNumber(1))) - .build(); - FileDescriptor barFile = Descriptors.FileDescriptor.buildFrom(barProto, new FileDescriptor[0]); - FileDescriptor forwardFile = - Descriptors.FileDescriptor.buildFrom(forwardProto, new FileDescriptor[] {barFile}); - - try { + @Test + public void testReservedEnumFields() { + EnumDescriptor d = TestReservedEnumFields.getDescriptor(); + assertThat(d.isReservedNumber(2)).isTrue(); + assertThat(d.isReservedNumber(8)).isFalse(); + assertThat(d.isReservedNumber(9)).isTrue(); + assertThat(d.isReservedNumber(10)).isTrue(); + assertThat(d.isReservedNumber(11)).isTrue(); + assertThat(d.isReservedNumber(12)).isFalse(); + assertThat(d.isReservedName("foo")).isFalse(); + assertThat(d.isReservedName("bar")).isTrue(); + assertThat(d.isReservedName("baz")).isTrue(); + } + + @Test + public void testToString() { + assertThat( + UnittestProto.TestAllTypes.getDescriptor() + .findFieldByNumber(UnittestProto.TestAllTypes.OPTIONAL_UINT64_FIELD_NUMBER) + .toString()) + .isEqualTo("protobuf_unittest.TestAllTypes.optional_uint64"); + } + + @Test + public void testPackedEnumField() throws Exception { + FileDescriptorProto fileDescriptorProto = + FileDescriptorProto.newBuilder() + .setName("foo.proto") + .addEnumType( + EnumDescriptorProto.newBuilder() + .setName("Enum") + .addValue( + EnumValueDescriptorProto.newBuilder().setName("FOO").setNumber(1).build()) + .build()) + .addMessageType( + DescriptorProto.newBuilder() + .setName("Message") + .addField( + FieldDescriptorProto.newBuilder() + .setName("foo") + .setTypeName("Enum") + .setNumber(1) + .setLabel(FieldDescriptorProto.Label.LABEL_REPEATED) + .setOptions( + DescriptorProtos.FieldOptions.newBuilder() + .setPacked(true) + .build()) + .build()) + .build()) + .build(); FileDescriptor unused = - Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[] {forwardFile}); - assertWithMessage("DescriptorValidationException expected").fail(); - } catch (DescriptorValidationException e) { - assertThat(e).hasMessageThat().contains("Bar"); - assertThat(e).hasMessageThat().contains("is not defined"); + Descriptors.FileDescriptor.buildFrom(fileDescriptorProto, new FileDescriptor[0]); } - } - @Test - public void testPublicDependency() throws Exception { - FileDescriptorProto barProto = - FileDescriptorProto.newBuilder() - .setName("bar.proto") - .addMessageType(DescriptorProto.newBuilder().setName("Bar")) - .build(); - FileDescriptorProto forwardProto = - FileDescriptorProto.newBuilder() - .setName("forward.proto") - .addDependency("bar.proto") - .addPublicDependency(0) - .build(); - FileDescriptorProto fooProto = - FileDescriptorProto.newBuilder() - .setName("foo.proto") - .addDependency("forward.proto") - .addMessageType( - DescriptorProto.newBuilder() - .setName("Foo") - .addField( - FieldDescriptorProto.newBuilder() - .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) - .setTypeName("Bar") - .setName("bar") - .setNumber(1))) - .build(); - FileDescriptor barFile = Descriptors.FileDescriptor.buildFrom(barProto, new FileDescriptor[0]); - FileDescriptor forwardFile = - Descriptors.FileDescriptor.buildFrom(forwardProto, new FileDescriptor[] {barFile}); - FileDescriptor unused = - Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[] {forwardFile}); - } + @Test + public void testFieldJsonName() throws Exception { + Descriptor d = TestJsonName.getDescriptor(); + assertThat(d.getFields()).hasSize(7); + assertThat(d.getFields().get(0).getJsonName()).isEqualTo("fieldName1"); + assertThat(d.getFields().get(1).getJsonName()).isEqualTo("fieldName2"); + assertThat(d.getFields().get(2).getJsonName()).isEqualTo("FieldName3"); + assertThat(d.getFields().get(3).getJsonName()).isEqualTo("FieldName4"); + assertThat(d.getFields().get(4).getJsonName()).isEqualTo("FIELDNAME5"); + assertThat(d.getFields().get(5).getJsonName()).isEqualTo("@type"); + assertThat(d.getFields().get(6).getJsonName()).isEqualTo("fieldname7"); + } - /** Tests the translate/crosslink for an example with a more complex namespace referencing. */ - @Test - public void testComplexNamespacePublicDependency() throws Exception { - FileDescriptorProto fooProto = - FileDescriptorProto.newBuilder() - .setName("bar.proto") - .setPackage("a.b.c.d.bar.shared") - .addEnumType( - EnumDescriptorProto.newBuilder() - .setName("MyEnum") - .addValue(EnumValueDescriptorProto.newBuilder().setName("BLAH").setNumber(1))) - .build(); - FileDescriptorProto barProto = - FileDescriptorProto.newBuilder() - .setName("foo.proto") - .addDependency("bar.proto") - .setPackage("a.b.c.d.foo.shared") - .addMessageType( - DescriptorProto.newBuilder() - .setName("MyMessage") - .addField( - FieldDescriptorProto.newBuilder() - .setLabel(FieldDescriptorProto.Label.LABEL_REPEATED) - .setTypeName("bar.shared.MyEnum") - .setName("MyField") - .setNumber(1))) - .build(); - // translate and crosslink - FileDescriptor fooFile = Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0]); - FileDescriptor barFile = - Descriptors.FileDescriptor.buildFrom(barProto, new FileDescriptor[] {fooFile}); - // verify resulting descriptors - assertThat(barFile).isNotNull(); - List msglist = barFile.getMessageTypes(); - assertThat(msglist).isNotNull(); - assertThat(msglist).hasSize(1); - Descriptor desc = msglist.get(0); - if (desc.getName().equals("MyMessage")) { - assertThat(desc.getFields()).isNotNull(); - List fieldlist = desc.getFields(); - assertThat(fieldlist).isNotNull(); - assertThat(fieldlist).hasSize(1); - FieldDescriptor field = fieldlist.get(0); - assertThat(field.getType()).isSameInstanceAs(FieldDescriptor.Type.ENUM); - assertThat(field.getEnumType().getName().equals("MyEnum")).isTrue(); - assertThat(field.getEnumType().getFile().getName().equals("bar.proto")).isTrue(); - assertThat(field.getEnumType().getFile().getPackage().equals("a.b.c.d.bar.shared")).isTrue(); + @Test + public void testExtensionRenamesKeywords() { + assertThat(NonNestedExtension.if_).isInstanceOf(GeneratedMessage.GeneratedExtension.class); + assertThat(NestedExtension.MyNestedExtension.default_) + .isInstanceOf(GeneratedMessage.GeneratedExtension.class); + + NonNestedExtension.MessageToBeExtended msg = + NonNestedExtension.MessageToBeExtended.newBuilder() + .setExtension(NonNestedExtension.if_, "!fi") + .build(); + assertThat(msg.getExtension(NonNestedExtension.if_)).isEqualTo("!fi"); + + msg = + NonNestedExtension.MessageToBeExtended.newBuilder() + .setExtension(NestedExtension.MyNestedExtension.default_, 8) + .build(); + assertThat(msg.getExtension(NestedExtension.MyNestedExtension.default_).intValue()) + .isEqualTo(8); } - } - @Test - public void testOneofDescriptor() throws Exception { - Descriptor messageType = TestAllTypes.getDescriptor(); - FieldDescriptor field = messageType.findFieldByName("oneof_nested_message"); - OneofDescriptor oneofDescriptor = field.getContainingOneof(); - assertThat(oneofDescriptor).isNotNull(); - assertThat(messageType.getOneofs().get(0)).isSameInstanceAs(oneofDescriptor); - assertThat(oneofDescriptor.getName()).isEqualTo("oneof_field"); + @Test + public void testDefaultDescriptorExtensionRange() throws Exception { + assertThat(new Descriptor("default").isExtensionNumber(1)).isTrue(); + } - assertThat(oneofDescriptor.getFieldCount()).isEqualTo(7); - assertThat(field).isSameInstanceAs(oneofDescriptor.getField(1)); + @Test + public void testGetOptionsStripsFeatures() { + FieldDescriptor field = + UnittestLegacyFeatures.TestEditionsMessage.getDescriptor() + .findFieldByName("required_field"); + assertThat(field.getOptions().hasFeatures()).isFalse(); + } - assertThat(oneofDescriptor.getFields()).hasSize(7); - assertThat(field).isEqualTo(oneofDescriptor.getFields().get(1)); - } + @Test + public void testLegacyRequiredTransform() { + Descriptor descriptor = UnittestLegacyFeatures.TestEditionsMessage.getDescriptor(); + assertThat(descriptor.findFieldByName("required_field").isRequired()).isTrue(); + } - @Test - public void testMessageDescriptorExtensions() throws Exception { - assertThat(TestAllTypes.getDescriptor().isExtendable()).isFalse(); - assertThat(TestAllExtensions.getDescriptor().isExtendable()).isTrue(); - assertThat(TestMultipleExtensionRanges.getDescriptor().isExtendable()).isTrue(); - - assertThat(TestAllTypes.getDescriptor().isExtensionNumber(3)).isFalse(); - assertThat(TestAllExtensions.getDescriptor().isExtensionNumber(3)).isTrue(); - assertThat(TestMultipleExtensionRanges.getDescriptor().isExtensionNumber(42)).isTrue(); - assertThat(TestMultipleExtensionRanges.getDescriptor().isExtensionNumber(43)).isFalse(); - assertThat(TestMultipleExtensionRanges.getDescriptor().isExtensionNumber(4142)).isFalse(); - assertThat(TestMultipleExtensionRanges.getDescriptor().isExtensionNumber(4143)).isTrue(); - } + @Test + public void testLegacyGroupTransform() { + Descriptor descriptor = UnittestLegacyFeatures.TestEditionsMessage.getDescriptor(); + assertThat(descriptor.findFieldByName("delimited_field").getType()) + .isEqualTo(FieldDescriptor.Type.GROUP); + } - @Test - public void testReservedFields() { - Descriptor d = TestReservedFields.getDescriptor(); - assertThat(d.isReservedNumber(2)).isTrue(); - assertThat(d.isReservedNumber(8)).isFalse(); - assertThat(d.isReservedNumber(9)).isTrue(); - assertThat(d.isReservedNumber(10)).isTrue(); - assertThat(d.isReservedNumber(11)).isTrue(); - assertThat(d.isReservedNumber(12)).isFalse(); - assertThat(d.isReservedName("foo")).isFalse(); - assertThat(d.isReservedName("bar")).isTrue(); - assertThat(d.isReservedName("baz")).isTrue(); - } + @Test + public void testLegacyInferRequired() throws Exception { + FileDescriptor file = + FileDescriptor.buildFrom( + FileDescriptorProto.newBuilder() + .setName("some/filename/some.proto") + .setSyntax("proto2") + .addMessageType( + DescriptorProto.newBuilder() + .setName("Foo") + .addField( + FieldDescriptorProto.newBuilder() + .setName("a") + .setNumber(1) + .setType(FieldDescriptorProto.Type.TYPE_INT32) + .setLabel(FieldDescriptorProto.Label.LABEL_REQUIRED) + .build()) + .build()) + .build(), + new FileDescriptor[0]); + FieldDescriptor field = file.findMessageTypeByName("Foo").findFieldByName("a"); + assertThat(field.features.getFieldPresence()) + .isEqualTo(DescriptorProtos.FeatureSet.FieldPresence.LEGACY_REQUIRED); + } - @Test - public void testReservedEnumFields() { - EnumDescriptor d = TestReservedEnumFields.getDescriptor(); - assertThat(d.isReservedNumber(2)).isTrue(); - assertThat(d.isReservedNumber(8)).isFalse(); - assertThat(d.isReservedNumber(9)).isTrue(); - assertThat(d.isReservedNumber(10)).isTrue(); - assertThat(d.isReservedNumber(11)).isTrue(); - assertThat(d.isReservedNumber(12)).isFalse(); - assertThat(d.isReservedName("foo")).isFalse(); - assertThat(d.isReservedName("bar")).isTrue(); - assertThat(d.isReservedName("baz")).isTrue(); - } + @Test + public void testLegacyInferGroup() throws Exception { + FileDescriptor file = + FileDescriptor.buildFrom( + FileDescriptorProto.newBuilder() + .setName("some/filename/some.proto") + .setSyntax("proto2") + .addMessageType( + DescriptorProto.newBuilder() + .setName("Foo") + .addNestedType( + DescriptorProto.newBuilder().setName("OptionalGroup").build()) + .addField( + FieldDescriptorProto.newBuilder() + .setName("optionalgroup") + .setNumber(1) + .setType(FieldDescriptorProto.Type.TYPE_GROUP) + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setTypeName("Foo.OptionalGroup") + .build()) + .build()) + .build(), + new FileDescriptor[0]); + FieldDescriptor field = file.findMessageTypeByName("Foo").findFieldByName("optionalgroup"); + assertThat(field.features.getMessageEncoding()) + .isEqualTo(DescriptorProtos.FeatureSet.MessageEncoding.DELIMITED); + } - @Test - public void testToString() { - assertThat( - UnittestProto.TestAllTypes.getDescriptor() - .findFieldByNumber(UnittestProto.TestAllTypes.OPTIONAL_UINT64_FIELD_NUMBER) - .toString()) - .isEqualTo("protobuf_unittest.TestAllTypes.optional_uint64"); - } + @Test + public void testLegacyInferProto2Packed() throws Exception { + FileDescriptor file = + FileDescriptor.buildFrom( + FileDescriptorProto.newBuilder() + .setName("some/filename/some.proto") + .setSyntax("proto2") + .addMessageType( + DescriptorProto.newBuilder() + .setName("Foo") + .addField( + FieldDescriptorProto.newBuilder() + .setName("a") + .setNumber(1) + .setLabel(FieldDescriptorProto.Label.LABEL_REPEATED) + .setType(FieldDescriptorProto.Type.TYPE_INT32) + .setOptions(FieldOptions.newBuilder().setPacked(true).build()) + .build()) + .build()) + .build(), + new FileDescriptor[0]); + FieldDescriptor field = file.findMessageTypeByName("Foo").findFieldByName("a"); + assertThat(field.features.getRepeatedFieldEncoding()) + .isEqualTo(DescriptorProtos.FeatureSet.RepeatedFieldEncoding.PACKED); + } - @Test - public void testPackedEnumField() throws Exception { - FileDescriptorProto fileDescriptorProto = - FileDescriptorProto.newBuilder() - .setName("foo.proto") - .addEnumType( - EnumDescriptorProto.newBuilder() - .setName("Enum") - .addValue( - EnumValueDescriptorProto.newBuilder().setName("FOO").setNumber(1).build()) - .build()) - .addMessageType( - DescriptorProto.newBuilder() - .setName("Message") - .addField( - FieldDescriptorProto.newBuilder() - .setName("foo") - .setTypeName("Enum") - .setNumber(1) - .setLabel(FieldDescriptorProto.Label.LABEL_REPEATED) - .setOptions( - DescriptorProtos.FieldOptions.newBuilder().setPacked(true).build()) - .build()) - .build()) - .build(); - FileDescriptor unused = - Descriptors.FileDescriptor.buildFrom(fileDescriptorProto, new FileDescriptor[0]); - } + @Test + public void testLegacyInferProto3Expanded() throws Exception { + FileDescriptor file = + FileDescriptor.buildFrom( + FileDescriptorProto.newBuilder() + .setName("some/filename/some.proto") + .setSyntax("proto3") + .addMessageType( + DescriptorProto.newBuilder() + .setName("Foo") + .addField( + FieldDescriptorProto.newBuilder() + .setName("a") + .setNumber(1) + .setType(FieldDescriptorProto.Type.TYPE_INT32) + .setLabel(FieldDescriptorProto.Label.LABEL_REPEATED) + .setOptions(FieldOptions.newBuilder().setPacked(false).build()) + .build()) + .build()) + .build(), + new FileDescriptor[0]); + FieldDescriptor field = file.findMessageTypeByName("Foo").findFieldByName("a"); + assertThat(field.features.getRepeatedFieldEncoding()) + .isEqualTo(DescriptorProtos.FeatureSet.RepeatedFieldEncoding.EXPANDED); + } - @Test - public void testFieldJsonName() throws Exception { - Descriptor d = TestJsonName.getDescriptor(); - assertThat(d.getFields()).hasSize(7); - assertThat(d.getFields().get(0).getJsonName()).isEqualTo("fieldName1"); - assertThat(d.getFields().get(1).getJsonName()).isEqualTo("fieldName2"); - assertThat(d.getFields().get(2).getJsonName()).isEqualTo("FieldName3"); - assertThat(d.getFields().get(3).getJsonName()).isEqualTo("FieldName4"); - assertThat(d.getFields().get(4).getJsonName()).isEqualTo("FIELDNAME5"); - assertThat(d.getFields().get(5).getJsonName()).isEqualTo("@type"); - assertThat(d.getFields().get(6).getJsonName()).isEqualTo("fieldname7"); - } + @Test + public void testLegacyInferProto2Utf8Validation() throws Exception { + FileDescriptor file = + FileDescriptor.buildFrom( + FileDescriptorProto.newBuilder() + .setName("some/filename/some.proto") + .setPackage("protobuf_unittest") + .setSyntax("proto2") + .setOptions(FileOptions.newBuilder().setJavaStringCheckUtf8(true)) + .build(), + new FileDescriptor[0]); + assertThat(file.features.getExtension(JavaFeaturesProto.java_).getUtf8Validation()) + .isEqualTo(JavaFeaturesProto.JavaFeatures.Utf8Validation.VERIFY); + } + + @Test + public void testProto2Defaults() throws Exception { + FileDescriptor proto2File = + FileDescriptor.buildFrom( + FileDescriptorProto.newBuilder() + .setName("some/filename/some.proto") + .setPackage("protobuf_unittest") + .setSyntax("proto2") + .build(), + new FileDescriptor[0]); + DescriptorProtos.FeatureSet features = proto2File.features; + assertThat(features.getFieldPresence()) + .isEqualTo(DescriptorProtos.FeatureSet.FieldPresence.EXPLICIT); + assertThat(features.getEnumType()).isEqualTo(DescriptorProtos.FeatureSet.EnumType.CLOSED); + assertThat(features.getRepeatedFieldEncoding()) + .isEqualTo(DescriptorProtos.FeatureSet.RepeatedFieldEncoding.EXPANDED); + assertThat(features.getUtf8Validation()) + .isEqualTo(DescriptorProtos.FeatureSet.Utf8Validation.NONE); + assertThat(features.getMessageEncoding()) + .isEqualTo(DescriptorProtos.FeatureSet.MessageEncoding.LENGTH_PREFIXED); + assertThat(features.getJsonFormat()) + .isEqualTo(DescriptorProtos.FeatureSet.JsonFormat.LEGACY_BEST_EFFORT); + + assertThat(features.getExtension(JavaFeaturesProto.java_).getLegacyClosedEnum()).isTrue(); + assertThat(features.getExtension(JavaFeaturesProto.java_).getUtf8Validation()) + .isEqualTo(JavaFeaturesProto.JavaFeatures.Utf8Validation.DEFAULT); + } + + @Test + public void testProto3Defaults() throws Exception { + FileDescriptor proto3File = + FileDescriptor.buildFrom( + FileDescriptorProto.newBuilder() + .setName("some/filename/some.proto") + .setPackage("proto3_unittest") + .setSyntax("proto3") + .build(), + new FileDescriptor[0]); + DescriptorProtos.FeatureSet features = proto3File.features; + assertThat(features.getFieldPresence()) + .isEqualTo(DescriptorProtos.FeatureSet.FieldPresence.IMPLICIT); + assertThat(features.getEnumType()).isEqualTo(DescriptorProtos.FeatureSet.EnumType.OPEN); + assertThat(features.getRepeatedFieldEncoding()) + .isEqualTo(DescriptorProtos.FeatureSet.RepeatedFieldEncoding.PACKED); + assertThat(features.getUtf8Validation()) + .isEqualTo(DescriptorProtos.FeatureSet.Utf8Validation.VERIFY); + assertThat(features.getMessageEncoding()) + .isEqualTo(DescriptorProtos.FeatureSet.MessageEncoding.LENGTH_PREFIXED); + + assertThat(features.getExtension(JavaFeaturesProto.java_).getLegacyClosedEnum()).isFalse(); + assertThat(features.getExtension(JavaFeaturesProto.java_).getUtf8Validation()) + .isEqualTo(JavaFeaturesProto.JavaFeatures.Utf8Validation.DEFAULT); + } + + @Test + public void testProto3ExtensionPresence() { + FileDescriptorProto.Builder file = FileDescriptorProto.newBuilder(); + + assertThat(file.getOptions().hasExtension(Proto3FileExtensions.singularInt)).isFalse(); + assertThat(file.getOptions().getExtension(Proto3FileExtensions.singularInt)).isEqualTo(0); + + file.getOptionsBuilder().setExtension(Proto3FileExtensions.singularInt, 1); - @Test - public void testExtensionRenamesKeywords() { - assertThat(NonNestedExtension.if_).isInstanceOf(GeneratedMessage.GeneratedExtension.class); - assertThat(NestedExtension.MyNestedExtension.default_) - .isInstanceOf(GeneratedMessage.GeneratedExtension.class); - - NonNestedExtension.MessageToBeExtended msg = - NonNestedExtension.MessageToBeExtended.newBuilder() - .setExtension(NonNestedExtension.if_, "!fi") - .build(); - assertThat(msg.getExtension(NonNestedExtension.if_)).isEqualTo("!fi"); - - msg = - NonNestedExtension.MessageToBeExtended.newBuilder() - .setExtension(NestedExtension.MyNestedExtension.default_, 8) - .build(); - assertThat(msg.getExtension(NestedExtension.MyNestedExtension.default_).intValue()) - .isEqualTo(8); + assertThat(file.getOptions().hasExtension(Proto3FileExtensions.singularInt)).isTrue(); + assertThat(file.getOptions().getExtension(Proto3FileExtensions.singularInt)).isEqualTo(1); + } + + @Test + public void testProto3ExtensionHasPresence() { + assertThat(Proto3FileExtensions.singularInt.getDescriptor().hasPresence()).isTrue(); + assertThat(Proto3FileExtensions.repeatedInt.getDescriptor().hasPresence()).isFalse(); + } } - @Test - public void testDefaultDescriptorExtensionRange() throws Exception { - assertThat(new Descriptor("default").isExtensionNumber(1)).isTrue(); + public static class FeatureInheritanceTest { + FileDescriptorProto.Builder fileProto; + FieldDescriptorProto.Builder topExtensionProto; + EnumDescriptorProto.Builder topEnumProto; + EnumValueDescriptorProto.Builder enumValueProto; + DescriptorProto.Builder topMessageProto; + FieldDescriptorProto.Builder fieldProto; + FieldDescriptorProto.Builder nestedExtensionProto; + DescriptorProto.Builder nestedMessageProto; + EnumDescriptorProto.Builder nestedEnumProto; + OneofDescriptorProto.Builder oneofProto; + FieldDescriptorProto.Builder oneofFieldProto; + ServiceDescriptorProto.Builder serviceProto; + MethodDescriptorProto.Builder methodProto; + + @Before + public void setUp() { + FeatureSetDefaults.Builder defaults = Descriptors.getJavaEditionDefaults().toBuilder(); + for (FeatureSetEditionDefault.Builder editionDefaults : defaults.getDefaultsBuilderList()) { + setTestFeature(editionDefaults.getOverridableFeaturesBuilder(), 1); + } + Descriptors.setTestJavaEditionDefaults(defaults.build()); + + this.fileProto = + DescriptorProtos.FileDescriptorProto.newBuilder() + .setName("some/filename/some.proto") + .setPackage("protobuf_unittest") + .setEdition(DescriptorProtos.Edition.EDITION_2023) + .setSyntax("editions"); + + this.topExtensionProto = + this.fileProto + .addExtensionBuilder() + .setName("top_extension") + .setNumber(10) + .setType(FieldDescriptorProto.Type.TYPE_INT32) + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setExtendee(".protobuf_unittest.TopMessage"); + + this.topEnumProto = this.fileProto.addEnumTypeBuilder().setName("TopEnum"); + this.enumValueProto = this.topEnumProto.addValueBuilder().setName("TOP_VALUE").setNumber(0); + + this.topMessageProto = + this.fileProto + .addMessageTypeBuilder() + .setName("TopMessage") + .addExtensionRange(ExtensionRange.newBuilder().setStart(10).setEnd(20).build()); + + this.fieldProto = + this.topMessageProto + .addFieldBuilder() + .setName("field") + .setNumber(1) + .setType(FieldDescriptorProto.Type.TYPE_INT32) + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL); + this.nestedExtensionProto = + this.topMessageProto + .addExtensionBuilder() + .setName("nested_extension") + .setNumber(11) + .setType(FieldDescriptorProto.Type.TYPE_INT32) + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setExtendee(".protobuf_unittest.TopMessage"); + this.nestedMessageProto = + this.topMessageProto.addNestedTypeBuilder().setName("NestedMessage"); + this.nestedEnumProto = + this.topMessageProto + .addEnumTypeBuilder() + .setName("NestedEnum") + .addValue(EnumValueDescriptorProto.newBuilder().setName("NESTED_VALUE").setNumber(0)); + this.oneofProto = this.topMessageProto.addOneofDeclBuilder().setName("Oneof"); + this.oneofFieldProto = + this.topMessageProto + .addFieldBuilder() + .setName("oneof_field") + .setNumber(2) + .setType(FieldDescriptorProto.Type.TYPE_INT32) + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setOneofIndex(0); + this.serviceProto = this.fileProto.addServiceBuilder().setName("TestService"); + this.methodProto = + this.serviceProto + .addMethodBuilder() + .setName("CallMethod") + .setInputType(".protobuf_unittest.TopMessage") + .setOutputType(".protobuf_unittest.TopMessage"); + } + + void setTestFeature(DescriptorProtos.FeatureSet.Builder features, int value) { + features.setExtension( + UnittestFeatures.test, + features.getExtension(UnittestFeatures.test).toBuilder() + .setMultipleFeature(UnittestFeatures.EnumFeature.forNumber(value)) + .build()); + } + + int getTestFeature(DescriptorProtos.FeatureSet features) { + return features.getExtension(UnittestFeatures.test).getMultipleFeature().getNumber(); + } + + FileDescriptor buildFrom(FileDescriptorProto fileProto) throws Exception { + return FileDescriptor.buildFrom(fileProto, new FileDescriptor[0]); + } + + @Test + public void testFileDefaults() throws Exception { + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.features)).isEqualTo(1); + } + + @Test + public void testFileOverrides() throws Exception { + setTestFeature(fileProto.getOptionsBuilder().getFeaturesBuilder(), 3); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.features)).isEqualTo(3); + } + + @Test + public void testFileMessageInherit() throws Exception { + setTestFeature(fileProto.getOptionsBuilder().getFeaturesBuilder(), 3); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getMessageTypes().get(0).features)).isEqualTo(3); + } + + @Test + public void testFileMessageOverride() throws Exception { + setTestFeature(fileProto.getOptionsBuilder().getFeaturesBuilder(), 3); + setTestFeature(topMessageProto.getOptionsBuilder().getFeaturesBuilder(), 5); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getMessageTypes().get(0).features)).isEqualTo(5); + } + + @Test + public void testFileEnumInherit() throws Exception { + setTestFeature(fileProto.getOptionsBuilder().getFeaturesBuilder(), 3); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getEnumTypes().get(0).features)).isEqualTo(3); + } + + @Test + public void testFileEnumOverride() throws Exception { + setTestFeature(fileProto.getOptionsBuilder().getFeaturesBuilder(), 3); + setTestFeature(topEnumProto.getOptionsBuilder().getFeaturesBuilder(), 5); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getEnumTypes().get(0).features)).isEqualTo(5); + } + + @Test + public void testFileExtensionInherit() throws Exception { + setTestFeature(fileProto.getOptionsBuilder().getFeaturesBuilder(), 3); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getExtensions().get(0).features)).isEqualTo(3); + } + + @Test + public void testFileExtensionOverride() throws Exception { + setTestFeature(fileProto.getOptionsBuilder().getFeaturesBuilder(), 3); + setTestFeature(topExtensionProto.getOptionsBuilder().getFeaturesBuilder(), 5); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getExtensions().get(0).features)).isEqualTo(5); + } + + @Test + public void testFileServiceInherit() throws Exception { + setTestFeature(fileProto.getOptionsBuilder().getFeaturesBuilder(), 3); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getServices().get(0).features)).isEqualTo(3); + } + + @Test + public void testFileServiceOverride() throws Exception { + setTestFeature(fileProto.getOptionsBuilder().getFeaturesBuilder(), 3); + setTestFeature(serviceProto.getOptionsBuilder().getFeaturesBuilder(), 5); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getServices().get(0).features)).isEqualTo(5); + } + + @Test + public void testMessageFieldInherit() throws Exception { + setTestFeature(topMessageProto.getOptionsBuilder().getFeaturesBuilder(), 3); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getMessageTypes().get(0).getFields().get(0).features)) + .isEqualTo(3); + } + + @Test + public void testMessageFieldOverride() throws Exception { + setTestFeature(topMessageProto.getOptionsBuilder().getFeaturesBuilder(), 3); + setTestFeature(fieldProto.getOptionsBuilder().getFeaturesBuilder(), 5); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getMessageTypes().get(0).getFields().get(0).features)) + .isEqualTo(5); + } + + @Test + public void testMessageEnumInherit() throws Exception { + setTestFeature(topMessageProto.getOptionsBuilder().getFeaturesBuilder(), 3); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getMessageTypes().get(0).getEnumTypes().get(0).features)) + .isEqualTo(3); + } + + @Test + public void testMessageEnumOverride() throws Exception { + setTestFeature(topMessageProto.getOptionsBuilder().getFeaturesBuilder(), 3); + setTestFeature(nestedEnumProto.getOptionsBuilder().getFeaturesBuilder(), 5); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getMessageTypes().get(0).getEnumTypes().get(0).features)) + .isEqualTo(5); + } + + @Test + public void testMessageMessageInherit() throws Exception { + setTestFeature(topMessageProto.getOptionsBuilder().getFeaturesBuilder(), 3); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat( + getTestFeature(descriptor.getMessageTypes().get(0).getNestedTypes().get(0).features)) + .isEqualTo(3); + } + + @Test + public void testMessageMessageOverride() throws Exception { + setTestFeature(topMessageProto.getOptionsBuilder().getFeaturesBuilder(), 3); + setTestFeature(nestedMessageProto.getOptionsBuilder().getFeaturesBuilder(), 5); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat( + getTestFeature(descriptor.getMessageTypes().get(0).getNestedTypes().get(0).features)) + .isEqualTo(5); + } + + @Test + public void testMessageExtensionInherit() throws Exception { + setTestFeature(topMessageProto.getOptionsBuilder().getFeaturesBuilder(), 3); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat( + getTestFeature(descriptor.getMessageTypes().get(0).getExtensions().get(0).features)) + .isEqualTo(3); + } + + @Test + public void testMessageExtensionOverride() throws Exception { + setTestFeature(topMessageProto.getOptionsBuilder().getFeaturesBuilder(), 3); + setTestFeature(nestedExtensionProto.getOptionsBuilder().getFeaturesBuilder(), 5); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat( + getTestFeature(descriptor.getMessageTypes().get(0).getExtensions().get(0).features)) + .isEqualTo(5); + } + + @Test + public void testMessageOneofInherit() throws Exception { + setTestFeature(topMessageProto.getOptionsBuilder().getFeaturesBuilder(), 3); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getMessageTypes().get(0).getOneofs().get(0).features)) + .isEqualTo(3); + } + + @Test + public void testMessageOneofOverride() throws Exception { + setTestFeature(topMessageProto.getOptionsBuilder().getFeaturesBuilder(), 3); + setTestFeature(oneofProto.getOptionsBuilder().getFeaturesBuilder(), 5); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getMessageTypes().get(0).getOneofs().get(0).features)) + .isEqualTo(5); + } + + @Test + public void testOneofFieldInherit() throws Exception { + setTestFeature(oneofProto.getOptionsBuilder().getFeaturesBuilder(), 3); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat( + getTestFeature( + descriptor + .getMessageTypes() + .get(0) + .getOneofs() + .get(0) + .getFields() + .get(0) + .features)) + .isEqualTo(3); + } + + @Test + public void testOneofFieldOverride() throws Exception { + setTestFeature(oneofProto.getOptionsBuilder().getFeaturesBuilder(), 3); + setTestFeature(oneofFieldProto.getOptionsBuilder().getFeaturesBuilder(), 5); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat( + getTestFeature( + descriptor + .getMessageTypes() + .get(0) + .getOneofs() + .get(0) + .getFields() + .get(0) + .features)) + .isEqualTo(5); + } + + @Test + public void testEnumValueInherit() throws Exception { + setTestFeature(topEnumProto.getOptionsBuilder().getFeaturesBuilder(), 3); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getEnumTypes().get(0).getValues().get(0).features)) + .isEqualTo(3); + } + + @Test + public void testEnumValueOverride() throws Exception { + setTestFeature(topEnumProto.getOptionsBuilder().getFeaturesBuilder(), 3); + setTestFeature(enumValueProto.getOptionsBuilder().getFeaturesBuilder(), 5); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getEnumTypes().get(0).getValues().get(0).features)) + .isEqualTo(5); + } + + @Test + public void testServiceMethodInherit() throws Exception { + setTestFeature(serviceProto.getOptionsBuilder().getFeaturesBuilder(), 3); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getServices().get(0).getMethods().get(0).features)) + .isEqualTo(3); + } + + @Test + public void testServiceMethodOverride() throws Exception { + setTestFeature(serviceProto.getOptionsBuilder().getFeaturesBuilder(), 3); + setTestFeature(methodProto.getOptionsBuilder().getFeaturesBuilder(), 5); + FileDescriptor descriptor = buildFrom(fileProto.build()); + assertThat(getTestFeature(descriptor.getServices().get(0).getMethods().get(0).features)) + .isEqualTo(5); + } } } diff --git a/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java b/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java index dbe3072fb..920693947 100644 --- a/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java +++ b/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java @@ -9,6 +9,7 @@ package com.google.protobuf; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static org.junit.Assert.assertThrows; import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.FieldDescriptor; @@ -50,6 +51,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Map; import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; @@ -81,7 +83,7 @@ public class GeneratedMessageTest { @After public void tearDown() { - GeneratedMessageV3.setAlwaysUseFieldBuildersForTesting(false); + GeneratedMessage.setAlwaysUseFieldBuildersForTesting(false); } @Test @@ -213,28 +215,28 @@ public class GeneratedMessageTest { @Test public void testGetExtensionFieldOutOfBound() { TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); - try { - builder.getRepeatedField(UnittestProto.repeatedNestedMessageExtension.getDescriptor(), 0); - assertWithMessage("Expected IndexOutOfBoundsException to be thrown").fail(); - } catch (IndexOutOfBoundsException expected) { - } - try { - builder.getExtension(UnittestProto.repeatedNestedMessageExtension, 0); - assertWithMessage("Expected IndexOutOfBoundsException to be thrown").fail(); - } catch (IndexOutOfBoundsException expected) { - } + + assertThrows( + "Expected IndexOutOfBoundsException to be thrown", + IndexOutOfBoundsException.class, + () -> + builder.getRepeatedField( + UnittestProto.repeatedNestedMessageExtension.getDescriptor(), 0)); + assertThrows( + "Expected IndexOutOfBoundsException to be thrown", + IndexOutOfBoundsException.class, + () -> builder.getExtension(UnittestProto.repeatedNestedMessageExtension, 0)); TestAllExtensions extensionsMessage = builder.build(); - try { - extensionsMessage.getRepeatedField( - UnittestProto.repeatedNestedMessageExtension.getDescriptor(), 0); - assertWithMessage("Expected IndexOutOfBoundsException to be thrown").fail(); - } catch (IndexOutOfBoundsException expected) { - } - try { - extensionsMessage.getExtension(UnittestProto.repeatedNestedMessageExtension, 0); - assertWithMessage("Expected IndexOutOfBoundsException to be thrown").fail(); - } catch (IndexOutOfBoundsException expected) { - } + assertThrows( + "Expected IndexOutOfBoundsException to be thrown", + IndexOutOfBoundsException.class, + () -> + extensionsMessage.getRepeatedField( + UnittestProto.repeatedNestedMessageExtension.getDescriptor(), 0)); + assertThrows( + "Expected IndexOutOfBoundsException to be thrown", + IndexOutOfBoundsException.class, + () -> extensionsMessage.getExtension(UnittestProto.repeatedNestedMessageExtension, 0)); } @Test @@ -365,78 +367,51 @@ public class GeneratedMessageTest { if (list == Collections.emptyList()) { // OKAY -- Need to check this b/c EmptyList allows you to call clear. } else { - try { - list.clear(); - assertWithMessage("List wasn't immutable").fail(); - } catch (UnsupportedOperationException e) { - // good - } + assertThrows( + "List wasn't immutable", UnsupportedOperationException.class, () -> list.clear()); } } @Test public void testSettersRejectNull() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); - try { - builder.setOptionalString(null); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } - try { - builder.setOptionalBytes(null); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } - try { - builder.setOptionalNestedMessage((TestAllTypes.NestedMessage) null); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } - try { - builder.setOptionalNestedMessage((TestAllTypes.NestedMessage.Builder) null); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } - try { - builder.setOptionalNestedEnum(null); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } - try { - builder.addRepeatedString(null); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } - try { - builder.addRepeatedBytes(null); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } - try { - builder.addRepeatedNestedMessage((TestAllTypes.NestedMessage) null); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } - try { - builder.addRepeatedNestedMessage((TestAllTypes.NestedMessage.Builder) null); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } - try { - builder.addRepeatedNestedEnum(null); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } + + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.setOptionalString(null)); + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.setOptionalNestedMessage((TestAllTypes.NestedMessage) null)); + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.setOptionalNestedMessage((TestAllTypes.NestedMessage.Builder) null)); + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.setOptionalNestedEnum(null)); + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.addRepeatedString(null)); + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.addRepeatedBytes(null)); + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.addRepeatedNestedMessage((TestAllTypes.NestedMessage) null)); + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.addRepeatedNestedMessage((TestAllTypes.NestedMessage.Builder) null)); + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.addRepeatedNestedEnum(null)); } @Test @@ -454,45 +429,35 @@ public class GeneratedMessageTest { builder.addRepeatedString("one"); builder.addRepeatedString("two"); - try { - builder.setRepeatedString(1, null); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.setRepeatedString(1, null)); builder.addRepeatedBytes(TestUtil.toBytes("one")); builder.addRepeatedBytes(TestUtil.toBytes("two")); - try { - builder.setRepeatedBytes(1, null); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.setRepeatedBytes(1, null)); builder.addRepeatedNestedMessage(TestAllTypes.NestedMessage.newBuilder().setBb(218).build()); builder.addRepeatedNestedMessage(TestAllTypes.NestedMessage.newBuilder().setBb(456).build()); - try { - builder.setRepeatedNestedMessage(1, (TestAllTypes.NestedMessage) null); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } - try { - builder.setRepeatedNestedMessage(1, (TestAllTypes.NestedMessage.Builder) null); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.setRepeatedNestedMessage(1, (TestAllTypes.NestedMessage) null)); + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.setRepeatedNestedMessage(1, (TestAllTypes.NestedMessage.Builder) null)); builder.addRepeatedNestedEnum(TestAllTypes.NestedEnum.FOO); builder.addRepeatedNestedEnum(TestAllTypes.NestedEnum.BAR); - try { - builder.setRepeatedNestedEnum(1, null); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.setRepeatedNestedEnum(1, null)); } @Test @@ -518,33 +483,24 @@ public class GeneratedMessageTest { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); ForeignMessage foreignMessage = ForeignMessage.newBuilder().setC(12).build(); - try { - builder.addAllRepeatedForeignMessage(Arrays.asList(foreignMessage, (ForeignMessage) null)); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } - - try { - builder.addAllRepeatedForeignEnum(Arrays.asList(ForeignEnum.FOREIGN_BAZ, null)); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } - - try { - builder.addAllRepeatedString(Arrays.asList("one", null)); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } - - try { - builder.addAllRepeatedBytes(Arrays.asList(TestUtil.toBytes("one"), null)); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> + builder.addAllRepeatedForeignMessage( + Arrays.asList(foreignMessage, (ForeignMessage) null))); + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.addAllRepeatedForeignEnum(Arrays.asList(ForeignEnum.FOREIGN_BAZ, null))); + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.addAllRepeatedString(Arrays.asList("one", null))); + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.addAllRepeatedBytes(Arrays.asList(TestUtil.toBytes("one"), null))); } @Test @@ -570,23 +526,19 @@ public class GeneratedMessageTest { assertThat(builder.getRepeatedString(1)).isEqualTo("two"); assertThat(builder.getRepeatedString(2)).isEqualTo("three"); - try { - builder.addAllRepeatedString(stringIterable); - assertWithMessage("Exception was not thrown").fail(); - } catch (IllegalStateException e) { - // We expect this exception. - } + assertThrows( + "Exception was not thrown", + IllegalStateException.class, + () -> builder.addAllRepeatedString(stringIterable)); } @Test public void testMergeFromOtherRejectsNull() throws Exception { - try { - TestAllTypes.Builder builder = TestAllTypes.newBuilder(); - builder.mergeFrom((TestAllTypes) null); - assertWithMessage("Exception was not thrown").fail(); - } catch (NullPointerException e) { - // We expect this exception. - } + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + assertThrows( + "Exception was not thrown", + NullPointerException.class, + () -> builder.mergeFrom((TestAllTypes) null)); } @Test @@ -781,13 +733,11 @@ public class GeneratedMessageTest { @Test public void testGetBuilderForNonMessageExtensionField() { TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); - try { - // This should throw an exception because the extension field is not a message. - builder.newBuilderForField(UnittestProto.optionalInt32Extension.getDescriptor()); - assertWithMessage("Exception was not thrown").fail(); - } catch (UnsupportedOperationException e) { - // This exception is expected. - } + // This should throw an exception because the extension field is not a message. + assertThrows( + "Exception was not thrown", + UnsupportedOperationException.class, + () -> builder.newBuilderForField(UnittestProto.optionalInt32Extension.getDescriptor())); } @Test @@ -1153,7 +1103,7 @@ public class GeneratedMessageTest { @Test public void testInvalidations() throws Exception { - GeneratedMessageV3.setAlwaysUseFieldBuildersForTesting(true); + GeneratedMessage.setAlwaysUseFieldBuildersForTesting(true); TestAllTypes.NestedMessage nestedMessage1 = TestAllTypes.NestedMessage.newBuilder().build(); TestAllTypes.NestedMessage nestedMessage2 = TestAllTypes.NestedMessage.newBuilder().build(); @@ -1388,36 +1338,26 @@ public class GeneratedMessageTest { public void testGetFieldBuilderNotSupportedException() { Descriptor descriptor = TestAllTypes.getDescriptor(); TestAllTypes.Builder builder = TestAllTypes.newBuilder(); - try { - builder.getFieldBuilder(descriptor.findFieldByName("optional_int32")); - assertWithMessage("Exception was not thrown").fail(); - } catch (UnsupportedOperationException e) { - // We expect this exception. - } - try { - builder.getFieldBuilder(descriptor.findFieldByName("optional_nested_enum")); - assertWithMessage("Exception was not thrown").fail(); - } catch (UnsupportedOperationException e) { - // We expect this exception. - } - try { - builder.getFieldBuilder(descriptor.findFieldByName("repeated_int32")); - assertWithMessage("Exception was not thrown").fail(); - } catch (UnsupportedOperationException e) { - // We expect this exception. - } - try { - builder.getFieldBuilder(descriptor.findFieldByName("repeated_nested_enum")); - assertWithMessage("Exception was not thrown").fail(); - } catch (UnsupportedOperationException e) { - // We expect this exception. - } - try { - builder.getFieldBuilder(descriptor.findFieldByName("repeated_nested_message")); - assertWithMessage("Exception was not thrown").fail(); - } catch (UnsupportedOperationException e) { - // We expect this exception. - } + assertThrows( + "Exception was not thrown", + UnsupportedOperationException.class, + () -> builder.getFieldBuilder(descriptor.findFieldByName("optional_int32"))); + assertThrows( + "Exception was not thrown", + UnsupportedOperationException.class, + () -> builder.getFieldBuilder(descriptor.findFieldByName("optional_nested_enum"))); + assertThrows( + "Exception was not thrown", + UnsupportedOperationException.class, + () -> builder.getFieldBuilder(descriptor.findFieldByName("repeated_int32"))); + assertThrows( + "Exception was not thrown", + UnsupportedOperationException.class, + () -> builder.getFieldBuilder(descriptor.findFieldByName("repeated_nested_enum"))); + assertThrows( + "Exception was not thrown", + UnsupportedOperationException.class, + () -> builder.getFieldBuilder(descriptor.findFieldByName("repeated_nested_message"))); } // Test that when the default outer class name conflicts with another type @@ -1445,6 +1385,7 @@ public class GeneratedMessageTest { // ================================================================= // oneof generated code test @Test + @SuppressWarnings("RedundantSetterCall") public void testOneofEnumCase() throws Exception { TestOneof2 message = TestOneof2.newBuilder().setFooInt(123).setFooString("foo").setFooCord("bar").build(); @@ -1460,6 +1401,7 @@ public class GeneratedMessageTest { } @Test + @SuppressWarnings("RedundantSetterCall") public void testSetOneofClearsOthers() throws Exception { TestOneof2.Builder builder = TestOneof2.newBuilder(); TestOneof2 message = builder.setFooInt(123).setFooString("foo").buildPartial(); @@ -1873,47 +1815,43 @@ public class GeneratedMessageTest { public void testGetRepeatedFieldBuilderNotSupportedException() { Descriptor descriptor = TestAllTypes.getDescriptor(); TestAllTypes.Builder builder = TestAllTypes.newBuilder(); - try { - builder.getRepeatedFieldBuilder(descriptor.findFieldByName("repeated_int32"), 0); - assertWithMessage("Exception was not thrown").fail(); - } catch (UnsupportedOperationException e) { - // We expect this exception. - } - try { - builder.getRepeatedFieldBuilder(descriptor.findFieldByName("repeated_nested_enum"), 0); - assertWithMessage("Exception was not thrown").fail(); - } catch (UnsupportedOperationException e) { - // We expect this exception. - } - try { - builder.getRepeatedFieldBuilder(descriptor.findFieldByName("optional_int32"), 0); - assertWithMessage("Exception was not thrown").fail(); - } catch (UnsupportedOperationException e) { - // We expect this exception. - } - try { - builder.getRepeatedFieldBuilder(descriptor.findFieldByName("optional_nested_enum"), 0); - assertWithMessage("Exception was not thrown").fail(); - } catch (UnsupportedOperationException e) { - // We expect this exception. - } - try { - builder.getRepeatedFieldBuilder(descriptor.findFieldByName("optional_nested_message"), 0); - assertWithMessage("Exception was not thrown").fail(); - } catch (UnsupportedOperationException e) { - // We expect this exception. - } + + assertThrows( + "Exception was not thrown", + UnsupportedOperationException.class, + () -> builder.getRepeatedFieldBuilder(descriptor.findFieldByName("repeated_int32"), 0)); + assertThrows( + "Exception was not thrown", + UnsupportedOperationException.class, + () -> + builder.getRepeatedFieldBuilder(descriptor.findFieldByName("repeated_nested_enum"), 0)); + assertThrows( + "Exception was not thrown", + UnsupportedOperationException.class, + () -> builder.getRepeatedFieldBuilder(descriptor.findFieldByName("optional_int32"), 0)); + assertThrows( + "Exception was not thrown", + UnsupportedOperationException.class, + () -> + builder.getRepeatedFieldBuilder(descriptor.findFieldByName("optional_nested_enum"), 0)); + assertThrows( + "Exception was not thrown", + UnsupportedOperationException.class, + () -> + builder.getRepeatedFieldBuilder( + descriptor.findFieldByName("optional_nested_message"), 0)); } private static final FieldDescriptor OPTIONAL_NESTED_MESSAGE_EXTENSION = UnittestProto.getDescriptor().findExtensionByName("optional_nested_message_extension"); private static final FieldDescriptor REPEATED_NESTED_MESSAGE_EXTENSION = UnittestProto.getDescriptor().findExtensionByName("repeated_nested_message_extension"); + // A compile-time check that TestAllExtensions.Builder does in fact extend - // GeneratedMessageV3.ExtendableBuilder. The tests below assume that it does. + // GeneratedMessage.ExtendableBuilder. The tests below assume that it does. static { @SuppressWarnings("unused") - Class> ignored = + Class> ignored = TestAllExtensions.Builder.class; } @@ -1999,4 +1937,40 @@ public class GeneratedMessageTest { assertThat(builder.getRepeatedField(REPEATED_NESTED_MESSAGE_EXTENSION, 0)) .isEqualTo(NestedMessage.newBuilder().setBb(100).build()); } + + @Test + public void getAllFields_repeatedFieldsAreNotMutable() { + TestAllTypes testMsg = + TestAllTypes.newBuilder() + .addRepeatedInt32(1) + .addRepeatedInt32(2) + .addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(111).build()) + .build(); + + FieldDescriptor repeatedInt32Field = + TestAllTypes.getDescriptor().findFieldByNumber(TestAllTypes.REPEATED_INT32_FIELD_NUMBER); + FieldDescriptor repeatedMsgField = + TestAllTypes.getDescriptor() + .findFieldByNumber(TestAllTypes.REPEATED_NESTED_MESSAGE_FIELD_NUMBER); + Map allFields = testMsg.getAllFields(); + List list = (List) allFields.get(repeatedInt32Field); + assertThat(list).hasSize(2); + assertThrows(UnsupportedOperationException.class, list::clear); + list = (List) allFields.get(repeatedMsgField); + assertThat(list).hasSize(1); + assertThrows(UnsupportedOperationException.class, list::clear); + + TestAllTypes.Builder builder = testMsg.toBuilder(); + allFields = builder.getAllFields(); + list = (List) allFields.get(repeatedInt32Field); + assertThat(list).hasSize(2); + assertThrows(UnsupportedOperationException.class, list::clear); + builder.clearField(repeatedInt32Field); + assertThat(list).hasSize(2); + list = (List) allFields.get(repeatedMsgField); + assertThat(list).hasSize(1); + assertThrows(UnsupportedOperationException.class, list::clear); + builder.clearField(repeatedMsgField); + assertThat(list).hasSize(1); + } } diff --git a/java/core/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java b/java/core/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java index d0c33de29..196664195 100644 --- a/java/core/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java +++ b/java/core/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java @@ -42,7 +42,7 @@ final class IsValidUtf8TestUtil { new ByteStringFactory() { @Override public ByteString newByteString(byte[] bytes) { - return new NioByteString(ByteBuffer.wrap(bytes)); + return ByteString.nioByteString(ByteBuffer.wrap(bytes)); } }; @@ -67,7 +67,7 @@ final class IsValidUtf8TestUtil { buffer.clear(); buffer.put(bytes); buffer.flip(); - return new NioByteString(buffer); + return ByteString.nioByteString(buffer); } }; diff --git a/java/core/src/test/java/com/google/protobuf/LazilyParsedMessageSetTest.java b/java/core/src/test/java/com/google/protobuf/LazilyParsedMessageSetTest.java new file mode 100644 index 000000000..c41a38182 --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/LazilyParsedMessageSetTest.java @@ -0,0 +1,162 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +package com.google.protobuf; + +import static com.google.common.truth.Truth.assertThat; + +import protobuf_unittest.UnittestMset.RawMessageSet; +import protobuf_unittest.UnittestMset.TestMessageSetExtension1; +import protobuf_unittest.UnittestMset.TestMessageSetExtension2; +import protobuf_unittest.UnittestMset.TestMessageSetExtension3; +import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests related to handling of MessageSets with lazily parsed extensions. */ +@RunWith(JUnit4.class) +public class LazilyParsedMessageSetTest { + private static final int TYPE_ID_1 = + TestMessageSetExtension1.getDescriptor().getExtensions().get(0).getNumber(); + private static final int TYPE_ID_2 = + TestMessageSetExtension2.getDescriptor().getExtensions().get(0).getNumber(); + private static final int TYPE_ID_3 = + TestMessageSetExtension3.getDescriptor().getExtensions().get(0).getNumber(); + private static final ByteString CORRUPTED_MESSAGE_PAYLOAD = + ByteString.copyFrom(new byte[] {(byte) 0xff}); + + @Before + public void setUp() { + ExtensionRegistryLite.setEagerlyParseMessageSets(false); + } + + @Test + public void testParseAndUpdateMessageSet_unaccessedLazyFieldsAreNotLoaded() throws Exception { + ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance(); + extensionRegistry.add(TestMessageSetExtension1.messageSetExtension); + extensionRegistry.add(TestMessageSetExtension2.messageSetExtension); + extensionRegistry.add(TestMessageSetExtension3.messageSetExtension); + + // Set up a TestMessageSet with 2 extensions. The first extension has corrupted payload + // data. The test below makes sure that we never load this extension. If we ever do, then we + // will handle the exception and replace the value with the default empty message (this behavior + // is tested below in testLoadCorruptedLazyField_getsReplacedWithEmptyMessage). Later on we + // check that when we serialize the message set, we still have corrupted payload for the first + // extension. + RawMessageSet inputRaw = + RawMessageSet.newBuilder() + .addItem( + RawMessageSet.Item.newBuilder() + .setTypeId(TYPE_ID_1) + .setMessage(CORRUPTED_MESSAGE_PAYLOAD)) + .addItem( + RawMessageSet.Item.newBuilder() + .setTypeId(TYPE_ID_2) + .setMessage( + TestMessageSetExtension2.newBuilder().setStr("foo").build().toByteString())) + .build(); + + ByteString inputData = inputRaw.toByteString(); + + // Re-parse as a TestMessageSet, so that all extensions are lazy + TestMessageSet messageSet = TestMessageSet.parseFrom(inputData, extensionRegistry); + + // Update one extension and add a new one. + TestMessageSet.Builder builder = messageSet.toBuilder(); + builder.setExtension( + TestMessageSetExtension2.messageSetExtension, + TestMessageSetExtension2.newBuilder().setStr("bar").build()); + + // Call .build() in the middle of updating the builder. This triggers a codepath that we want to + // make sure preserves lazy fields. + TestMessageSet unusedIntermediateMessageSet = builder.build(); + + builder.setExtension( + TestMessageSetExtension3.messageSetExtension, + TestMessageSetExtension3.newBuilder().setRequiredInt(666).build()); + + TestMessageSet updatedMessageSet = builder.build(); + + // Check that hasExtension call does not load lazy fields. + assertThat(updatedMessageSet.hasExtension(TestMessageSetExtension1.messageSetExtension)) + .isTrue(); + + // Serialize. The first extension should still be unloaded and will get serialized using the + // same corrupted byte array. + ByteString outputData = updatedMessageSet.toByteString(); + + // Re-parse as RawMessageSet + RawMessageSet actualRaw = + RawMessageSet.parseFrom(outputData, ExtensionRegistry.getEmptyRegistry()); + + RawMessageSet expectedRaw = + RawMessageSet.newBuilder() + .addItem( + RawMessageSet.Item.newBuilder() + .setTypeId(TYPE_ID_1) + // This is the important part -- we want to make sure that the payload of the + // 1st extensions is the same corrupted byte array. If we ever load the + // extension during our manipulations above, then we would have replaced it with + // the default empty message. + .setMessage(CORRUPTED_MESSAGE_PAYLOAD)) + .addItem( + RawMessageSet.Item.newBuilder() + .setTypeId(TYPE_ID_2) + .setMessage( + TestMessageSetExtension2.newBuilder().setStr("bar").build().toByteString())) + .addItem( + RawMessageSet.Item.newBuilder() + .setTypeId(TYPE_ID_3) + .setMessage( + TestMessageSetExtension3.newBuilder() + .setRequiredInt(666) + .build() + .toByteString())) + .build(); + + assertThat(actualRaw).isEqualTo(expectedRaw); + } + + @Test + public void testLoadCorruptedLazyField_getsReplacedWithEmptyMessage() throws Exception { + ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance(); + extensionRegistry.add(TestMessageSetExtension1.messageSetExtension); + + RawMessageSet inputRaw = + RawMessageSet.newBuilder() + .addItem( + RawMessageSet.Item.newBuilder() + .setTypeId(TYPE_ID_1) + .setMessage(CORRUPTED_MESSAGE_PAYLOAD)) + .build(); + + ByteString inputData = inputRaw.toByteString(); + + // Re-parse as a TestMessageSet, so that all extensions are lazy + TestMessageSet messageSet = TestMessageSet.parseFrom(inputData, extensionRegistry); + + assertThat(messageSet.getExtension(TestMessageSetExtension1.messageSetExtension)) + .isEqualTo(TestMessageSetExtension1.getDefaultInstance()); + + // Serialize. The first extension should be serialized as an empty message. + ByteString outputData = messageSet.toByteString(); + + // Re-parse as RawMessageSet + RawMessageSet actualRaw = + RawMessageSet.parseFrom(outputData, ExtensionRegistry.getEmptyRegistry()); + + RawMessageSet expectedRaw = + RawMessageSet.newBuilder() + .addItem( + RawMessageSet.Item.newBuilder().setTypeId(TYPE_ID_1).setMessage(ByteString.empty())) + .build(); + + assertThat(actualRaw).isEqualTo(expectedRaw); + } +} diff --git a/java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java b/java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java index e29bb2512..182d9d387 100644 --- a/java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java +++ b/java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java @@ -24,6 +24,7 @@ import org.junit.runners.JUnit4; public class LazyMessageLiteTest { @Test + @SuppressWarnings("RedundantSetterCall") public void testSetValues() { LazyNestedInnerMessageLite nested = LazyNestedInnerMessageLite.newBuilder().setNum(3).build(); LazyInnerMessageLite inner = diff --git a/java/core/src/test/java/com/google/protobuf/LegacyUnredactedTextFormatTest.java b/java/core/src/test/java/com/google/protobuf/LegacyUnredactedTextFormatTest.java new file mode 100644 index 000000000..61b164dec --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/LegacyUnredactedTextFormatTest.java @@ -0,0 +1,132 @@ +package com.google.protobuf; + +import static com.google.common.truth.Truth.assertThat; + +import protobuf_unittest.UnittestProto; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public final class LegacyUnredactedTextFormatTest { + + @Test + public void legacyUnredactedTextFormatMessage_defaultMessageIsEmpty() { + UnittestProto.TestEmptyMessage message = UnittestProto.TestEmptyMessage.getDefaultInstance(); + assertThat(LegacyUnredactedTextFormat.legacyUnredactedMultilineString(message)).isEmpty(); + + UnittestProto.TestEmptyMessage singleLineMessage = + UnittestProto.TestEmptyMessage.getDefaultInstance(); + assertThat(LegacyUnredactedTextFormat.legacyUnredactedSingleLineString(singleLineMessage)) + .isEmpty(); + } + + @Test + public void legacyUnredactedTextFormatMessage_hasExpectedFormats() { + UnittestProto.RedactedFields message = + UnittestProto.RedactedFields.newBuilder() + .setOptionalRedactedString("redacted") + .setOptionalUnredactedString("hello") + .setOptionalRedactedMessage( + UnittestProto.TestNestedMessageRedaction.newBuilder() + .setOptionalRedactedNestedString("nested") + .build()) + .setOptionalUnredactedMessage( + UnittestProto.TestNestedMessageRedaction.newBuilder() + .setOptionalUnredactedNestedString("unredacted") + .build()) + .build(); + assertThat(LegacyUnredactedTextFormat.legacyUnredactedMultilineString(message)) + .isEqualTo( + "optional_redacted_string: \"redacted\"\n" + + "optional_unredacted_string: \"hello\"\n" + + "optional_redacted_message {\n" + + " optional_redacted_nested_string: \"nested\"\n" + + "}\n" + + "optional_unredacted_message {\n" + + " optional_unredacted_nested_string: \"unredacted\"\n" + + "}\n"); + assertThat(LegacyUnredactedTextFormat.legacyUnredactedSingleLineString(message)) + .isEqualTo( + "optional_redacted_string: \"redacted\"" + + " optional_unredacted_string: \"hello\"" + + " optional_redacted_message {" + + " optional_redacted_nested_string: \"nested\"" + + " }" + + " optional_unredacted_message {" + + " optional_unredacted_nested_string: \"unredacted\"" + + " }"); + } + + private UnknownFieldSet makeUnknownFieldSet() { + return UnknownFieldSet.newBuilder() + .addField( + 5, + UnknownFieldSet.Field.newBuilder() + .addVarint(1) + .addFixed32(2) + .addFixed64(3) + .addLengthDelimited(ByteString.copyFromUtf8("4")) + .addLengthDelimited( + UnknownFieldSet.newBuilder() + .addField(12, UnknownFieldSet.Field.newBuilder().addVarint(6).build()) + .build() + .toByteString()) + .addGroup( + UnknownFieldSet.newBuilder() + .addField(10, UnknownFieldSet.Field.newBuilder().addVarint(5).build()) + .build()) + .build()) + .addField( + 8, UnknownFieldSet.Field.newBuilder().addVarint(1).addVarint(2).addVarint(3).build()) + .addField( + 15, + UnknownFieldSet.Field.newBuilder() + .addVarint(0xABCDEF1234567890L) + .addFixed32(0xABCD1234) + .addFixed64(0xABCDEF1234567890L) + .build()) + .build(); + } + + @Test + public void legacyUnredactedTextFormatUnknownFields_hasExpectedFormats() { + UnknownFieldSet unknownFields = makeUnknownFieldSet(); + assertThat(LegacyUnredactedTextFormat.legacyUnredactedMultilineString(unknownFields)) + .isEqualTo( + "5: 1\n" + + "5: 0x00000002\n" + + "5: 0x0000000000000003\n" + + "5: \"4\"\n" + + "5: {\n" + + " 12: 6\n" + + "}\n" + + "5 {\n" + + " 10: 5\n" + + "}\n" + + "8: 1\n" + + "8: 2\n" + + "8: 3\n" + + "15: 12379813812177893520\n" + + "15: 0xabcd1234\n" + + "15: 0xabcdef1234567890\n"); + assertThat(LegacyUnredactedTextFormat.legacyUnredactedSingleLineString(unknownFields)) + .isEqualTo( + "5: 1" + + " 5: 0x00000002" + + " 5: 0x0000000000000003" + + " 5: \"4\"" + + " 5: {" + + " 12: 6" + + " }" + + " 5 {" + + " 10: 5" + + " }" + + " 8: 1" + + " 8: 2" + + " 8: 3" + + " 15: 12379813812177893520" + + " 15: 0xabcd1234" + + " 15: 0xabcdef1234567890"); + } +} diff --git a/java/core/src/test/java/com/google/protobuf/MapLiteTest.java b/java/core/src/test/java/com/google/protobuf/MapLiteTest.java index 58f73ec35..3e55eba20 100644 --- a/java/core/src/test/java/com/google/protobuf/MapLiteTest.java +++ b/java/core/src/test/java/com/google/protobuf/MapLiteTest.java @@ -51,16 +51,10 @@ public final class MapLiteTest { @Test public void testSetMapValues() { - TestMap.Builder usingMutableMapBuilder = TestMap.newBuilder(); - setMapValues(usingMutableMapBuilder); - TestMap usingMutableMap = usingMutableMapBuilder.build(); - assertMapValuesSet(usingMutableMap); - - TestMap.Builder usingAccessorsBuilder = TestMap.newBuilder(); - setMapValues(usingAccessorsBuilder); - TestMap usingAccessors = usingAccessorsBuilder.build(); - assertMapValuesSet(usingAccessors); - assertThat(usingMutableMap).isEqualTo(usingAccessors); + TestMap.Builder testMapBuilder = TestMap.newBuilder(); + setMapValues(testMapBuilder); + TestMap testMap = testMapBuilder.build(); + assertMapValuesSet(testMap); } private void copyMapValues(TestMap source, TestMap.Builder destination) { diff --git a/java/core/src/test/java/com/google/protobuf/MapTest.java b/java/core/src/test/java/com/google/protobuf/MapTest.java index 7058d4316..e158f72c2 100644 --- a/java/core/src/test/java/com/google/protobuf/MapTest.java +++ b/java/core/src/test/java/com/google/protobuf/MapTest.java @@ -10,6 +10,7 @@ package com.google.protobuf; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import com.google.protobuf.Descriptors.Descriptor; @@ -1592,4 +1593,38 @@ public class MapTest { assertThat(expected).hasMessageThat().isNotNull(); } } + + @Test + public void getAllFields_mapEntryListMutability() { + TestMap testMap = + TestMap.newBuilder() + .putInt32ToInt32Field(1, 11) + .putInt32ToInt32Field(2, 22) + .putInt32ToMessageField(1, TestMap.MessageValue.newBuilder().setValue(111).build()) + .build(); + FieldDescriptor int2IntMapField = + TestMap.getDescriptor().findFieldByNumber(TestMap.INT32_TO_INT32_FIELD_FIELD_NUMBER); + FieldDescriptor int2MessageMapField = + TestMap.getDescriptor().findFieldByNumber(TestMap.INT32_TO_MESSAGE_FIELD_FIELD_NUMBER); + Map allFields = testMap.getAllFields(); + List mapEntries = (List) allFields.get(int2IntMapField); + assertThat(mapEntries).hasSize(2); + assertThrows(UnsupportedOperationException.class, mapEntries::clear); + mapEntries = (List) allFields.get(int2MessageMapField); + assertThat(mapEntries).hasSize(1); + assertThrows(UnsupportedOperationException.class, mapEntries::clear); + + TestMap.Builder builder = testMap.toBuilder(); + allFields = builder.getAllFields(); + mapEntries = (List) allFields.get(int2IntMapField); + assertThat(mapEntries).hasSize(2); + assertThrows(UnsupportedOperationException.class, mapEntries::clear); + builder.clearField(int2IntMapField); + assertThat(mapEntries).hasSize(2); + mapEntries = (List) allFields.get(int2MessageMapField); + assertThat(mapEntries).hasSize(1); + assertThrows(UnsupportedOperationException.class, mapEntries::clear); + builder.clearField(int2MessageMapField); + assertThat(mapEntries).hasSize(1); + } } diff --git a/java/core/src/test/java/com/google/protobuf/NioByteStringTest.java b/java/core/src/test/java/com/google/protobuf/NioByteStringTest.java index aa4523a45..34c79b823 100644 --- a/java/core/src/test/java/com/google/protobuf/NioByteStringTest.java +++ b/java/core/src/test/java/com/google/protobuf/NioByteStringTest.java @@ -32,13 +32,13 @@ import org.junit.runners.JUnit4; /** Tests for {@link NioByteString}. */ @RunWith(JUnit4.class) public class NioByteStringTest { - private static final ByteString EMPTY = new NioByteString(ByteBuffer.wrap(new byte[0])); - private static final String CLASSNAME = NioByteString.class.getSimpleName(); + private static final ByteString EMPTY = ByteString.nioByteString(ByteBuffer.wrap(new byte[0])); + private static final String CLASSNAME = "ByteString$NioByteString"; private static final byte[] BYTES = ByteStringTest.getTestBytes(1234, 11337766L); private static final int EXPECTED_HASH = ByteString.wrap(BYTES).hashCode(); private final ByteBuffer backingBuffer = ByteBuffer.wrap(BYTES.clone()); - private final ByteString testString = new NioByteString(backingBuffer); + private final ByteString testString = ByteString.nioByteString(backingBuffer); @Test public void testExpectedType() { @@ -487,7 +487,7 @@ public class NioByteStringTest { public void testToString_returnsCanonicalEmptyString() { assertWithMessage("%s must be the same string references", CLASSNAME) .that(EMPTY.toString(UTF_8)) - .isSameInstanceAs(new NioByteString(ByteBuffer.wrap(new byte[0])).toString(UTF_8)); + .isSameInstanceAs(ByteString.nioByteString(ByteBuffer.wrap(new byte[0])).toString(UTF_8)); } @Test @@ -520,11 +520,11 @@ public class NioByteStringTest { .isEqualTo(testString.substring(55, 55)); assertWithMessage("%s must equal another string with the same value", CLASSNAME) .that(testString) - .isEqualTo(new NioByteString(backingBuffer)); + .isEqualTo(ByteString.nioByteString(backingBuffer)); byte[] mungedBytes = mungedBytes(); assertWithMessage("%s must not equal every string with the same length", CLASSNAME) - .that(testString.equals(new NioByteString(ByteBuffer.wrap(mungedBytes)))) + .that(testString.equals(ByteString.nioByteString(ByteBuffer.wrap(mungedBytes)))) .isFalse(); } @@ -602,7 +602,7 @@ public class NioByteStringTest { @Test public void testPeekCachedHashCode() { - ByteString newString = new NioByteString(backingBuffer); + ByteString newString = ByteString.nioByteString(backingBuffer); assertWithMessage("%s.peekCachedHashCode() should return zero at first", CLASSNAME) .that(newString.peekCachedHashCode()) .isEqualTo(0); @@ -718,6 +718,6 @@ public class NioByteStringTest { } private static ByteString forString(String str) { - return new NioByteString(ByteBuffer.wrap(str.getBytes(UTF_8))); + return ByteString.nioByteString(ByteBuffer.wrap(str.getBytes(UTF_8))); } } diff --git a/java/core/src/test/java/com/google/protobuf/RepeatedFieldBuilderV3Test.java b/java/core/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java similarity index 86% rename from java/core/src/test/java/com/google/protobuf/RepeatedFieldBuilderV3Test.java rename to java/core/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java index 8cc552713..19587e588 100644 --- a/java/core/src/test/java/com/google/protobuf/RepeatedFieldBuilderV3Test.java +++ b/java/core/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java @@ -19,17 +19,17 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** - * Tests for {@link RepeatedFieldBuilderV3}. This tests basic functionality. More extensive testing is + * Tests for {@link RepeatedFieldBuilder}. This tests basic functionality. More extensive testing is * provided via other tests that exercise the builder. */ @RunWith(JUnit4.class) -public class RepeatedFieldBuilderV3Test { +public class RepeatedFieldBuilderTest { @Test public void testBasicUse() { TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); - RepeatedFieldBuilderV3 builder = - newRepeatedFieldBuilderV3(mockParent); + RepeatedFieldBuilder builder = + newRepeatedFieldBuilder(mockParent); builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build()); builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build()); assertThat(builder.getMessage(0).getOptionalInt32()).isEqualTo(0); @@ -50,8 +50,8 @@ public class RepeatedFieldBuilderV3Test { @Test public void testGoingBackAndForth() { TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); - RepeatedFieldBuilderV3 builder = - newRepeatedFieldBuilderV3(mockParent); + RepeatedFieldBuilder builder = + newRepeatedFieldBuilder(mockParent); builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build()); builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build()); assertThat(builder.getMessage(0).getOptionalInt32()).isEqualTo(0); @@ -80,8 +80,8 @@ public class RepeatedFieldBuilderV3Test { @Test public void testVariousMethods() { TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); - RepeatedFieldBuilderV3 builder = - newRepeatedFieldBuilderV3(mockParent); + RepeatedFieldBuilder builder = + newRepeatedFieldBuilder(mockParent); builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build()); builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(2).build()); builder.addBuilder(0, TestAllTypes.getDefaultInstance()).setOptionalInt32(0); @@ -122,8 +122,8 @@ public class RepeatedFieldBuilderV3Test { @Test public void testLists() { TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); - RepeatedFieldBuilderV3 builder = - newRepeatedFieldBuilderV3(mockParent); + RepeatedFieldBuilder builder = + newRepeatedFieldBuilder(mockParent); builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build()); builder.addMessage(0, TestAllTypes.newBuilder().setOptionalInt32(0).build()); assertThat(builder.getMessage(0).getOptionalInt32()).isEqualTo(0); @@ -160,9 +160,9 @@ public class RepeatedFieldBuilderV3Test { } } - private RepeatedFieldBuilderV3 - newRepeatedFieldBuilderV3(AbstractMessage.BuilderParent parent) { - return new RepeatedFieldBuilderV3( + private RepeatedFieldBuilder + newRepeatedFieldBuilder(GeneratedMessage.BuilderParent parent) { + return new RepeatedFieldBuilder( Collections.emptyList(), false, parent, false); } } diff --git a/java/core/src/test/java/com/google/protobuf/RuntimeVersionTest.java b/java/core/src/test/java/com/google/protobuf/RuntimeVersionTest.java new file mode 100644 index 000000000..1714ce42e --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/RuntimeVersionTest.java @@ -0,0 +1,165 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +package com.google.protobuf; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; + +import java.util.logging.Logger; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public final class RuntimeVersionTest { + + @Test + public void versionValidation_invalidVersionNumbers() { + RuntimeVersion.ProtobufRuntimeVersionException thrown = + assertThrows( + RuntimeVersion.ProtobufRuntimeVersionException.class, + () -> + RuntimeVersion.validateProtobufGencodeVersion( + RuntimeVersion.DOMAIN, 1, -2, -3, "", "dummy")); + assertThat(thrown).hasMessageThat().contains("Invalid gencode version: 1.-2.-3"); + } + + @Test + public void versionValidation_crossDomainDisallowed() { + + RuntimeVersion.RuntimeDomain gencodeDomain = + RuntimeVersion.RuntimeDomain.GOOGLE_INTERNAL; + RuntimeVersion.ProtobufRuntimeVersionException thrown = + assertThrows( + RuntimeVersion.ProtobufRuntimeVersionException.class, + () -> + RuntimeVersion.validateProtobufGencodeVersion( + gencodeDomain, 1, 2, 3, "", "testing.Foo")); + assertThat(thrown) + .hasMessageThat() + .contains("Detected mismatched Protobuf Gencode/Runtime domains when loading testing.Foo"); + } + + @Test + public void versionValidation_mismatchingMajorDisallowed() { + int gencodeMajor = 1; + RuntimeVersion.ProtobufRuntimeVersionException thrown = + assertThrows( + RuntimeVersion.ProtobufRuntimeVersionException.class, + () -> + RuntimeVersion.validateProtobufGencodeVersion( + RuntimeVersion.DOMAIN, + gencodeMajor, + RuntimeVersion.MINOR, + RuntimeVersion.PATCH, + RuntimeVersion.SUFFIX, + "testing.Foo")); + assertThat(thrown) + .hasMessageThat() + .contains( + "Detected mismatched Protobuf Gencode/Runtime major versions when loading testing.Foo"); + } + + @Test + public void versionValidation_versionNumbersAllTheSameAllowed() { + RuntimeVersion.validateProtobufGencodeVersion( + RuntimeVersion.DOMAIN, + RuntimeVersion.MAJOR, + RuntimeVersion.MINOR, + RuntimeVersion.PATCH, + RuntimeVersion.SUFFIX, + "dummy"); + } + + @Test + public void versionValidation_newerRuntimeVersionAllowed() { + int gencodeMinor = RuntimeVersion.MINOR - 1; + RuntimeVersion.validateProtobufGencodeVersion( + RuntimeVersion.DOMAIN, + RuntimeVersion.MAJOR, + gencodeMinor, + RuntimeVersion.PATCH, + RuntimeVersion.SUFFIX, + "dummy"); + } + + @Test + public void versionValidation_olderRuntimeVersionDisallowed() { + int gencodeMinor = RuntimeVersion.MINOR + 1; + RuntimeVersion.ProtobufRuntimeVersionException thrown = + assertThrows( + RuntimeVersion.ProtobufRuntimeVersionException.class, + () -> + RuntimeVersion.validateProtobufGencodeVersion( + RuntimeVersion.DOMAIN, + RuntimeVersion.MAJOR, + gencodeMinor, + RuntimeVersion.PATCH, + RuntimeVersion.SUFFIX, + "testing.Foo")); + assertThat(thrown) + .hasMessageThat() + .contains( + "Detected incompatible Protobuf Gencode/Runtime versions when loading testing.Foo"); + + int gencodePatch = RuntimeVersion.PATCH + 1; + thrown = + assertThrows( + RuntimeVersion.ProtobufRuntimeVersionException.class, + () -> + RuntimeVersion.validateProtobufGencodeVersion( + RuntimeVersion.DOMAIN, + RuntimeVersion.MAJOR, + RuntimeVersion.MINOR, + gencodePatch, + RuntimeVersion.SUFFIX, + "testing.Bar")); + assertThat(thrown) + .hasMessageThat() + .contains( + "Detected incompatible Protobuf Gencode/Runtime versions when loading testing.Bar"); + } + + @Test + public void versionValidation_differentVesionSuffixDisallowed() { + String gencodeSuffix = "-test"; + RuntimeVersion.ProtobufRuntimeVersionException thrown = + assertThrows( + RuntimeVersion.ProtobufRuntimeVersionException.class, + () -> + RuntimeVersion.validateProtobufGencodeVersion( + RuntimeVersion.DOMAIN, + RuntimeVersion.MAJOR, + RuntimeVersion.MINOR, + RuntimeVersion.PATCH, + gencodeSuffix, + "testing.Foo")); + assertThat(thrown) + .hasMessageThat() + .contains( + "Detected mismatched Protobuf Gencode/Runtime version suffixes when loading" + + " testing.Foo"); + } + + @Test + public void versionValidation_gencodeOneMajorVersionOlderWarning() { + TestUtil.TestLogHandler logHandler = new TestUtil.TestLogHandler(); + Logger logger = Logger.getLogger(RuntimeVersion.class.getName()); + logger.addHandler(logHandler); + RuntimeVersion.validateProtobufGencodeVersion( + RuntimeVersion.DOMAIN, + RuntimeVersion.MAJOR - 1, + RuntimeVersion.MINOR, + RuntimeVersion.PATCH, + RuntimeVersion.SUFFIX, + "dummy"); + assertThat(logHandler.getStoredLogRecords()).hasSize(1); + assertThat(logHandler.getStoredLogRecords().get(0).getMessage()) + .contains("is exactly one major version older than the runtime version"); + } +} diff --git a/java/core/src/test/java/com/google/protobuf/SingleFieldBuilderV3Test.java b/java/core/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java similarity index 82% rename from java/core/src/test/java/com/google/protobuf/SingleFieldBuilderV3Test.java rename to java/core/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java index e31617619..e1806110d 100644 --- a/java/core/src/test/java/com/google/protobuf/SingleFieldBuilderV3Test.java +++ b/java/core/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java @@ -16,17 +16,17 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** - * Tests for {@link SingleFieldBuilderV3}. This tests basic functionality. More extensive testing is + * Tests for {@link SingleFieldBuilder}. This tests basic functionality. More extensive testing is * provided via other tests that exercise the builder. */ @RunWith(JUnit4.class) -public class SingleFieldBuilderV3Test { +public class SingleFieldBuilderTest { @Test public void testBasicUseAndInvalidations() { TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); - SingleFieldBuilderV3 builder = - new SingleFieldBuilderV3<>(TestAllTypes.getDefaultInstance(), mockParent, false); + SingleFieldBuilder builder = + new SingleFieldBuilder<>(TestAllTypes.getDefaultInstance(), mockParent, false); assertThat(builder.getMessage()).isSameInstanceAs(TestAllTypes.getDefaultInstance()); assertThat(builder.getBuilder().buildPartial()).isEqualTo(TestAllTypes.getDefaultInstance()); assertThat(mockParent.getInvalidationCount()).isEqualTo(0); @@ -49,8 +49,8 @@ public class SingleFieldBuilderV3Test { @Test public void testSetMessage() { TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); - SingleFieldBuilderV3 builder = - new SingleFieldBuilderV3<>(TestAllTypes.getDefaultInstance(), mockParent, false); + SingleFieldBuilder builder = + new SingleFieldBuilder<>(TestAllTypes.getDefaultInstance(), mockParent, false); builder.setMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build()); assertThat(builder.getMessage().getOptionalInt32()).isEqualTo(0); @@ -71,8 +71,8 @@ public class SingleFieldBuilderV3Test { @Test public void testClear() { TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); - SingleFieldBuilderV3 builder = - new SingleFieldBuilderV3<>(TestAllTypes.getDefaultInstance(), mockParent, false); + SingleFieldBuilder builder = + new SingleFieldBuilder<>(TestAllTypes.getDefaultInstance(), mockParent, false); builder.setMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build()); assertThat(TestAllTypes.getDefaultInstance()).isNotSameInstanceAs(builder.getMessage()); builder.clear(); @@ -87,8 +87,8 @@ public class SingleFieldBuilderV3Test { @Test public void testMerge() { TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); - SingleFieldBuilderV3 builder = - new SingleFieldBuilderV3<>(TestAllTypes.getDefaultInstance(), mockParent, false); + SingleFieldBuilder builder = + new SingleFieldBuilder<>(TestAllTypes.getDefaultInstance(), mockParent, false); // Merge into default field. builder.mergeFrom(TestAllTypes.getDefaultInstance()); diff --git a/java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java b/java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java index dbac08aba..a2f04b987 100644 --- a/java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java +++ b/java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java @@ -9,6 +9,7 @@ package com.google.protobuf; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static com.google.protobuf.SmallSortedMap.DEFAULT_FIELD_MAP_ARRAY_SIZE; import static java.lang.Math.min; import java.util.AbstractMap; @@ -29,42 +30,33 @@ public class SmallSortedMapTest { @Test public void testPutAndGetArrayEntriesOnly() { - runPutAndGetTest(3); + runPutAndGetTest(DEFAULT_FIELD_MAP_ARRAY_SIZE); } @Test public void testPutAndGetOverflowEntries() { - runPutAndGetTest(6); + runPutAndGetTest(DEFAULT_FIELD_MAP_ARRAY_SIZE * 2); } private void runPutAndGetTest(int numElements) { - // Test with even and odd arraySize - SmallSortedMap map1 = SmallSortedMap.newInstanceForTest(3); - SmallSortedMap map2 = SmallSortedMap.newInstanceForTest(4); - SmallSortedMap map3 = SmallSortedMap.newInstanceForTest(3); - SmallSortedMap map4 = SmallSortedMap.newInstanceForTest(4); + SmallSortedMap map1 = SmallSortedMap.newInstanceForTest(); + SmallSortedMap map3 = SmallSortedMap.newInstanceForTest(); // Test with puts in ascending order. for (int i = 0; i < numElements; i++) { assertThat(map1.put(i, i + 1)).isNull(); - assertThat(map2.put(i, i + 1)).isNull(); } // Test with puts in descending order. for (int i = numElements - 1; i >= 0; i--) { assertThat(map3.put(i, i + 1)).isNull(); - assertThat(map4.put(i, i + 1)).isNull(); } - assertThat(map1.getNumArrayEntries()).isEqualTo(min(3, numElements)); - assertThat(map2.getNumArrayEntries()).isEqualTo(min(4, numElements)); - assertThat(map3.getNumArrayEntries()).isEqualTo(min(3, numElements)); - assertThat(map4.getNumArrayEntries()).isEqualTo(min(4, numElements)); + assertThat(map1.getNumArrayEntries()).isEqualTo(min(16, numElements)); + assertThat(map3.getNumArrayEntries()).isEqualTo(min(16, numElements)); List> allMaps = new ArrayList<>(); allMaps.add(map1); - allMaps.add(map2); allMaps.add(map3); - allMaps.add(map4); for (SmallSortedMap map : allMaps) { assertThat(map).hasSize(numElements); @@ -73,69 +65,72 @@ public class SmallSortedMapTest { } } - assertThat(map1).isEqualTo(map2); - assertThat(map2).isEqualTo(map3); - assertThat(map3).isEqualTo(map4); + assertThat(map1).isEqualTo(map3); } @Test public void testReplacingPut() { - SmallSortedMap map = SmallSortedMap.newInstanceForTest(3); - for (int i = 0; i < 6; i++) { + SmallSortedMap map = SmallSortedMap.newInstanceForTest(); + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { assertThat(map.put(i, i + 1)).isNull(); assertThat(map.remove(i + 1)).isNull(); } - for (int i = 0; i < 6; i++) { + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { assertThat(map.put(i, i + 2)).isEqualTo(Integer.valueOf(i + 1)); } } @Test public void testRemove() { - SmallSortedMap map = SmallSortedMap.newInstanceForTest(3); - for (int i = 0; i < 6; i++) { + SmallSortedMap map = SmallSortedMap.newInstanceForTest(); + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE + 3; i++) { assertThat(map.put(i, i + 1)).isNull(); assertThat(map.remove(i + 1)).isNull(); } - assertThat(map.getNumArrayEntries()).isEqualTo(3); + assertThat(map.getNumArrayEntries()).isEqualTo(16); assertThat(map.getNumOverflowEntries()).isEqualTo(3); - assertThat(map).hasSize(6); - assertThat(map.keySet()).isEqualTo(makeSortedKeySet(0, 1, 2, 3, 4, 5)); + assertThat(map).hasSize(19); + assertThat(map.keySet()) + .isEqualTo( + makeSortedKeySet(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)); assertThat(map.remove(1)).isEqualTo(Integer.valueOf(2)); - assertThat(map.getNumArrayEntries()).isEqualTo(3); + assertThat(map.getNumArrayEntries()).isEqualTo(16); assertThat(map.getNumOverflowEntries()).isEqualTo(2); - assertThat(map).hasSize(5); - assertThat(map.keySet()).isEqualTo(makeSortedKeySet(0, 2, 3, 4, 5)); + assertThat(map).hasSize(18); + assertThat(map.keySet()) + .isEqualTo(makeSortedKeySet(0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)); assertThat(map.remove(4)).isEqualTo(Integer.valueOf(5)); - assertThat(map.getNumArrayEntries()).isEqualTo(3); + assertThat(map.getNumArrayEntries()).isEqualTo(16); assertThat(map.getNumOverflowEntries()).isEqualTo(1); - assertThat(map).hasSize(4); - assertThat(map.keySet()).isEqualTo(makeSortedKeySet(0, 2, 3, 5)); + assertThat(map).hasSize(17); + assertThat(map.keySet()) + .isEqualTo(makeSortedKeySet(0, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)); assertThat(map.remove(3)).isEqualTo(Integer.valueOf(4)); - assertThat(map.getNumArrayEntries()).isEqualTo(3); + assertThat(map.getNumArrayEntries()).isEqualTo(16); assertThat(map.getNumOverflowEntries()).isEqualTo(0); - assertThat(map).hasSize(3); - assertThat(map.keySet()).isEqualTo(makeSortedKeySet(0, 2, 5)); + assertThat(map).hasSize(16); + assertThat(map.keySet()) + .isEqualTo(makeSortedKeySet(0, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)); assertThat(map.remove(3)).isNull(); - assertThat(map.getNumArrayEntries()).isEqualTo(3); + assertThat(map.getNumArrayEntries()).isEqualTo(16); assertThat(map.getNumOverflowEntries()).isEqualTo(0); - assertThat(map).hasSize(3); + assertThat(map).hasSize(16); assertThat(map.remove(0)).isEqualTo(Integer.valueOf(1)); - assertThat(map.getNumArrayEntries()).isEqualTo(2); + assertThat(map.getNumArrayEntries()).isEqualTo(15); assertThat(map.getNumOverflowEntries()).isEqualTo(0); - assertThat(map).hasSize(2); + assertThat(map).hasSize(15); } @Test public void testClear() { - SmallSortedMap map = SmallSortedMap.newInstanceForTest(3); - for (int i = 0; i < 6; i++) { + SmallSortedMap map = SmallSortedMap.newInstanceForTest(); + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { assertThat(map.put(i, i + 1)).isNull(); } map.clear(); @@ -146,18 +141,19 @@ public class SmallSortedMapTest { @Test public void testGetArrayEntryAndOverflowEntries() { - SmallSortedMap map = SmallSortedMap.newInstanceForTest(3); - for (int i = 0; i < 6; i++) { + SmallSortedMap map = SmallSortedMap.newInstanceForTest(); + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { assertThat(map.put(i, i + 1)).isNull(); } - assertThat(map.getNumArrayEntries()).isEqualTo(3); - for (int i = 0; i < 3; i++) { + assertThat(map.getNumArrayEntries()).isEqualTo(DEFAULT_FIELD_MAP_ARRAY_SIZE); + for (int i = 0; i < map.getNumArrayEntries(); i++) { Map.Entry entry = map.getArrayEntryAt(i); assertThat(entry.getKey()).isEqualTo(Integer.valueOf(i)); assertThat(entry.getValue()).isEqualTo(Integer.valueOf(i + 1)); } Iterator> it = map.getOverflowEntries().iterator(); - for (int i = 3; i < 6; i++) { + assertThat(map.getNumOverflowEntries()).isEqualTo(DEFAULT_FIELD_MAP_ARRAY_SIZE); + for (int i = DEFAULT_FIELD_MAP_ARRAY_SIZE; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { assertThat(it.hasNext()).isTrue(); Map.Entry entry = it.next(); assertThat(entry.getKey()).isEqualTo(Integer.valueOf(i)); @@ -168,12 +164,12 @@ public class SmallSortedMapTest { @Test public void testEntrySetContains() { - SmallSortedMap map = SmallSortedMap.newInstanceForTest(3); - for (int i = 0; i < 6; i++) { + SmallSortedMap map = SmallSortedMap.newInstanceForTest(); + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { assertThat(map.put(i, i + 1)).isNull(); } Set> entrySet = map.entrySet(); - for (int i = 0; i < 6; i++) { + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { assertThat(entrySet).contains(new AbstractMap.SimpleEntry(i, i + 1)); assertThat(entrySet).doesNotContain(new AbstractMap.SimpleEntry(i, i)); } @@ -181,29 +177,29 @@ public class SmallSortedMapTest { @Test public void testEntrySetAdd() { - SmallSortedMap map = SmallSortedMap.newInstanceForTest(3); + SmallSortedMap map = SmallSortedMap.newInstanceForTest(); Set> entrySet = map.entrySet(); - for (int i = 0; i < 6; i++) { + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { Map.Entry entry = new AbstractMap.SimpleEntry<>(i, i + 1); assertThat(entrySet.add(entry)).isTrue(); assertThat(entrySet.add(entry)).isFalse(); } - for (int i = 0; i < 6; i++) { + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { assertThat(map).containsEntry(i, Integer.valueOf(i + 1)); } - assertThat(map.getNumArrayEntries()).isEqualTo(3); - assertThat(map.getNumOverflowEntries()).isEqualTo(3); - assertThat(map).hasSize(6); + assertThat(map.getNumArrayEntries()).isEqualTo(16); + assertThat(map.getNumOverflowEntries()).isEqualTo(16); + assertThat(map).hasSize(32); } @Test public void testEntrySetRemove() { - SmallSortedMap map = SmallSortedMap.newInstanceForTest(3); + SmallSortedMap map = SmallSortedMap.newInstanceForTest(); Set> entrySet = map.entrySet(); - for (int i = 0; i < 6; i++) { + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { assertThat(map.put(i, i + 1)).isNull(); } - for (int i = 0; i < 6; i++) { + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { Map.Entry entry = new AbstractMap.SimpleEntry<>(i, i + 1); assertThat(entrySet.remove(entry)).isTrue(); assertThat(entrySet.remove(entry)).isFalse(); @@ -216,8 +212,8 @@ public class SmallSortedMapTest { @Test public void testEntrySetClear() { - SmallSortedMap map = SmallSortedMap.newInstanceForTest(3); - for (int i = 0; i < 6; i++) { + SmallSortedMap map = SmallSortedMap.newInstanceForTest(); + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { assertThat(map.put(i, i + 1)).isNull(); } map.clear(); @@ -229,12 +225,12 @@ public class SmallSortedMapTest { @Test public void testEntrySetIteratorNext() { - SmallSortedMap map = SmallSortedMap.newInstanceForTest(3); - for (int i = 0; i < 6; i++) { + SmallSortedMap map = SmallSortedMap.newInstanceForTest(); + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { assertThat(map.put(i, i + 1)).isNull(); } Iterator> it = map.entrySet().iterator(); - for (int i = 0; i < 6; i++) { + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { assertThat(it.hasNext()).isTrue(); Map.Entry entry = it.next(); assertThat(entry.getKey()).isEqualTo(Integer.valueOf(i)); @@ -245,45 +241,45 @@ public class SmallSortedMapTest { @Test public void testEntrySetIteratorRemove() { - SmallSortedMap map = SmallSortedMap.newInstanceForTest(3); - for (int i = 0; i < 6; i++) { + SmallSortedMap map = SmallSortedMap.newInstanceForTest(); + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { assertThat(map.put(i, i + 1)).isNull(); } Iterator> it = map.entrySet().iterator(); - for (int i = 0; i < 6; i++) { + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { assertThat(map).containsKey(i); it.next(); it.remove(); assertThat(map).doesNotContainKey(i); - assertThat(map).hasSize(6 - i - 1); + assertThat(map).hasSize(32 - i - 1); } } @Test public void testMapEntryModification() { - SmallSortedMap map = SmallSortedMap.newInstanceForTest(3); - for (int i = 0; i < 6; i++) { + SmallSortedMap map = SmallSortedMap.newInstanceForTest(); + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { assertThat(map.put(i, i + 1)).isNull(); } Iterator> it = map.entrySet().iterator(); - for (int i = 0; i < 6; i++) { + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { Map.Entry entry = it.next(); entry.setValue(i + 23); } - for (int i = 0; i < 6; i++) { + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { assertThat(map).containsEntry(i, Integer.valueOf(i + 23)); } } @Test public void testMakeImmutable() { - SmallSortedMap map = SmallSortedMap.newInstanceForTest(3); - for (int i = 0; i < 6; i++) { + SmallSortedMap map = SmallSortedMap.newInstanceForTest(); + for (int i = 0; i < DEFAULT_FIELD_MAP_ARRAY_SIZE * 2; i++) { assertThat(map.put(i, i + 1)).isNull(); } map.makeImmutable(); assertThat(map).containsEntry(0, Integer.valueOf(1)); - assertThat(map).hasSize(6); + assertThat(map).hasSize(DEFAULT_FIELD_MAP_ARRAY_SIZE * 2); try { map.put(23, 23); diff --git a/java/core/src/test/java/com/google/protobuf/TextFormatTest.java b/java/core/src/test/java/com/google/protobuf/TextFormatTest.java index e2d637c54..54d0f3ba3 100644 --- a/java/core/src/test/java/com/google/protobuf/TextFormatTest.java +++ b/java/core/src/test/java/com/google/protobuf/TextFormatTest.java @@ -11,6 +11,7 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static com.google.protobuf.TestUtil.TEST_REQUIRED_INITIALIZED; import static com.google.protobuf.TestUtil.TEST_REQUIRED_UNINITIALIZED; +import static protobuf_unittest.UnittestProto.optionalInt32Extension; import static org.junit.Assert.assertThrows; import com.google.protobuf.DescriptorProtos.DescriptorProto; @@ -24,6 +25,11 @@ import com.google.protobuf.TextFormat.Parser.SingularOverwritePolicy; import com.google.protobuf.testing.proto.TestProto3Optional; import com.google.protobuf.testing.proto.TestProto3Optional.NestedEnum; import any_test.AnyTestProto.TestAny; +import editions_unittest.GroupLikeFileScope; +import editions_unittest.MessageImport; +import editions_unittest.NotGroupLikeScope; +import editions_unittest.TestDelimited; +import editions_unittest.UnittestDelimited; import map_test.MapTestProto.TestMap; import protobuf_unittest.UnittestMset.TestMessageSetExtension1; import protobuf_unittest.UnittestMset.TestMessageSetExtension2; @@ -606,6 +612,83 @@ public class TextFormatTest { assertThat(actual).isEqualTo(expected); } + @Test + public void testPrintAny_anyWithDynamicMessageContainingExtensionTreatedAsUnknown() + throws Exception { + Descriptor descriptor = + createDescriptorForAny( + FieldDescriptorProto.newBuilder() + .setName("type_url") + .setNumber(1) + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setType(FieldDescriptorProto.Type.TYPE_STRING) + .build(), + FieldDescriptorProto.newBuilder() + .setName("value") + .setNumber(2) + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setType(FieldDescriptorProto.Type.TYPE_BYTES) + .build()); + DynamicMessage testAny = + DynamicMessage.newBuilder(descriptor) + .setField( + descriptor.findFieldByNumber(1), + "type.googleapis.com/" + TestAllExtensions.getDescriptor().getFullName()) + .setField( + descriptor.findFieldByNumber(2), + TestAllExtensions.newBuilder() + .setExtension(optionalInt32Extension, 12345) + .build() + .toByteString()) + .build(); + String actual = + TextFormat.printer() + .usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build()) + .printToString(testAny); + String expected = "[type.googleapis.com/protobuf_unittest.TestAllExtensions] {\n 1: 12345\n}\n"; + assertThat(actual).isEqualTo(expected); + } + + @Test + public void testPrintAny_anyWithDynamicMessageContainingExtensionWithRegistry() throws Exception { + Descriptor descriptor = + createDescriptorForAny( + FieldDescriptorProto.newBuilder() + .setName("type_url") + .setNumber(1) + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setType(FieldDescriptorProto.Type.TYPE_STRING) + .build(), + FieldDescriptorProto.newBuilder() + .setName("value") + .setNumber(2) + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setType(FieldDescriptorProto.Type.TYPE_BYTES) + .build()); + DynamicMessage testAny = + DynamicMessage.newBuilder(descriptor) + .setField( + descriptor.findFieldByNumber(1), + "type.googleapis.com/" + TestAllExtensions.getDescriptor().getFullName()) + .setField( + descriptor.findFieldByNumber(2), + TestAllExtensions.newBuilder() + .setExtension(optionalInt32Extension, 12345) + .build() + .toByteString()) + .build(); + String actual = + TextFormat.printer() + .usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build()) + .usingExtensionRegistry(TestUtil.getFullExtensionRegistry()) + .printToString(testAny); + String expected = + "[type.googleapis.com/protobuf_unittest.TestAllExtensions] {\n" + + " [protobuf_unittest.optional_int32_extension]: 12345\n" + + "}\n"; + assertThat(actual).isEqualTo(expected); + } + @Test public void testPrintAny_anyFromWithNoValueField() throws Exception { Descriptor descriptor = @@ -1495,6 +1578,202 @@ public class TextFormatTest { "1:17: Couldn't parse integer: For input string: \"[\"", "optional_int32: []\n"); } + // ======================================================================= + // test delimited + + @Test + public void testPrintGroupLikeDelimited() throws Exception { + TestDelimited message = + TestDelimited.newBuilder() + .setGroupLike(TestDelimited.GroupLike.newBuilder().setA(1).build()) + .build(); + assertThat(TextFormat.printer().printToString(message)).isEqualTo("GroupLike {\n a: 1\n}\n"); + } + + @Test + public void testPrintGroupLikeDelimitedExtension() throws Exception { + TestDelimited message = + TestDelimited.newBuilder() + .setExtension( + UnittestDelimited.groupLikeFileScope, + GroupLikeFileScope.newBuilder().setA(1).build()) + .build(); + assertThat(TextFormat.printer().printToString(message)) + .isEqualTo("[editions_unittest.grouplikefilescope] {\n a: 1\n}\n"); + } + + @Test + public void testPrintGroupLikeNotDelimited() throws Exception { + TestDelimited message = + TestDelimited.newBuilder() + .setLengthprefixed(TestDelimited.LengthPrefixed.newBuilder().setA(1).build()) + .build(); + assertThat(TextFormat.printer().printToString(message)) + .isEqualTo("lengthprefixed {\n a: 1\n}\n"); + } + + @Test + public void testPrintGroupLikeMismatchedName() throws Exception { + TestDelimited message = + TestDelimited.newBuilder() + .setNotgrouplike(TestDelimited.GroupLike.newBuilder().setA(1).build()) + .build(); + assertThat(TextFormat.printer().printToString(message)) + .isEqualTo("notgrouplike {\n a: 1\n}\n"); + } + + @Test + public void testPrintGroupLikeExtensionMismatchedName() throws Exception { + TestDelimited message = + TestDelimited.newBuilder() + .setExtension( + UnittestDelimited.notGroupLikeScope, NotGroupLikeScope.newBuilder().setA(1).build()) + .build(); + assertThat(TextFormat.printer().printToString(message)) + .isEqualTo("[editions_unittest.not_group_like_scope] {\n a: 1\n}\n"); + } + + @Test + public void testPrintGroupLikeMismatchedScope() throws Exception { + TestDelimited message = + TestDelimited.newBuilder() + .setNotgrouplikescope(NotGroupLikeScope.newBuilder().setA(1).build()) + .build(); + assertThat(TextFormat.printer().printToString(message)) + .isEqualTo("notgrouplikescope {\n a: 1\n}\n"); + } + + @Test + public void testPrintGroupLikeExtensionMismatchedScope() throws Exception { + TestDelimited message = + TestDelimited.newBuilder() + .setExtension( + UnittestDelimited.grouplike, TestDelimited.GroupLike.newBuilder().setA(1).build()) + .build(); + assertThat(TextFormat.printer().printToString(message)) + .isEqualTo("[editions_unittest.grouplike] {\n a: 1\n}\n"); + } + + @Test + public void testPrintGroupLikeMismatchedFile() throws Exception { + TestDelimited message = + TestDelimited.newBuilder() + .setMessageimport(MessageImport.newBuilder().setA(1).build()) + .build(); + assertThat(TextFormat.printer().printToString(message)) + .isEqualTo("messageimport {\n a: 1\n}\n"); + } + + @Test + public void testParseDelimitedGroupLikeType() throws Exception { + TestDelimited.Builder message = TestDelimited.newBuilder(); + TextFormat.merge("GroupLike { a: 1 }", message); + assertThat(message.build()) + .isEqualTo( + TestDelimited.newBuilder() + .setGroupLike(TestDelimited.GroupLike.newBuilder().setA(1).build()) + .build()); + } + + @Test + public void testParseDelimitedGroupLikeField() throws Exception { + TestDelimited.Builder message = TestDelimited.newBuilder(); + TextFormat.merge("grouplike { a: 2 }", message); + assertThat(message.build()) + .isEqualTo( + TestDelimited.newBuilder() + .setGroupLike(TestDelimited.GroupLike.newBuilder().setA(2).build()) + .build()); + } + + @Test + public void testParseDelimitedGroupLikeExtension() throws Exception { + TestDelimited.Builder message = TestDelimited.newBuilder(); + ExtensionRegistry registry = ExtensionRegistry.newInstance(); + registry.add(UnittestDelimited.grouplike); + TextFormat.merge("[editions_unittest.grouplike] { a: 2 }", registry, message); + assertThat(message.build()) + .isEqualTo( + TestDelimited.newBuilder() + .setExtension( + UnittestDelimited.grouplike, + TestDelimited.GroupLike.newBuilder().setA(2).build()) + .build()); + } + + @Test + public void testParseDelimitedGroupLikeInvalid() throws Exception { + TestDelimited.Builder message = TestDelimited.newBuilder(); + try { + TextFormat.merge("GROUPlike { a: 3 }", message); + assertWithMessage("Expected parse exception.").fail(); + } catch (TextFormat.ParseException e) { + assertThat(e) + .hasMessageThat() + .isEqualTo( + "1:1: Input contains unknown fields and/or extensions:\n" + + "1:1:\teditions_unittest.TestDelimited.GROUPlike"); + } + } + + @Test + public void testParseDelimitedGroupLikeInvalidExtension() throws Exception { + TestDelimited.Builder message = TestDelimited.newBuilder(); + ExtensionRegistry registry = ExtensionRegistry.newInstance(); + registry.add(UnittestDelimited.grouplike); + try { + TextFormat.merge("[editions_unittest.GroupLike] { a: 2 }", registry, message); + assertWithMessage("Expected parse exception.").fail(); + } catch (TextFormat.ParseException e) { + assertThat(e) + .hasMessageThat() + .isEqualTo( + "1:20: Input contains unknown fields and/or extensions:\n" + + "1:20:\teditions_unittest.TestDelimited.[editions_unittest.GroupLike]"); + } + } + + @Test + public void testParseDelimited() throws Exception { + TestDelimited.Builder message = TestDelimited.newBuilder(); + TextFormat.merge("notgrouplike { b: 3 }", message); + assertThat(message.build()) + .isEqualTo( + TestDelimited.newBuilder() + .setNotgrouplike(TestDelimited.GroupLike.newBuilder().setB(3).build()) + .build()); + } + + @Test + public void testParseDelimitedInvalid() throws Exception { + TestDelimited.Builder message = TestDelimited.newBuilder(); + try { + TextFormat.merge("NotGroupLike { a: 3 }", message); + assertWithMessage("Expected parse exception.").fail(); + } catch (TextFormat.ParseException e) { + assertThat(e) + .hasMessageThat() + .isEqualTo( + "1:1: Input contains unknown fields and/or extensions:\n" + + "1:1:\teditions_unittest.TestDelimited.NotGroupLike"); + } + } + + @Test + public void testParseDelimitedInvalidScope() throws Exception { + TestDelimited.Builder message = TestDelimited.newBuilder(); + try { + TextFormat.merge("NotGroupLikeScope { a: 3 }", message); + assertWithMessage("Expected parse exception.").fail(); + } catch (TextFormat.ParseException e) { + assertThat(e) + .hasMessageThat() + .isEqualTo( + "1:1: Input contains unknown fields and/or extensions:\n" + + "1:1:\teditions_unittest.TestDelimited.NotGroupLikeScope"); + } + } + // ======================================================================= // test oneof diff --git a/java/core/src/test/java/com/google/protobuf/Utf8Test.java b/java/core/src/test/java/com/google/protobuf/Utf8Test.java index 986702de4..2a53e8204 100644 --- a/java/core/src/test/java/com/google/protobuf/Utf8Test.java +++ b/java/core/src/test/java/com/google/protobuf/Utf8Test.java @@ -194,7 +194,7 @@ public class Utf8Test { private static byte[] encodeToByteArray(String message, int length, Utf8.Processor processor) { byte[] output = new byte[length]; - processor.encodeUtf8(message, output, 0, output.length); + int unused = processor.encodeUtf8(message, output, 0, output.length); return output; } diff --git a/java/core/src/test/java/com/google/protobuf/WireFormatTest.java b/java/core/src/test/java/com/google/protobuf/WireFormatTest.java index 020409e9c..bbf8d0cba 100644 --- a/java/core/src/test/java/com/google/protobuf/WireFormatTest.java +++ b/java/core/src/test/java/com/google/protobuf/WireFormatTest.java @@ -25,6 +25,7 @@ import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.List; +import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -39,6 +40,13 @@ public class WireFormatTest { TestMessageSetExtension2.getDescriptor().getExtensions().get(0).getNumber(); private static final int UNKNOWN_TYPE_ID = 1550055; + @After + public void tearDown() { + // Whether to parse message sets eagerly is stored in a global static. Since some tests modify + // the value, make sure to reset it between test runs. + ExtensionRegistryLite.setEagerlyParseMessageSets(false); + } + @Test public void testSerialization() throws Exception { TestAllTypes message = TestUtil.getAllSet(); diff --git a/java/core/src/test/proto/com/google/protobuf/any_test.proto b/java/core/src/test/proto/com/google/protobuf/any_test.proto index d180f710f..36453b591 100644 --- a/java/core/src/test/proto/com/google/protobuf/any_test.proto +++ b/java/core/src/test/proto/com/google/protobuf/any_test.proto @@ -5,7 +5,7 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -syntax = "proto3"; +edition = "2023"; package any_test; diff --git a/java/core/src/test/proto/com/google/protobuf/cached_field_size_test.proto b/java/core/src/test/proto/com/google/protobuf/cached_field_size_test.proto index bcb219e29..bfc81e8ed 100644 --- a/java/core/src/test/proto/com/google/protobuf/cached_field_size_test.proto +++ b/java/core/src/test/proto/com/google/protobuf/cached_field_size_test.proto @@ -6,7 +6,7 @@ // https://developers.google.com/open-source/licenses/bsd // A regression test for b/74087933 -syntax = "proto2"; +edition = "2023"; package protobuf_unittest; @@ -17,6 +17,6 @@ option optimize_for = CODE_SIZE; option java_multiple_files = true; message TestCachedFieldSizeMessage { - optional protobuf_unittest.TestPackedTypes proto2_child = 1; - optional proto3_unittest.TestPackedTypes proto3_child = 2; + protobuf_unittest.TestPackedTypes proto2_child = 1; + proto3_unittest.TestPackedTypes proto3_child = 2; } diff --git a/java/core/src/test/proto/com/google/protobuf/dynamic_message_test.proto b/java/core/src/test/proto/com/google/protobuf/dynamic_message_test.proto index 170e6b5be..62466ffb3 100644 --- a/java/core/src/test/proto/com/google/protobuf/dynamic_message_test.proto +++ b/java/core/src/test/proto/com/google/protobuf/dynamic_message_test.proto @@ -5,10 +5,11 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -syntax = "proto2"; +edition = "2023"; package dynamic_message_test; +option features.utf8_validation = NONE; option java_package = "dynamicmessagetest"; option java_outer_classname = "DynamicMessageTestProto"; diff --git a/java/core/src/test/proto/com/google/protobuf/field_presence_test.proto b/java/core/src/test/proto/com/google/protobuf/field_presence_test.proto index 638ffc1d7..d73822260 100644 --- a/java/core/src/test/proto/com/google/protobuf/field_presence_test.proto +++ b/java/core/src/test/proto/com/google/protobuf/field_presence_test.proto @@ -5,12 +5,13 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -syntax = "proto3"; +edition = "2023"; package field_presence_test; import "google/protobuf/unittest.proto"; +option features.field_presence = IMPLICIT; option java_package = "com.google.protobuf"; option java_outer_classname = "FieldPresenceTestProto"; @@ -20,6 +21,7 @@ message TestAllTypes { BAR = 1; BAZ = 2; } + message NestedMessage { int32 value = 1; } @@ -48,7 +50,7 @@ message TestAllTypes { repeated NestedEnum repeated_nested_enum = 24; repeated NestedMessage repeated_nested_message = 25; repeated protobuf_unittest.TestRequired repeated_proto2_message = 26; - repeated NestedEnum packed_nested_enum = 27 [packed = true]; + repeated NestedEnum packed_nested_enum = 27; } message TestOptionalFieldsOnly { diff --git a/java/core/src/test/proto/com/google/protobuf/lazy_fields_lite.proto b/java/core/src/test/proto/com/google/protobuf/lazy_fields_lite.proto index acd28cd3c..381423e52 100644 --- a/java/core/src/test/proto/com/google/protobuf/lazy_fields_lite.proto +++ b/java/core/src/test/proto/com/google/protobuf/lazy_fields_lite.proto @@ -9,15 +9,19 @@ // // A proto file with lazy fields -syntax = "proto2"; +edition = "2023"; package protobuf_unittest; +option features.utf8_validation = NONE; + message LazyMessageLite { - optional int32 num = 1; - optional int32 num_with_default = 2 [default = 421]; - optional LazyInnerMessageLite inner = 3 [lazy = true]; - repeated LazyInnerMessageLite repeated_inner = 4 [lazy = true]; + int32 num = 1; + int32 num_with_default = 2 [default = 421]; + + LazyInnerMessageLite inner = 3 [lazy = true]; + + repeated LazyInnerMessageLite repeated_inner = 4; oneof oneof_field { int32 oneof_num = 5; @@ -26,21 +30,23 @@ message LazyMessageLite { } message LazyInnerMessageLite { - optional int32 num = 1; - optional int32 num_with_default = 2 [default = 42]; - optional LazyNestedInnerMessageLite nested = 3 [lazy = true]; + int32 num = 1; + int32 num_with_default = 2 [default = 42]; + + LazyNestedInnerMessageLite nested = 3 [lazy = true]; extensions 1000 to max; } message LazyExtension { extend LazyInnerMessageLite { - optional LazyExtension extension = 1000; + LazyExtension extension = 1000; } - optional string name = 1; + + string name = 1; } message LazyNestedInnerMessageLite { - optional int32 num = 1; - optional int32 num_with_default = 2 [default = 4]; + int32 num = 1; + int32 num_with_default = 2 [default = 4]; } diff --git a/java/core/src/test/proto/com/google/protobuf/lite_equals_and_hash.proto b/java/core/src/test/proto/com/google/protobuf/lite_equals_and_hash.proto index 3dabb1633..141ba3141 100644 --- a/java/core/src/test/proto/com/google/protobuf/lite_equals_and_hash.proto +++ b/java/core/src/test/proto/com/google/protobuf/lite_equals_and_hash.proto @@ -7,10 +7,11 @@ // Author: pbogle@google.com (Phil Bogle) -syntax = "proto2"; +edition = "2023"; package protobuf_unittest.lite_equals_and_hash; +option features.utf8_validation = NONE; message TestOneofEquals { oneof oneof_field { @@ -20,15 +21,17 @@ message TestOneofEquals { } message Foo { - optional int32 value = 1; + int32 value = 1; repeated Bar bar = 2; map my_map = 3; + oneof Single { sint64 sint64 = 4; - // LINT: ALLOW_GROUPS - group MyGroup = 5 { - optional int32 value = 1; - } + MyGroup mygroup = 5 [features.message_encoding = DELIMITED]; + } + + message MyGroup { + int32 value = 1; } extensions 100 to max; @@ -36,25 +39,27 @@ message Foo { message Bar { extend Foo { - optional Bar foo_ext = 100; + Bar foo_ext = 100; } - optional string name = 1; + string name = 1; } message BarPrime { - optional string name = 1; + string name = 1; } message Empty {} extend Foo { - optional int32 varint = 101; - optional fixed32 fixed32 = 102; - optional fixed64 fixed64 = 103; - optional group MyGroup = 104 { - optional string group_value = 1; - } + int32 varint = 101; + fixed32 fixed32 = 102; + fixed64 fixed64 = 103; + MyGroup mygroup = 104 [features.message_encoding = DELIMITED]; +} + +message MyGroup { + string group_value = 1; } message TestRecursiveOneof { diff --git a/java/core/src/test/proto/com/google/protobuf/map_for_proto2_lite_test.proto b/java/core/src/test/proto/com/google/protobuf/map_for_proto2_lite_test.proto index fb5320d84..ca551699e 100644 --- a/java/core/src/test/proto/com/google/protobuf/map_for_proto2_lite_test.proto +++ b/java/core/src/test/proto/com/google/protobuf/map_for_proto2_lite_test.proto @@ -5,18 +5,21 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -syntax = "proto2"; +edition = "2023"; package map_for_proto2_lite_test; +option features.enum_type = CLOSED; +option features.utf8_validation = NONE; option java_outer_classname = "MapForProto2TestProto"; option optimize_for = LITE_RUNTIME; option java_package = "map_lite_test"; message TestMap { message MessageValue { - optional int32 value = 1; + int32 value = 1; } + enum EnumValue { FOO = 0; BAR = 1; @@ -32,8 +35,9 @@ message TestMap { map string_to_int32_field = 6; message MessageWithRequiredFields { - required int32 value = 1; + int32 value = 1 [features.field_presence = LEGACY_REQUIRED]; } + map required_message_map = 11; } @@ -46,7 +50,7 @@ message TestUnknownEnumValue { // Test that the maps initialization code works correctly when the map field // references the containing message. message TestRecursiveMap { - optional int32 value = 1; + int32 value = 1; map recursive_map_field = 2; } @@ -54,14 +58,19 @@ message TestRecursiveMap { message BizarroTestMap { // same key type, different value map int32_to_int32_field = 1; + // different key and value types map int32_to_string_field = 2; + // different key types, same value map int32_to_bytes_field = 3; + // different key and value types map int32_to_enum_field = 4; + // different key and value types map int32_to_message_field = 5; + // same key type, different value map string_to_int32_field = 6; } @@ -77,11 +86,14 @@ message ReservedAsMapField { map class = 4; map int = 5; map void = 6; + // These are also proto keywords map string = 7; map package = 8; + // Most recent Java reserved word map enum = 9; + // null is not a 'reserved word' per se but as a literal needs similar care map null = 10; } @@ -91,17 +103,21 @@ message ReservedAsMapFieldWithEnumValue { A = 0; B = 1; } + map if = 1; map const = 2; map private = 3; map class = 4; map int = 5; map void = 6; + // These are also proto keywords map string = 7; map package = 8; + // Most recent Java reserved word map enum = 9; + // null is not a 'reserved word' per se but as a literal needs similar care map null = 10; } diff --git a/java/core/src/test/proto/com/google/protobuf/map_for_proto2_test.proto b/java/core/src/test/proto/com/google/protobuf/map_for_proto2_test.proto index 90d17cf51..f5b989d38 100644 --- a/java/core/src/test/proto/com/google/protobuf/map_for_proto2_test.proto +++ b/java/core/src/test/proto/com/google/protobuf/map_for_proto2_test.proto @@ -5,17 +5,20 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -syntax = "proto2"; +edition = "2023"; package map_for_proto2_test; +option features.enum_type = CLOSED; +option features.utf8_validation = NONE; option java_package = "map_test"; option java_outer_classname = "MapForProto2TestProto"; message TestMap { message MessageValue { - optional int32 value = 1; + int32 value = 1; } + enum EnumValue { FOO = 0; BAR = 1; @@ -31,8 +34,9 @@ message TestMap { map string_to_int32_field = 6; message MessageWithRequiredFields { - required int32 value = 1; + int32 value = 1 [features.field_presence = LEGACY_REQUIRED]; } + map required_message_map = 11; } @@ -45,7 +49,7 @@ message TestUnknownEnumValue { // Test that the maps initialization code works correctly when the map field // references the containing message. message TestRecursiveMap { - optional int32 value = 1; + int32 value = 1; map recursive_map_field = 2; } @@ -53,14 +57,19 @@ message TestRecursiveMap { message BizarroTestMap { // same key type, different value map int32_to_int32_field = 1; + // different key and value types map int32_to_string_field = 2; + // different key types, same value map int32_to_bytes_field = 3; + // different key and value types map int32_to_enum_field = 4; + // different key and value types map int32_to_message_field = 5; + // same key type, different value map string_to_int32_field = 6; } @@ -76,11 +85,14 @@ message ReservedAsMapField { map class = 4; map int = 5; map void = 6; + // These are also proto keywords map string = 7; map package = 8; + // Most recent Java reserved word map enum = 9; + // null is not a 'reserved word' per se but as a literal needs similar care map null = 10; } @@ -90,17 +102,21 @@ message ReservedAsMapFieldWithEnumValue { A = 0; B = 1; } + map if = 1; map const = 2; map private = 3; map class = 4; map int = 5; map void = 6; + // These are also proto keywords map string = 7; map package = 8; + // Most recent Java reserved word map enum = 9; + // null is not a 'reserved word' per se but as a literal needs similar care map null = 10; } diff --git a/java/core/src/test/proto/com/google/protobuf/map_initialization_order_test.proto b/java/core/src/test/proto/com/google/protobuf/map_initialization_order_test.proto index b816d94ea..9683c01eb 100644 --- a/java/core/src/test/proto/com/google/protobuf/map_initialization_order_test.proto +++ b/java/core/src/test/proto/com/google/protobuf/map_initialization_order_test.proto @@ -14,10 +14,11 @@ // a. getDescriptor() is called on another message in the same file. // b. use protobuf reflection to access the map field. // The symptom is a NullPointerException being thrown. -syntax = "proto2"; +edition = "2023"; package map_test; +option features.utf8_validation = NONE; option java_package = "map_test"; option java_outer_classname = "MapInitializationOrderTest"; option java_multiple_files = true; @@ -27,11 +28,12 @@ option java_multiple_files = true; message Message1 { map map_field = 1; + extensions 1000 to max; } extend Message1 { - optional Message1 recursive_extension = 1001; + Message1 recursive_extension = 1001; } message RedactAllTypes {} diff --git a/java/core/src/test/proto/com/google/protobuf/map_lite_test.proto b/java/core/src/test/proto/com/google/protobuf/map_lite_test.proto index 636a5466f..c40ea5461 100644 --- a/java/core/src/test/proto/com/google/protobuf/map_lite_test.proto +++ b/java/core/src/test/proto/com/google/protobuf/map_lite_test.proto @@ -5,10 +5,12 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -syntax = "proto3"; +edition = "2023"; package map_lite_test; +option features.field_presence = IMPLICIT; + option optimize_for = LITE_RUNTIME; option java_package = "map_lite_test"; option java_outer_classname = "MapTestProto"; @@ -17,6 +19,7 @@ message TestMap { message MessageValue { int32 value = 1; } + enum EnumValue { FOO = 0; BAR = 1; @@ -45,14 +48,19 @@ message TestOnChangeEventPropagation { message BizarroTestMap { // same key type, different value map int32_to_int32_field = 1; + // different key and value types map int32_to_string_field = 2; + // different key types, same value map int32_to_bytes_field = 3; + // different key and value types map int32_to_enum_field = 4; + // different key and value types map int32_to_message_field = 5; + // same key type, different value map string_to_int32_field = 6; } @@ -68,11 +76,14 @@ message ReservedAsMapField { map class = 4; map int = 5; map void = 6; + // These are also proto keywords map string = 7; map package = 8; + // Most recent Java reserved word map enum = 9; + // null is not a 'reserved word' per se but as a literal needs similar care map null = 10; } @@ -82,17 +93,21 @@ message ReservedAsMapFieldWithEnumValue { A = 0; B = 1; } + map if = 1; map const = 2; map private = 3; map class = 4; map int = 5; map void = 6; + // These are also proto keywords map string = 7; map package = 8; + // Most recent Java reserved word map enum = 9; + // null is not a 'reserved word' per se but as a literal needs similar care map null = 10; } diff --git a/java/core/src/test/proto/com/google/protobuf/map_test.proto b/java/core/src/test/proto/com/google/protobuf/map_test.proto index 7b250a6ef..80bdc0d85 100644 --- a/java/core/src/test/proto/com/google/protobuf/map_test.proto +++ b/java/core/src/test/proto/com/google/protobuf/map_test.proto @@ -5,10 +5,12 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -syntax = "proto3"; +edition = "2023"; package map_test; +option features.field_presence = IMPLICIT; + option java_package = "map_test"; option java_outer_classname = "MapTestProto"; @@ -16,6 +18,7 @@ message TestMap { message MessageValue { int32 value = 1; } + enum EnumValue { FOO = 0; BAR = 1; @@ -44,14 +47,19 @@ message TestOnChangeEventPropagation { message BizarroTestMap { // same key type, different value map int32_to_int32_field = 1; + // different key and value types map int32_to_string_field = 2; + // different key types, same value map int32_to_bytes_field = 3; + // different key and value types map int32_to_enum_field = 4; + // different key and value types map int32_to_message_field = 5; + // same key type, different value map string_to_int32_field = 6; } @@ -67,11 +75,14 @@ message ReservedAsMapField { map class = 4; map int = 5; map void = 6; + // These are also proto keywords map string = 7; map package = 8; + // Most recent Java reserved word map enum = 9; + // null is not a 'reserved word' per se but as a literal needs similar care map null = 10; } @@ -81,17 +92,21 @@ message ReservedAsMapFieldWithEnumValue { A = 0; B = 1; } + map if = 1; map const = 2; map private = 3; map class = 4; map int = 5; map void = 6; + // These are also proto keywords map string = 7; map package = 8; + // Most recent Java reserved word map enum = 9; + // null is not a 'reserved word' per se but as a literal needs similar care map null = 10; } diff --git a/java/core/src/test/proto/com/google/protobuf/multiple_files_test.proto b/java/core/src/test/proto/com/google/protobuf/multiple_files_test.proto index baadf0616..57ae5fed2 100644 --- a/java/core/src/test/proto/com/google/protobuf/multiple_files_test.proto +++ b/java/core/src/test/proto/com/google/protobuf/multiple_files_test.proto @@ -9,7 +9,7 @@ // // A proto file which tests the java_multiple_files option. -syntax = "proto2"; +edition = "2023"; // Some generic_services option(s) added automatically. // See: http://go/proto2-generic-services-default @@ -18,29 +18,33 @@ package protobuf_unittest; import "google/protobuf/descriptor.proto"; import "google/protobuf/unittest.proto"; +option features.enum_type = CLOSED; option java_generic_services = true; // auto-added option java_multiple_files = true; option java_outer_classname = "MultipleFilesTestProto"; message MessageWithNoOuter { message NestedMessage { - optional int32 i = 1; + int32 i = 1; } + enum NestedEnum { BAZ = 3; } - optional NestedMessage nested = 1; + + NestedMessage nested = 1; repeated TestAllTypes foreign = 2; - optional NestedEnum nested_enum = 3; - optional EnumWithNoOuter foreign_enum = 4; + NestedEnum nested_enum = 3; + EnumWithNoOuter foreign_enum = 4; } extend google.protobuf.EnumValueOptions { - optional int32 enum_value_option = 7654321; + int32 enum_value_option = 7654321; } enum EnumWithNoOuter { FOO = 1 [(enum_value_option) = 12345]; + BAR = 2; } @@ -49,5 +53,5 @@ service ServiceWithNoOuter { } extend TestAllExtensions { - optional int32 extension_with_outer = 1234567; + int32 extension_with_outer = 1234567; } diff --git a/java/core/src/test/proto/com/google/protobuf/nested_builders_test.proto b/java/core/src/test/proto/com/google/protobuf/nested_builders_test.proto index 04a50047b..c5ce68c1c 100644 --- a/java/core/src/test/proto/com/google/protobuf/nested_builders_test.proto +++ b/java/core/src/test/proto/com/google/protobuf/nested_builders_test.proto @@ -6,8 +6,7 @@ // https://developers.google.com/open-source/licenses/bsd // Author: jonp@google.com (Jon Perlow) -// -syntax = "proto2"; +edition = "2023"; package protobuf_unittest; @@ -15,21 +14,21 @@ option java_multiple_files = true; option java_outer_classname = "NestedBuilders"; message Vehicle { - optional Engine engine = 1; + Engine engine = 1; repeated Wheel wheel = 2; } message Engine { - optional int32 cylinder = 1; - optional int32 liters = 2; - optional TimingBelt timing_belt = 3; + int32 cylinder = 1; + int32 liters = 2; + TimingBelt timing_belt = 3; } message TimingBelt { - optional int32 number_of_teeth = 1; + int32 number_of_teeth = 1; } message Wheel { - optional int32 radius = 1; - optional int32 width = 2; + int32 radius = 1; + int32 width = 2; } diff --git a/java/core/src/test/proto/com/google/protobuf/nested_extension.proto b/java/core/src/test/proto/com/google/protobuf/nested_extension.proto index ef7c56086..1427c5727 100644 --- a/java/core/src/test/proto/com/google/protobuf/nested_extension.proto +++ b/java/core/src/test/proto/com/google/protobuf/nested_extension.proto @@ -10,7 +10,9 @@ // A proto file with nested extensions. Note that this must be defined in // a separate file to properly test the initialization of the outer class. -syntax = "proto2"; +// LINT: LEGACY_NAMES + +edition = "2023"; package protobuf_unittest; @@ -19,7 +21,7 @@ import "com/google/protobuf/non_nested_extension.proto"; message MyNestedExtension { extend MessageToBeExtended { - optional MessageToBeExtended recursiveExtension = 2; - optional int32 default = 2002; + MessageToBeExtended recursiveExtension = 2; + int32 default = 2002; } } diff --git a/java/core/src/test/proto/com/google/protobuf/nested_extension_lite.proto b/java/core/src/test/proto/com/google/protobuf/nested_extension_lite.proto index c409b1a3c..c5fa560f7 100644 --- a/java/core/src/test/proto/com/google/protobuf/nested_extension_lite.proto +++ b/java/core/src/test/proto/com/google/protobuf/nested_extension_lite.proto @@ -11,7 +11,9 @@ // this must be defined in a separate file to properly test the initialization // of the outer class. -syntax = "proto2"; +// LINT: LEGACY_NAMES + +edition = "2023"; package protobuf_unittest; @@ -21,7 +23,7 @@ option optimize_for = LITE_RUNTIME; message MyNestedExtensionLite { extend MessageLiteToBeExtended { - optional MessageLiteToBeExtended recursiveExtensionLite = 3; - optional double private = 2004; + MessageLiteToBeExtended recursiveExtensionLite = 3; + double private = 2004; } } diff --git a/java/core/src/test/proto/com/google/protobuf/non_nested_extension.proto b/java/core/src/test/proto/com/google/protobuf/non_nested_extension.proto index 4ab97c81e..230f56185 100644 --- a/java/core/src/test/proto/com/google/protobuf/non_nested_extension.proto +++ b/java/core/src/test/proto/com/google/protobuf/non_nested_extension.proto @@ -9,10 +9,13 @@ // // A proto file with extensions. -syntax = "proto2"; +// LINT: LEGACY_NAMES + +edition = "2023"; package protobuf_unittest; +option features.utf8_validation = NONE; message MessageToBeExtended { extensions 1 to max; @@ -21,6 +24,6 @@ message MessageToBeExtended { message MyNonNestedExtension {} extend MessageToBeExtended { - optional MyNonNestedExtension nonNestedExtension = 1; - optional string if = 2000; + MyNonNestedExtension nonNestedExtension = 1; + string if = 2000; } diff --git a/java/core/src/test/proto/com/google/protobuf/non_nested_extension_lite.proto b/java/core/src/test/proto/com/google/protobuf/non_nested_extension_lite.proto index d5051a440..f25fb6311 100644 --- a/java/core/src/test/proto/com/google/protobuf/non_nested_extension_lite.proto +++ b/java/core/src/test/proto/com/google/protobuf/non_nested_extension_lite.proto @@ -9,7 +9,9 @@ // // A proto file with extensions for a MessageLite messages. -syntax = "proto2"; +// LINT: LEGACY_NAMES + +edition = "2023"; package protobuf_unittest; @@ -22,6 +24,6 @@ message MessageLiteToBeExtended { message MyNonNestedExtensionLite {} extend MessageLiteToBeExtended { - optional MyNonNestedExtensionLite nonNestedExtensionLite = 1; - optional bool package = 2006; + MyNonNestedExtensionLite nonNestedExtensionLite = 1; + bool package = 2006; } diff --git a/java/core/src/test/proto/com/google/protobuf/outer_class_name_test.proto b/java/core/src/test/proto/com/google/protobuf/outer_class_name_test.proto index 3020616e1..fbbc4dcc6 100644 --- a/java/core/src/test/proto/com/google/protobuf/outer_class_name_test.proto +++ b/java/core/src/test/proto/com/google/protobuf/outer_class_name_test.proto @@ -5,7 +5,7 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -syntax = "proto2"; +edition = "2023"; package protobuf_unittest; diff --git a/java/core/src/test/proto/com/google/protobuf/outer_class_name_test2.proto b/java/core/src/test/proto/com/google/protobuf/outer_class_name_test2.proto index 032716793..3bc3c1240 100644 --- a/java/core/src/test/proto/com/google/protobuf/outer_class_name_test2.proto +++ b/java/core/src/test/proto/com/google/protobuf/outer_class_name_test2.proto @@ -5,7 +5,7 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -syntax = "proto2"; +edition = "2023"; package protobuf_unittest; diff --git a/java/core/src/test/proto/com/google/protobuf/outer_class_name_test3.proto b/java/core/src/test/proto/com/google/protobuf/outer_class_name_test3.proto index f22298916..ba40da245 100644 --- a/java/core/src/test/proto/com/google/protobuf/outer_class_name_test3.proto +++ b/java/core/src/test/proto/com/google/protobuf/outer_class_name_test3.proto @@ -5,7 +5,7 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -syntax = "proto2"; +edition = "2023"; package protobuf_unittest; @@ -15,6 +15,10 @@ message TestMessage3 { // This enum's name is the same with the default outer class name of this // proto file. It's used to test if the compiler can avoid this conflict // correctly. - enum OuterClassNameTest3 { DUMMY_VALUE = 1; } + enum OuterClassNameTest3 { + option features.enum_type = CLOSED; + + DUMMY_VALUE = 1; + } } } diff --git a/java/core/src/test/proto/com/google/protobuf/packed_field_test.proto b/java/core/src/test/proto/com/google/protobuf/packed_field_test.proto index b53b5e33b..a522ce1a8 100644 --- a/java/core/src/test/proto/com/google/protobuf/packed_field_test.proto +++ b/java/core/src/test/proto/com/google/protobuf/packed_field_test.proto @@ -5,7 +5,7 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -syntax = "proto3"; +edition = "2023"; package packed_field_test; @@ -36,18 +36,45 @@ message TestAllTypes { } message TestUnpackedTypes { - repeated int32 repeated_int32 = 1 [packed = false]; - repeated int64 repeated_int64 = 2 [packed = false]; - repeated uint32 repeated_uint32 = 3 [packed = false]; - repeated uint64 repeated_uint64 = 4 [packed = false]; - repeated sint32 repeated_sint32 = 5 [packed = false]; - repeated sint64 repeated_sint64 = 6 [packed = false]; - repeated fixed32 repeated_fixed32 = 7 [packed = false]; - repeated fixed64 repeated_fixed64 = 8 [packed = false]; - repeated sfixed32 repeated_sfixed32 = 9 [packed = false]; - repeated sfixed64 repeated_sfixed64 = 10 [packed = false]; - repeated float repeated_float = 11 [packed = false]; - repeated double repeated_double = 12 [packed = false]; - repeated bool repeated_bool = 13 [packed = false]; - repeated TestAllTypes.NestedEnum repeated_nested_enum = 14 [packed = false]; + repeated int32 repeated_int32 = 1 + [features.repeated_field_encoding = EXPANDED]; + + repeated int64 repeated_int64 = 2 + [features.repeated_field_encoding = EXPANDED]; + + repeated uint32 repeated_uint32 = 3 + [features.repeated_field_encoding = EXPANDED]; + + repeated uint64 repeated_uint64 = 4 + [features.repeated_field_encoding = EXPANDED]; + + repeated sint32 repeated_sint32 = 5 + [features.repeated_field_encoding = EXPANDED]; + + repeated sint64 repeated_sint64 = 6 + [features.repeated_field_encoding = EXPANDED]; + + repeated fixed32 repeated_fixed32 = 7 + [features.repeated_field_encoding = EXPANDED]; + + repeated fixed64 repeated_fixed64 = 8 + [features.repeated_field_encoding = EXPANDED]; + + repeated sfixed32 repeated_sfixed32 = 9 + [features.repeated_field_encoding = EXPANDED]; + + repeated sfixed64 repeated_sfixed64 = 10 + [features.repeated_field_encoding = EXPANDED]; + + repeated float repeated_float = 11 + [features.repeated_field_encoding = EXPANDED]; + + repeated double repeated_double = 12 + [features.repeated_field_encoding = EXPANDED]; + + repeated bool repeated_bool = 13 + [features.repeated_field_encoding = EXPANDED]; + + repeated TestAllTypes.NestedEnum repeated_nested_enum = 14 + [features.repeated_field_encoding = EXPANDED]; } diff --git a/java/core/src/test/proto/com/google/protobuf/proto2_message.proto b/java/core/src/test/proto/com/google/protobuf/proto2_message.proto index 7b3411ef2..c3f74c976 100644 --- a/java/core/src/test/proto/com/google/protobuf/proto2_message.proto +++ b/java/core/src/test/proto/com/google/protobuf/proto2_message.proto @@ -6,82 +6,121 @@ // https://developers.google.com/open-source/licenses/bsd // LINT: ALLOW_GROUPS -syntax = "proto2"; +edition = "2023"; package protobuf.experimental; +option features.utf8_validation = NONE; option java_package = "com.google.protobuf.testing"; option java_outer_classname = "Proto2Testing"; message Proto2SpecialFieldName { - optional double regular_name = 1; - optional int32 cached_size = 2; - optional int64 serialized_size = 3; - optional string class = 4; + double regular_name = 1; + int32 cached_size = 2; + int64 serialized_size = 3; + string class = 4; } message Proto2Message { enum TestEnum { + option features.enum_type = CLOSED; + ZERO = 0; ONE = 1; TWO = 2; } - optional double field_double_1 = 1; - optional float field_float_2 = 2; - optional int64 field_int64_3 = 3; - optional uint64 field_uint64_4 = 4; - optional int32 field_int32_5 = 5; - optional fixed64 field_fixed64_6 = 6; - optional fixed32 field_fixed32_7 = 7; - optional bool field_bool_8 = 8; - optional string field_string_9 = 9; - optional Proto2Message field_message_10 = 10; - optional bytes field_bytes_11 = 11; - optional uint32 field_uint32_12 = 12; - optional TestEnum field_enum_13 = 13; - optional sfixed32 field_sfixed32_14 = 14; - optional sfixed64 field_sfixed64_15 = 15; - optional sint32 field_sint32_16 = 16; - optional sint64 field_sint64_17 = 17; - repeated double field_double_list_18 = 18 [packed = false]; - repeated float field_float_list_19 = 19 [packed = false]; - repeated int64 field_int64_list_20 = 20 [packed = false]; - repeated uint64 field_uint64_list_21 = 21 [packed = false]; - repeated int32 field_int32_list_22 = 22 [packed = false]; - repeated fixed64 field_fixed64_list_23 = 23 [packed = false]; - repeated fixed32 field_fixed32_list_24 = 24 [packed = false]; - repeated bool field_bool_list_25 = 25 [packed = false]; - repeated string field_string_list_26 = 26 [packed = false]; - repeated Proto2Message field_message_list_27 = 27 [packed = false]; - repeated bytes field_bytes_list_28 = 28 [packed = false]; - repeated uint32 field_uint32_list_29 = 29 [packed = false]; - repeated TestEnum field_enum_list_30 = 30 [packed = false]; - repeated sfixed32 field_sfixed32_list_31 = 31 [packed = false]; - repeated sfixed64 field_sfixed64_list_32 = 32 [packed = false]; - repeated sint32 field_sint32_list_33 = 33 [packed = false]; - repeated sint64 field_sint64_list_34 = 34 [packed = false]; - repeated double field_double_list_packed_35 = 35 [packed = true]; - repeated float field_float_list_packed_36 = 36 [packed = true]; - repeated int64 field_int64_list_packed_37 = 37 [packed = true]; - repeated uint64 field_uint64_list_packed_38 = 38 [packed = true]; - repeated int32 field_int32_list_packed_39 = 39 [packed = true]; - repeated fixed64 field_fixed64_list_packed_40 = 40 [packed = true]; - repeated fixed32 field_fixed32_list_packed_41 = 41 [packed = true]; - repeated bool field_bool_list_packed_42 = 42 [packed = true]; - repeated uint32 field_uint32_list_packed_43 = 43 [packed = true]; - repeated TestEnum field_enum_list_packed_44 = 44 [packed = true]; - repeated sfixed32 field_sfixed32_list_packed_45 = 45 [packed = true]; - repeated sfixed64 field_sfixed64_list_packed_46 = 46 [packed = true]; - repeated sint32 field_sint32_list_packed_47 = 47 [packed = true]; - repeated sint64 field_sint64_list_packed_48 = 48 [packed = true]; - optional group FieldGroup49 = 49 { - optional int32 field_int32_50 = 50; + double field_double_1 = 1; + float field_float_2 = 2; + int64 field_int64_3 = 3; + uint64 field_uint64_4 = 4; + int32 field_int32_5 = 5; + fixed64 field_fixed64_6 = 6; + fixed32 field_fixed32_7 = 7; + bool field_bool_8 = 8; + string field_string_9 = 9; + Proto2Message field_message_10 = 10; + bytes field_bytes_11 = 11; + uint32 field_uint32_12 = 12; + TestEnum field_enum_13 = 13; + sfixed32 field_sfixed32_14 = 14; + sfixed64 field_sfixed64_15 = 15; + sint32 field_sint32_16 = 16; + sint64 field_sint64_17 = 17; + repeated double field_double_list_18 = 18 + [features.repeated_field_encoding = EXPANDED]; + + repeated float field_float_list_19 = 19 + [features.repeated_field_encoding = EXPANDED]; + + repeated int64 field_int64_list_20 = 20 + [features.repeated_field_encoding = EXPANDED]; + + repeated uint64 field_uint64_list_21 = 21 + [features.repeated_field_encoding = EXPANDED]; + + repeated int32 field_int32_list_22 = 22 + [features.repeated_field_encoding = EXPANDED]; + + repeated fixed64 field_fixed64_list_23 = 23 + [features.repeated_field_encoding = EXPANDED]; + + repeated fixed32 field_fixed32_list_24 = 24 + [features.repeated_field_encoding = EXPANDED]; + + repeated bool field_bool_list_25 = 25 + [features.repeated_field_encoding = EXPANDED]; + + repeated string field_string_list_26 = 26; + repeated Proto2Message field_message_list_27 = 27; + repeated bytes field_bytes_list_28 = 28; + repeated uint32 field_uint32_list_29 = 29 + [features.repeated_field_encoding = EXPANDED]; + + repeated TestEnum field_enum_list_30 = 30 + [features.repeated_field_encoding = EXPANDED]; + + repeated sfixed32 field_sfixed32_list_31 = 31 + [features.repeated_field_encoding = EXPANDED]; + + repeated sfixed64 field_sfixed64_list_32 = 32 + [features.repeated_field_encoding = EXPANDED]; + + repeated sint32 field_sint32_list_33 = 33 + [features.repeated_field_encoding = EXPANDED]; + + repeated sint64 field_sint64_list_34 = 34 + [features.repeated_field_encoding = EXPANDED]; + + repeated double field_double_list_packed_35 = 35; + repeated float field_float_list_packed_36 = 36; + repeated int64 field_int64_list_packed_37 = 37; + repeated uint64 field_uint64_list_packed_38 = 38; + repeated int32 field_int32_list_packed_39 = 39; + repeated fixed64 field_fixed64_list_packed_40 = 40; + repeated fixed32 field_fixed32_list_packed_41 = 41; + repeated bool field_bool_list_packed_42 = 42; + repeated uint32 field_uint32_list_packed_43 = 43; + repeated TestEnum field_enum_list_packed_44 = 44; + repeated sfixed32 field_sfixed32_list_packed_45 = 45; + repeated sfixed64 field_sfixed64_list_packed_46 = 46; + repeated sint32 field_sint32_list_packed_47 = 47; + repeated sint64 field_sint64_list_packed_48 = 48; + + message FieldGroup49 { + int32 field_int32_50 = 50; } - repeated group FieldGroupList51 = 51 { - optional int32 field_int32_52 = 52; + + FieldGroup49 fieldgroup49 = 49 [features.message_encoding = DELIMITED]; + + message FieldGroupList51 { + int32 field_int32_52 = 52; } + + repeated FieldGroupList51 fieldgrouplist51 = 51 + [features.message_encoding = DELIMITED]; + oneof test_oneof { double field_double_53 = 53; float field_float_54 = 54; @@ -99,35 +138,75 @@ message Proto2Message { sfixed64 field_sfixed64_66 = 66; sint32 field_sint32_67 = 67; sint64 field_sint64_68 = 68; - group FieldGroup69 = 69 { - optional int32 field_int32_70 = 70; - } + FieldGroup69 fieldgroup69 = 69 [features.message_encoding = DELIMITED]; + } + + message FieldGroup69 { + int32 field_int32_70 = 70; } message RequiredNestedMessage { - optional int32 value = 1; + int32 value = 1; } - required double field_required_double_71 = 71; - required float field_required_float_72 = 72; - required int64 field_required_int64_73 = 73; - required uint64 field_required_uint64_74 = 74; - required int32 field_required_int32_75 = 75; - required fixed64 field_required_fixed64_76 = 76; - required fixed32 field_required_fixed32_77 = 77; - required bool field_required_bool_78 = 78; - required string field_required_string_79 = 79; - required RequiredNestedMessage field_required_message_80 = 80; - required bytes field_required_bytes_81 = 81; - required uint32 field_required_uint32_82 = 82; - required TestEnum field_required_enum_83 = 83; - required sfixed32 field_required_sfixed32_84 = 84; - required sfixed64 field_required_sfixed64_85 = 85; - required sint32 field_required_sint32_86 = 86; - required sint64 field_required_sint64_87 = 87; - required group FieldRequiredGroup88 = 88 { - optional int32 field_int32_89 = 89; + double field_required_double_71 = 71 + [features.field_presence = LEGACY_REQUIRED]; + + float field_required_float_72 = 72 + [features.field_presence = LEGACY_REQUIRED]; + + int64 field_required_int64_73 = 73 + [features.field_presence = LEGACY_REQUIRED]; + + uint64 field_required_uint64_74 = 74 + [features.field_presence = LEGACY_REQUIRED]; + + int32 field_required_int32_75 = 75 + [features.field_presence = LEGACY_REQUIRED]; + + fixed64 field_required_fixed64_76 = 76 + [features.field_presence = LEGACY_REQUIRED]; + + fixed32 field_required_fixed32_77 = 77 + [features.field_presence = LEGACY_REQUIRED]; + + bool field_required_bool_78 = 78 [features.field_presence = LEGACY_REQUIRED]; + + string field_required_string_79 = 79 + [features.field_presence = LEGACY_REQUIRED]; + + RequiredNestedMessage field_required_message_80 = 80 + [features.field_presence = LEGACY_REQUIRED]; + + bytes field_required_bytes_81 = 81 + [features.field_presence = LEGACY_REQUIRED]; + + uint32 field_required_uint32_82 = 82 + [features.field_presence = LEGACY_REQUIRED]; + + TestEnum field_required_enum_83 = 83 + [features.field_presence = LEGACY_REQUIRED]; + + sfixed32 field_required_sfixed32_84 = 84 + [features.field_presence = LEGACY_REQUIRED]; + + sfixed64 field_required_sfixed64_85 = 85 + [features.field_presence = LEGACY_REQUIRED]; + + sint32 field_required_sint32_86 = 86 + [features.field_presence = LEGACY_REQUIRED]; + + sint64 field_required_sint64_87 = 87 + [features.field_presence = LEGACY_REQUIRED]; + + message FieldRequiredGroup88 { + int32 field_int32_89 = 89; } + + FieldRequiredGroup88 fieldrequiredgroup88 = 88 [ + features.field_presence = LEGACY_REQUIRED, + features.message_encoding = DELIMITED + ]; } message Proto2Empty {} @@ -137,65 +216,94 @@ message Proto2MessageWithExtensions { } extend Proto2MessageWithExtensions { - optional double field_double_1 = 1; - optional float field_float_2 = 2; - optional int64 field_int64_3 = 3; - optional uint64 field_uint64_4 = 4; - optional int32 field_int32_5 = 5; - optional fixed64 field_fixed64_6 = 6; - optional fixed32 field_fixed32_7 = 7; - optional bool field_bool_8 = 8; - optional string field_string_9 = 9; - optional Proto2Message field_message_10 = 10; - optional bytes field_bytes_11 = 11; - optional uint32 field_uint32_12 = 12; - optional Proto2Message.TestEnum field_enum_13 = 13; - optional sfixed32 field_sfixed32_14 = 14; - optional sfixed64 field_sfixed64_15 = 15; - optional sint32 field_sint32_16 = 16; - optional sint64 field_sint64_17 = 17; - - repeated double field_double_list_18 = 18 [packed = false]; - repeated float field_float_list_19 = 19 [packed = false]; - repeated int64 field_int64_list_20 = 20 [packed = false]; - repeated uint64 field_uint64_list_21 = 21 [packed = false]; - repeated int32 field_int32_list_22 = 22 [packed = false]; - repeated fixed64 field_fixed64_list_23 = 23 [packed = false]; - repeated fixed32 field_fixed32_list_24 = 24 [packed = false]; - repeated bool field_bool_list_25 = 25 [packed = false]; - repeated string field_string_list_26 = 26 [packed = false]; - repeated Proto2Message field_message_list_27 = 27 [packed = false]; - repeated bytes field_bytes_list_28 = 28 [packed = false]; - repeated uint32 field_uint32_list_29 = 29 [packed = false]; - repeated Proto2Message.TestEnum field_enum_list_30 = 30 [packed = false]; - repeated sfixed32 field_sfixed32_list_31 = 31 [packed = false]; - repeated sfixed64 field_sfixed64_list_32 = 32 [packed = false]; - repeated sint32 field_sint32_list_33 = 33 [packed = false]; - repeated sint64 field_sint64_list_34 = 34 [packed = false]; - - repeated double field_double_list_packed_35 = 35 [packed = true]; - repeated float field_float_list_packed_36 = 36 [packed = true]; - repeated int64 field_int64_list_packed_37 = 37 [packed = true]; - repeated uint64 field_uint64_list_packed_38 = 38 [packed = true]; - repeated int32 field_int32_list_packed_39 = 39 [packed = true]; - repeated fixed64 field_fixed64_list_packed_40 = 40 [packed = true]; - repeated fixed32 field_fixed32_list_packed_41 = 41 [packed = true]; - repeated bool field_bool_list_packed_42 = 42 [packed = true]; - repeated uint32 field_uint32_list_packed_43 = 43 [packed = true]; - repeated Proto2Message.TestEnum field_enum_list_packed_44 = 44 - [packed = true]; - repeated sfixed32 field_sfixed32_list_packed_45 = 45 [packed = true]; - repeated sfixed64 field_sfixed64_list_packed_46 = 46 [packed = true]; - repeated sint32 field_sint32_list_packed_47 = 47 [packed = true]; - repeated sint64 field_sint64_list_packed_48 = 48 [packed = true]; - - optional group FieldGroup49 = 49 { - optional int32 field_int32_50 = 50; - } + double field_double_1 = 1; + float field_float_2 = 2; + int64 field_int64_3 = 3; + uint64 field_uint64_4 = 4; + int32 field_int32_5 = 5; + fixed64 field_fixed64_6 = 6; + fixed32 field_fixed32_7 = 7; + bool field_bool_8 = 8; + string field_string_9 = 9; + Proto2Message field_message_10 = 10; + bytes field_bytes_11 = 11; + uint32 field_uint32_12 = 12; + Proto2Message.TestEnum field_enum_13 = 13; + sfixed32 field_sfixed32_14 = 14; + sfixed64 field_sfixed64_15 = 15; + sint32 field_sint32_16 = 16; + sint64 field_sint64_17 = 17; + repeated double field_double_list_18 = 18 + [features.repeated_field_encoding = EXPANDED]; - repeated group FieldGroupList51 = 51 { - optional int32 field_int32_52 = 52; - } + repeated float field_float_list_19 = 19 + [features.repeated_field_encoding = EXPANDED]; + + repeated int64 field_int64_list_20 = 20 + [features.repeated_field_encoding = EXPANDED]; + + repeated uint64 field_uint64_list_21 = 21 + [features.repeated_field_encoding = EXPANDED]; + + repeated int32 field_int32_list_22 = 22 + [features.repeated_field_encoding = EXPANDED]; + + repeated fixed64 field_fixed64_list_23 = 23 + [features.repeated_field_encoding = EXPANDED]; + + repeated fixed32 field_fixed32_list_24 = 24 + [features.repeated_field_encoding = EXPANDED]; + + repeated bool field_bool_list_25 = 25 + [features.repeated_field_encoding = EXPANDED]; + + repeated string field_string_list_26 = 26; + repeated Proto2Message field_message_list_27 = 27; + repeated bytes field_bytes_list_28 = 28; + repeated uint32 field_uint32_list_29 = 29 + [features.repeated_field_encoding = EXPANDED]; + + repeated Proto2Message.TestEnum field_enum_list_30 = 30 + [features.repeated_field_encoding = EXPANDED]; + + repeated sfixed32 field_sfixed32_list_31 = 31 + [features.repeated_field_encoding = EXPANDED]; + + repeated sfixed64 field_sfixed64_list_32 = 32 + [features.repeated_field_encoding = EXPANDED]; + + repeated sint32 field_sint32_list_33 = 33 + [features.repeated_field_encoding = EXPANDED]; + + repeated sint64 field_sint64_list_34 = 34 + [features.repeated_field_encoding = EXPANDED]; + + repeated double field_double_list_packed_35 = 35; + repeated float field_float_list_packed_36 = 36; + repeated int64 field_int64_list_packed_37 = 37; + repeated uint64 field_uint64_list_packed_38 = 38; + repeated int32 field_int32_list_packed_39 = 39; + repeated fixed64 field_fixed64_list_packed_40 = 40; + repeated fixed32 field_fixed32_list_packed_41 = 41; + repeated bool field_bool_list_packed_42 = 42; + repeated uint32 field_uint32_list_packed_43 = 43; + repeated Proto2Message.TestEnum field_enum_list_packed_44 = 44; + repeated sfixed32 field_sfixed32_list_packed_45 = 45; + repeated sfixed64 field_sfixed64_list_packed_46 = 46; + repeated sint32 field_sint32_list_packed_47 = 47; + repeated sint64 field_sint64_list_packed_48 = 48; + FieldGroup49 fieldgroup49 = 49 [features.message_encoding = DELIMITED]; + + repeated FieldGroupList51 fieldgrouplist51 = 51 + [features.message_encoding = DELIMITED]; +} + +message FieldGroup49 { + int32 field_int32_50 = 50; +} + +message FieldGroupList51 { + int32 field_int32_52 = 52; } message Proto2MessageWithMaps { diff --git a/java/core/src/test/proto/com/google/protobuf/proto2_message_lite.proto b/java/core/src/test/proto/com/google/protobuf/proto2_message_lite.proto index 14930bb01..d903d89c8 100644 --- a/java/core/src/test/proto/com/google/protobuf/proto2_message_lite.proto +++ b/java/core/src/test/proto/com/google/protobuf/proto2_message_lite.proto @@ -6,75 +6,114 @@ // https://developers.google.com/open-source/licenses/bsd // LINT: ALLOW_GROUPS -syntax = "proto2"; +edition = "2023"; package protobuf.experimental.lite; +option features.utf8_validation = NONE; option java_package = "com.google.protobuf.testing"; option java_outer_classname = "Proto2TestingLite"; message Proto2MessageLite { enum TestEnum { + option features.enum_type = CLOSED; + ZERO = 0; ONE = 1; TWO = 2; } - optional double field_double_1 = 1; - optional float field_float_2 = 2; - optional int64 field_int64_3 = 3; - optional uint64 field_uint64_4 = 4; - optional int32 field_int32_5 = 5; - optional fixed64 field_fixed64_6 = 6; - optional fixed32 field_fixed32_7 = 7; - optional bool field_bool_8 = 8; - optional string field_string_9 = 9; - optional Proto2MessageLite field_message_10 = 10; - optional bytes field_bytes_11 = 11; - optional uint32 field_uint32_12 = 12; - optional TestEnum field_enum_13 = 13; - optional sfixed32 field_sfixed32_14 = 14; - optional sfixed64 field_sfixed64_15 = 15; - optional sint32 field_sint32_16 = 16; - optional sint64 field_sint64_17 = 17; - repeated double field_double_list_18 = 18 [packed = false]; - repeated float field_float_list_19 = 19 [packed = false]; - repeated int64 field_int64_list_20 = 20 [packed = false]; - repeated uint64 field_uint64_list_21 = 21 [packed = false]; - repeated int32 field_int32_list_22 = 22 [packed = false]; - repeated fixed64 field_fixed64_list_23 = 23 [packed = false]; - repeated fixed32 field_fixed32_list_24 = 24 [packed = false]; - repeated bool field_bool_list_25 = 25 [packed = false]; - repeated string field_string_list_26 = 26 [packed = false]; - repeated Proto2MessageLite field_message_list_27 = 27 [packed = false]; - repeated bytes field_bytes_list_28 = 28 [packed = false]; - repeated uint32 field_uint32_list_29 = 29 [packed = false]; - repeated TestEnum field_enum_list_30 = 30 [packed = false]; - repeated sfixed32 field_sfixed32_list_31 = 31 [packed = false]; - repeated sfixed64 field_sfixed64_list_32 = 32 [packed = false]; - repeated sint32 field_sint32_list_33 = 33 [packed = false]; - repeated sint64 field_sint64_list_34 = 34 [packed = false]; - repeated double field_double_list_packed_35 = 35 [packed = true]; - repeated float field_float_list_packed_36 = 36 [packed = true]; - repeated int64 field_int64_list_packed_37 = 37 [packed = true]; - repeated uint64 field_uint64_list_packed_38 = 38 [packed = true]; - repeated int32 field_int32_list_packed_39 = 39 [packed = true]; - repeated fixed64 field_fixed64_list_packed_40 = 40 [packed = true]; - repeated fixed32 field_fixed32_list_packed_41 = 41 [packed = true]; - repeated bool field_bool_list_packed_42 = 42 [packed = true]; - repeated uint32 field_uint32_list_packed_43 = 43 [packed = true]; - repeated TestEnum field_enum_list_packed_44 = 44 [packed = true]; - repeated sfixed32 field_sfixed32_list_packed_45 = 45 [packed = true]; - repeated sfixed64 field_sfixed64_list_packed_46 = 46 [packed = true]; - repeated sint32 field_sint32_list_packed_47 = 47 [packed = true]; - repeated sint64 field_sint64_list_packed_48 = 48 [packed = true]; - optional group FieldGroup49 = 49 { - optional int32 field_int32_50 = 50; + double field_double_1 = 1; + float field_float_2 = 2; + int64 field_int64_3 = 3; + uint64 field_uint64_4 = 4; + int32 field_int32_5 = 5; + fixed64 field_fixed64_6 = 6; + fixed32 field_fixed32_7 = 7; + bool field_bool_8 = 8; + string field_string_9 = 9; + Proto2MessageLite field_message_10 = 10; + bytes field_bytes_11 = 11; + uint32 field_uint32_12 = 12; + TestEnum field_enum_13 = 13; + sfixed32 field_sfixed32_14 = 14; + sfixed64 field_sfixed64_15 = 15; + sint32 field_sint32_16 = 16; + sint64 field_sint64_17 = 17; + repeated double field_double_list_18 = 18 + [features.repeated_field_encoding = EXPANDED]; + + repeated float field_float_list_19 = 19 + [features.repeated_field_encoding = EXPANDED]; + + repeated int64 field_int64_list_20 = 20 + [features.repeated_field_encoding = EXPANDED]; + + repeated uint64 field_uint64_list_21 = 21 + [features.repeated_field_encoding = EXPANDED]; + + repeated int32 field_int32_list_22 = 22 + [features.repeated_field_encoding = EXPANDED]; + + repeated fixed64 field_fixed64_list_23 = 23 + [features.repeated_field_encoding = EXPANDED]; + + repeated fixed32 field_fixed32_list_24 = 24 + [features.repeated_field_encoding = EXPANDED]; + + repeated bool field_bool_list_25 = 25 + [features.repeated_field_encoding = EXPANDED]; + + repeated string field_string_list_26 = 26; + repeated Proto2MessageLite field_message_list_27 = 27; + repeated bytes field_bytes_list_28 = 28; + repeated uint32 field_uint32_list_29 = 29 + [features.repeated_field_encoding = EXPANDED]; + + repeated TestEnum field_enum_list_30 = 30 + [features.repeated_field_encoding = EXPANDED]; + + repeated sfixed32 field_sfixed32_list_31 = 31 + [features.repeated_field_encoding = EXPANDED]; + + repeated sfixed64 field_sfixed64_list_32 = 32 + [features.repeated_field_encoding = EXPANDED]; + + repeated sint32 field_sint32_list_33 = 33 + [features.repeated_field_encoding = EXPANDED]; + + repeated sint64 field_sint64_list_34 = 34 + [features.repeated_field_encoding = EXPANDED]; + + repeated double field_double_list_packed_35 = 35; + repeated float field_float_list_packed_36 = 36; + repeated int64 field_int64_list_packed_37 = 37; + repeated uint64 field_uint64_list_packed_38 = 38; + repeated int32 field_int32_list_packed_39 = 39; + repeated fixed64 field_fixed64_list_packed_40 = 40; + repeated fixed32 field_fixed32_list_packed_41 = 41; + repeated bool field_bool_list_packed_42 = 42; + repeated uint32 field_uint32_list_packed_43 = 43; + repeated TestEnum field_enum_list_packed_44 = 44; + repeated sfixed32 field_sfixed32_list_packed_45 = 45; + repeated sfixed64 field_sfixed64_list_packed_46 = 46; + repeated sint32 field_sint32_list_packed_47 = 47; + repeated sint64 field_sint64_list_packed_48 = 48; + + message FieldGroup49 { + int32 field_int32_50 = 50; } - repeated group FieldGroupList51 = 51 { - optional int32 field_int32_52 = 52; + + FieldGroup49 fieldgroup49 = 49 [features.message_encoding = DELIMITED]; + + message FieldGroupList51 { + int32 field_int32_52 = 52; } + + repeated FieldGroupList51 fieldgrouplist51 = 51 + [features.message_encoding = DELIMITED]; + oneof test_oneof { double field_double_53 = 53; float field_float_54 = 54; @@ -92,35 +131,75 @@ message Proto2MessageLite { sfixed64 field_sfixed64_66 = 66; sint32 field_sint32_67 = 67; sint64 field_sint64_68 = 68; - group FieldGroup69 = 69 { - optional int32 field_int32_70 = 70; - } + FieldGroup69 fieldgroup69 = 69 [features.message_encoding = DELIMITED]; + } + + message FieldGroup69 { + int32 field_int32_70 = 70; } message RequiredNestedMessage { - optional int32 value = 1; + int32 value = 1; } - required double field_required_double_71 = 71; - required float field_required_float_72 = 72; - required int64 field_required_int64_73 = 73; - required uint64 field_required_uint64_74 = 74; - required int32 field_required_int32_75 = 75; - required fixed64 field_required_fixed64_76 = 76; - required fixed32 field_required_fixed32_77 = 77; - required bool field_required_bool_78 = 78; - required string field_required_string_79 = 79; - required RequiredNestedMessage field_required_message_80 = 80; - required bytes field_required_bytes_81 = 81; - required uint32 field_required_uint32_82 = 82; - required TestEnum field_required_enum_83 = 83; - required sfixed32 field_required_sfixed32_84 = 84; - required sfixed64 field_required_sfixed64_85 = 85; - required sint32 field_required_sint32_86 = 86; - required sint64 field_required_sint64_87 = 87; - required group FieldRequiredGroup88 = 88 { - optional int32 field_int32_89 = 89; + double field_required_double_71 = 71 + [features.field_presence = LEGACY_REQUIRED]; + + float field_required_float_72 = 72 + [features.field_presence = LEGACY_REQUIRED]; + + int64 field_required_int64_73 = 73 + [features.field_presence = LEGACY_REQUIRED]; + + uint64 field_required_uint64_74 = 74 + [features.field_presence = LEGACY_REQUIRED]; + + int32 field_required_int32_75 = 75 + [features.field_presence = LEGACY_REQUIRED]; + + fixed64 field_required_fixed64_76 = 76 + [features.field_presence = LEGACY_REQUIRED]; + + fixed32 field_required_fixed32_77 = 77 + [features.field_presence = LEGACY_REQUIRED]; + + bool field_required_bool_78 = 78 [features.field_presence = LEGACY_REQUIRED]; + + string field_required_string_79 = 79 + [features.field_presence = LEGACY_REQUIRED]; + + RequiredNestedMessage field_required_message_80 = 80 + [features.field_presence = LEGACY_REQUIRED]; + + bytes field_required_bytes_81 = 81 + [features.field_presence = LEGACY_REQUIRED]; + + uint32 field_required_uint32_82 = 82 + [features.field_presence = LEGACY_REQUIRED]; + + TestEnum field_required_enum_83 = 83 + [features.field_presence = LEGACY_REQUIRED]; + + sfixed32 field_required_sfixed32_84 = 84 + [features.field_presence = LEGACY_REQUIRED]; + + sfixed64 field_required_sfixed64_85 = 85 + [features.field_presence = LEGACY_REQUIRED]; + + sint32 field_required_sint32_86 = 86 + [features.field_presence = LEGACY_REQUIRED]; + + sint64 field_required_sint64_87 = 87 + [features.field_presence = LEGACY_REQUIRED]; + + message FieldRequiredGroup88 { + int32 field_int32_89 = 89; } + + FieldRequiredGroup88 fieldrequiredgroup88 = 88 [ + features.field_presence = LEGACY_REQUIRED, + features.message_encoding = DELIMITED + ]; } message Proto2EmptyLite {} @@ -130,65 +209,94 @@ message Proto2MessageLiteWithExtensions { } extend Proto2MessageLiteWithExtensions { - optional double field_double_1 = 1; - optional float field_float_2 = 2; - optional int64 field_int64_3 = 3; - optional uint64 field_uint64_4 = 4; - optional int32 field_int32_5 = 5; - optional fixed64 field_fixed64_6 = 6; - optional fixed32 field_fixed32_7 = 7; - optional bool field_bool_8 = 8; - optional string field_string_9 = 9; - optional Proto2MessageLite field_message_10 = 10; - optional bytes field_bytes_11 = 11; - optional uint32 field_uint32_12 = 12; - optional Proto2MessageLite.TestEnum field_enum_13 = 13; - optional sfixed32 field_sfixed32_14 = 14; - optional sfixed64 field_sfixed64_15 = 15; - optional sint32 field_sint32_16 = 16; - optional sint64 field_sint64_17 = 17; - - repeated double field_double_list_18 = 18 [packed = false]; - repeated float field_float_list_19 = 19 [packed = false]; - repeated int64 field_int64_list_20 = 20 [packed = false]; - repeated uint64 field_uint64_list_21 = 21 [packed = false]; - repeated int32 field_int32_list_22 = 22 [packed = false]; - repeated fixed64 field_fixed64_list_23 = 23 [packed = false]; - repeated fixed32 field_fixed32_list_24 = 24 [packed = false]; - repeated bool field_bool_list_25 = 25 [packed = false]; - repeated string field_string_list_26 = 26 [packed = false]; - repeated Proto2MessageLite field_message_list_27 = 27 [packed = false]; - repeated bytes field_bytes_list_28 = 28 [packed = false]; - repeated uint32 field_uint32_list_29 = 29 [packed = false]; - repeated Proto2MessageLite.TestEnum field_enum_list_30 = 30 [packed = false]; - repeated sfixed32 field_sfixed32_list_31 = 31 [packed = false]; - repeated sfixed64 field_sfixed64_list_32 = 32 [packed = false]; - repeated sint32 field_sint32_list_33 = 33 [packed = false]; - repeated sint64 field_sint64_list_34 = 34 [packed = false]; - - repeated double field_double_list_packed_35 = 35 [packed = true]; - repeated float field_float_list_packed_36 = 36 [packed = true]; - repeated int64 field_int64_list_packed_37 = 37 [packed = true]; - repeated uint64 field_uint64_list_packed_38 = 38 [packed = true]; - repeated int32 field_int32_list_packed_39 = 39 [packed = true]; - repeated fixed64 field_fixed64_list_packed_40 = 40 [packed = true]; - repeated fixed32 field_fixed32_list_packed_41 = 41 [packed = true]; - repeated bool field_bool_list_packed_42 = 42 [packed = true]; - repeated uint32 field_uint32_list_packed_43 = 43 [packed = true]; - repeated Proto2MessageLite.TestEnum field_enum_list_packed_44 = 44 - [packed = true]; - repeated sfixed32 field_sfixed32_list_packed_45 = 45 [packed = true]; - repeated sfixed64 field_sfixed64_list_packed_46 = 46 [packed = true]; - repeated sint32 field_sint32_list_packed_47 = 47 [packed = true]; - repeated sint64 field_sint64_list_packed_48 = 48 [packed = true]; - - optional group FieldGroup49 = 49 { - optional int32 field_int32_50 = 50; - } + double field_double_1 = 1; + float field_float_2 = 2; + int64 field_int64_3 = 3; + uint64 field_uint64_4 = 4; + int32 field_int32_5 = 5; + fixed64 field_fixed64_6 = 6; + fixed32 field_fixed32_7 = 7; + bool field_bool_8 = 8; + string field_string_9 = 9; + Proto2MessageLite field_message_10 = 10; + bytes field_bytes_11 = 11; + uint32 field_uint32_12 = 12; + Proto2MessageLite.TestEnum field_enum_13 = 13; + sfixed32 field_sfixed32_14 = 14; + sfixed64 field_sfixed64_15 = 15; + sint32 field_sint32_16 = 16; + sint64 field_sint64_17 = 17; + repeated double field_double_list_18 = 18 + [features.repeated_field_encoding = EXPANDED]; - repeated group FieldGroupList51 = 51 { - optional int32 field_int32_52 = 52; - } + repeated float field_float_list_19 = 19 + [features.repeated_field_encoding = EXPANDED]; + + repeated int64 field_int64_list_20 = 20 + [features.repeated_field_encoding = EXPANDED]; + + repeated uint64 field_uint64_list_21 = 21 + [features.repeated_field_encoding = EXPANDED]; + + repeated int32 field_int32_list_22 = 22 + [features.repeated_field_encoding = EXPANDED]; + + repeated fixed64 field_fixed64_list_23 = 23 + [features.repeated_field_encoding = EXPANDED]; + + repeated fixed32 field_fixed32_list_24 = 24 + [features.repeated_field_encoding = EXPANDED]; + + repeated bool field_bool_list_25 = 25 + [features.repeated_field_encoding = EXPANDED]; + + repeated string field_string_list_26 = 26; + repeated Proto2MessageLite field_message_list_27 = 27; + repeated bytes field_bytes_list_28 = 28; + repeated uint32 field_uint32_list_29 = 29 + [features.repeated_field_encoding = EXPANDED]; + + repeated Proto2MessageLite.TestEnum field_enum_list_30 = 30 + [features.repeated_field_encoding = EXPANDED]; + + repeated sfixed32 field_sfixed32_list_31 = 31 + [features.repeated_field_encoding = EXPANDED]; + + repeated sfixed64 field_sfixed64_list_32 = 32 + [features.repeated_field_encoding = EXPANDED]; + + repeated sint32 field_sint32_list_33 = 33 + [features.repeated_field_encoding = EXPANDED]; + + repeated sint64 field_sint64_list_34 = 34 + [features.repeated_field_encoding = EXPANDED]; + + repeated double field_double_list_packed_35 = 35; + repeated float field_float_list_packed_36 = 36; + repeated int64 field_int64_list_packed_37 = 37; + repeated uint64 field_uint64_list_packed_38 = 38; + repeated int32 field_int32_list_packed_39 = 39; + repeated fixed64 field_fixed64_list_packed_40 = 40; + repeated fixed32 field_fixed32_list_packed_41 = 41; + repeated bool field_bool_list_packed_42 = 42; + repeated uint32 field_uint32_list_packed_43 = 43; + repeated Proto2MessageLite.TestEnum field_enum_list_packed_44 = 44; + repeated sfixed32 field_sfixed32_list_packed_45 = 45; + repeated sfixed64 field_sfixed64_list_packed_46 = 46; + repeated sint32 field_sint32_list_packed_47 = 47; + repeated sint64 field_sint64_list_packed_48 = 48; + FieldGroup49 fieldgroup49 = 49 [features.message_encoding = DELIMITED]; + + repeated FieldGroupList51 fieldgrouplist51 = 51 + [features.message_encoding = DELIMITED]; +} + +message FieldGroup49 { + int32 field_int32_50 = 50; +} + +message FieldGroupList51 { + int32 field_int32_52 = 52; } message Proto2MessageLiteWithMaps { diff --git a/java/core/src/test/proto/com/google/protobuf/proto2_text_format_performance_test.proto b/java/core/src/test/proto/com/google/protobuf/proto2_text_format_performance_test.proto index bce621fb4..96473739b 100644 --- a/java/core/src/test/proto/com/google/protobuf/proto2_text_format_performance_test.proto +++ b/java/core/src/test/proto/com/google/protobuf/proto2_text_format_performance_test.proto @@ -5,15 +5,16 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -syntax = "proto2"; +edition = "2023"; package protobuf.testing.textformat.performance.proto2; +option features.repeated_field_encoding = EXPANDED; option java_package = "com.google.protobuf.testing.textformat.performance.proto2"; option java_outer_classname = "Proto2TextFormatPerformanceProto"; message ContainsSubMessageWithRepeatedInt32 { - optional RepeatedInt32 sub_msg = 1; + RepeatedInt32 sub_msg = 1; } message RepeatedInt32 { @@ -25,5 +26,5 @@ message ContainsExtensionSubMessage { } extend ContainsExtensionSubMessage { - optional RepeatedInt32 sub_msg_ext = 1; + RepeatedInt32 sub_msg_ext = 1; } diff --git a/java/core/src/test/proto/com/google/protobuf/proto2_unknown_enum_values.proto b/java/core/src/test/proto/com/google/protobuf/proto2_unknown_enum_values.proto index bb7d9596c..bb51b2660 100644 --- a/java/core/src/test/proto/com/google/protobuf/proto2_unknown_enum_values.proto +++ b/java/core/src/test/proto/com/google/protobuf/proto2_unknown_enum_values.proto @@ -5,10 +5,11 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -syntax = "proto2"; +edition = "2023"; package proto2_unknown_enum_values; +option features.enum_type = CLOSED; option java_package = "com.google.protobuf"; option java_outer_classname = "Proto2UnknownEnumValuesTestProto"; @@ -19,7 +20,7 @@ enum Proto2TestEnum { } message Proto2EnumMessage { - repeated Proto2TestEnum repeated_packed_enum = 1 [packed = true]; + repeated Proto2TestEnum repeated_packed_enum = 1; } // An enum containing a subset of the values of Proto2TestEnum, to test @@ -27,11 +28,12 @@ message Proto2EnumMessage { enum Proto2TestEnumSubset { TESTENUM_SUBSET_ZERO = 0; TESTENUM_SUBSET_ONE = 1; + // No enum value with number 2. } // Test messages for packed enum, with identical field number as // Proto2Message, to test parsing unknown packed enums. message Proto2EnumMessageWithEnumSubset { - repeated Proto2TestEnumSubset repeated_packed_enum = 1 [packed = true]; + repeated Proto2TestEnumSubset repeated_packed_enum = 1; } diff --git a/java/core/src/test/proto/com/google/protobuf/proto3_message.proto b/java/core/src/test/proto/com/google/protobuf/proto3_message.proto index 9b439c0ae..a64bce52f 100644 --- a/java/core/src/test/proto/com/google/protobuf/proto3_message.proto +++ b/java/core/src/test/proto/com/google/protobuf/proto3_message.proto @@ -5,10 +5,11 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -syntax = "proto3"; +edition = "2023"; package protobuf.experimental; +option features.field_presence = IMPLICIT; option java_package = "com.google.protobuf.testing"; option java_outer_classname = "Proto3Testing"; @@ -43,37 +44,72 @@ message Proto3Message { sfixed64 field_sfixed64_15 = 15; sint32 field_sint32_16 = 16; sint64 field_sint64_17 = 17; - repeated double field_double_list_18 = 18 [packed = false]; - repeated float field_float_list_19 = 19 [packed = false]; - repeated int64 field_int64_list_20 = 20 [packed = false]; - repeated uint64 field_uint64_list_21 = 21 [packed = false]; - repeated int32 field_int32_list_22 = 22 [packed = false]; - repeated fixed64 field_fixed64_list_23 = 23 [packed = false]; - repeated fixed32 field_fixed32_list_24 = 24 [packed = false]; - repeated bool field_bool_list_25 = 25 [packed = false]; - repeated string field_string_list_26 = 26 [packed = false]; - repeated Proto3Message field_message_list_27 = 27 [packed = false]; - repeated bytes field_bytes_list_28 = 28 [packed = false]; - repeated uint32 field_uint32_list_29 = 29 [packed = false]; - repeated TestEnum field_enum_list_30 = 30 [packed = false]; - repeated sfixed32 field_sfixed32_list_31 = 31 [packed = false]; - repeated sfixed64 field_sfixed64_list_32 = 32 [packed = false]; - repeated sint32 field_sint32_list_33 = 33 [packed = false]; - repeated sint64 field_sint64_list_34 = 34 [packed = false]; - repeated double field_double_list_packed_35 = 35 [packed = true]; - repeated float field_float_list_packed_36 = 36 [packed = true]; - repeated int64 field_int64_list_packed_37 = 37 [packed = true]; - repeated uint64 field_uint64_list_packed_38 = 38 [packed = true]; - repeated int32 field_int32_list_packed_39 = 39 [packed = true]; - repeated fixed64 field_fixed64_list_packed_40 = 40 [packed = true]; - repeated fixed32 field_fixed32_list_packed_41 = 41 [packed = true]; - repeated bool field_bool_list_packed_42 = 42 [packed = true]; - repeated uint32 field_uint32_list_packed_43 = 43 [packed = true]; - repeated TestEnum field_enum_list_packed_44 = 44 [packed = true]; - repeated sfixed32 field_sfixed32_list_packed_45 = 45 [packed = true]; - repeated sfixed64 field_sfixed64_list_packed_46 = 46 [packed = true]; - repeated sint32 field_sint32_list_packed_47 = 47 [packed = true]; - repeated sint64 field_sint64_list_packed_48 = 48 [packed = true]; + repeated double field_double_list_18 = 18 + [features.repeated_field_encoding = EXPANDED]; + + repeated float field_float_list_19 = 19 + [features.repeated_field_encoding = EXPANDED]; + + repeated int64 field_int64_list_20 = 20 + [features.repeated_field_encoding = EXPANDED]; + + repeated uint64 field_uint64_list_21 = 21 + [features.repeated_field_encoding = EXPANDED]; + + repeated int32 field_int32_list_22 = 22 + [features.repeated_field_encoding = EXPANDED]; + + repeated fixed64 field_fixed64_list_23 = 23 + [features.repeated_field_encoding = EXPANDED]; + + repeated fixed32 field_fixed32_list_24 = 24 + [features.repeated_field_encoding = EXPANDED]; + + repeated bool field_bool_list_25 = 25 + [features.repeated_field_encoding = EXPANDED]; + + repeated string field_string_list_26 = 26 + [features.repeated_field_encoding = EXPANDED]; + + repeated Proto3Message field_message_list_27 = 27 + [features.repeated_field_encoding = EXPANDED]; + + repeated bytes field_bytes_list_28 = 28 + [features.repeated_field_encoding = EXPANDED]; + + repeated uint32 field_uint32_list_29 = 29 + [features.repeated_field_encoding = EXPANDED]; + + repeated TestEnum field_enum_list_30 = 30 + [features.repeated_field_encoding = EXPANDED]; + + repeated sfixed32 field_sfixed32_list_31 = 31 + [features.repeated_field_encoding = EXPANDED]; + + repeated sfixed64 field_sfixed64_list_32 = 32 + [features.repeated_field_encoding = EXPANDED]; + + repeated sint32 field_sint32_list_33 = 33 + [features.repeated_field_encoding = EXPANDED]; + + repeated sint64 field_sint64_list_34 = 34 + [features.repeated_field_encoding = EXPANDED]; + + repeated double field_double_list_packed_35 = 35; + repeated float field_float_list_packed_36 = 36; + repeated int64 field_int64_list_packed_37 = 37; + repeated uint64 field_uint64_list_packed_38 = 38; + repeated int32 field_int32_list_packed_39 = 39; + repeated fixed64 field_fixed64_list_packed_40 = 40; + repeated fixed32 field_fixed32_list_packed_41 = 41; + repeated bool field_bool_list_packed_42 = 42; + repeated uint32 field_uint32_list_packed_43 = 43; + repeated TestEnum field_enum_list_packed_44 = 44; + repeated sfixed32 field_sfixed32_list_packed_45 = 45; + repeated sfixed64 field_sfixed64_list_packed_46 = 46; + repeated sint32 field_sint32_list_packed_47 = 47; + repeated sint64 field_sint64_list_packed_48 = 48; + oneof test_oneof { double field_double_53 = 53; float field_float_54 = 54; diff --git a/java/core/src/test/proto/com/google/protobuf/proto3_message_lite.proto b/java/core/src/test/proto/com/google/protobuf/proto3_message_lite.proto index 09537cb1d..79b029cd8 100644 --- a/java/core/src/test/proto/com/google/protobuf/proto3_message_lite.proto +++ b/java/core/src/test/proto/com/google/protobuf/proto3_message_lite.proto @@ -5,10 +5,11 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -syntax = "proto3"; +edition = "2023"; package protobuf.experimental; +option features.field_presence = IMPLICIT; option java_package = "com.google.protobuf.testing"; option java_outer_classname = "Proto3TestingLite"; @@ -36,37 +37,72 @@ message Proto3MessageLite { sfixed64 field_sfixed64_15 = 15; sint32 field_sint32_16 = 16; sint64 field_sint64_17 = 17; - repeated double field_double_list_18 = 18 [packed = false]; - repeated float field_float_list_19 = 19 [packed = false]; - repeated int64 field_int64_list_20 = 20 [packed = false]; - repeated uint64 field_uint64_list_21 = 21 [packed = false]; - repeated int32 field_int32_list_22 = 22 [packed = false]; - repeated fixed64 field_fixed64_list_23 = 23 [packed = false]; - repeated fixed32 field_fixed32_list_24 = 24 [packed = false]; - repeated bool field_bool_list_25 = 25 [packed = false]; - repeated string field_string_list_26 = 26 [packed = false]; - repeated Proto3MessageLite field_message_list_27 = 27 [packed = false]; - repeated bytes field_bytes_list_28 = 28 [packed = false]; - repeated uint32 field_uint32_list_29 = 29 [packed = false]; - repeated TestEnum field_enum_list_30 = 30 [packed = false]; - repeated sfixed32 field_sfixed32_list_31 = 31 [packed = false]; - repeated sfixed64 field_sfixed64_list_32 = 32 [packed = false]; - repeated sint32 field_sint32_list_33 = 33 [packed = false]; - repeated sint64 field_sint64_list_34 = 34 [packed = false]; - repeated double field_double_list_packed_35 = 35 [packed = true]; - repeated float field_float_list_packed_36 = 36 [packed = true]; - repeated int64 field_int64_list_packed_37 = 37 [packed = true]; - repeated uint64 field_uint64_list_packed_38 = 38 [packed = true]; - repeated int32 field_int32_list_packed_39 = 39 [packed = true]; - repeated fixed64 field_fixed64_list_packed_40 = 40 [packed = true]; - repeated fixed32 field_fixed32_list_packed_41 = 41 [packed = true]; - repeated bool field_bool_list_packed_42 = 42 [packed = true]; - repeated uint32 field_uint32_list_packed_43 = 43 [packed = true]; - repeated TestEnum field_enum_list_packed_44 = 44 [packed = true]; - repeated sfixed32 field_sfixed32_list_packed_45 = 45 [packed = true]; - repeated sfixed64 field_sfixed64_list_packed_46 = 46 [packed = true]; - repeated sint32 field_sint32_list_packed_47 = 47 [packed = true]; - repeated sint64 field_sint64_list_packed_48 = 48 [packed = true]; + repeated double field_double_list_18 = 18 + [features.repeated_field_encoding = EXPANDED]; + + repeated float field_float_list_19 = 19 + [features.repeated_field_encoding = EXPANDED]; + + repeated int64 field_int64_list_20 = 20 + [features.repeated_field_encoding = EXPANDED]; + + repeated uint64 field_uint64_list_21 = 21 + [features.repeated_field_encoding = EXPANDED]; + + repeated int32 field_int32_list_22 = 22 + [features.repeated_field_encoding = EXPANDED]; + + repeated fixed64 field_fixed64_list_23 = 23 + [features.repeated_field_encoding = EXPANDED]; + + repeated fixed32 field_fixed32_list_24 = 24 + [features.repeated_field_encoding = EXPANDED]; + + repeated bool field_bool_list_25 = 25 + [features.repeated_field_encoding = EXPANDED]; + + repeated string field_string_list_26 = 26 + [features.repeated_field_encoding = EXPANDED]; + + repeated Proto3MessageLite field_message_list_27 = 27 + [features.repeated_field_encoding = EXPANDED]; + + repeated bytes field_bytes_list_28 = 28 + [features.repeated_field_encoding = EXPANDED]; + + repeated uint32 field_uint32_list_29 = 29 + [features.repeated_field_encoding = EXPANDED]; + + repeated TestEnum field_enum_list_30 = 30 + [features.repeated_field_encoding = EXPANDED]; + + repeated sfixed32 field_sfixed32_list_31 = 31 + [features.repeated_field_encoding = EXPANDED]; + + repeated sfixed64 field_sfixed64_list_32 = 32 + [features.repeated_field_encoding = EXPANDED]; + + repeated sint32 field_sint32_list_33 = 33 + [features.repeated_field_encoding = EXPANDED]; + + repeated sint64 field_sint64_list_34 = 34 + [features.repeated_field_encoding = EXPANDED]; + + repeated double field_double_list_packed_35 = 35; + repeated float field_float_list_packed_36 = 36; + repeated int64 field_int64_list_packed_37 = 37; + repeated uint64 field_uint64_list_packed_38 = 38; + repeated int32 field_int32_list_packed_39 = 39; + repeated fixed64 field_fixed64_list_packed_40 = 40; + repeated fixed32 field_fixed32_list_packed_41 = 41; + repeated bool field_bool_list_packed_42 = 42; + repeated uint32 field_uint32_list_packed_43 = 43; + repeated TestEnum field_enum_list_packed_44 = 44; + repeated sfixed32 field_sfixed32_list_packed_45 = 45; + repeated sfixed64 field_sfixed64_list_packed_46 = 46; + repeated sint32 field_sint32_list_packed_47 = 47; + repeated sint64 field_sint64_list_packed_48 = 48; + oneof test_oneof { double field_double_53 = 53; float field_float_54 = 54; diff --git a/java/core/src/test/proto/com/google/protobuf/proto3_text_format_performance_test.proto b/java/core/src/test/proto/com/google/protobuf/proto3_text_format_performance_test.proto index 5a837fc7e..30454fcbc 100644 --- a/java/core/src/test/proto/com/google/protobuf/proto3_text_format_performance_test.proto +++ b/java/core/src/test/proto/com/google/protobuf/proto3_text_format_performance_test.proto @@ -5,7 +5,7 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -syntax = "proto3"; +edition = "2023"; package protobuf.testing.textformat.performance.proto3; @@ -13,7 +13,7 @@ option java_package = "com.google.protobuf.testing.textformat.performance.proto3 option java_outer_classname = "Proto3TextFormatPerformanceProto"; message ContainsSubMessageWithRepeatedInt32 { - optional RepeatedInt32 sub_msg = 1; + RepeatedInt32 sub_msg = 1; } message RepeatedInt32 { diff --git a/java/core/src/test/proto/com/google/protobuf/test_bad_identifiers.proto b/java/core/src/test/proto/com/google/protobuf/test_bad_identifiers.proto index f79521361..5c70ed43c 100644 --- a/java/core/src/test/proto/com/google/protobuf/test_bad_identifiers.proto +++ b/java/core/src/test/proto/com/google/protobuf/test_bad_identifiers.proto @@ -11,10 +11,13 @@ // though the same identifiers are used internally by the java code generator. // LINT: LEGACY_NAMES -syntax = "proto2"; +edition = "2023"; package io_protocol_tests; +option features.repeated_field_encoding = EXPANDED; +option features.utf8_validation = NONE; +option features.enum_type = CLOSED; option java_generic_services = true; // auto-added option java_package = "com.google.protobuf"; option java_outer_classname = "TestBadIdentifiersProto"; @@ -24,19 +27,19 @@ option java_outer_classname = "TestBadIdentifiersProto"; // src/google/protobuf/compiler/java/java_helpers.cc message ForbiddenWordsUnderscoreMessage { // java.lang.Object - optional bool class = 1; + bool class = 1; // com.google.protobuf.MessageLiteOrBuilder - optional bool default_instance_for_type = 2; + bool default_instance_for_type = 2; // com.google.protobuf.MessageLite - optional bool parser_for_type = 3; - optional bool serialized_size = 4; + bool parser_for_type = 3; + bool serialized_size = 4; // com.google.protobuf.MessageOrBuilder - optional bool all_fields = 5; - optional bool descriptor_for_type = 6; - optional bool initialization_error_string = 7; - optional bool unknown_fields = 8; + bool all_fields = 5; + bool descriptor_for_type = 6; + bool initialization_error_string = 7; + bool unknown_fields = 8; // obsolete. kept for backwards compatibility of generated code - optional bool cached_size = 9; + bool cached_size = 9; } // Message with field names using leading underscores that conflict with @@ -44,20 +47,20 @@ message ForbiddenWordsUnderscoreMessage { // src/google/protobuf/compiler/java/java_helpers.cc message ForbiddenWordsLeadingUnderscoreMessage { // java.lang.Object - optional bool _class = 1; + bool _class = 1; // com.google.protobuf.MessageLiteOrBuilder - optional bool _default_instance_for_type = 2; + bool _default_instance_for_type = 2; // com.google.protobuf.MessageLite - optional bool _parser_for_type = 3; - optional bool _serialized_size = 4; + bool _parser_for_type = 3; + bool _serialized_size = 4; // com.google.protobuf.MessageOrBuilder - optional bool _all_fields = 5; - optional bool _descriptor_for_type = 6; - optional bool _initialization_error_string = 7; + bool _all_fields = 5; + bool _descriptor_for_type = 6; + bool _initialization_error_string = 7; // TODO: re-enable - // optional bool _unknown_fields = 8; + // bool _unknown_fields = 8; // obsolete. kept for backwards compatibility of generated code - optional bool _cached_size = 9; + bool _cached_size = 9; } // Message with field names in camel case that conflict with accessors in the @@ -65,32 +68,32 @@ message ForbiddenWordsLeadingUnderscoreMessage { // src/google/protobuf/compiler/java/java_helpers.cc message ForbiddenWordsCamelMessage { // java.lang.Object - optional bool class = 1; + bool class = 1; // com.google.protobuf.MessageLiteOrBuilder - optional bool defaultInstanceForType = 2; + bool defaultInstanceForType = 2; // com.google.protobuf.MessageLite - optional bool serializedSize = 3; - optional bool parserForType = 4; + bool serializedSize = 3; + bool parserForType = 4; // com.google.protobuf.MessageOrBuilder: - optional bool initializationErrorString = 5; - optional bool descriptorForType = 6; - optional bool allFields = 7; + bool initializationErrorString = 5; + bool descriptorForType = 6; + bool allFields = 7; // TODO: re-enable - // optional bool unknownFields = 8; + // bool unknownFields = 8; // obsolete. kept for backwards compatibility of generated code - optional bool cachedSize = 9; + bool cachedSize = 9; } message Descriptor { option no_standard_descriptor_accessor = true; - optional string descriptor = 1; + string descriptor = 1; message NestedDescriptor { option no_standard_descriptor_accessor = true; - optional string descriptor = 1; + string descriptor = 1; } - optional NestedDescriptor nested_descriptor = 2; + NestedDescriptor nested_descriptor = 2; enum NestedEnum { UNKNOWN = 0; FOO = 1; @@ -102,7 +105,7 @@ message Parser { UNKNOWN = 0; PARSER = 1; } - optional ParserEnum parser = 1; + ParserEnum parser = 1; } message Deprecated { @@ -114,38 +117,38 @@ message Deprecated { BAR = 2 [deprecated = true]; } - optional int32 field1 = 1 [deprecated = true]; - optional TestEnum field2 = 2 [deprecated = true]; - optional ForbiddenWordsUnderscoreMessage field3 = 3 [deprecated = true]; + int32 field1 = 1 [deprecated = true]; + TestEnum field2 = 2 [deprecated = true]; + ForbiddenWordsUnderscoreMessage field3 = 3 [deprecated = true]; } message Override { - optional int32 override = 1; + int32 override = 1; } message Object { - optional int32 object = 1; - optional string string_object = 2; + int32 object = 1; + string string_object = 2; } message String { - optional string string = 1; + string string = 1; } message Integer { - optional int32 integer = 1; + int32 integer = 1; } message Long { - optional int32 long = 1; + int32 long = 1; } message Float { - optional float float = 1; + float float = 1; } message Double { - optional double double = 1; + double double = 1; } service TestConflictingMethodNames { @@ -168,11 +171,11 @@ message TestConflictingFieldNames { repeated bytes bytes_field = 4; repeated ForbiddenWordsUnderscoreMessage message_field = 5; - optional int32 int32_field_count = 11; - optional TestEnum enum_field_count = 12; - optional string string_field_count = 13; - optional bytes bytes_field_count = 14; - optional ForbiddenWordsUnderscoreMessage message_field_count = 15; + int32 int32_field_count = 11; + TestEnum enum_field_count = 12; + string string_field_count = 13; + bytes bytes_field_count = 14; + ForbiddenWordsUnderscoreMessage message_field_count = 15; repeated int32 Int32Field = 21; // NO_PROTO3 repeated TestEnum EnumField = 22; // NO_PROTO3 @@ -182,13 +185,14 @@ message TestConflictingFieldNames { // This field conflicts with "int32_field" as they both generate // the method getInt32FieldList(). - required int32 int32_field_list = 31; // NO_PROTO3 + int32 int32_field_list = 31 + [features.field_presence = LEGACY_REQUIRED]; // NO_PROTO3 // These field pairs have the same Java converted name - optional string field_name = 32; // NO_PROTO3 - optional string field__name = 33; // NO_PROTO3 - optional int32 _2conflict = 34; // NO_PROTO3 - optional int32 __2conflict = 35; + string field_name = 32; // NO_PROTO3 + string field__name = 33; // NO_PROTO3 + int32 _2conflict = 34; // NO_PROTO3 + int32 __2conflict = 35; extensions 1000 to max; // NO_PROTO3 @@ -196,9 +200,9 @@ message TestConflictingFieldNames { extend TestConflictingFieldNames { // NO_PROTO3 // We don't generate accessors for extensions so the following extension // fields don't conflict with the repeated field "int64_field". - optional int64 int64_field_count = 1001; // NO_PROTO3 - optional int64 int64_field_list = 1002; // NO_PROTO3 - } // NO_PROTO3 + int64 int64_field_count = 1001; // NO_PROTO3 + int64 int64_field_list = 1002; // NO_PROTO3 + } // NO_PROTO3 } message TestMapField { @@ -210,12 +214,12 @@ message TestMapField { } message TestLeadingNumberFields { - optional int32 _30day_impressions = 1; + int32 _30day_impressions = 1; repeated string _60day_impressions = 2; - optional string __2_underscores = 3; + string __2_underscores = 3; repeated string __2repeated_underscores = 4; - optional int32 _32 = 32; + int32 _32 = 32; repeated int64 _64 = 64; } diff --git a/java/core/src/test/proto/com/google/protobuf/test_check_utf8.proto b/java/core/src/test/proto/com/google/protobuf/test_check_utf8.proto index 06e1af657..fff2805e7 100644 --- a/java/core/src/test/proto/com/google/protobuf/test_check_utf8.proto +++ b/java/core/src/test/proto/com/google/protobuf/test_check_utf8.proto @@ -8,21 +8,26 @@ // Author: Jacob Butcher (jbaum@google.com) // // Test file option java_string_check_utf8. -syntax = "proto2"; +edition = "2023"; package proto2_test_check_utf8; +import "google/protobuf/java_features.proto"; + +option features.utf8_validation = NONE; +option features.(pb.java).utf8_validation = VERIFY; option java_outer_classname = "TestCheckUtf8"; -option java_string_check_utf8 = true; message StringWrapper { - required string req = 1; - optional string opt = 2; + string req = 1 [features.field_presence = LEGACY_REQUIRED]; + + string opt = 2; repeated string rep = 3; } message BytesWrapper { - required bytes req = 1; - optional bytes opt = 2; + bytes req = 1 [features.field_presence = LEGACY_REQUIRED]; + + bytes opt = 2; repeated bytes rep = 3; } diff --git a/java/core/src/test/proto/com/google/protobuf/test_check_utf8_size.proto b/java/core/src/test/proto/com/google/protobuf/test_check_utf8_size.proto index 8e27a9b91..e4d09b95f 100644 --- a/java/core/src/test/proto/com/google/protobuf/test_check_utf8_size.proto +++ b/java/core/src/test/proto/com/google/protobuf/test_check_utf8_size.proto @@ -8,22 +8,24 @@ // Author: Jacob Butcher (jbaum@google.com) // // Test file option java_string_check_utf8. -syntax = "proto2"; +edition = "2023"; package proto2_test_check_utf8_size; +option features.utf8_validation = VERIFY; option java_outer_classname = "TestCheckUtf8Size"; -option java_string_check_utf8 = true; option optimize_for = CODE_SIZE; message StringWrapperSize { - required string req = 1; - optional string opt = 2; + string req = 1 [features.field_presence = LEGACY_REQUIRED]; + + string opt = 2; repeated string rep = 3; } message BytesWrapperSize { - required bytes req = 1; - optional bytes opt = 2; + bytes req = 1 [features.field_presence = LEGACY_REQUIRED]; + + bytes opt = 2; repeated bytes rep = 3; } diff --git a/java/core/src/test/proto/com/google/protobuf/wrappers_test.proto b/java/core/src/test/proto/com/google/protobuf/wrappers_test.proto index dd2cee4da..4ecf2dfe0 100644 --- a/java/core/src/test/proto/com/google/protobuf/wrappers_test.proto +++ b/java/core/src/test/proto/com/google/protobuf/wrappers_test.proto @@ -5,12 +5,13 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -syntax = "proto3"; +edition = "2023"; package wrappers_test; import "google/protobuf/wrappers.proto"; +option features.field_presence = IMPLICIT; option java_package = "com.google.protobuf.wrapperstest"; option java_outer_classname = "WrappersTestProto"; diff --git a/java/internal/BUILD.bazel b/java/internal/BUILD.bazel index ff111e7aa..96958e986 100644 --- a/java/internal/BUILD.bazel +++ b/java/internal/BUILD.bazel @@ -1,4 +1,4 @@ -load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix") +load("@rules_pkg//pkg:mappings.bzl", "pkg_files", "strip_prefix") package(default_visibility = ["//java:__subpackages__"]) diff --git a/java/internal/testing.bzl b/java/internal/testing.bzl index b2a781bab..e55f7b5a2 100644 --- a/java/internal/testing.bzl +++ b/java/internal/testing.bzl @@ -2,6 +2,9 @@ Generates a side-car JUnit suite test runner class for each input src. """ + +load("@rules_java//java:defs.bzl", "java_library", "java_test") + _template = """import org.junit.runners.Suite; import org.junit.runner.RunWith; @@ -36,12 +39,13 @@ _gen_suite = rule( def junit_tests(name, srcs, data = [], deps = [], package_name = "com.google.protobuf", test_prefix = None, **kwargs): testlib_name = "%s_lib" % name - native.java_library( + java_library( name = testlib_name, srcs = srcs, deps = deps, resources = data, data = data, + testonly = True, ) test_names = [] prefix = name.replace("-", "_") + "TestSuite" @@ -52,14 +56,14 @@ def junit_tests(name, srcs, data = [], deps = [], package_name = "com.google.pro if test_prefix: test_name = "%s%s" % (test_prefix, test_name) test_names = test_names + [test_name] - suite_name = prefix + '_' + test_name + suite_name = prefix + "_" + test_name _gen_suite( name = suite_name, srcs = [src], package_name = package_name, outname = suite_name, ) - native.java_test( + java_test( name = test_name, test_class = suite_name, srcs = [src] + [":" + suite_name], diff --git a/java/kotlin-lite/BUILD.bazel b/java/kotlin-lite/BUILD.bazel index 39f8b33ee..4e11ad8df 100644 --- a/java/kotlin-lite/BUILD.bazel +++ b/java/kotlin-lite/BUILD.bazel @@ -1,9 +1,10 @@ load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") -load("@rules_java//java:defs.bzl", "java_lite_proto_library") load("@rules_jvm_external//:kt_defs.bzl", "kt_jvm_export") -load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix") +load("@rules_pkg//pkg:mappings.bzl", "pkg_files", "strip_prefix") load("//:protobuf.bzl", "internal_gen_kt_protos") load("//:protobuf_version.bzl", "PROTOBUF_JAVA_VERSION") +load("//bazel:java_lite_proto_library.bzl", "java_lite_proto_library") +load("//build_defs:kotlin_opts.bzl", "protobuf_versioned_kt_jvm_library") java_lite_proto_library( name = "example_extensible_message_java_proto_lite", @@ -22,9 +23,11 @@ internal_gen_kt_protos( deps = [ "//:any_proto", "//:api_proto", + "//:descriptor_proto", "//:duration_proto", "//:empty_proto", "//:field_mask_proto", + "//:java_features_proto", "//:source_context_proto", "//:struct_proto", "//:timestamp_proto", @@ -45,6 +48,24 @@ kt_jvm_library( ], ) +protobuf_versioned_kt_jvm_library( + name = "kotlin-lite_bundle", + automatic_module_name = "com.google.protobuf", + bundle_description = "Kotlin lite Protocol Buffers library. Protocol " + + "Buffers are a way of encoding structured data in " + + "an efficient yet extensible format.", + bundle_name = "Protocol Buffers [Kotlin-Lite]", + bundle_symbolic_name = "com.google.protobuf", + visibility = ["//visibility:public"], + exports = [ + ":lite_extensions", + ":well_known_protos_kotlin_lite", + "//java/kotlin:bytestring_lib", + "//java/kotlin:only_for_use_in_proto_generated_code_its_generator_and_tests", + "//java/kotlin:shared_runtime", + ], +) + kt_jvm_export( name = "kotlin-lite_mvn", deploy_env = [ @@ -53,14 +74,14 @@ kt_jvm_export( ], maven_coordinates = "com.google.protobuf:protobuf-kotlin-lite:%s" % PROTOBUF_JAVA_VERSION, pom_template = "//java/kotlin-lite:pom_template.xml", - resources = ["//:well_known_type_protos"], + resources = [ + "//:java_features_proto", + "//:well_known_type_protos", + "//src/google/protobuf:descriptor_proto_srcs", + ], tags = ["manual"], runtime_deps = [ - ":lite_extensions", - ":well_known_protos_kotlin_lite", - "//java/kotlin:bytestring_lib", - "//java/kotlin:only_for_use_in_proto_generated_code_its_generator_and_tests", - "//java/kotlin:shared_runtime", + ":kotlin-lite_bundle", ], ) @@ -227,7 +248,6 @@ pkg_files( "generate-sources-build.xml", "generate-test-sources-build.xml", "lite.awk", - "pom.xml", "pom_template.xml", "process-lite-sources-build.xml", ], diff --git a/java/kotlin-lite/generate-sources-build.xml b/java/kotlin-lite/generate-sources-build.xml index ab9cfea4e..e15f169e3 100644 --- a/java/kotlin-lite/generate-sources-build.xml +++ b/java/kotlin-lite/generate-sources-build.xml @@ -4,8 +4,11 @@ + + + diff --git a/java/kotlin-lite/pom.xml b/java/kotlin-lite/pom.xml deleted file mode 100644 index 882e4e1f9..000000000 --- a/java/kotlin-lite/pom.xml +++ /dev/null @@ -1,237 +0,0 @@ - - - 4.0.0 - - com.google.protobuf - protobuf-parent - 3.25.5 - - - protobuf-kotlin-lite - - Protocol Buffers [Kotlin-Lite] - - Lite version of Kotlin Protocol Buffers library. This version is optimized for code size, but does - not guarantee API/ABI stability. - - - - 1.6.0 - - - - - ${project.groupId} - protobuf-javalite - ${project.version} - - - junit - junit - test - - - org.mockito - mockito-core - test - - - com.google.guava - guava - test - - - com.google.guava - guava-testlib - test - - - com.google.truth - truth - test - - - org.jetbrains.kotlin - kotlin-stdlib - ${kotlin.version} - - - org.jetbrains.kotlin - kotlin-test - ${kotlin.version} - test - - - - - - - ${protobuf.source.dir} - - google/protobuf/testdata/golden_message_oneof_implemented - google/protobuf/testdata/golden_packed_fields_message - - - - - - - maven-resources-plugin - 3.1.0 - - - copy-kotlin-source-files - generate-sources - - copy-resources - - - ${generated.sources.dir}/com/google/protobuf - - - ${basedir}/../kotlin/src/main/kotlin/com/google/protobuf - - ByteStrings.kt - DslList.kt - DslMap.kt - DslProxy.kt - ExtensionList.kt - OnlyForUseByGeneratedProtoCode.kt - ProtoDslMarker.kt - UnmodifiableCollections.kt - - - - - - - copy-test-source-files - generate-test-sources - - copy-resources - - - ${generated.testsources.dir}/com/google/protobuf - - - ${basedir}/../core/src/test/java/com/google/protobuf - - TestUtilLite.java - - - - - - - - - - - maven-antrun-plugin - - - - generate-sources - generate-sources - - - - - - - run - - - - - - generate-test-sources - generate-test-sources - - - - - - - run - - - - - process-lite-sources - generate-test-sources - - - - - - - run - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - add-generated-sources - generate-sources - - add-source - - - - ${generated.sources.dir} - - - - - - add-generated-test-sources - generate-test-sources - - add-test-source - - - - ${generated.testsources.dir} - - - - - - - - org.jetbrains.kotlin - kotlin-maven-plugin - ${kotlin.version} - true - - - compile - compile - - - ${project.basedir}/src/main/kotlin - ${generated.sources.dir} - - - - - test-compile - test-compile - - - ${project.basedir}/src/test/kotlin - ${generated.testsources.dir} - - - - - - - - diff --git a/java/kotlin-lite/src/test/kotlin/com/google/protobuf/ExtendableMessageLiteExtensionsTest.kt b/java/kotlin-lite/src/test/kotlin/com/google/protobuf/ExtendableMessageLiteExtensionsTest.kt index 892385915..743e04be1 100644 --- a/java/kotlin-lite/src/test/kotlin/com/google/protobuf/ExtendableMessageLiteExtensionsTest.kt +++ b/java/kotlin-lite/src/test/kotlin/com/google/protobuf/ExtendableMessageLiteExtensionsTest.kt @@ -1,32 +1,9 @@ // 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. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd package com.google.protobuf.kotlin diff --git a/java/kotlin-lite/src/test/kotlin/com/google/protobuf/Proto2LiteTest.kt b/java/kotlin-lite/src/test/kotlin/com/google/protobuf/Proto2LiteTest.kt index 9b62b3e8f..bf292608b 100644 --- a/java/kotlin-lite/src/test/kotlin/com/google/protobuf/Proto2LiteTest.kt +++ b/java/kotlin-lite/src/test/kotlin/com/google/protobuf/Proto2LiteTest.kt @@ -1,32 +1,9 @@ // 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. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd package com.google.protobuf.kotlin diff --git a/java/kotlin/BUILD.bazel b/java/kotlin/BUILD.bazel index 2d17a71d4..bf6e72be9 100644 --- a/java/kotlin/BUILD.bazel +++ b/java/kotlin/BUILD.bazel @@ -1,10 +1,11 @@ load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") -load("@rules_java//java:defs.bzl", "java_proto_library") load("@rules_jvm_external//:kt_defs.bzl", "kt_jvm_export") -load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix") -load("@rules_proto//proto:defs.bzl", "proto_library") -load("//:protobuf_version.bzl", "PROTOBUF_JAVA_VERSION") +load("@rules_pkg//pkg:mappings.bzl", "pkg_files", "strip_prefix") load("//:protobuf.bzl", "internal_gen_kt_protos") +load("//:protobuf_version.bzl", "PROTOBUF_JAVA_VERSION") +load("//bazel:java_proto_library.bzl", "java_proto_library") +load("//bazel:proto_library.bzl", "proto_library") +load("//build_defs:kotlin_opts.bzl", "protobuf_versioned_kt_jvm_library") exports_files([ "src/test/kotlin/com/google/protobuf/Proto3Test.kt", @@ -50,6 +51,24 @@ kt_jvm_library( deps = ["//java/core"], ) +protobuf_versioned_kt_jvm_library( + name = "kotlin_bundle", + automatic_module_name = "com.google.protobuf", + bundle_description = "Kotlin core Protocol Buffers library. Protocol " + + "Buffers are a way of encoding structured data in an" + + "efficient yet extensible format.", + bundle_name = "Protocol Buffers [Kotlin-Core]", + bundle_symbolic_name = "com.google.protobuf", + visibility = ["//visibility:public"], + exports = [ + ":bytestring_lib", + ":full_extensions", + ":only_for_use_in_proto_generated_code_its_generator_and_tests", + ":shared_runtime", + ":well_known_protos_kotlin", + ], +) + kt_jvm_export( name = "kotlin_mvn", deploy_env = [ @@ -59,16 +78,13 @@ kt_jvm_export( maven_coordinates = "com.google.protobuf:protobuf-kotlin:%s" % PROTOBUF_JAVA_VERSION, pom_template = "//java/kotlin:pom_template.xml", resources = [ + "//:java_features_proto", "//:well_known_type_protos", "//src/google/protobuf:descriptor_proto_srcs", ], tags = ["manual"], runtime_deps = [ - ":bytestring_lib", - ":full_extensions", - ":only_for_use_in_proto_generated_code_its_generator_and_tests", - ":shared_runtime", - ":well_known_protos_kotlin", + ":kotlin_bundle", ], ) @@ -314,6 +330,7 @@ internal_gen_kt_protos( "//:duration_proto", "//:empty_proto", "//:field_mask_proto", + "//:java_features_proto", "//:source_context_proto", "//:struct_proto", "//:timestamp_proto", diff --git a/java/kotlin/generate-sources-build.xml b/java/kotlin/generate-sources-build.xml index 6963f3717..9a0ec1dcc 100644 --- a/java/kotlin/generate-sources-build.xml +++ b/java/kotlin/generate-sources-build.xml @@ -4,6 +4,8 @@ + + diff --git a/java/kotlin/pom.xml b/java/kotlin/pom.xml index 5ef4b36ee..042fff803 100644 --- a/java/kotlin/pom.xml +++ b/java/kotlin/pom.xml @@ -4,7 +4,7 @@ com.google.protobuf protobuf-parent - 3.25.5 + 4.28.3 protobuf-kotlin diff --git a/java/kotlin/src/main/kotlin/com/google/protobuf/ExtendableMessageExtensions.kt b/java/kotlin/src/main/kotlin/com/google/protobuf/ExtendableMessageExtensions.kt index 2723f2783..9f62a3f7e 100644 --- a/java/kotlin/src/main/kotlin/com/google/protobuf/ExtendableMessageExtensions.kt +++ b/java/kotlin/src/main/kotlin/com/google/protobuf/ExtendableMessageExtensions.kt @@ -31,25 +31,25 @@ package com.google.protobuf.kotlin import com.google.protobuf.ExtensionLite -import com.google.protobuf.GeneratedMessageV3 +import com.google.protobuf.GeneratedMessage /** Sets the current value of the proto extension in this builder. */ operator fun < - M : GeneratedMessageV3.ExtendableMessage, - B : GeneratedMessageV3.ExtendableBuilder, + M : GeneratedMessage.ExtendableMessage, + B : GeneratedMessage.ExtendableBuilder, T : Any> B.set(extension: ExtensionLite, value: T) { setExtension(extension, value) } /** Gets the current value of the proto extension. */ operator fun < - M : GeneratedMessageV3.ExtendableMessage, - MorBT : GeneratedMessageV3.ExtendableMessageOrBuilder, + M : GeneratedMessage.ExtendableMessage, + MorBT : GeneratedMessage.ExtendableMessageOrBuilder, T : Any> MorBT.get(extension: ExtensionLite): T = getExtension(extension) /** Returns true if the specified extension is set on this builder. */ operator fun < - M : GeneratedMessageV3.ExtendableMessage, - MorBT : GeneratedMessageV3.ExtendableMessageOrBuilder> MorBT.contains( + M : GeneratedMessage.ExtendableMessage, + MorBT : GeneratedMessage.ExtendableMessageOrBuilder> MorBT.contains( extension: ExtensionLite ): Boolean = hasExtension(extension) diff --git a/java/kotlin/src/test/kotlin/com/google/protobuf/AniesTest.kt b/java/kotlin/src/test/kotlin/com/google/protobuf/AniesTest.kt index 808c644a9..f7b5e69cd 100644 --- a/java/kotlin/src/test/kotlin/com/google/protobuf/AniesTest.kt +++ b/java/kotlin/src/test/kotlin/com/google/protobuf/AniesTest.kt @@ -1,32 +1,9 @@ // 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. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd package com.google.protobuf.kotlin diff --git a/java/kotlin/src/test/kotlin/com/google/protobuf/ByteStringsTest.kt b/java/kotlin/src/test/kotlin/com/google/protobuf/ByteStringsTest.kt index 9b8dbf50f..d9c64c64b 100644 --- a/java/kotlin/src/test/kotlin/com/google/protobuf/ByteStringsTest.kt +++ b/java/kotlin/src/test/kotlin/com/google/protobuf/ByteStringsTest.kt @@ -1,32 +1,9 @@ // 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. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd package com.google.protobuf.kotlin diff --git a/java/kotlin/src/test/kotlin/com/google/protobuf/DslListTest.kt b/java/kotlin/src/test/kotlin/com/google/protobuf/DslListTest.kt index 9044ce3b2..556d618c9 100644 --- a/java/kotlin/src/test/kotlin/com/google/protobuf/DslListTest.kt +++ b/java/kotlin/src/test/kotlin/com/google/protobuf/DslListTest.kt @@ -1,32 +1,9 @@ // 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. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd package com.google.protobuf.kotlin diff --git a/java/kotlin/src/test/kotlin/com/google/protobuf/DslMapTest.kt b/java/kotlin/src/test/kotlin/com/google/protobuf/DslMapTest.kt index 3487f221e..5e75a8790 100644 --- a/java/kotlin/src/test/kotlin/com/google/protobuf/DslMapTest.kt +++ b/java/kotlin/src/test/kotlin/com/google/protobuf/DslMapTest.kt @@ -1,32 +1,9 @@ // 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. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd package com.google.protobuf.kotlin diff --git a/java/kotlin/src/test/kotlin/com/google/protobuf/ExtendableMessageExtensionsTest.kt b/java/kotlin/src/test/kotlin/com/google/protobuf/ExtendableMessageExtensionsTest.kt index f4ad142a1..d2140e95b 100644 --- a/java/kotlin/src/test/kotlin/com/google/protobuf/ExtendableMessageExtensionsTest.kt +++ b/java/kotlin/src/test/kotlin/com/google/protobuf/ExtendableMessageExtensionsTest.kt @@ -1,32 +1,9 @@ // 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. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd package com.google.protobuf.kotlin diff --git a/java/kotlin/src/test/kotlin/com/google/protobuf/ExtensionListTest.kt b/java/kotlin/src/test/kotlin/com/google/protobuf/ExtensionListTest.kt index e311b825a..9cd60dfd9 100644 --- a/java/kotlin/src/test/kotlin/com/google/protobuf/ExtensionListTest.kt +++ b/java/kotlin/src/test/kotlin/com/google/protobuf/ExtensionListTest.kt @@ -1,32 +1,9 @@ // 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. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd package com.google.protobuf.kotlin diff --git a/java/kotlin/src/test/kotlin/com/google/protobuf/Proto2Test.kt b/java/kotlin/src/test/kotlin/com/google/protobuf/Proto2Test.kt index 0aa9fdfe8..7c319bfaa 100644 --- a/java/kotlin/src/test/kotlin/com/google/protobuf/Proto2Test.kt +++ b/java/kotlin/src/test/kotlin/com/google/protobuf/Proto2Test.kt @@ -1,32 +1,9 @@ // 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. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd package com.google.protobuf.kotlin @@ -248,7 +225,7 @@ class Proto2Test { TestAllTypesKt.repeatedGroup { a = 1 }, TestAllTypesKt.repeatedGroup { a = 2 }, TestAllTypesKt.repeatedGroup { a = 3 }, - TestAllTypesKt.repeatedGroup { a = 4 } + TestAllTypesKt.repeatedGroup { a = 4 }, ) ) repeatedGroup[0] = TestAllTypesKt.repeatedGroup { a = 5 } @@ -258,7 +235,7 @@ class Proto2Test { TestAllTypesKt.repeatedGroup { a = 5 }, TestAllTypesKt.repeatedGroup { a = 2 }, TestAllTypesKt.repeatedGroup { a = 3 }, - TestAllTypesKt.repeatedGroup { a = 4 } + TestAllTypesKt.repeatedGroup { a = 4 }, ) ) @@ -272,7 +249,7 @@ class Proto2Test { nestedMessage { bb = 1 }, nestedMessage { bb = 2 }, nestedMessage { bb = 3 }, - nestedMessage { bb = 4 } + nestedMessage { bb = 4 }, ) ) repeatedNestedMessage[0] = nestedMessage { bb = 5 } @@ -282,7 +259,7 @@ class Proto2Test { nestedMessage { bb = 5 }, nestedMessage { bb = 2 }, nestedMessage { bb = 3 }, - nestedMessage { bb = 4 } + nestedMessage { bb = 4 }, ) ) @@ -571,7 +548,7 @@ class Proto2Test { repeatedGroupExtension { a = 1 }, repeatedGroupExtension { a = 2 }, repeatedGroupExtension { a = 3 }, - repeatedGroupExtension { a = 4 } + repeatedGroupExtension { a = 4 }, ) ) this[UnittestProto.repeatedGroupExtension][0] = repeatedGroupExtension { a = 5 } @@ -581,7 +558,7 @@ class Proto2Test { repeatedGroupExtension { a = 5 }, repeatedGroupExtension { a = 2 }, repeatedGroupExtension { a = 3 }, - repeatedGroupExtension { a = 4 } + repeatedGroupExtension { a = 4 }, ) ) @@ -598,7 +575,7 @@ class Proto2Test { nestedMessage { bb = 1 }, nestedMessage { bb = 2 }, nestedMessage { bb = 3 }, - nestedMessage { bb = 4 } + nestedMessage { bb = 4 }, ) ) this[UnittestProto.repeatedNestedMessageExtension][0] = nestedMessage { bb = 5 } @@ -608,7 +585,7 @@ class Proto2Test { nestedMessage { bb = 5 }, nestedMessage { bb = 2 }, nestedMessage { bb = 3 }, - nestedMessage { bb = 4 } + nestedMessage { bb = 4 }, ) ) @@ -780,7 +757,7 @@ class Proto2Test { 1 to Proto2MapEnum.PROTO2_MAP_ENUM_FOO, 2 to Proto2MapEnum.PROTO2_MAP_ENUM_BAR, 3 to Proto2MapEnum.PROTO2_MAP_ENUM_BAZ, - 4 to Proto2MapEnum.PROTO2_MAP_ENUM_FOO + 4 to Proto2MapEnum.PROTO2_MAP_ENUM_FOO, ) ) } @@ -867,6 +844,11 @@ class Proto2Test { cachedSize_ = "foo" serializedSize_ = true by = "foo" + dEPRECATEDFoo = "foo" + DEPRECATEDBar = "foo" + iD = "foo" + aBNotification = "foo" + notDEPRECATEDFoo = "foo" } ) .isEqualTo( @@ -892,6 +874,11 @@ class Proto2Test { .setCachedSize_("foo") .setSerializedSize_(true) .setBy("foo") + .setDEPRECATEDFoo("foo") + .setDEPRECATEDBar("foo") + .setID("foo") + .setABNotification("foo") + .setNotDEPRECATEDFoo("foo") .build() ) diff --git a/java/kotlin/src/test/kotlin/com/google/protobuf/Proto3Test.kt b/java/kotlin/src/test/kotlin/com/google/protobuf/Proto3Test.kt index 8ce00946d..72ab8e1f8 100644 --- a/java/kotlin/src/test/kotlin/com/google/protobuf/Proto3Test.kt +++ b/java/kotlin/src/test/kotlin/com/google/protobuf/Proto3Test.kt @@ -1,32 +1,9 @@ // 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. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd package com.google.protobuf.kotlin @@ -104,7 +81,7 @@ class Proto3Test { nestedMessage { bb = 1 }, nestedMessage { bb = 2 }, nestedMessage { bb = 3 }, - nestedMessage { bb = 4 } + nestedMessage { bb = 4 }, ) ) repeatedNestedMessage[0] = nestedMessage { bb = 5 } @@ -114,7 +91,7 @@ class Proto3Test { nestedMessage { bb = 5 }, nestedMessage { bb = 2 }, nestedMessage { bb = 3 }, - nestedMessage { bb = 4 } + nestedMessage { bb = 4 }, ) ) @@ -223,6 +200,11 @@ class Proto3Test { pairs["foo"] = 1 LeadingUnderscore = "foo" option = 1 + dEPRECATEDFoo = "foo" + iD = "foo" + aBNotification = "foo" + DEPRECATEDBar = "foo" + notDEPRECATEDFoo = "foo" } ) .isEqualTo( @@ -260,6 +242,11 @@ class Proto3Test { .putPairs("foo", 1) .setLeadingUnderscore("foo") .setOption(1) + .setDEPRECATEDFoo("foo") + .setID("foo") + .setABNotification("foo") + .setDEPRECATEDBar("foo") + .setNotDEPRECATEDFoo("foo") .build() ) diff --git a/java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto2.proto b/java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto2.proto index 4ada5eaeb..f771bf97b 100644 --- a/java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto2.proto +++ b/java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto2.proto @@ -1,32 +1,9 @@ // 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. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd // LINT: LEGACY_NAMES syntax = "proto2"; @@ -66,6 +43,15 @@ message EvilNamesProto2 { optional string cached_size = 23; optional bool serialized_size = 24; optional string by = 25; + + optional string DEPRECATED_foo = 26; + optional group DEPRECATED_NavigationImageRequested = 27 { + optional int32 DEPRECATED_FooBar = 28; + } + optional string __DEPRECATED_Bar = 29; + optional string ID = 30; + optional string a_b_notification = 31; + optional string not_DEPRECATED_foo = 32; } message List {} diff --git a/java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto3.proto b/java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto3.proto index 9feb219cf..238bebc06 100644 --- a/java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto3.proto +++ b/java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto3.proto @@ -1,32 +1,9 @@ // 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. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd // LINT: LEGACY_NAMES syntax = "proto3"; @@ -79,6 +56,12 @@ message EvilNamesProto3 { oneof _leading_underscore_oneof { int32 option = 34; } + + optional string DEPRECATED_foo = 35; + optional string ID = 36; + optional string a_b_notification = 37; + optional string __DEPRECATED_Bar = 38; + optional string not_DEPRECATED_foo = 39; } message HardKeywordsAllTypesProto3 { diff --git a/java/kotlin/src/test/proto/com/google/protobuf/example_extensible_message.proto b/java/kotlin/src/test/proto/com/google/protobuf/example_extensible_message.proto index b82c82b03..a57512d77 100644 --- a/java/kotlin/src/test/proto/com/google/protobuf/example_extensible_message.proto +++ b/java/kotlin/src/test/proto/com/google/protobuf/example_extensible_message.proto @@ -1,32 +1,9 @@ // 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. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd syntax = "proto2"; diff --git a/java/kotlin/src/test/proto/com/google/protobuf/multiple_files_proto3.proto b/java/kotlin/src/test/proto/com/google/protobuf/multiple_files_proto3.proto index 61141c54e..a6d4eccd9 100644 --- a/java/kotlin/src/test/proto/com/google/protobuf/multiple_files_proto3.proto +++ b/java/kotlin/src/test/proto/com/google/protobuf/multiple_files_proto3.proto @@ -1,32 +1,9 @@ // 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. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd syntax = "proto3"; diff --git a/java/lite.md b/java/lite.md index 21469e7a8..729e7afd9 100644 --- a/java/lite.md +++ b/java/lite.md @@ -29,10 +29,14 @@ protobuf Java Lite runtime. If you are using Maven, include the following: com.google.protobuf protobuf-javalite - 3.25.4 + ``` +And **replace `` with a version from the +[Maven Protocol Buffers \[Lite\] Repository](https://mvnrepository.com/artifact/com.google.protobuf/protobuf-javalite).** +For example, `3.25.3`. + ## R8 rule to make production app builds work The Lite runtime internally uses reflection to avoid generating hashCode/equals/(de)serialization methods. diff --git a/java/lite/BUILD.bazel b/java/lite/BUILD.bazel index f84b5c514..f19cc38dd 100644 --- a/java/lite/BUILD.bazel +++ b/java/lite/BUILD.bazel @@ -1,6 +1,6 @@ load("@bazel_skylib//rules:build_test.bzl", "build_test") -load("@rules_pkg//:mappings.bzl", "pkg_filegroup", "pkg_files", "strip_prefix") -load("@rules_proto//proto:defs.bzl", "proto_lang_toolchain") +load("@rules_pkg//pkg:mappings.bzl", "pkg_filegroup", "pkg_files", "strip_prefix") +load("//bazel/toolchains:proto_lang_toolchain.bzl", "proto_lang_toolchain") load("//conformance:defs.bzl", "conformance_test") load("//java/internal:testing.bzl", "junit_tests") @@ -24,6 +24,7 @@ proto_lang_toolchain( name = "toolchain", # keep this in sync w/ LITE_WELL_KNOWN_PROTO_MAP in //:BUILD blacklisted_protos = [ + "//:java_features_proto", "//:any_proto", "//:api_proto", "//:duration_proto", @@ -60,6 +61,7 @@ build_test( conformance_test( name = "conformance_test", failure_list = "//conformance:failure_list_java_lite.txt", + maximum_edition = "2023", testee = "//conformance:conformance_java_lite", text_format_failure_list = "//conformance:text_format_failure_list_java_lite.txt", ) @@ -88,7 +90,6 @@ pkg_files( "generate-sources-build.xml", "generate-test-sources-build.xml", "lite.awk", - "pom.xml", "pom_template.xml", "process-lite-sources-build.xml", ], diff --git a/java/lite/generate-sources-build.xml b/java/lite/generate-sources-build.xml index 74d96a2f1..f1dcadb15 100644 --- a/java/lite/generate-sources-build.xml +++ b/java/lite/generate-sources-build.xml @@ -4,8 +4,11 @@ + + + diff --git a/java/lite/pom.xml b/java/lite/pom.xml deleted file mode 100644 index d64fa31b5..000000000 --- a/java/lite/pom.xml +++ /dev/null @@ -1,347 +0,0 @@ - - - 4.0.0 - - com.google.protobuf - protobuf-parent - 3.25.5 - - - protobuf-javalite - bundle - - Protocol Buffers [Lite] - - Lite version of Protocol Buffers library. This version is optimized for code size, but does - not guarantee API/ABI stability. - - - - - junit - junit - test - - - org.mockito - mockito-core - test - - - com.google.guava - guava - test - - - com.google.truth - truth - test - - - - - - - - ${protobuf.source.dir} - - google/protobuf/any.proto - google/protobuf/api.proto - google/protobuf/duration.proto - google/protobuf/empty.proto - google/protobuf/field_mask.proto - google/protobuf/source_context.proto - google/protobuf/struct.proto - google/protobuf/timestamp.proto - google/protobuf/type.proto - google/protobuf/wrappers.proto - - - - - - ${protobuf.source.dir} - - google/protobuf/testdata/golden_message_oneof_implemented - google/protobuf/testdata/golden_packed_fields_message - - - - - - - maven-resources-plugin - 3.1.0 - - - copy-source-files - generate-sources - - copy-resources - - - ${generated.sources.dir}/com/google/protobuf - - - ${basedir}/../core/src/main/java/com/google/protobuf - - - AbstractMessageLite.java - AbstractParser.java - AbstractProtobufList.java - AllocatedBuffer.java - Android.java - ArrayDecoders.java - BinaryReader.java - BinaryWriter.java - BooleanArrayList.java - BufferAllocator.java - ByteBufferWriter.java - ByteOutput.java - ByteString.java - CanIgnoreReturnValue.java - CheckReturnValue.java - CodedInputStream.java - CodedInputStreamReader.java - CodedOutputStream.java - CodedOutputStreamWriter.java - CompileTimeConstant.java - DoubleArrayList.java - ExperimentalApi.java - ExtensionLite.java - ExtensionRegistryFactory.java - ExtensionRegistryLite.java - ExtensionSchema.java - ExtensionSchemaLite.java - ExtensionSchemas.java - FieldInfo.java - FieldSet.java - FieldType.java - FloatArrayList.java - GeneratedMessageInfoFactory.java - GeneratedMessageLite.java - InlineMe.java - IntArrayList.java - Internal.java - InvalidProtocolBufferException.java - IterableByteBufferInputStream.java - Java8Compatibility.java - JavaType.java - LazyField.java - LazyFieldLite.java - LazyStringArrayList.java - LazyStringList.java - ListFieldSchema.java - LongArrayList.java - ManifestSchemaFactory.java - MapEntryLite.java - MapFieldLite.java - MapFieldSchema.java - MapFieldSchemaLite.java - MapFieldSchemas.java - MessageInfo.java - MessageInfoFactory.java - MessageLite.java - MessageLiteOrBuilder.java - MessageLiteToString.java - MessageSchema.java - MessageSetSchema.java - MutabilityOracle.java - NewInstanceSchema.java - NewInstanceSchemaLite.java - NewInstanceSchemas.java - NioByteString.java - OneofInfo.java - Parser.java - PrimitiveNonBoxingCollection.java - ProtoSyntax.java - Protobuf.java - ProtobufArrayList.java - ProtocolStringList.java - RawMessageInfo.java - Reader.java - RopeByteString.java - Schema.java - SchemaFactory.java - SchemaUtil.java - SmallSortedMap.java - StructuralMessageInfo.java - TextFormatEscaper.java - UninitializedMessageException.java - UnknownFieldSchema.java - UnknownFieldSetLite.java - UnknownFieldSetLiteSchema.java - UnmodifiableLazyStringList.java - UnsafeByteOperations.java - UnsafeUtil.java - Utf8.java - WireFormat.java - Writer.java - - - - - - - copy-test-source-files - generate-test-sources - - copy-resources - - - ${generated.testsources.dir}/com/google/protobuf - - - ${basedir}/../core/src/test/java/com/google/protobuf - - - - AbstractMessageTest.java - AbstractProto2SchemaTest.java - AnyTest.java - CodedInputStreamTest.java - DeprecatedFieldTest.java - DescriptorsTest.java - DiscardUnknownFieldsTest.java - DynamicMessageTest.java - ExtensionRegistryFactoryTest.java - FieldPresenceTest.java - ForceFieldBuildersPreRun.java - GeneratedMessageTest.java - LazyFieldTest.java - LazyStringEndToEndTest.java - MapForProto2Test.java - MapTest.java - MessageTest.java - NestedBuildersTest.java - PackedFieldTest.java - ParserTest.java - ParseExceptionsTest.java - Proto2ExtensionLookupSchemaTest.java - Proto2SchemaTest.java - Proto2UnknownEnumValueTest.java - RepeatedFieldBuilderV3Test.java - ServiceTest.java - SingleFieldBuilderV3Test.java - TestBadIdentifiers.java - TextFormatParseInfoTreeTest.java - TextFormatParseLocationTest.java - TextFormatPerformanceTest.java - TextFormatTest.java - TestUtil.java - TypeRegistryTest.java - UnknownEnumValueTest.java - UnknownFieldSetLiteTest.java - UnknownFieldSetPerformanceTest.java - UnknownFieldSetTest.java - WellKnownTypesTest.java - WireFormatTest.java - - - - - - - - - - - maven-antrun-plugin - - - - generate-sources - generate-sources - - - - - - - run - - - - - - generate-test-sources - generate-test-sources - - - - - - - run - - - - - process-lite-sources - generate-test-sources - - - - - - - run - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - add-generated-sources - generate-sources - - add-source - - - - ${generated.sources.dir} - - - - - - add-generated-test-sources - generate-test-sources - - add-test-source - - - - ${generated.testsources.dir} - - - - - - - - - org.apache.felix - maven-bundle-plugin - true - - - com.google.protobuf - https://developers.google.com/protocol-buffers/ - com.google.protobuf - com.google.protobuf;version=${project.version} - sun.misc;resolution:=optional,* - - - - - - - diff --git a/java/lite/src/test/java/com/google/protobuf/LiteTest.java b/java/lite/src/test/java/com/google/protobuf/LiteTest.java index 51652397f..ecf997f83 100644 --- a/java/lite/src/test/java/com/google/protobuf/LiteTest.java +++ b/java/lite/src/test/java/com/google/protobuf/LiteTest.java @@ -17,6 +17,7 @@ import com.google.protobuf.UnittestImportLite.ImportEnumLite; import com.google.protobuf.UnittestImportPublicLite.PublicImportMessageLite; import com.google.protobuf.UnittestLite.ForeignEnumLite; import com.google.protobuf.UnittestLite.ForeignMessageLite; +import com.google.protobuf.UnittestLite.RecursiveGroup; import com.google.protobuf.UnittestLite.RecursiveMessage; import com.google.protobuf.UnittestLite.TestAllExtensionsLite; import com.google.protobuf.UnittestLite.TestAllTypesLite; @@ -2426,6 +2427,12 @@ public class LiteTest { int unused = TestRecursiveOneof.getDefaultInstance().hashCode(); } + @Test + public void testParseFromEmptyBytes() throws Exception { + assertThat(TestAllTypesLite.parseFrom(new byte[] {})) + .isSameInstanceAs(TestAllTypesLite.getDefaultInstance()); + } + @Test public void testParseFromByteBuffer() throws Exception { TestAllTypesLite message = @@ -2613,6 +2620,17 @@ public class LiteTest { assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting"); } + @Test + public void testParseFromBytes_recursiveKnownGroups() throws Exception { + byte[] data99 = makeRecursiveGroup(99).toByteArray(); + byte[] data100 = makeRecursiveGroup(100).toByteArray(); + + RecursiveGroup unused = RecursiveGroup.parseFrom(data99); + Throwable thrown = + assertThrows(InvalidProtocolBufferException.class, () -> RecursiveGroup.parseFrom(data100)); + assertThat(thrown).hasMessageThat().contains("Protocol message had too many levels of nesting"); + } + @Test @SuppressWarnings("ProtoParseFromByteString") public void testMaliciousSGroupTagsWithMapField_fromByteArray() throws Exception { @@ -2915,7 +2933,7 @@ public class LiteTest { @Test public void testNullExtensionRegistry() throws Exception { try { - TestAllTypesLite.parseFrom(new byte[] {}, null); + TestAllTypesLite.parseFrom(TestUtilLite.getAllLiteSetBuilder().build().toByteArray(), null); assertWithMessage("expected exception").fail(); } catch (NullPointerException expected) { } @@ -3031,4 +3049,12 @@ public class LiteTest { return RecursiveMessage.newBuilder().setRecurse(makeRecursiveMessage(num - 1)).build(); } } + + private static RecursiveGroup makeRecursiveGroup(int num) { + if (num == 0) { + return RecursiveGroup.getDefaultInstance(); + } else { + return RecursiveGroup.newBuilder().setRecurse(makeRecursiveGroup(num - 1)).build(); + } + } } diff --git a/java/osgi/kotlin_osgi.bzl b/java/osgi/kotlin_osgi.bzl new file mode 100644 index 000000000..a396e9350 --- /dev/null +++ b/java/osgi/kotlin_osgi.bzl @@ -0,0 +1,215 @@ +""" Custom rule to generate OSGi Manifest for Kotlin """ + +load("@rules_java//java:defs.bzl", "JavaInfo") +load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +def osgi_kt_jvm_library( + name, + automatic_module_name, + bundle_description, + bundle_doc_url, + bundle_license, + bundle_name, + bundle_symbolic_name, + bundle_version, + bundle_additional_imports = [], + bundle_additional_exports = [], + deps = [], + exports = [], + exported_plugins = [], + neverlink = False, + runtime_deps = [], + visibility = [], + **kwargs): + """Extends `kt_jvm_library` to add OSGi headers to the MANIFEST.MF using bndlib + + This macro should be usable as a drop-in replacement for kt_jvm_library. + + The additional arguments are given the bndlib tool to generate an OSGi-compliant manifest file. + See [bnd documentation](https://bnd.bndtools.org/chapters/110-introduction.html) + + Args: + name: (required) A unique name for this target. + automatic_module_name: (required) The Automatic-Module-Name header that represents + the name of the module when this bundle is used as an automatic + module. + bundle_description: (required) The Bundle-Description header defines a short + description of this bundle. + bundle_doc_url: (required) The Bundle-DocURL headers must contain a URL pointing + to documentation about this bundle. + bundle_license: (required) The Bundle-License header provides an optional machine + readable form of license information. + bundle_name: (required) The Bundle-Name header defines a readable name for this + bundle. This should be a short, human-readable name that can + contain spaces. + bundle_symbolic_name: (required) The Bundle-SymbolicName header specifies a + non-localizable name for this bundle. The bundle symbolic name + together with a version must identify a unique bundle though it can + be installed multiple times in a framework. The bundle symbolic + name should be based on the reverse domain name convention. + bundle_version: (required) The Bundle-Version header specifies the version string + for this bundle. The version string is expected to follow semantic + versioning conventions MAJOR.MINOR.PATCH[.BUILD] + bundle_additional_exports: The Export-Package header contains a + declaration of exported packages. These are additional export + package statements to be added before the default wildcard export + "*;version={$Bundle-Version}". + bundle_additional_imports: The Import-Package header declares the + imported packages for this bundle. These are additional import + package statements to be added before the default wildcard import + "*". + deps: The list of libraries to link into this library. See general + comments about deps at Typical attributes defined by most build + rules. The jars built by java_library rules listed in deps will be + on the compile-time classpath of this rule. Furthermore the + transitive closure of their deps, runtime_deps and exports will be + on the runtime classpath. By contrast, targets in the data + attribute are included in the runfiles but on neither the + compile-time nor runtime classpath. + exports: Exported libraries. + exported_plugins: The list of java_plugins (e.g. annotation processors) + to export to libraries that directly depend on this library. The + specified list of java_plugins will be applied to any library which + directly depends on this library, just as if that library had + explicitly declared these labels in plugins. + neverlink: Whether this library should only be used for compilation and + not at runtime. Useful if the library will be provided by the runtime + environment during execution. Examples of such libraries are the IDE + APIs for IDE plug-ins or tools.jar for anything running on a standard + JDK. + runtime_deps: Libraries to make available to the final binary or test + at runtime only. Like ordinary deps, these will appear on the runtime + classpath, but unlike them, not on the compile-time classpath. + Dependencies needed only at runtime should be listed here. + Dependency-analysis tools should ignore targets that appear in both + runtime_deps and deps + visibility: The visibility attribute on a target controls whether the + target can be used in other packages. See the documentation for + visibility. + **kwargs: Additional key-word arguments that are passed to the internal + java_library target. + + """ + + # Build the private jar without the OSGI manifest + private_library_name = "%s-no-manifest-do-not-use" % name + kt_jvm_library( + name = private_library_name, + deps = deps, + runtime_deps = runtime_deps, + neverlink = True, + visibility = ["//visibility:private"], + **kwargs + ) + + # Repackage the jar with an OSGI manifest + _osgi_kt_jvm_jar( + name = name, + automatic_module_name = automatic_module_name, + bundle_description = bundle_description, + bundle_doc_url = bundle_doc_url, + bundle_license = bundle_license, + bundle_name = bundle_name, + bundle_symbolic_name = bundle_symbolic_name, + bundle_version = bundle_version, + export_package = bundle_additional_exports + ["*;version=${Bundle-Version}"], + import_package = bundle_additional_imports + ["*"], + target = private_library_name, + deps = deps, + runtime_deps = runtime_deps, + exported_plugins = exported_plugins, + neverlink = neverlink, + exports = exports, + visibility = visibility, + ) + +def _run_osgi_wrapper(ctx, input_jar, output_jar): + args = ctx.actions.args() + args.add("--input_jar", input_jar.path) + args.add("--output_jar", output_jar.path) + args.add("--automatic_module_name", ctx.attr.automatic_module_name) + args.add("--bundle_copyright", ctx.attr.bundle_copyright) + args.add("--bundle_description", ctx.attr.bundle_description) + args.add("--bundle_doc_url", ctx.attr.bundle_doc_url) + args.add("--bundle_license", ctx.attr.bundle_license) + args.add("--bundle_name", ctx.attr.bundle_name) + args.add("--bundle_version", ctx.attr.bundle_version) + args.add("--bundle_symbolic_name", ctx.attr.bundle_symbolic_name) + args.add_joined("--export_package", ctx.attr.export_package, join_with = ",") + args.add_joined("--import_package", ctx.attr.import_package, join_with = ",") + + ctx.actions.run( + inputs = [input_jar], + executable = ctx.executable._osgi_wrapper_exe, + arguments = [args], + outputs = [output_jar], + progress_message = "Generating OSGi bundle Manifest for %s" % input_jar.path, + ) + +# Kotlin implementation of osgi jar, removes classpath and source_jar +def _osgi_kt_jvm_jar_impl(ctx): + if len(ctx.attr.target[JavaInfo].java_outputs) != 1: + fail("osgi_jar rule can only be used on a single java target.") + target_java_output = ctx.attr.target[JavaInfo].java_outputs[0] + + output_jar = ctx.outputs.output_jar + + input_jar = target_java_output.class_jar + + _run_osgi_wrapper(ctx, input_jar, output_jar) + + return [ + DefaultInfo( + files = depset([output_jar]), + # Workaround for https://github.com/bazelbuild/bazel/issues/15043 + # Bazel's native rule such as sh_test do not pick up 'files' in + # DefaultInfo for a target in 'data'. + data_runfiles = ctx.runfiles([output_jar]), + ), + JavaInfo( + output_jar = output_jar, + + # compile_jar should be an ijar, but using an ijar results in + # missing protobuf import version. + compile_jar = output_jar, + generated_class_jar = target_java_output.generated_class_jar, + native_headers_jar = target_java_output.native_headers_jar, + manifest_proto = target_java_output.manifest_proto, + neverlink = ctx.attr.neverlink, + deps = [dep[JavaInfo] for dep in ctx.attr.deps], + runtime_deps = [dep[JavaInfo] for dep in ctx.attr.runtime_deps], + exports = [exp[JavaInfo] for exp in ctx.attr.exports], + exported_plugins = ctx.attr.exported_plugins, + ), + ] + +_osgi_kt_jvm_jar = rule( + implementation = _osgi_kt_jvm_jar_impl, + outputs = { + "output_jar": "lib%{name}.jar", + }, + attrs = { + "automatic_module_name": attr.string(), + "bundle_copyright": attr.string(), + "bundle_description": attr.string(), + "bundle_doc_url": attr.string(), + "bundle_license": attr.string(), + "bundle_name": attr.string(), + "bundle_version": attr.string(), + "bundle_symbolic_name": attr.string(), + "export_package": attr.string_list(), + "import_package": attr.string_list(), + "target": attr.label(), + "deps": attr.label_list(), + "runtime_deps": attr.label_list(), + "exports": attr.label_list(), + "neverlink": attr.bool(), + "exported_plugins": attr.label_list(), + "_osgi_wrapper_exe": attr.label( + executable = True, + cfg = "exec", + allow_files = True, + default = Label("//java/osgi:osgi_wrapper"), + ), + }, +) diff --git a/java/osgi/osgi.bzl b/java/osgi/osgi.bzl index 68600b5d7..c83b4c02b 100644 --- a/java/osgi/osgi.bzl +++ b/java/osgi/osgi.bzl @@ -1,6 +1,6 @@ """ Custom rule to generate OSGi Manifest """ -load("@rules_java//java:defs.bzl", "java_library") +load("@rules_java//java:defs.bzl", "JavaInfo", "java_library") # Note that this rule is currently agnostic of protobuf concerns and could be # pulled out as a general purpose helper to allow migrations from maven to bazel @@ -174,7 +174,7 @@ def _osgi_jar_impl(ctx): source_jars = source_jars.to_list() if len(source_jars) > 1: fail("osgi_jar rule doesn't know how to deal with more than one source jar.") - source_jar = target_java_output.source_jars[0] + source_jar = source_jars[0] output_jar = ctx.outputs.output_jar diff --git a/java/pom.xml b/java/pom.xml index b59d26770..c00df45bc 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -4,7 +4,7 @@ com.google.protobuf protobuf-parent - 3.25.5 + 4.28.3 pom Protocol Buffers [Parent] @@ -33,6 +33,7 @@ ${project.basedir}/../.. ${protobuf.basedir}/src + ${protobuf.basedir}/java/core/src/main/resources ${protobuf.basedir}/protoc src/test/proto ${project.build.directory}/generated-sources @@ -111,8 +112,8 @@ maven-compiler-plugin 3.6.1 - 1.7 - 1.7 + 1.8 + 1.8 @@ -177,10 +178,15 @@ org.codehaus.mojo animal-sniffer-maven-plugin + + org.codehaus.mojo.signature + java18 + 1.0 + net.sf.androidscents.signature - android-api-level-14 - 4.0_r4 + android-api-level-21 + 5.0.1_r2 sun.misc.Unsafe diff --git a/java/protoc/pom.xml b/java/protoc/pom.xml index 2058f8bf5..38acc0133 100644 --- a/java/protoc/pom.xml +++ b/java/protoc/pom.xml @@ -8,7 +8,7 @@ com.google.protobuf protoc - 3.25.5 + 4.28.3 pom Protobuf Compiler diff --git a/java/util/BUILD.bazel b/java/util/BUILD.bazel index c8b9e7669..271bee1c1 100644 --- a/java/util/BUILD.bazel +++ b/java/util/BUILD.bazel @@ -1,8 +1,8 @@ -load("@rules_java//java:defs.bzl", "java_proto_library") -load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix") -load("@rules_proto//proto:defs.bzl", "proto_library") -load("//build_defs:java_opts.bzl", "protobuf_java_export", "protobuf_versioned_java_library") +load("@rules_pkg//pkg:mappings.bzl", "pkg_files", "strip_prefix") load("//:protobuf_version.bzl", "PROTOBUF_JAVA_VERSION") +load("//bazel:java_proto_library.bzl", "java_proto_library") +load("//bazel:proto_library.bzl", "proto_library") +load("//build_defs:java_opts.bzl", "protobuf_java_export", "protobuf_versioned_java_library") load("//java/internal:testing.bzl", "junit_tests") java_library( @@ -105,7 +105,6 @@ pkg_files( "src/test/**/*.proto", ]) + [ "BUILD.bazel", - "pom.xml", "pom_template.xml", ], strip_prefix = strip_prefix.from_root(""), diff --git a/java/util/pom.xml b/java/util/pom.xml deleted file mode 100644 index 9d0581cf5..000000000 --- a/java/util/pom.xml +++ /dev/null @@ -1,167 +0,0 @@ - - - 4.0.0 - - com.google.protobuf - protobuf-parent - 3.25.5 - - - protobuf-java-util - bundle - - Protocol Buffers [Util] - Utilities for Protocol Buffers - - - - ${project.groupId} - protobuf-java - - - com.google.guava - guava - - - com.google.errorprone - error_prone_annotations - 2.5.1 - - - com.google.j2objc - j2objc-annotations - 2.8 - - - com.google.code.findbugs - jsr305 - 3.0.2 - - - com.google.guava - guava-testlib - test - - - com.google.code.gson - gson - 2.8.9 - - - junit - junit - - - org.mockito - mockito-core - test - - - com.google.truth - truth - test - - - - - - ../core/src/test/proto - - - - - - maven-antrun-plugin - - - - generate-test-sources - generate-test-sources - - - - - - - - - - - - - - - - run - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - add-generated-test-sources - generate-test-sources - - add-test-source - - - - ${generated.testsources.dir} - - - - - - - - org.codehaus.mojo - animal-sniffer-maven-plugin - - - net.sf.androidscents.signature - android-api-level-19 - 4.4.2_r4 - - - - - android - test - - check - - - - - - - org.apache.felix - maven-bundle-plugin - true - - - com.google.protobuf.util - https://developers.google.com/protocol-buffers/ - com.google.protobuf.util - com.google.protobuf.util;version=${project.version} - - - - - - - maven-assembly-plugin - - - jar-with-dependencies - - - - - - diff --git a/java/util/src/main/java/com/google/protobuf/util/Durations.java b/java/util/src/main/java/com/google/protobuf/util/Durations.java index a73ab9f0a..05e0e217f 100644 --- a/java/util/src/main/java/com/google/protobuf/util/Durations.java +++ b/java/util/src/main/java/com/google/protobuf/util/Durations.java @@ -426,13 +426,13 @@ public final class Durations { * Add two durations. * * - *

Do not use this method for new code. Instead, convert to {@link java.time.Duration} using - * {@link com.google.protobuf.util.JavaTimeConversions#toJavaDuration}, do the arithmetic there, - * and convert back using {@link com.google.protobuf.util.JavaTimeConversions#toProtoDuration}. - * - *

This method will be deprecated once most uses have been eliminated. + * @deprecated Do not use this method for new code. Instead, convert to {@link java.time.Duration} + * using {@link com.google.protobuf.util.JavaTimeConversions#toJavaDuration}, do the + * arithmetic there, and convert back using {@link + * com.google.protobuf.util.JavaTimeConversions#toProtoDuration}. * */ + @Deprecated // MOE:strip_line public static Duration add(Duration d1, Duration d2) { checkValid(d1); checkValid(d2); @@ -444,13 +444,13 @@ public final class Durations { * Subtract a duration from another. * * - *

Do not use this method for new code. Instead, convert to {@link java.time.Duration} using - * {@link com.google.protobuf.util.JavaTimeConversions#toJavaDuration}, do the arithmetic there, - * and convert back using {@link com.google.protobuf.util.JavaTimeConversions#toProtoDuration}. - * - *

This method will be deprecated once most uses have been eliminated. + * @deprecated Do not use this method for new code. Instead, convert to {@link java.time.Duration} + * using {@link com.google.protobuf.util.JavaTimeConversions#toJavaDuration}, do the + * arithmetic there, and convert back using {@link + * com.google.protobuf.util.JavaTimeConversions#toProtoDuration}. * */ + @Deprecated // MOE:strip_line public static Duration subtract(Duration d1, Duration d2) { checkValid(d1); checkValid(d2); diff --git a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java index cdcc8d9cc..b1ee0e5c0 100644 --- a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java +++ b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java @@ -8,6 +8,7 @@ package com.google.protobuf.util; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; import com.google.common.io.BaseEncoding; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.gson.Gson; @@ -29,7 +30,6 @@ import com.google.protobuf.Descriptors.EnumDescriptor; import com.google.protobuf.Descriptors.EnumValueDescriptor; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.Descriptors.FileDescriptor; -import com.google.protobuf.Descriptors.OneofDescriptor; import com.google.protobuf.DoubleValue; import com.google.protobuf.Duration; import com.google.protobuf.DynamicMessage; @@ -86,30 +86,30 @@ public class JsonFormat { return new Printer( com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(), TypeRegistry.getEmptyTypeRegistry(), - /* alwaysOutputDefaultValueFields */ false, - /* includingDefaultValueFields */ Collections.emptySet(), + ShouldPrintDefaults.ONLY_IF_PRESENT, + /* includingDefaultValueFields */ ImmutableSet.of(), /* preservingProtoFieldNames */ false, /* omittingInsignificantWhitespace */ false, /* printingEnumsAsInts */ false, /* sortingMapKeys */ false); } - /** - * A Printer converts a protobuf message to the proto3 JSON format. - */ + private enum ShouldPrintDefaults { + ONLY_IF_PRESENT, // The "normal" behavior; the others add more compared to this baseline. + ALWAYS_PRINT_EXCEPT_MESSAGES_AND_ONEOFS, + ALWAYS_PRINT_WITHOUT_PRESENCE_FIELDS, + ALWAYS_PRINT_SPECIFIED_FIELDS + } + + /** A Printer converts a protobuf message to the proto3 JSON format. */ public static class Printer { private final com.google.protobuf.TypeRegistry registry; private final TypeRegistry oldRegistry; - // NOTE: There are 3 states for these *defaultValueFields variables: - // 1) Default - alwaysOutput is false & including is empty set. Fields only output if they are - // set to non-default values. - // 2) No-args includingDefaultValueFields() called - alwaysOutput is true & including is - // irrelevant (but set to empty set). All fields are output regardless of their values. - // 3) includingDefaultValueFields(Set) called - alwaysOutput is false & - // including is set to the specified set. Fields in that set are always output & fields not - // in that set are only output if set to non-default values. - private boolean alwaysOutputDefaultValueFields; - private Set includingDefaultValueFields; + private final ShouldPrintDefaults shouldPrintDefaults; + + // Empty unless shouldPrintDefaults is set to ALWAYS_PRINT_SPECIFIED_FIELDS. + private final Set includingDefaultValueFields; + private final boolean preservingProtoFieldNames; private final boolean omittingInsignificantWhitespace; private final boolean printingEnumsAsInts; @@ -118,7 +118,7 @@ public class JsonFormat { private Printer( com.google.protobuf.TypeRegistry registry, TypeRegistry oldRegistry, - boolean alwaysOutputDefaultValueFields, + ShouldPrintDefaults shouldOutputDefaults, Set includingDefaultValueFields, boolean preservingProtoFieldNames, boolean omittingInsignificantWhitespace, @@ -126,7 +126,7 @@ public class JsonFormat { boolean sortingMapKeys) { this.registry = registry; this.oldRegistry = oldRegistry; - this.alwaysOutputDefaultValueFields = alwaysOutputDefaultValueFields; + this.shouldPrintDefaults = shouldOutputDefaults; this.includingDefaultValueFields = includingDefaultValueFields; this.preservingProtoFieldNames = preservingProtoFieldNames; this.omittingInsignificantWhitespace = omittingInsignificantWhitespace; @@ -148,7 +148,7 @@ public class JsonFormat { return new Printer( com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(), oldRegistry, - alwaysOutputDefaultValueFields, + shouldPrintDefaults, includingDefaultValueFields, preservingProtoFieldNames, omittingInsignificantWhitespace, @@ -170,7 +170,7 @@ public class JsonFormat { return new Printer( registry, oldRegistry, - alwaysOutputDefaultValueFields, + shouldPrintDefaults, includingDefaultValueFields, preservingProtoFieldNames, omittingInsignificantWhitespace, @@ -179,18 +179,28 @@ public class JsonFormat { } /** - * Creates a new {@link Printer} that will also print fields set to their - * defaults. Empty repeated fields and map fields will be printed as well. - * The new Printer clones all other configurations from the current - * {@link Printer}. + * Creates a new {@link Printer} that will always print fields unless they are a message type or + * in a oneof. + * + *

Note that this does print Proto2 Optional but does not print Proto3 Optional fields, as + * the latter is represented using a synthetic oneof. + * + *

The new Printer clones all other configurations from the current {@link Printer}. + * + * @deprecated This method is deprecated, and slated for removal in the next Java breaking + * change (5.x). Prefer {@link #alwaysPrintFieldsWithNoPresence} */ + @Deprecated public Printer includingDefaultValueFields() { - checkUnsetIncludingDefaultValueFields(); + if (shouldPrintDefaults != ShouldPrintDefaults.ONLY_IF_PRESENT) { + throw new IllegalStateException( + "JsonFormat includingDefaultValueFields has already been set."); + } return new Printer( registry, oldRegistry, - true, - Collections.emptySet(), + ShouldPrintDefaults.ALWAYS_PRINT_EXCEPT_MESSAGES_AND_ONEOFS, + ImmutableSet.of(), preservingProtoFieldNames, omittingInsignificantWhitespace, printingEnumsAsInts, @@ -198,56 +208,75 @@ public class JsonFormat { } /** - * Creates a new {@link Printer} that prints enum field values as integers instead of as - * string. The new Printer clones all other configurations from the current {@link Printer}. + * Creates a new {@link Printer} that will also print default-valued fields if their + * FieldDescriptors are found in the supplied set. Empty repeated fields and map fields will be + * printed as well, if they match. The new Printer clones all other configurations from the + * current {@link Printer}. Call includingDefaultValueFields() with no args to unconditionally + * output all fields. + * + *

Note that non-repeated message fields or fields in a oneof are not honored if provided + * here. */ - public Printer printingEnumsAsInts() { - checkUnsetPrintingEnumsAsInts(); + public Printer includingDefaultValueFields(Set fieldsToAlwaysOutput) { + Preconditions.checkArgument( + null != fieldsToAlwaysOutput && !fieldsToAlwaysOutput.isEmpty(), + "Non-empty Set must be supplied for includingDefaultValueFields."); + if (shouldPrintDefaults != ShouldPrintDefaults.ONLY_IF_PRESENT) { + throw new IllegalStateException( + "JsonFormat includingDefaultValueFields has already been set."); + } return new Printer( registry, oldRegistry, - alwaysOutputDefaultValueFields, - includingDefaultValueFields, + ShouldPrintDefaults.ALWAYS_PRINT_SPECIFIED_FIELDS, + ImmutableSet.copyOf(fieldsToAlwaysOutput), preservingProtoFieldNames, omittingInsignificantWhitespace, - true, + printingEnumsAsInts, sortingMapKeys); } - private void checkUnsetPrintingEnumsAsInts() { - if (printingEnumsAsInts) { - throw new IllegalStateException("JsonFormat printingEnumsAsInts has already been set."); + /** + * Creates a new {@link Printer} that will print any field that does not support presence even + * if it would not otherwise be printed (empty repeated fields, empty map fields, and implicit + * presence scalars set to their default value). The new Printer clones all other configurations + * from the current {@link Printer}. + */ + public Printer alwaysPrintFieldsWithNoPresence() { + if (shouldPrintDefaults != ShouldPrintDefaults.ONLY_IF_PRESENT) { + throw new IllegalStateException("Only one of the JsonFormat defaults options can be set."); } + return new Printer( + registry, + oldRegistry, + ShouldPrintDefaults.ALWAYS_PRINT_WITHOUT_PRESENCE_FIELDS, + ImmutableSet.of(), + preservingProtoFieldNames, + omittingInsignificantWhitespace, + printingEnumsAsInts, + sortingMapKeys); } /** - * Creates a new {@link Printer} that will also print default-valued fields if their - * FieldDescriptors are found in the supplied set. Empty repeated fields and map fields will be - * printed as well, if they match. The new Printer clones all other configurations from the - * current {@link Printer}. Call includingDefaultValueFields() with no args to unconditionally - * output all fields. + * Creates a new {@link Printer} that prints enum field values as integers instead of as string. + * The new Printer clones all other configurations from the current {@link Printer}. */ - public Printer includingDefaultValueFields(Set fieldsToAlwaysOutput) { - Preconditions.checkArgument( - null != fieldsToAlwaysOutput && !fieldsToAlwaysOutput.isEmpty(), - "Non-empty Set must be supplied for includingDefaultValueFields."); - - checkUnsetIncludingDefaultValueFields(); + public Printer printingEnumsAsInts() { + checkUnsetPrintingEnumsAsInts(); return new Printer( registry, oldRegistry, - false, - Collections.unmodifiableSet(new HashSet<>(fieldsToAlwaysOutput)), + shouldPrintDefaults, + includingDefaultValueFields, preservingProtoFieldNames, omittingInsignificantWhitespace, - printingEnumsAsInts, + true, sortingMapKeys); } - private void checkUnsetIncludingDefaultValueFields() { - if (alwaysOutputDefaultValueFields || !includingDefaultValueFields.isEmpty()) { - throw new IllegalStateException( - "JsonFormat includingDefaultValueFields has already been set."); + private void checkUnsetPrintingEnumsAsInts() { + if (printingEnumsAsInts) { + throw new IllegalStateException("JsonFormat printingEnumsAsInts has already been set."); } } @@ -261,7 +290,7 @@ public class JsonFormat { return new Printer( registry, oldRegistry, - alwaysOutputDefaultValueFields, + shouldPrintDefaults, includingDefaultValueFields, true, omittingInsignificantWhitespace, @@ -290,7 +319,7 @@ public class JsonFormat { return new Printer( registry, oldRegistry, - alwaysOutputDefaultValueFields, + shouldPrintDefaults, includingDefaultValueFields, preservingProtoFieldNames, true, @@ -313,7 +342,7 @@ public class JsonFormat { return new Printer( registry, oldRegistry, - alwaysOutputDefaultValueFields, + shouldPrintDefaults, includingDefaultValueFields, preservingProtoFieldNames, omittingInsignificantWhitespace, @@ -334,7 +363,7 @@ public class JsonFormat { new PrinterImpl( registry, oldRegistry, - alwaysOutputDefaultValueFields, + shouldPrintDefaults, includingDefaultValueFields, preservingProtoFieldNames, output, @@ -685,7 +714,7 @@ public class JsonFormat { private static final class PrinterImpl { private final com.google.protobuf.TypeRegistry registry; private final TypeRegistry oldRegistry; - private final boolean alwaysOutputDefaultValueFields; + private final ShouldPrintDefaults shouldPrintDefaults; private final Set includingDefaultValueFields; private final boolean preservingProtoFieldNames; private final boolean printingEnumsAsInts; @@ -703,7 +732,7 @@ public class JsonFormat { PrinterImpl( com.google.protobuf.TypeRegistry registry, TypeRegistry oldRegistry, - boolean alwaysOutputDefaultValueFields, + ShouldPrintDefaults shouldPrintDefaults, Set includingDefaultValueFields, boolean preservingProtoFieldNames, Appendable jsonOutput, @@ -712,7 +741,7 @@ public class JsonFormat { boolean sortingMapKeys) { this.registry = registry; this.oldRegistry = oldRegistry; - this.alwaysOutputDefaultValueFields = alwaysOutputDefaultValueFields; + this.shouldPrintDefaults = shouldPrintDefaults; this.includingDefaultValueFields = includingDefaultValueFields; this.preservingProtoFieldNames = preservingProtoFieldNames; this.printingEnumsAsInts = printingEnumsAsInts; @@ -965,6 +994,28 @@ public class JsonFormat { printRepeatedFieldValue(field, message.getField(field)); } + // Whether a set option means the corresponding field should be printed even if it normally + // wouldn't be. + private boolean shouldSpeciallyPrint(FieldDescriptor field) { + switch (shouldPrintDefaults) { + case ONLY_IF_PRESENT: + return false; + case ALWAYS_PRINT_EXCEPT_MESSAGES_AND_ONEOFS: + return !field.hasPresence() + || (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE + && field.getContainingOneof() == null); + case ALWAYS_PRINT_WITHOUT_PRESENCE_FIELDS: + return !field.hasPresence(); + case ALWAYS_PRINT_SPECIFIED_FIELDS: + // For legacy code compatibility, we don't honor non-repeated message or oneof fields even + // if they're explicitly requested. :( + return !(field.getJavaType() == FieldDescriptor.JavaType.MESSAGE && !field.isRepeated()) + && field.getContainingOneof() == null + && includingDefaultValueFields.contains(field); + } + throw new AssertionError("Unknown shouldPrintDefaults: " + shouldPrintDefaults); + } + /** Prints a regular message with an optional type URL. */ private void print(MessageOrBuilder message, @Nullable String typeUrl) throws IOException { generator.print("{" + blankOrNewLine); @@ -975,31 +1026,23 @@ public class JsonFormat { generator.print("\"@type\":" + blankOrSpace + gson.toJson(typeUrl)); printedField = true; } - Map fieldsToPrint = null; - if (alwaysOutputDefaultValueFields || !includingDefaultValueFields.isEmpty()) { + + // message.getAllFields() will already contain all of the fields that would be + // printed normally (non-empty repeated fields, with-presence fields that are set, implicit + // presence fields that have a nonzero value). Loop over all of the fields to add any more + // fields that should be printed based on the shouldPrintDefaults setting. + Map fieldsToPrint; + if (shouldPrintDefaults == ShouldPrintDefaults.ONLY_IF_PRESENT) { + fieldsToPrint = message.getAllFields(); + } else { fieldsToPrint = new TreeMap(message.getAllFields()); for (FieldDescriptor field : message.getDescriptorForType().getFields()) { - if (field.isOptional()) { - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE - && !message.hasField(field)) { - // Always skip empty optional message fields. If not we will recurse indefinitely if - // a message has itself as a sub-field. - continue; - } - OneofDescriptor oneof = field.getContainingOneof(); - if (oneof != null && !message.hasField(field)) { - // Skip all oneof fields except the one that is actually set - continue; - } - } - if (!fieldsToPrint.containsKey(field) - && (alwaysOutputDefaultValueFields || includingDefaultValueFields.contains(field))) { + if (shouldSpeciallyPrint(field)) { fieldsToPrint.put(field, message.getField(field)); } } - } else { - fieldsToPrint = message.getAllFields(); } + for (Map.Entry field : fieldsToPrint.entrySet()) { if (printedField) { // Add line-endings for the previous field. diff --git a/java/util/src/main/java/com/google/protobuf/util/ProtoFileUtil.java b/java/util/src/main/java/com/google/protobuf/util/ProtoFileUtil.java new file mode 100644 index 000000000..189dd2341 --- /dev/null +++ b/java/util/src/main/java/com/google/protobuf/util/ProtoFileUtil.java @@ -0,0 +1,21 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +package com.google.protobuf.util; + +import com.google.protobuf.DescriptorProtos.Edition; + +/** Utility helper functions to work with {@link com.google.protobuf.FileDescriptorProto}. */ +public final class ProtoFileUtil { + + private ProtoFileUtil() {} + + /** Converts an Edition to its string representation as specified in ".proto" file. */ + public static String getEditionString(Edition edition) { + return edition.toString().substring("EDITION_".length()); + } +} diff --git a/java/util/src/main/java/com/google/protobuf/util/Timestamps.java b/java/util/src/main/java/com/google/protobuf/util/Timestamps.java index 99daeb326..706fc319e 100644 --- a/java/util/src/main/java/com/google/protobuf/util/Timestamps.java +++ b/java/util/src/main/java/com/google/protobuf/util/Timestamps.java @@ -137,7 +137,7 @@ public final class Timestamps { */ @SuppressWarnings("GoodTime") // this is a legacy conversion API public static boolean isValid(long seconds, int nanos) { - if (seconds < TIMESTAMP_SECONDS_MIN || seconds > TIMESTAMP_SECONDS_MAX) { + if (!isValidSeconds(seconds)) { return false; } if (nanos < 0 || nanos >= NANOS_PER_SECOND) { @@ -146,6 +146,16 @@ public final class Timestamps { return true; } + /** + * Returns true if the given number of seconds is valid, if combined with a valid number of nanos. + * The {@code seconds} value must be in the range [-62,135,596,800, +253,402,300,799] (i.e., + * between 0001-01-01T00:00:00Z and 9999-12-31T23:59:59Z). + */ + @SuppressWarnings("GoodTime") // this is a legacy conversion API + private static boolean isValidSeconds(long seconds) { + return seconds >= TIMESTAMP_SECONDS_MIN && seconds <= TIMESTAMP_SECONDS_MAX; + } + /** Throws an {@link IllegalArgumentException} if the given {@link Timestamp} is not valid. */ @CanIgnoreReturnValue public static Timestamp checkValid(Timestamp timestamp) { @@ -174,9 +184,9 @@ public final class Timestamps { /** * Convert Timestamp to RFC 3339 date string format. The output will always be Z-normalized and - * uses 3, 6 or 9 fractional digits as required to represent the exact value. Note that Timestamp - * can only represent time from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. See - * https://www.ietf.org/rfc/rfc3339.txt + * uses 0, 3, 6 or 9 fractional digits as required to represent the exact value. Note that + * Timestamp can only represent time from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. + * See https://www.ietf.org/rfc/rfc3339.txt * *

Example of generated format: "1972-01-01T10:00:20.021Z" * @@ -431,13 +441,13 @@ public final class Timestamps { * Calculate the difference between two timestamps. * * - *

Do not use this method for new code. Instead, convert to {@link java.time.Instant} using - * {@link com.google.protobuf.util.JavaTimeConversions#toJavaInstant}, do the arithmetic there, - * and convert back using {@link com.google.protobuf.util.JavaTimeConversions#toProtoDuration}. - * - *

This method will be deprecated once most uses have been eliminated. + * @deprecated Do not use this method for new code. Instead, convert to {@link java.time.Instant} + * using {@link com.google.protobuf.util.JavaTimeConversions#toJavaInstant}, do the arithmetic + * there, and convert back using {@link + * com.google.protobuf.util.JavaTimeConversions#toProtoDuration}. * */ + @Deprecated // MOE:strip_line public static Duration between(Timestamp from, Timestamp to) { checkValid(from); checkValid(to); @@ -450,15 +460,14 @@ public final class Timestamps { * Add a duration to a timestamp. * * - *

Do not use this method for new code. Instead, convert to {@link java.time.Instant} and - * {@link java.time.Duration} using {@link - * com.google.protobuf.util.JavaTimeConversions#toJavaInstant} and {@link - * com.google.protobuf.util.JavaTimeConversions#toJavaDuration}, do the arithmetic there, and - * convert back using {@link com.google.protobuf.util.JavaTimeConversions#toProtoTimestamp}. - * - *

This method will be deprecated once most uses have been eliminated. + * @deprecated Do not use this method for new code. Instead, convert to {@link java.time.Instant} + * and {@link java.time.Duration} using {@link + * com.google.protobuf.util.JavaTimeConversions#toJavaInstant} and {@link + * com.google.protobuf.util.JavaTimeConversions#toJavaDuration}, do the arithmetic there, and + * convert back using {@link com.google.protobuf.util.JavaTimeConversions#toProtoTimestamp}. * */ + @Deprecated // MOE:strip_line public static Timestamp add(Timestamp start, Duration length) { checkValid(start); Durations.checkValid(length); @@ -471,15 +480,14 @@ public final class Timestamps { * Subtract a duration from a timestamp. * * - *

Do not use this method for new code. Instead, convert to {@link java.time.Instant} and - * {@link java.time.Duration} using {@link - * com.google.protobuf.util.JavaTimeConversions#toJavaInstant} and {@link - * com.google.protobuf.util.JavaTimeConversions#toJavaDuration}, do the arithmetic there, and - * convert back using {@link com.google.protobuf.util.JavaTimeConversions#toProtoTimestamp}. - * - *

This method will be deprecated once most uses have been eliminated. + * @deprecated Do not use this method for new code. Instead, convert to {@link java.time.Instant} + * and {@link java.time.Duration} using {@link + * com.google.protobuf.util.JavaTimeConversions#toJavaInstant} and {@link + * com.google.protobuf.util.JavaTimeConversions#toJavaDuration}, do the arithmetic there, and + * convert back using {@link com.google.protobuf.util.JavaTimeConversions#toProtoTimestamp}. * */ + @Deprecated // MOE:strip_line public static Timestamp subtract(Timestamp start, Duration length) { checkValid(start); Durations.checkValid(length); @@ -489,6 +497,15 @@ public final class Timestamps { } static Timestamp normalizedTimestamp(long seconds, int nanos) { + // This only checks seconds, because nanos can intentionally overflow to increment the seconds + // when normalized. + if (!isValidSeconds(seconds)) { + throw new IllegalArgumentException( + String.format( + "Timestamp is not valid. Input seconds is too large. " + + "Seconds (%s) must be in range [-62,135,596,800, +253,402,300,799]. ", + seconds)); + } if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) { seconds = checkedAdd(seconds, nanos / NANOS_PER_SECOND); nanos = (int) (nanos % NANOS_PER_SECOND); diff --git a/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java index 6d75edb61..ae345b4aa 100644 --- a/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java +++ b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java @@ -46,6 +46,7 @@ import com.google.protobuf.util.proto.JsonTestProto.TestRecursive; import com.google.protobuf.util.proto.JsonTestProto.TestStruct; import com.google.protobuf.util.proto.JsonTestProto.TestTimestamp; import com.google.protobuf.util.proto.JsonTestProto.TestWrappers; +import com.google.protobuf.util.proto.JsonTestProto2.TestAllTypesProto2; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -1483,50 +1484,59 @@ public class JsonFormatTest { } @Test - public void testIncludingDefaultValueFields() throws Exception { + public void testDefaultValueOptionsProto3() throws Exception { TestAllTypes message = TestAllTypes.getDefaultInstance(); assertThat(JsonFormat.printer().print(message)).isEqualTo("{\n}"); - assertThat(JsonFormat.printer().includingDefaultValueFields().print(message)) - .isEqualTo( - "{\n" - + " \"optionalInt32\": 0,\n" - + " \"optionalInt64\": \"0\",\n" - + " \"optionalUint32\": 0,\n" - + " \"optionalUint64\": \"0\",\n" - + " \"optionalSint32\": 0,\n" - + " \"optionalSint64\": \"0\",\n" - + " \"optionalFixed32\": 0,\n" - + " \"optionalFixed64\": \"0\",\n" - + " \"optionalSfixed32\": 0,\n" - + " \"optionalSfixed64\": \"0\",\n" - + " \"optionalFloat\": 0.0,\n" - + " \"optionalDouble\": 0.0,\n" - + " \"optionalBool\": false,\n" - + " \"optionalString\": \"\",\n" - + " \"optionalBytes\": \"\",\n" - + " \"optionalNestedEnum\": \"FOO\",\n" - + " \"repeatedInt32\": [],\n" - + " \"repeatedInt64\": [],\n" - + " \"repeatedUint32\": [],\n" - + " \"repeatedUint64\": [],\n" - + " \"repeatedSint32\": [],\n" - + " \"repeatedSint64\": [],\n" - + " \"repeatedFixed32\": [],\n" - + " \"repeatedFixed64\": [],\n" - + " \"repeatedSfixed32\": [],\n" - + " \"repeatedSfixed64\": [],\n" - + " \"repeatedFloat\": [],\n" - + " \"repeatedDouble\": [],\n" - + " \"repeatedBool\": [],\n" - + " \"repeatedString\": [],\n" - + " \"repeatedBytes\": [],\n" - + " \"repeatedNestedMessage\": [],\n" - + " \"repeatedNestedEnum\": [],\n" - + " \"optionalAliasedEnum\": \"ALIAS_FOO\"\n" - + "}"); + + String expectedJsonWithDefaults = + "{\n" + + " \"optionalInt32\": 0,\n" + + " \"optionalInt64\": \"0\",\n" + + " \"optionalUint32\": 0,\n" + + " \"optionalUint64\": \"0\",\n" + + " \"optionalSint32\": 0,\n" + + " \"optionalSint64\": \"0\",\n" + + " \"optionalFixed32\": 0,\n" + + " \"optionalFixed64\": \"0\",\n" + + " \"optionalSfixed32\": 0,\n" + + " \"optionalSfixed64\": \"0\",\n" + + " \"optionalFloat\": 0.0,\n" + + " \"optionalDouble\": 0.0,\n" + + " \"optionalBool\": false,\n" + + " \"optionalString\": \"\",\n" + + " \"optionalBytes\": \"\",\n" + + " \"optionalNestedEnum\": \"FOO\",\n" + + " \"repeatedInt32\": [],\n" + + " \"repeatedInt64\": [],\n" + + " \"repeatedUint32\": [],\n" + + " \"repeatedUint64\": [],\n" + + " \"repeatedSint32\": [],\n" + + " \"repeatedSint64\": [],\n" + + " \"repeatedFixed32\": [],\n" + + " \"repeatedFixed64\": [],\n" + + " \"repeatedSfixed32\": [],\n" + + " \"repeatedSfixed64\": [],\n" + + " \"repeatedFloat\": [],\n" + + " \"repeatedDouble\": [],\n" + + " \"repeatedBool\": [],\n" + + " \"repeatedString\": [],\n" + + " \"repeatedBytes\": [],\n" + + " \"repeatedNestedMessage\": [],\n" + + " \"repeatedNestedEnum\": [],\n" + + " \"optionalAliasedEnum\": \"ALIAS_FOO\",\n" + + " \"repeatedRecursive\": []\n" + + "}"; + + assertThat(JsonFormat.printer().alwaysPrintFieldsWithNoPresence().print(message)) + .isEqualTo(expectedJsonWithDefaults); + } + + @Test + public void testDefaultValueForSpecificFieldsOptionProto2() throws Exception { + TestAllTypesProto2 message = TestAllTypesProto2.getDefaultInstance(); Set fixedFields = new HashSet<>(); - for (FieldDescriptor fieldDesc : TestAllTypes.getDescriptor().getFields()) { + for (FieldDescriptor fieldDesc : TestAllTypesProto2.getDescriptor().getFields()) { if (fieldDesc.getName().contains("_fixed")) { fixedFields.add(fieldDesc); } @@ -1541,7 +1551,7 @@ public class JsonFormatTest { + " \"repeatedFixed64\": []\n" + "}"); - TestAllTypes messageNonDefaults = + TestAllTypesProto2 messageNonDefaults = message.toBuilder().setOptionalInt64(1234).setOptionalFixed32(3232).build(); assertThat( JsonFormat.printer().includingDefaultValueFields(fixedFields).print(messageNonDefaults)) @@ -1553,168 +1563,6 @@ public class JsonFormatTest { + " \"repeatedFixed32\": [],\n" + " \"repeatedFixed64\": []\n" + "}"); - - try { - JsonFormat.printer().includingDefaultValueFields().includingDefaultValueFields(); - assertWithMessage("IllegalStateException is expected.").fail(); - } catch (IllegalStateException e) { - // Expected. - assertWithMessage("Exception message should mention includingDefaultValueFields.") - .that(e.getMessage().contains("includingDefaultValueFields")) - .isTrue(); - } - - try { - JsonFormat.printer().includingDefaultValueFields().includingDefaultValueFields(fixedFields); - assertWithMessage("IllegalStateException is expected.").fail(); - } catch (IllegalStateException e) { - // Expected. - assertWithMessage("Exception message should mention includingDefaultValueFields.") - .that(e.getMessage().contains("includingDefaultValueFields")) - .isTrue(); - } - - try { - JsonFormat.printer().includingDefaultValueFields(fixedFields).includingDefaultValueFields(); - assertWithMessage("IllegalStateException is expected.").fail(); - } catch (IllegalStateException e) { - // Expected. - assertWithMessage("Exception message should mention includingDefaultValueFields.") - .that(e.getMessage().contains("includingDefaultValueFields")) - .isTrue(); - } - - try { - JsonFormat.printer() - .includingDefaultValueFields(fixedFields) - .includingDefaultValueFields(fixedFields); - assertWithMessage("IllegalStateException is expected.").fail(); - } catch (IllegalStateException e) { - // Expected. - assertWithMessage("Exception message should mention includingDefaultValueFields.") - .that(e.getMessage().contains("includingDefaultValueFields")) - .isTrue(); - } - - Set intFields = new HashSet<>(); - for (FieldDescriptor fieldDesc : TestAllTypes.getDescriptor().getFields()) { - if (fieldDesc.getName().contains("_int")) { - intFields.add(fieldDesc); - } - } - - try { - JsonFormat.printer() - .includingDefaultValueFields(intFields) - .includingDefaultValueFields(fixedFields); - assertWithMessage("IllegalStateException is expected.").fail(); - } catch (IllegalStateException e) { - // Expected. - assertWithMessage("Exception message should mention includingDefaultValueFields.") - .that(e.getMessage().contains("includingDefaultValueFields")) - .isTrue(); - } - - try { - JsonFormat.printer().includingDefaultValueFields(null); - assertWithMessage("IllegalArgumentException is expected.").fail(); - } catch (IllegalArgumentException e) { - // Expected. - assertWithMessage("Exception message should mention includingDefaultValueFields.") - .that(e.getMessage().contains("includingDefaultValueFields")) - .isTrue(); - } - - try { - JsonFormat.printer().includingDefaultValueFields(Collections.emptySet()); - assertWithMessage("IllegalArgumentException is expected.").fail(); - } catch (IllegalArgumentException e) { - // Expected. - assertWithMessage("Exception message should mention includingDefaultValueFields.") - .that(e.getMessage().contains("includingDefaultValueFields")) - .isTrue(); - } - - TestMap mapMessage = TestMap.getDefaultInstance(); - assertThat(JsonFormat.printer().print(mapMessage)).isEqualTo("{\n}"); - assertThat(JsonFormat.printer().includingDefaultValueFields().print(mapMessage)) - .isEqualTo( - "{\n" - + " \"int32ToInt32Map\": {\n" - + " },\n" - + " \"int64ToInt32Map\": {\n" - + " },\n" - + " \"uint32ToInt32Map\": {\n" - + " },\n" - + " \"uint64ToInt32Map\": {\n" - + " },\n" - + " \"sint32ToInt32Map\": {\n" - + " },\n" - + " \"sint64ToInt32Map\": {\n" - + " },\n" - + " \"fixed32ToInt32Map\": {\n" - + " },\n" - + " \"fixed64ToInt32Map\": {\n" - + " },\n" - + " \"sfixed32ToInt32Map\": {\n" - + " },\n" - + " \"sfixed64ToInt32Map\": {\n" - + " },\n" - + " \"boolToInt32Map\": {\n" - + " },\n" - + " \"stringToInt32Map\": {\n" - + " },\n" - + " \"int32ToInt64Map\": {\n" - + " },\n" - + " \"int32ToUint32Map\": {\n" - + " },\n" - + " \"int32ToUint64Map\": {\n" - + " },\n" - + " \"int32ToSint32Map\": {\n" - + " },\n" - + " \"int32ToSint64Map\": {\n" - + " },\n" - + " \"int32ToFixed32Map\": {\n" - + " },\n" - + " \"int32ToFixed64Map\": {\n" - + " },\n" - + " \"int32ToSfixed32Map\": {\n" - + " },\n" - + " \"int32ToSfixed64Map\": {\n" - + " },\n" - + " \"int32ToFloatMap\": {\n" - + " },\n" - + " \"int32ToDoubleMap\": {\n" - + " },\n" - + " \"int32ToBoolMap\": {\n" - + " },\n" - + " \"int32ToStringMap\": {\n" - + " },\n" - + " \"int32ToBytesMap\": {\n" - + " },\n" - + " \"int32ToMessageMap\": {\n" - + " },\n" - + " \"int32ToEnumMap\": {\n" - + " }\n" - + "}"); - - TestOneof oneofMessage = TestOneof.getDefaultInstance(); - assertThat(JsonFormat.printer().print(oneofMessage)).isEqualTo("{\n}"); - assertThat(JsonFormat.printer().includingDefaultValueFields().print(oneofMessage)) - .isEqualTo("{\n}"); - - oneofMessage = TestOneof.newBuilder().setOneofInt32(42).build(); - assertThat(JsonFormat.printer().print(oneofMessage)).isEqualTo("{\n \"oneofInt32\": 42\n}"); - assertThat(JsonFormat.printer().includingDefaultValueFields().print(oneofMessage)) - .isEqualTo("{\n \"oneofInt32\": 42\n}"); - - TestOneof.Builder oneofBuilder = TestOneof.newBuilder(); - mergeFromJson("{\n" + " \"oneofNullValue\": null \n" + "}", oneofBuilder); - oneofMessage = oneofBuilder.build(); - assertThat(JsonFormat.printer().print(oneofMessage)) - .isEqualTo("{\n \"oneofNullValue\": null\n}"); - assertThat(JsonFormat.printer().includingDefaultValueFields().print(oneofMessage)) - .isEqualTo("{\n \"oneofNullValue\": null\n}"); } @Test diff --git a/java/util/src/test/java/com/google/protobuf/util/ProtoFileUtilTest.java b/java/util/src/test/java/com/google/protobuf/util/ProtoFileUtilTest.java new file mode 100644 index 000000000..4c5aeea85 --- /dev/null +++ b/java/util/src/test/java/com/google/protobuf/util/ProtoFileUtilTest.java @@ -0,0 +1,24 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +package com.google.protobuf.util; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.protobuf.DescriptorProtos.Edition; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link ProtoFileUtil}. */ +@RunWith(JUnit4.class) +public class ProtoFileUtilTest { + @Test + public void testGetEditionFromString() throws Exception { + assertThat(ProtoFileUtil.getEditionString(Edition.EDITION_2023)).isEqualTo("2023"); + } +} diff --git a/java/util/src/test/java/com/google/protobuf/util/TimestampsTest.java b/java/util/src/test/java/com/google/protobuf/util/TimestampsTest.java index 336b57fc6..aff882a79 100644 --- a/java/util/src/test/java/com/google/protobuf/util/TimestampsTest.java +++ b/java/util/src/test/java/com/google/protobuf/util/TimestampsTest.java @@ -857,6 +857,16 @@ public class TimestampsTest { .isEqualTo(timestamp(3, 1)); } + @Test + public void normalizedTimestamp_veryLarge_isInvalidNotOverflow() { + try { + Timestamps.normalizedTimestamp(9223372036854775807L, 2); + fail("should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + assertThat(expected).hasMessageThat().startsWith("Timestamp is not valid."); + } + } + static Timestamp timestamp(long seconds, int nanos) { return Timestamps.checkValid( Timestamps.checkValid(Timestamp.newBuilder().setSeconds(seconds).setNanos(nanos))); diff --git a/java/util/src/test/proto/com/google/protobuf/util/json_test.proto b/java/util/src/test/proto/com/google/protobuf/util/json_test.proto index 34c951005..3cde445ee 100644 --- a/java/util/src/test/proto/com/google/protobuf/util/json_test.proto +++ b/java/util/src/test/proto/com/google/protobuf/util/json_test.proto @@ -5,6 +5,8 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd +// LINT: LEGACY_NAMES + syntax = "proto3"; package json_test; @@ -58,6 +60,9 @@ message TestAllTypes { NestedMessage optional_nested_message = 18; NestedEnum optional_nested_enum = 21; AliasedEnum optional_aliased_enum = 52; + TestRecursive optional_recursive = 53; + + optional int32 explicit_presence_int32 = 54; // Repeated repeated int32 repeated_int32 = 31; @@ -77,6 +82,7 @@ message TestAllTypes { repeated bytes repeated_bytes = 45; repeated NestedMessage repeated_nested_message = 48; repeated NestedEnum repeated_nested_enum = 51; + repeated TestRecursive repeated_recursive = 55; } message TestOneof { diff --git a/java/util/src/test/proto/com/google/protobuf/util/json_test_proto2.proto b/java/util/src/test/proto/com/google/protobuf/util/json_test_proto2.proto new file mode 100644 index 000000000..b1cb80f7c --- /dev/null +++ b/java/util/src/test/proto/com/google/protobuf/util/json_test_proto2.proto @@ -0,0 +1,82 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +// LINT: LEGACY_NAMES + +syntax = "proto2"; + +package json_test_proto2; + +option java_package = "com.google.protobuf.util.proto"; +option java_outer_classname = "JsonTestProto2"; + +message TestAllTypesProto2 { + enum NestedEnum { + FOO = 0; + BAR = 1; + BAZ = 2; + } + + enum AliasedEnum { + option allow_alias = true; + + ALIAS_FOO = 0; + ALIAS_BAR = 1; + ALIAS_BAZ = 2; + QUX = 2; + qux = 2; + bAz = 2; + } + message NestedMessage { + optional int32 value = 1; + } + + optional int32 optional_int32 = 1; + optional int64 optional_int64 = 2; + optional uint32 optional_uint32 = 3; + optional uint64 optional_uint64 = 4; + optional sint32 optional_sint32 = 5; + optional sint64 optional_sint64 = 6; + optional fixed32 optional_fixed32 = 7; + optional fixed64 optional_fixed64 = 8; + optional sfixed32 optional_sfixed32 = 9; + optional sfixed64 optional_sfixed64 = 10; + optional float optional_float = 11; + optional double optional_double = 12; + optional bool optional_bool = 13; + optional string optional_string = 14; + optional bytes optional_bytes = 15; + optional NestedMessage optional_nested_message = 18; + optional NestedEnum optional_nested_enum = 21; + optional AliasedEnum optional_aliased_enum = 52; + optional TestRecursive optional_recursive = 53; + + // Repeated + repeated int32 repeated_int32 = 31; + repeated int64 repeated_int64 = 32; + repeated uint32 repeated_uint32 = 33; + repeated uint64 repeated_uint64 = 34; + repeated sint32 repeated_sint32 = 35; + repeated sint64 repeated_sint64 = 36; + repeated fixed32 repeated_fixed32 = 37; + repeated fixed64 repeated_fixed64 = 38; + repeated sfixed32 repeated_sfixed32 = 39; + repeated sfixed64 repeated_sfixed64 = 40; + repeated float repeated_float = 41; + repeated double repeated_double = 42; + repeated bool repeated_bool = 43; + repeated string repeated_string = 44; + repeated bytes repeated_bytes = 45; + repeated NestedMessage repeated_nested_message = 48; + repeated NestedEnum repeated_nested_enum = 51; + repeated TestRecursive repeated_recursive = 55; +} + +message TestRecursive { + optional int32 value = 1; + optional TestRecursive nested = 2; +}