Allow disabling lazy parsing in individual `ExtensionRegistryLite` which will signal the parsing whether should parse extension fields lazily. PiperOrigin-RevId: 968674119
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 61d69e5..b876fe8 100644 --- a/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java +++ b/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java
@@ -60,6 +60,12 @@ private static volatile LazyExtensionMode lazyExtensionMode = LazyExtensionMode.EAGER; + // Override for the lazy extension mode for this specific registry. + // -1 means fallback to the static lazyExtensionMode + // 0 means eager + // 1 means LAZY_VERIFY_ON_ACCESS + private byte lazyExtensionModeOverride = -1; + static void setLazyExtensionMode(LazyExtensionMode mode) { lazyExtensionMode = mode; } @@ -68,10 +74,28 @@ return lazyExtensionMode; } - static boolean lazyExtensionEnabled() { + boolean lazyExtensionEnabled() { + if (lazyExtensionModeOverride == 1) { + return true; + } else if (lazyExtensionModeOverride == 0) { + return false; + } return lazyExtensionMode == LazyExtensionMode.LAZY_VERIFY_ON_ACCESS; } + /** + * Returns a new {@link ExtensionRegistryLite} with the same contents as this registry but with + * the lazy extension mode overridden. + * + * @param lazy whether to enable lazy extensions + * @return a new {@link ExtensionRegistryLite} with the lazy extension mode overridden + */ + ExtensionRegistryLite withLazyExtensionsOverride(boolean lazy) { + ExtensionRegistryLite ret = getUnmodifiable(); + ret.lazyExtensionModeOverride = (byte) (lazy ? 1 : 0); + return ret; + } + // Visible for testing. static final String EXTENSION_CLASS_NAME = "com.google.protobuf.Extension"; @@ -192,6 +216,7 @@ } else { this.extensionsByNumber = Collections.unmodifiableMap(other.extensionsByNumber); } + this.lazyExtensionModeOverride = other.lazyExtensionModeOverride; } private final Map<ObjectIntPair, GeneratedMessageLite.GeneratedExtension<?, ?>>
diff --git a/java/core/src/main/java/com/google/protobuf/InternalLazyField.java b/java/core/src/main/java/com/google/protobuf/InternalLazyField.java index 81b106a..959d3c1 100644 --- a/java/core/src/main/java/com/google/protobuf/InternalLazyField.java +++ b/java/core/src/main/java/com/google/protobuf/InternalLazyField.java
@@ -215,7 +215,7 @@ // extension, and we should fall back to the old behavior of silently returning the default // instance on corrupted extensions i.e. full parse. value = - ExtensionRegistryLite.lazyExtensionEnabled() + extensionRegistry.lazyExtensionEnabled() ? defaultInstance.getParserForType().parsePartialFrom(bytes, extensionRegistry) : defaultInstance.getParserForType().parseFrom(bytes, extensionRegistry); } catch (InvalidProtocolBufferException e) { @@ -237,7 +237,7 @@ ensureInitialized(); return value; } catch (InvalidProtocolBufferException e) { - if (ExtensionRegistryLite.lazyExtensionEnabled()) { + if (extensionRegistry.lazyExtensionEnabled()) { // New behavior: runtime exception on corrupted extensions. throw new InvalidProtobufRuntimeException(e); } else {
diff --git a/java/core/src/main/java/com/google/protobuf/MessageReflection.java b/java/core/src/main/java/com/google/protobuf/MessageReflection.java index 064df1d..4359364 100644 --- a/java/core/src/main/java/com/google/protobuf/MessageReflection.java +++ b/java/core/src/main/java/com/google/protobuf/MessageReflection.java
@@ -1071,7 +1071,7 @@ throws IOException { if (!field.isRepeated()) { boolean isLazyField = - ExtensionRegistryLite.lazyExtensionEnabled() + extensionRegistry.lazyExtensionEnabled() && field.isExtension() && !field.getContainingType().isDescriptorProtoType(); if (hasField(field)) {
diff --git a/java/core/src/test/java/com/google/protobuf/ParserTest.java b/java/core/src/test/java/com/google/protobuf/ParserTest.java index d4a4685..0279a6f 100644 --- a/java/core/src/test/java/com/google/protobuf/ParserTest.java +++ b/java/core/src/test/java/com/google/protobuf/ParserTest.java
@@ -487,4 +487,41 @@ assertThat(thrown).hasMessageThat().contains("invalid tag"); ExtensionRegistryLite.setLazyExtensionMode(originalMode); } + + private static final byte[] MISSING_REQUIRED_EXTENSION_BYTES = + createMissingRequiredExtensionBytes(); + + private static byte[] createMissingRequiredExtensionBytes() { + TestMergeException.Builder message = TestMergeException.newBuilder(); + message + .getAllExtensionsBuilder() + .setExtension(TestRequired.single, TestRequired.newBuilder().setA(1).buildPartial()); + ByteString byteString = message.buildPartial().toByteString(); + return byteString.concat(byteString).toByteArray(); + } + + @Test + public void testLazyExtensionsOverride_true_doesNotThrow() throws Exception { + ExtensionRegistry registry = ExtensionRegistry.newInstance(); + UnittestProto.registerAllExtensions(registry); + + // Should pass without throwing exception when overridden to lazy + TestMergeException result = + TestMergeException.parseFrom( + MISSING_REQUIRED_EXTENSION_BYTES, registry.withLazyExtensionsOverride(true)); + assertThat(result).isNotNull(); + } + + @Test + public void testLazyExtensionsOverride_false_throwsException() throws Exception { + ExtensionRegistry registry = ExtensionRegistry.newInstance(); + UnittestProto.registerAllExtensions(registry); + + // Should throw exception when overridden to eager + assertThrows( + InvalidProtocolBufferException.class, + () -> + TestMergeException.parseFrom( + MISSING_REQUIRED_EXTENSION_BYTES, registry.withLazyExtensionsOverride(false))); + } }