From 6c62aa4650b2555aec80505e47fb8584fbdcccc2 Mon Sep 17 00:00:00 2001 From: Roberto Lublinerman Date: Sat, 18 Apr 2026 09:05:14 -0700 Subject: [PATCH] Expose record component accessors also as properties. PiperOrigin-RevId: 901811211 --- .../j2objc/gen/PropertyGenerator.java | 15 +++++++++++---- .../j2objc/gen/TypeDeclarationGenerator.java | 19 +++++++++++++++++-- .../j2objc/translate/RecordExpanderTest.java | 7 ++++++- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/translator/src/main/java/com/google/devtools/j2objc/gen/PropertyGenerator.java b/translator/src/main/java/com/google/devtools/j2objc/gen/PropertyGenerator.java index ae700b0a6f..f6c521c2e6 100644 --- a/translator/src/main/java/com/google/devtools/j2objc/gen/PropertyGenerator.java +++ b/translator/src/main/java/com/google/devtools/j2objc/gen/PropertyGenerator.java @@ -17,13 +17,11 @@ import com.google.devtools.j2objc.Options; import com.google.devtools.j2objc.ast.FieldDeclaration; import com.google.devtools.j2objc.ast.PropertyAnnotation; -import com.google.devtools.j2objc.ast.TreeUtil; import com.google.devtools.j2objc.ast.VariableDeclarationFragment; import com.google.devtools.j2objc.util.ElementUtil; import com.google.devtools.j2objc.util.ErrorUtil; import com.google.devtools.j2objc.util.NameTable; import com.google.devtools.j2objc.util.TypeUtil; -import com.google.j2objc.annotations.Property; import com.google.j2objc.annotations.Weak; import java.util.Optional; import java.util.Set; @@ -44,6 +42,7 @@ public static Optional generate( NameTable nameTable, TypeUtil typeUtil, boolean parametersNonnullByDefault) { + return new PropertyGenerator(fragment, options, nameTable, typeUtil, parametersNonnullByDefault) .build(); } @@ -70,9 +69,14 @@ private PropertyGenerator( this.nameTable = nameTable; this.typeUtil = typeUtil; this.parametersNonnullByDefault = parametersNonnullByDefault; + declaration = (FieldDeclaration) fragment.getParent(); PropertyAnnotation annotation = - (PropertyAnnotation) TreeUtil.getAnnotation(Property.class, declaration.getAnnotations()); + declaration.getAnnotations().stream() + .filter(PropertyAnnotation.class::isInstance) + .map(PropertyAnnotation.class::cast) + .findFirst() + .orElse(null); varElement = fragment.getVariableElement(); if (annotation == null && options.classProperties() @@ -134,8 +138,11 @@ private void processAccessorAttributes(Set attributes) { if (getter != null) { // Update getter from its Java name to its selector. This is normally the // same since getters have no parameters, but the name may be reserved. + String getterSelector = nameTable.getMethodSelector(getter); attributes.remove("getter=" + annotation.getGetter()); - attributes.add("getter=" + nameTable.getMethodSelector(getter)); + if (!getterSelector.equals(propertyName)) { + attributes.add("getter=" + getterSelector); + } if (!ElementUtil.isSynchronized(getter)) { attributes.add("nonatomic"); } diff --git a/translator/src/main/java/com/google/devtools/j2objc/gen/TypeDeclarationGenerator.java b/translator/src/main/java/com/google/devtools/j2objc/gen/TypeDeclarationGenerator.java index 2106b7f540..b318305594 100644 --- a/translator/src/main/java/com/google/devtools/j2objc/gen/TypeDeclarationGenerator.java +++ b/translator/src/main/java/com/google/devtools/j2objc/gen/TypeDeclarationGenerator.java @@ -30,6 +30,8 @@ import com.google.devtools.j2objc.ast.MethodDeclaration; import com.google.devtools.j2objc.ast.Name; import com.google.devtools.j2objc.ast.NativeDeclaration; +import com.google.devtools.j2objc.ast.PropertyAnnotation; +import com.google.devtools.j2objc.ast.RecordDeclaration; import com.google.devtools.j2objc.ast.TreeNode; import com.google.devtools.j2objc.ast.VariableDeclarationFragment; import com.google.devtools.j2objc.util.ElementUtil; @@ -419,13 +421,26 @@ protected void printInstanceVariables() { protected void printProperties() { Iterable fields = getAllFields(); + for (VariableDeclarationFragment fragment : fields) { + VariableElement var = fragment.getVariableElement(); + if (typeNode instanceof RecordDeclaration && !ElementUtil.isStatic(var)) { + // Instance files of a record are its components. + + // Synthesize a declaration as if it was annotated with @Property. + FieldDeclaration componentDeclaration = new FieldDeclaration(var, null); + PropertyAnnotation componentAnnotation = new PropertyAnnotation(); + componentAnnotation.getPropertyAttributes().add("readonly"); + componentAnnotation.getPropertyAttributes().add("nonatomic"); + componentDeclaration.addAnnotation(componentAnnotation); + fragment = componentDeclaration.getFragment(); + } PropertyGenerator.generate(fragment, options, nameTable, typeUtil, parametersNonnullByDefault) .ifPresent(this::println); } - if (options.classProperties() && typeNode instanceof EnumDeclaration) { - for (EnumConstantDeclaration constant : ((EnumDeclaration) typeNode).getEnumConstants()) { + if (options.classProperties() && typeNode instanceof EnumDeclaration enumDeclaration) { + for (EnumConstantDeclaration constant : enumDeclaration.getEnumConstants()) { String accessorName = nameTable.getStaticAccessorName(constant.getVariableElement()); print("\n@property (readonly, class"); if (options.nullability()) { diff --git a/translator/src/test/java/com/google/devtools/j2objc/translate/RecordExpanderTest.java b/translator/src/test/java/com/google/devtools/j2objc/translate/RecordExpanderTest.java index be581b28ec..387e52e1f3 100644 --- a/translator/src/test/java/com/google/devtools/j2objc/translate/RecordExpanderTest.java +++ b/translator/src/test/java/com/google/devtools/j2objc/translate/RecordExpanderTest.java @@ -34,7 +34,10 @@ public void testMinimalRecord() throws IOException { assertInTranslation( translation, "void Point_initWithInt_withInt_(Point *self, int32_t x, int32_t y);"); - // Verify accessors declared for record's members. + // Verify properties declared for record's members and + assertInTranslation(translation, "@property (readonly, nonatomic) int32_t x;"); + assertInTranslation(translation, "@property (readonly, nonatomic) int32_t y;"); + // also as accessor methods assertInTranslation(translation, "- (int32_t)x;"); assertInTranslation(translation, "- (int32_t)y;"); @@ -105,6 +108,8 @@ public String toString() { "Point", "Point.h"); assertInTranslation(translation, "@interface Point : JavaLangRecord"); + assertInTranslation(translation, "@property (readonly, nonatomic) int32_t x;"); + assertInTranslation(translation, "@property (readonly, nonatomic) int32_t y;"); assertInTranslation(translation, "- (int32_t)x;"); assertInTranslation(translation, "- (int32_t)y;"); assertInTranslation(translation, "- (bool)isEqual:(id)o;");