Add support for edition 2023 protos
Bug: b/339100248
Change-Id: I69b79e45e555cd749d26e17655eae8b4bf8d933b
diff --git a/.gitignore b/.gitignore
index 8114ef4..9aa08ca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -273,8 +273,12 @@
third_party/proguard/*
third_party/proguardsettings.tar.gz
third_party/proguardsettings/
+third_party/proto/runtime/edition2023
+third_party/proto/runtime/edition2023.tar.gz
third_party/proto/runtime/legacy
third_party/proto/runtime/legacy.tar.gz
+third_party/proto/test/edition2023
+third_party/proto/test/edition2023.tar.gz
third_party/proto/test/proto2
third_party/proto/test/proto2.tar.gz
third_party/proto/test/proto3
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
index 2620a18..99d3f6c 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
@@ -614,11 +614,21 @@
Paths.get("third_party", "proguardsettings").toFile(),
Paths.get("third_party", "proguardsettings.tar.gz.sha1").toFile(),
DependencyType.X20)
+ val protoRuntimeEdition2023 = ThirdPartyDependency(
+ "protoRuntimeEdition2023",
+ Paths.get("third_party", "proto", "runtime", "edition2023").toFile(),
+ Paths.get("third_party", "proto", "runtime", "edition2023.tar.gz.sha1").toFile(),
+ DependencyType.X20)
val protoRuntimeLegacy = ThirdPartyDependency(
"protoRuntimeLegacy",
Paths.get("third_party", "proto", "runtime", "legacy").toFile(),
Paths.get("third_party", "proto", "runtime", "legacy.tar.gz.sha1").toFile(),
DependencyType.X20)
+ val protoTestEdition2023 = ThirdPartyDependency(
+ "protoTestEdition2023",
+ Paths.get("third_party", "proto", "test", "edition2023").toFile(),
+ Paths.get("third_party", "proto", "test", "edition2023.tar.gz.sha1").toFile(),
+ DependencyType.X20)
val protoTestProto2 = ThirdPartyDependency(
"protoTestProto2",
Paths.get("third_party", "proto", "test", "proto2").toFile(),
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldType.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldType.java
index b6ec3b3..157268e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldType.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoFieldType.java
@@ -24,14 +24,14 @@
private static final int FIELD_IS_REQUIRED_MASK = 0x100;
private static final int FIELD_ENFORCE_UTF8_MASK = 0x200;
private static final int FIELD_NEEDS_IS_INITIALIZED_CHECK_MASK = 0x400;
- private static final int FIELD_IS_MAP_FIELD_WITH_PROTO_2_ENUM_VALUE_MASK = 0x800;
+ private static final int FIELD_IS_LEGACY_ENUM_CLOSED_BIT_MASK = 0x800;
private static final int FIELD_HAS_HAS_BIT_MASK = 0x1000;
private final int id;
private final boolean isRequired;
private final boolean enforceUtf8Mask;
private final boolean needsIsInitializedCheck;
- private final boolean isMapFieldWithProto2EnumValue;
+ private final boolean isLegacyEnumClosedBit;
private final boolean hasHasBit;
ProtoFieldType(
@@ -39,13 +39,13 @@
boolean isRequired,
boolean enforceUtf8Mask,
boolean needsIsInitializedCheck,
- boolean isMapFieldWithProto2EnumValue,
+ boolean isLegacyEnumClosedBit,
boolean hasHasBit) {
this.id = id;
this.isRequired = isRequired;
this.enforceUtf8Mask = enforceUtf8Mask;
this.needsIsInitializedCheck = needsIsInitializedCheck;
- this.isMapFieldWithProto2EnumValue = isMapFieldWithProto2EnumValue;
+ this.isLegacyEnumClosedBit = isLegacyEnumClosedBit;
this.hasHasBit = hasHasBit;
assert isValid();
}
@@ -58,7 +58,7 @@
isBitInMaskSet(fieldTypeWithExtraBits, FIELD_IS_REQUIRED_MASK),
isBitInMaskSet(fieldTypeWithExtraBits, FIELD_ENFORCE_UTF8_MASK),
isBitInMaskSet(fieldTypeWithExtraBits, FIELD_NEEDS_IS_INITIALIZED_CHECK_MASK),
- isBitInMaskSet(fieldTypeWithExtraBits, FIELD_IS_MAP_FIELD_WITH_PROTO_2_ENUM_VALUE_MASK),
+ isBitInMaskSet(fieldTypeWithExtraBits, FIELD_IS_LEGACY_ENUM_CLOSED_BIT_MASK),
isBitInMaskSet(fieldTypeWithExtraBits, FIELD_HAS_HAS_BIT_MASK));
} else {
return new ProtoOneOfFieldType(
@@ -66,7 +66,7 @@
isBitInMaskSet(fieldTypeWithExtraBits, FIELD_IS_REQUIRED_MASK),
isBitInMaskSet(fieldTypeWithExtraBits, FIELD_ENFORCE_UTF8_MASK),
isBitInMaskSet(fieldTypeWithExtraBits, FIELD_NEEDS_IS_INITIALIZED_CHECK_MASK),
- isBitInMaskSet(fieldTypeWithExtraBits, FIELD_IS_MAP_FIELD_WITH_PROTO_2_ENUM_VALUE_MASK),
+ isBitInMaskSet(fieldTypeWithExtraBits, FIELD_IS_LEGACY_ENUM_CLOSED_BIT_MASK),
isBitInMaskSet(fieldTypeWithExtraBits, FIELD_HAS_HAS_BIT_MASK));
}
}
@@ -98,8 +98,8 @@
return id == MAP_ID;
}
- public boolean isMapFieldWithProto2EnumValue() {
- return isMapFieldWithProto2EnumValue;
+ public boolean isLegacyEnumClosedBit() {
+ return isLegacyEnumClosedBit;
}
public boolean isMessage() {
@@ -147,9 +147,9 @@
case ENUM_ID:
case ENUM_LIST_ID:
case ENUM_LIST_PACKAGED_ID:
- return BooleanUtils.intValue(isProto2) + 1;
+ return BooleanUtils.intValue(isProto2 || isLegacyEnumClosedBit) + 1;
case MAP_ID:
- return BooleanUtils.intValue(isMapFieldWithProto2EnumValue) + 2;
+ return BooleanUtils.intValue(isLegacyEnumClosedBit) + 2;
default:
return 1;
}
@@ -166,8 +166,8 @@
if (needsIsInitializedCheck) {
result |= FIELD_NEEDS_IS_INITIALIZED_CHECK_MASK;
}
- if (isMapFieldWithProto2EnumValue) {
- result |= FIELD_IS_MAP_FIELD_WITH_PROTO_2_ENUM_VALUE_MASK;
+ if (isLegacyEnumClosedBit) {
+ result |= FIELD_IS_LEGACY_ENUM_CLOSED_BIT_MASK;
}
if (hasHasBit) {
result |= FIELD_HAS_HAS_BIT_MASK;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoOneOfFieldType.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoOneOfFieldType.java
index 1bc2ab5..9bfb3fd 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoOneOfFieldType.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoOneOfFieldType.java
@@ -15,15 +15,10 @@
boolean isRequired,
boolean enforceUtf8Mask,
boolean needsIsInitializedCheck,
- boolean isMapFieldWithProto2EnumValue,
+ boolean isLegacyEnumClosedBit,
boolean hasHasBit) {
super(
- id,
- isRequired,
- enforceUtf8Mask,
- needsIsInitializedCheck,
- isMapFieldWithProto2EnumValue,
- hasHasBit);
+ id, isRequired, enforceUtf8Mask, needsIsInitializedCheck, isLegacyEnumClosedBit, hasHasBit);
}
public ProtoFieldType getActualFieldType(ProtoFieldTypeFactory factory) {
@@ -69,7 +64,7 @@
case GROUP_ID:
return 1;
case ENUM_ID:
- return BooleanUtils.intValue(isProto2);
+ return BooleanUtils.intValue(isProto2 || isLegacyEnumClosedBit());
default:
return 0;
}
diff --git a/src/main/java/com/android/tools/r8/utils/ThrowingConsumer.java b/src/main/java/com/android/tools/r8/utils/ThrowingConsumer.java
index 2b6eb66..1825fff 100644
--- a/src/main/java/com/android/tools/r8/utils/ThrowingConsumer.java
+++ b/src/main/java/com/android/tools/r8/utils/ThrowingConsumer.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
+import com.android.tools.r8.errors.Unreachable;
import java.util.function.Consumer;
/**
@@ -26,4 +27,10 @@
throw runtimeException;
}
}
+
+ static <T, E extends Throwable> ThrowingConsumer<T, E> unreachable() {
+ return t -> {
+ throw new Unreachable();
+ };
+ }
}
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderOnlyReferencedFromDynamicMethodTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderOnlyReferencedFromDynamicMethodTest.java
index ed6f6fd..349f9fd 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderOnlyReferencedFromDynamicMethodTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderOnlyReferencedFromDynamicMethodTest.java
@@ -4,22 +4,27 @@
package com.android.tools.r8.internal.proto;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbstract;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
+// TODO(b/339100248): Rename class to ProtoBuilderOnlyReferencedFromDynamicMethodTest.
+// TODO(b/339100248): Avoid creating edition2023 messages in proto2 package.
+// Instead move proto2 and proto edition2023 messages to com.android.tools.r8.proto.
@RunWith(Parameterized.class)
public class Proto2BuilderOnlyReferencedFromDynamicMethodTest extends ProtoShrinkingTestBase {
@@ -28,16 +33,38 @@
@Parameter(0)
public TestParameters parameters;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withDefaultDexRuntime().withAllApiLevels().build();
+ @Parameter(1)
+ public ProtoRuntime protoRuntime;
+
+ @Parameter(2)
+ public ProtoTestSources protoTestSources;
+
+ @Parameters(name = "{0}, {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDefaultDexRuntime().withAllApiLevels().build(),
+ ProtoRuntime.values(),
+ ProtoTestSources.getEdition2023AndProto2());
}
@Test
- public void test() throws Exception {
+ public void testD8() throws Exception {
+ protoRuntime.assumeIsNewerThanOrEqualToMinimumRequiredRuntime(protoTestSources);
+ testForD8()
+ .addProgramFiles(protoRuntime.getProgramFiles())
+ .addProgramFiles(protoTestSources.getProgramFiles())
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), MAIN)
+ .apply(this::checkRunResult);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ protoRuntime.assumeIsNewerThanOrEqualToMinimumRequiredRuntime(protoTestSources);
testForR8(parameters.getBackend())
- .apply(this::addProto2TestSources)
- .apply(this::addLegacyRuntime)
+ .apply(protoRuntime::addRuntime)
+ .apply(protoRuntime::workaroundProtoMessageRemoval)
+ .addProgramFiles(protoTestSources.getProgramFiles())
.addKeepMainRule(MAIN)
.allowAccessModification()
.allowDiagnosticMessages()
@@ -51,8 +78,7 @@
.apply(this::inspectWarningMessages)
.inspect(this::inspect)
.run(parameters.getRuntime(), MAIN)
- .assertSuccessWithOutputLines(
- "false", "0", "false", "", "false", "0", "false", "0", "false", "");
+ .apply(this::checkRunResult);
}
private void inspect(CodeInspector outputInspector) {
@@ -63,9 +89,18 @@
ClassSubject generatedMessageLiteBuilder =
outputInspector.clazz("com.google.protobuf.GeneratedMessageLite$Builder");
assertThat(generatedMessageLiteBuilder, isPresent());
- assertFalse(generatedMessageLiteBuilder.isAbstract());
+ assertThat(generatedMessageLiteBuilder, not(isAbstract()));
assertThat(
outputInspector.clazz("com.android.tools.r8.proto2.TestProto$Primitives$Builder"),
- not(isPresent()));
+ isAbsent());
+ }
+
+ private void checkRunResult(SingleTestRunResult<?> runResult) {
+ runResult.applyIf(
+ protoTestSources.getCorrespondingRuntime() == protoRuntime,
+ rr ->
+ rr.assertSuccessWithOutputLines(
+ "false", "0", "false", "", "false", "0", "false", "0", "false", ""),
+ rr -> rr.assertFailureWithErrorThatThrows(ArrayIndexOutOfBoundsException.class));
}
}
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
index abedac8..3c6211e 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
@@ -4,22 +4,26 @@
package com.android.tools.r8.internal.proto;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.D8TestCompileResult;
import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
+import com.google.protobuf.InvalidProtocolBufferException;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -27,6 +31,9 @@
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
+// TODO(b/339100248): Rename class to ProtoBuilderShrinkingTest.
+// TODO(b/339100248): Avoid creating edition2023 messages in proto2 package.
+// Instead move proto2 and proto edition2023 messages to com.android.tools.r8.proto.
@RunWith(Parameterized.class)
public class Proto2BuilderShrinkingTest extends ProtoShrinkingTestBase {
@@ -76,19 +83,46 @@
@Parameter(1)
public TestParameters parameters;
- @Parameters(name = "{1}, {0}")
+ @Parameter(2)
+ public ProtoRuntime protoRuntime;
+
+ @Parameter(3)
+ public ProtoTestSources protoTestSources;
+
+ @Parameters(name = "{1}, {2}, {3}, {0}")
public static List<Object[]> data() {
return buildParameters(
MainClassesConfig.values(),
- getTestParameters().withDefaultDexRuntime().withAllApiLevels().build());
+ getTestParameters().withDefaultDexRuntime().withAllApiLevels().build(),
+ ProtoRuntime.values(),
+ ProtoTestSources.getEdition2023AndProto2());
}
@Test
- public void test() throws Exception {
- R8TestCompileResult result =
+ public void testD8() throws Exception {
+ protoRuntime.assumeIsNewerThanOrEqualToMinimumRequiredRuntime(protoTestSources);
+ D8TestCompileResult compileResult =
+ testForD8()
+ .addProgramFiles(protoRuntime.getProgramFiles())
+ .addProgramFiles(protoTestSources.getProgramFiles())
+ .setMinApi(parameters)
+ .compile();
+
+ for (String main : config.getMainClasses()) {
+ compileResult
+ .run(parameters.getRuntime(), main)
+ .apply(runResult -> checkRunResult(runResult, main, false));
+ }
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ protoRuntime.assumeIsNewerThanOrEqualToMinimumRequiredRuntime(protoTestSources);
+ R8TestCompileResult compileResult =
testForR8(parameters.getBackend())
- .apply(this::addProto2TestSources)
- .apply(this::addLegacyRuntime)
+ .apply(protoRuntime::addRuntime)
+ .apply(protoRuntime::workaroundProtoMessageRemoval)
+ .addProgramFiles(protoTestSources.getProgramFiles())
.addKeepMainRules(config.getMainClasses())
.allowAccessModification()
.allowDiagnosticMessages()
@@ -104,7 +138,28 @@
.inspect(this::inspect);
for (String main : config.getMainClasses()) {
- result.run(parameters.getRuntime(), main).assertSuccessWithOutput(getExpectedOutput(main));
+ compileResult
+ .run(parameters.getRuntime(), main)
+ .apply(runResult -> checkRunResult(runResult, main, true));
+ }
+ }
+
+ private void checkRunResult(SingleTestRunResult<?> runResult, String main, boolean isR8) {
+ if (main.equals("proto2.HasFlaggedOffExtensionBuilderTestClass")) {
+ runResult.applyIf(
+ isR8 && protoRuntime.isEdition2023() && parameters.getApiLevel() == AndroidApiLevel.O,
+ rr -> rr.assertFailureWithErrorThatThrows(IllegalAccessException.class),
+ !isR8 && protoRuntime.isEdition2023() && protoTestSources.isProto2(),
+ rr -> rr.assertFailureWithErrorThatThrows(InvalidProtocolBufferException.class),
+ rr -> rr.assertSuccessWithOutput(getExpectedOutput(main)));
+ } else {
+ runResult.applyIf(
+ protoTestSources.getCorrespondingRuntime() == protoRuntime,
+ rr -> rr.assertSuccessWithOutput(getExpectedOutput(main)),
+ main.equals("proto2.BuilderWithProtoBuilderSetterTestClass")
+ || main.equals("proto2.BuilderWithProtoSetterTestClass"),
+ rr -> rr.assertFailureWithErrorThatThrows(StringIndexOutOfBoundsException.class),
+ rr -> rr.assertFailureWithErrorThatThrows(ArrayIndexOutOfBoundsException.class));
}
}
@@ -193,16 +248,16 @@
assertThat(
outputInspector.clazz(
"com.android.tools.r8.proto2.Shrinking$HasFlaggedOffExtension$Builder"),
- not(isPresent()));
+ isAbsent());
assertThat(
outputInspector.clazz("com.android.tools.r8.proto2.TestProto$Primitives$Builder"),
- not(isPresent()));
+ isAbsent());
assertThat(
outputInspector.clazz("com.android.tools.r8.proto2.TestProto$OuterMessage$Builder"),
- not(isPresent()));
+ isAbsent());
assertThat(
outputInspector.clazz("com.android.tools.r8.proto2.TestProto$NestedMessage$Builder"),
- not(isPresent()));
+ isAbsent());
}
private void verifyMethodToInvokeValuesAreAbsent(CodeInspector outputInspector) {
@@ -220,20 +275,23 @@
assertThat(mainMethodSubject, isPresent());
// Verify that the calls to GeneratedMessageLite.createBuilder() have been inlined.
- assertTrue(
- mainMethodSubject
- .streamInstructions()
- .filter(InstructionSubject::isInvoke)
- .map(InstructionSubject::getMethod)
- .allMatch(
- method ->
- method.getHolderType()
- != generatedMessageLiteClassSubject.getDexProgramClass().getType()
- || (isInitializedMethodSubject.isPresent()
- && method
- == isInitializedMethodSubject
- .getProgramMethod()
- .getReference())));
+ // TODO(b/339100248): Investigate inadequate inlining with edition2023.
+ if (protoRuntime.isLegacy()) {
+ assertTrue(
+ mainMethodSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .map(InstructionSubject::getMethod)
+ .allMatch(
+ method ->
+ method.getHolderType()
+ != generatedMessageLiteClassSubject.getDexProgramClass().getType()
+ || (isInitializedMethodSubject.isPresent()
+ && method
+ == isInitializedMethodSubject
+ .getProgramMethod()
+ .getReference())));
+ }
// Verify that there are no accesses to MethodToInvoke after inlining createBuilder() -- and
// specifically no accesses to MethodToInvoke.NEW_BUILDER.
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
index 462515f..1a81f39 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
@@ -1,7 +1,6 @@
// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-
package com.android.tools.r8.internal.proto;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
@@ -10,8 +9,10 @@
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.DexItemFactory;
@@ -28,6 +29,9 @@
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
+// TODO(b/339100248): Rename class to ProtoShrinkingTest.
+// TODO(b/339100248): Avoid creating edition2023 messages in proto2 package.
+// Instead move proto2 and proto edition2023 messages to com.android.tools.r8.proto.
@RunWith(Parameterized.class)
public class Proto2ShrinkingTest extends ProtoShrinkingTestBase {
@@ -51,6 +55,8 @@
private static final String USES_ONLY_REPEATED_FIELDS =
"com.android.tools.r8.proto2.Shrinking$UsesOnlyRepeatedFields";
+ private static final String MAIN = "proto2.TestClass";
+
@Parameter(0)
public boolean allowAccessModification;
@@ -60,21 +66,42 @@
@Parameter(2)
public TestParameters parameters;
- @Parameters(name = "{2}, allow access modification: {0}, enable minification: {1}")
+ @Parameter(3)
+ public ProtoRuntime protoRuntime;
+
+ @Parameter(4)
+ public ProtoTestSources protoTestSources;
+
+ @Parameters(name = "{2}, {3}, {4}, allow access modification: {0}, enable minification: {1}")
public static List<Object[]> data() {
return buildParameters(
BooleanUtils.values(),
BooleanUtils.values(),
- getTestParameters().withDefaultDexRuntime().withAllApiLevels().build());
+ getTestParameters().withDefaultDexRuntime().withAllApiLevels().build(),
+ ProtoRuntime.values(),
+ ProtoTestSources.getEdition2023AndProto2());
}
@Test
- public void test() throws Exception {
+ public void testD8() throws Exception {
+ protoRuntime.assumeIsNewerThanOrEqualToMinimumRequiredRuntime(protoTestSources);
+ testForD8()
+ .addProgramFiles(protoRuntime.getProgramFiles())
+ .addProgramFiles(protoTestSources.getProgramFiles())
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), MAIN)
+ .apply(this::checkRunResult);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ protoRuntime.assumeIsNewerThanOrEqualToMinimumRequiredRuntime(protoTestSources);
R8TestRunResult result =
testForR8(parameters.getBackend())
- .apply(this::addProto2TestSources)
- .apply(this::addLegacyRuntime)
- .addKeepMainRule("proto2.TestClass")
+ .apply(protoRuntime::addRuntime)
+ .apply(protoRuntime::workaroundProtoMessageRemoval)
+ .addProgramFiles(protoTestSources.getProgramFiles())
+ .addKeepMainRule(MAIN)
// TODO(b/173340579): This rule should not be needed to allow shrinking of
// PartiallyUsed$Enum.
.addNoHorizontalClassMergingRule(PARTIALLY_USED + "$Enum$1")
@@ -92,15 +119,41 @@
.apply(this::inspectWarningMessages)
.inspect(
outputInspector -> {
- CodeInspector inputInspector = getProto2TestSourcesInspector();
+ CodeInspector inputInspector = protoTestSources.getInspector();
verifyMapAndRequiredFieldsAreKept(inputInspector, outputInspector);
verifyUnusedExtensionsAreRemoved(inputInspector, outputInspector);
verifyUnusedFieldsAreRemoved(inputInspector, outputInspector);
verifyUnusedHazzerBitFieldsAreRemoved(inputInspector, outputInspector);
verifyUnusedTypesAreRemoved(inputInspector, outputInspector);
})
- .run(parameters.getRuntime(), "proto2.TestClass")
- .assertSuccessWithOutputLines(
+ .run(parameters.getRuntime(), MAIN)
+ .apply(this::checkRunResult);
+
+ if (protoTestSources.getCorrespondingRuntime() != protoRuntime) {
+ result.assertFailure();
+ return;
+ }
+
+ DexItemFactory dexItemFactory = new DexItemFactory();
+ ProtoApplicationStats original =
+ new ProtoApplicationStats(dexItemFactory, protoTestSources.getInspector());
+ ProtoApplicationStats actual =
+ new ProtoApplicationStats(dexItemFactory, result.inspector(), original);
+
+ assertEquals(
+ ImmutableSet.of(),
+ actual.getGeneratedExtensionRegistryStats().getSpuriouslyRetainedExtensionFields());
+
+ if (ToolHelper.isLocalDevelopment()) {
+ System.out.println(actual.getStats());
+ }
+ }
+
+ private void checkRunResult(SingleTestRunResult<?> runResult) {
+ runResult.applyIf(
+ protoTestSources.getCorrespondingRuntime() == protoRuntime,
+ rr ->
+ rr.assertSuccessWithOutputLines(
"--- roundtrip ---",
"true",
"123",
@@ -126,21 +179,8 @@
"true",
"10",
"10",
- "10");
-
- DexItemFactory dexItemFactory = new DexItemFactory();
- ProtoApplicationStats original =
- new ProtoApplicationStats(dexItemFactory, getProto2TestSourcesInspector());
- ProtoApplicationStats actual =
- new ProtoApplicationStats(dexItemFactory, result.inspector(), original);
-
- assertEquals(
- ImmutableSet.of(),
- actual.getGeneratedExtensionRegistryStats().getSpuriouslyRetainedExtensionFields());
-
- if (ToolHelper.isLocalDevelopment()) {
- System.out.println(actual.getStats());
- }
+ "10"),
+ rr -> rr.assertFailureWithErrorThatThrows(ArrayIndexOutOfBoundsException.class));
}
private void verifyMapAndRequiredFieldsAreKept(
@@ -370,11 +410,13 @@
}
@Test
- public void testNoRewriting() throws Exception {
+ public void testR8NoRewriting() throws Exception {
+ protoRuntime.assumeIsNewerThanOrEqualToMinimumRequiredRuntime(protoTestSources);
testForR8(parameters.getBackend())
- .apply(this::addProto2TestSources)
- .apply(this::addLegacyRuntime)
- .addKeepMainRule("proto2.TestClass")
+ .apply(protoRuntime::addRuntime)
+ .apply(protoRuntime::workaroundProtoMessageRemoval)
+ .addProgramFiles(protoTestSources.getProgramFiles())
+ .addKeepMainRule(MAIN)
// Retain all protos.
.addKeepRules(keepAllProtosRule())
// Retain the signature of dynamicMethod() and newMessageInfo().
@@ -392,15 +434,18 @@
.apply(this::inspectWarningMessages)
.inspect(
inspector ->
- assertRewrittenProtoSchemasMatch(getProto2TestSourcesInspector(), inspector));
+ assertRewrittenProtoSchemasMatch(protoTestSources.getInspector(), inspector));
}
@Test
- public void testTwoExtensionRegistrys() throws Exception {
+ public void testR8TwoExtensionRegistries() throws Exception {
+ protoRuntime.assumeIsNewerThanOrEqualToMinimumRequiredRuntime(protoTestSources);
+ assumeTrue("Only proto2 has two extension registries", protoTestSources.isProto2());
testForR8(parameters.getBackend())
- .apply(this::addProto2TestSources)
- .apply(this::addLegacyRuntime)
- .addKeepMainRule("proto2.TestClass")
+ .apply(protoRuntime::addRuntime)
+ .apply(protoRuntime::workaroundProtoMessageRemoval)
+ .addProgramFiles(protoTestSources.getProgramFiles())
+ .addKeepMainRule(MAIN)
.addKeepRules(findLiteExtensionByNumberInDuplicateCalledRule())
// TODO(b/173340579): This rule should not be needed to allow shrinking of
// PartiallyUsed$Enum.
@@ -418,7 +463,7 @@
.apply(this::inspectWarningMessages)
.inspect(
outputInspector -> {
- CodeInspector inputInspector = getProto2TestSourcesInspector();
+ CodeInspector inputInspector = protoTestSources.getInspector();
verifyUnusedExtensionsAreRemoved(inputInspector, outputInspector);
verifyUnusedExtensionsAreRemoved(
inputInspector,
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
index 49fdd08..21314c0 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto3ShrinkingTest.java
@@ -16,10 +16,12 @@
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
@RunWith(Parameterized.class)
public class Proto3ShrinkingTest extends ProtoShrinkingTestBase {
@@ -27,30 +29,39 @@
private static final String PARTIALLY_USED =
"com.android.tools.r8.proto3.Shrinking$PartiallyUsed";
- private final boolean allowAccessModification;
- private final boolean enableMinification;
- private final TestParameters parameters;
+ @Parameter(0)
+ public boolean allowAccessModification;
- @Parameterized.Parameters(name = "{2}, allow access modification: {0}, enable minification: {1}")
+ @Parameter(1)
+ public boolean enableMinification;
+
+ @Parameter(2)
+ public TestParameters parameters;
+
+ @Parameter(3)
+ public ProtoRuntime protoRuntime;
+
+ @Parameter(4)
+ public ProtoTestSources protoTestSources;
+
+ @Parameterized.Parameters(
+ name = "{2}, {3}, {4}, allow access modification: {0}, enable minification: {1}")
public static List<Object[]> data() {
return buildParameters(
BooleanUtils.values(),
BooleanUtils.values(),
- getTestParameters().withDefaultDexRuntime().withAllApiLevels().build());
- }
-
- public Proto3ShrinkingTest(
- boolean allowAccessModification, boolean enableMinification, TestParameters parameters) {
- this.allowAccessModification = allowAccessModification;
- this.enableMinification = enableMinification;
- this.parameters = parameters;
+ getTestParameters().withDefaultDexRuntime().withAllApiLevels().build(),
+ ProtoRuntime.values(),
+ ImmutableList.of(ProtoTestSources.PROTO3));
}
@Test
- public void test() throws Exception {
+ public void testR8() throws Exception {
+ protoRuntime.assumeIsNewerThanOrEqualToMinimumRequiredRuntime(protoTestSources);
testForR8(parameters.getBackend())
- .apply(this::addProto3TestSources)
- .apply(this::addLegacyRuntime)
+ .apply(protoRuntime::addRuntime)
+ .apply(protoRuntime::workaroundProtoMessageRemoval)
+ .addProgramFiles(protoTestSources.getProgramFiles())
.addKeepMainRule("proto3.TestClass")
.allowAccessModification(allowAccessModification)
.allowDiagnosticMessages()
@@ -65,7 +76,7 @@
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.inspect(
outputInspector -> {
- CodeInspector inputInspector = getProto3TestSourcesInspector();
+ CodeInspector inputInspector = protoTestSources.getInspector();
verifyUnusedFieldsAreRemoved(inputInspector, outputInspector);
})
.run(parameters.getRuntime(), "proto3.TestClass")
@@ -93,10 +104,12 @@
}
@Test
- public void testNoRewriting() throws Exception {
+ public void testR8NoRewriting() throws Exception {
+ protoRuntime.assumeIsNewerThanOrEqualToMinimumRequiredRuntime(protoTestSources);
testForR8(parameters.getBackend())
- .apply(this::addProto3TestSources)
- .apply(this::addLegacyRuntime)
+ .apply(protoRuntime::addRuntime)
+ .apply(protoRuntime::workaroundProtoMessageRemoval)
+ .addProgramFiles(protoTestSources.getProgramFiles())
.addKeepMainRule("proto3.TestClass")
// Retain all protos.
.addKeepRules(keepAllProtosRule())
@@ -118,6 +131,6 @@
containsString("required for default or static interface methods desugaring")))
.inspect(
inspector ->
- assertRewrittenProtoSchemasMatch(getProto3TestSourcesInspector(), inspector));
+ assertRewrittenProtoSchemasMatch(protoTestSources.getInspector(), inspector));
}
}
diff --git a/src/test/java/com/android/tools/r8/internal/proto/ProtoRuntime.java b/src/test/java/com/android/tools/r8/internal/proto/ProtoRuntime.java
new file mode 100644
index 0000000..a9dd2b2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/proto/ProtoRuntime.java
@@ -0,0 +1,66 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal.proto;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.ToolHelper;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+
+public enum ProtoRuntime {
+ EDITION2023("edition2023", 1),
+ LEGACY("legacy", 0);
+
+ private final String runtimeName;
+ private final int syntheticVersionNumber;
+
+ ProtoRuntime(String runtimeName, int syntheticVersionNumber) {
+ this.runtimeName = runtimeName;
+ this.syntheticVersionNumber = syntheticVersionNumber;
+ }
+
+ public void addRuntime(R8TestBuilder<?> testBuilder) {
+ Path runtimeDir = Paths.get(ToolHelper.PROTO_RUNTIME_DIR, runtimeName);
+ testBuilder
+ .addProgramFiles(runtimeDir.resolve("libprotobuf_lite.jar"))
+ .addKeepRuleFiles(runtimeDir.resolve("lite_proguard.pgcfg"));
+ }
+
+ public Collection<Path> getKeepRuleFiles() {
+ Path runtimeDir = Paths.get(ToolHelper.PROTO_RUNTIME_DIR, runtimeName);
+ return ImmutableList.of(runtimeDir.resolve("lite_proguard.pgcfg"));
+ }
+
+ public Collection<Path> getProgramFiles() {
+ Path runtimeDir = Paths.get(ToolHelper.PROTO_RUNTIME_DIR, runtimeName);
+ return ImmutableList.of(runtimeDir.resolve("libprotobuf_lite.jar"));
+ }
+
+ public void assumeIsNewerThanOrEqualToMinimumRequiredRuntime(ProtoTestSources protoTestSources) {
+ assumeTrue(isNewerThanOrEqualTo(protoTestSources.getMinimumRequiredRuntime()));
+ }
+
+ public boolean isEdition2023() {
+ return this == EDITION2023;
+ }
+
+ public boolean isLegacy() {
+ return this == LEGACY;
+ }
+
+ public boolean isNewerThanOrEqualTo(ProtoRuntime protoRuntime) {
+ return syntheticVersionNumber >= protoRuntime.syntheticVersionNumber;
+ }
+
+ // The class com.google.protobuf.ProtoMessage is not present in newer proto lite runtimes.
+ public void workaroundProtoMessageRemoval(R8TestBuilder<?> testBuilder) {
+ if (isNewerThanOrEqualTo(ProtoRuntime.EDITION2023)) {
+ testBuilder.addDontWarn("com.google.protobuf.ProtoMessage");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/proto/ProtoShrinkingTestBase.java b/src/test/java/com/android/tools/r8/internal/proto/ProtoShrinkingTestBase.java
index cd21b9f..f881944 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/ProtoShrinkingTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/ProtoShrinkingTestBase.java
@@ -14,10 +14,8 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
-import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteShrinker;
import com.android.tools.r8.ir.analysis.proto.ProtoReferences;
@@ -28,13 +26,8 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
-import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@@ -57,47 +50,6 @@
}
}
- void addLegacyRuntime(R8TestBuilder<?> testBuilder) {
- Path runtimeDir = Paths.get(ToolHelper.PROTO_RUNTIME_DIR, "legacy");
- addRuntime(testBuilder, runtimeDir);
- }
-
- private void addRuntime(R8TestBuilder<?> testBuilder, Path runtimeDir) {
- testBuilder
- .addProgramFiles(runtimeDir.resolve("libprotobuf_lite.jar"))
- .addKeepRuleFiles(runtimeDir.resolve("lite_proguard.pgcfg"));
- }
-
- void addProto2TestSources(R8TestBuilder<?> testBuilder) {
- testBuilder.addProgramFiles(getProto2TestSources());
- }
-
- void addProto3TestSources(R8TestBuilder<?> testBuilder) {
- testBuilder.addProgramFiles(getProto3TestSources());
- }
-
- private Collection<Path> getProto2TestSources() {
- Path testDir = Paths.get(ToolHelper.PROTO_TEST_DIR, "proto2");
- return getTestSources(testDir);
- }
-
- private Collection<Path> getProto3TestSources() {
- Path testDir = Paths.get(ToolHelper.PROTO_TEST_DIR, "proto3");
- return getTestSources(testDir);
- }
-
- private Collection<Path> getTestSources(Path testDir) {
- return ImmutableList.of(testDir.resolve("proto.jar"), testDir.resolve("test.jar"));
- }
-
- CodeInspector getProto2TestSourcesInspector() throws IOException {
- return new CodeInspector(getProto2TestSources());
- }
-
- CodeInspector getProto3TestSourcesInspector() throws IOException {
- return new CodeInspector(getProto3TestSources());
- }
-
static String findLiteExtensionByNumberInDuplicateCalledRule() {
return StringUtils.lines(
"-keep class com.google.protobuf.proto2_registryGeneratedExtensionRegistryLiteDuplicate {",
diff --git a/src/test/java/com/android/tools/r8/internal/proto/ProtoTestSources.java b/src/test/java/com/android/tools/r8/internal/proto/ProtoTestSources.java
new file mode 100644
index 0000000..2322434
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/proto/ProtoTestSources.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal.proto;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+
+public enum ProtoTestSources {
+ EDITION2023("edition2023"),
+ PROTO2("proto2"),
+ PROTO3("proto3");
+
+ private final String testName;
+
+ ProtoTestSources(String testName) {
+ this.testName = testName;
+ }
+
+ public static Collection<ProtoTestSources> getEdition2023AndProto2() {
+ return ImmutableList.of(EDITION2023, PROTO2);
+ }
+
+ public ProtoRuntime getCorrespondingRuntime() {
+ switch (this) {
+ case EDITION2023:
+ return ProtoRuntime.EDITION2023;
+ case PROTO2:
+ case PROTO3:
+ return ProtoRuntime.LEGACY;
+ default:
+ throw new Unreachable();
+ }
+ }
+
+ public CodeInspector getInspector() throws IOException {
+ return new CodeInspector(getProgramFiles());
+ }
+
+ public ProtoRuntime getMinimumRequiredRuntime() {
+ switch (this) {
+ case EDITION2023:
+ return ProtoRuntime.EDITION2023;
+ case PROTO2:
+ case PROTO3:
+ return ProtoRuntime.LEGACY;
+ default:
+ throw new Unreachable();
+ }
+ }
+
+ public Collection<Path> getProgramFiles() {
+ Path testDir = getTestDir();
+ ImmutableList.Builder<Path> builder =
+ ImmutableList.<Path>builder()
+ .add(testDir.resolve("proto.jar"), testDir.resolve("test.jar"));
+ Path registryJar = testDir.resolve("registry.jar");
+ if (Files.exists(registryJar)) {
+ builder.add(registryJar);
+ }
+ return builder.build();
+ }
+
+ public Path getTestDir() {
+ return Paths.get(ToolHelper.PROTO_TEST_DIR, testName);
+ }
+
+ public boolean isProto2() {
+ return this == PROTO2;
+ }
+}
diff --git a/src/test/testbase/java/com/android/tools/r8/TestRunResult.java b/src/test/testbase/java/com/android/tools/r8/TestRunResult.java
index 595b3b7..dc55969 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestRunResult.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestRunResult.java
@@ -54,16 +54,13 @@
}
public <S extends Throwable, T extends Throwable> RR applyIf(
- boolean condition, ThrowingConsumer<RR, S> thenConsumer, ThrowingConsumer<RR, T> elseConsumer)
+ boolean condition1,
+ ThrowingConsumer<RR, S> thenConsumer1,
+ ThrowingConsumer<RR, T> elseConsumer)
throws S, T {
- return applyIf(
- condition,
- thenConsumer,
- true,
- elseConsumer,
- r -> {
- assert false;
- });
+ boolean condition2 = false;
+ ThrowingConsumer<RR, RuntimeException> thenConsumer2 = ThrowingConsumer.unreachable();
+ return applyIf(condition1, thenConsumer1, condition2, thenConsumer2, elseConsumer);
}
public <S extends Throwable, T extends Throwable, U extends Throwable> RR applyIf(
@@ -73,16 +70,16 @@
ThrowingConsumer<RR, T> thenConsumer2,
ThrowingConsumer<RR, U> elseConsumer)
throws S, T, U {
+ boolean condition3 = false;
+ ThrowingConsumer<RR, RuntimeException> thenConsumer3 = ThrowingConsumer.unreachable();
return applyIf(
condition1,
thenConsumer1,
condition2,
thenConsumer2,
- true,
- elseConsumer,
- r -> {
- assert false;
- });
+ condition3,
+ thenConsumer3,
+ elseConsumer);
}
public <S extends Throwable, T extends Throwable, U extends Throwable, V extends Throwable>
@@ -95,12 +92,45 @@
ThrowingConsumer<RR, U> thenConsumer3,
ThrowingConsumer<RR, V> elseConsumer)
throws S, T, U, V {
+ boolean condition4 = false;
+ ThrowingConsumer<RR, RuntimeException> thenConsumer4 = ThrowingConsumer.unreachable();
+ return applyIf(
+ condition1,
+ thenConsumer1,
+ condition2,
+ thenConsumer2,
+ condition3,
+ thenConsumer3,
+ condition4,
+ thenConsumer4,
+ elseConsumer);
+ }
+
+ public <
+ S extends Throwable,
+ T extends Throwable,
+ U extends Throwable,
+ V extends Throwable,
+ W extends Throwable>
+ RR applyIf(
+ boolean condition1,
+ ThrowingConsumer<RR, S> thenConsumer1,
+ boolean condition2,
+ ThrowingConsumer<RR, T> thenConsumer2,
+ boolean condition3,
+ ThrowingConsumer<RR, U> thenConsumer3,
+ boolean condition4,
+ ThrowingConsumer<RR, W> thenConsumer4,
+ ThrowingConsumer<RR, V> elseConsumer)
+ throws S, T, U, V, W {
if (condition1) {
thenConsumer1.accept(self());
} else if (condition2) {
thenConsumer2.accept(self());
} else if (condition3) {
thenConsumer3.accept(self());
+ } else if (condition4) {
+ thenConsumer4.accept(self());
} else {
elseConsumer.accept(self());
}
diff --git a/third_party/proto/runtime/edition2023.tar.gz.sha1 b/third_party/proto/runtime/edition2023.tar.gz.sha1
new file mode 100644
index 0000000..8c8140e
--- /dev/null
+++ b/third_party/proto/runtime/edition2023.tar.gz.sha1
@@ -0,0 +1 @@
+583591cbd3ee7d7dd5ca9e0fefc4f015fa7b6007
\ No newline at end of file
diff --git a/third_party/proto/test/edition2023.tar.gz.sha1 b/third_party/proto/test/edition2023.tar.gz.sha1
new file mode 100644
index 0000000..9a01bb3
--- /dev/null
+++ b/third_party/proto/test/edition2023.tar.gz.sha1
@@ -0,0 +1 @@
+c9ec54459ab7787744f41e403fe641582b2146d7
\ No newline at end of file