diff --git a/java/src/com/google/template/soy/jbcsrc/ProtoUtils.java b/java/src/com/google/template/soy/jbcsrc/ProtoUtils.java index 00861f6d7c..6ad15213e4 100644 --- a/java/src/com/google/template/soy/jbcsrc/ProtoUtils.java +++ b/java/src/com/google/template/soy/jbcsrc/ProtoUtils.java @@ -89,10 +89,12 @@ import com.google.template.soy.jbcsrc.restricted.SoyRuntimeType; import com.google.template.soy.jbcsrc.restricted.Statement; import com.google.template.soy.jbcsrc.restricted.TypeInfo; +import com.google.template.soy.jbcsrc.runtime.JbcSrcExternRuntime; import com.google.template.soy.jbcsrc.runtime.JbcSrcRuntime; import com.google.template.soy.soytree.defn.TemplateStateVar; import com.google.template.soy.types.AbstractIterableType; import com.google.template.soy.types.AbstractMapType; +import com.google.template.soy.types.AnyType; import com.google.template.soy.types.BoolType; import com.google.template.soy.types.GbigintType; import com.google.template.soy.types.ListType; @@ -329,9 +331,22 @@ private static SoyExpression accessProtoUnionField( SoyType fieldType, LocalVariableManager varManager, Function memberGenerator) { - return new ProtoUnionAccessorGenerator( - baseExpr, fieldName, fieldType, varManager, memberGenerator) - .generate(); + if (SoyTypes.isNullish(baseExpr.soyType()) + || baseExpr.soyType().equals(AnyType.getInstance()) + || baseExpr.soyType().equals(UnknownType.getInstance())) { + MethodRef helperRef = + MethodRef.createPure( + JbcSrcExternRuntime.class, "getProtoUnionField", SoyValue.class, String.class); + Expression helperCall = helperRef.invoke(baseExpr.box(), constant(fieldName)); + Expression soyValueCall = JbcSrcExternRuntime.CONVERT_OBJECT_TO_SOY_VALUE.invoke(helperCall); + SoyRuntimeType resultType = SoyRuntimeType.getBoxedType(fieldType); + return SoyExpression.forSoyValue( + fieldType, soyValueCall.checkedCast(resultType.runtimeType())); + } else { + return new ProtoUnionAccessorGenerator( + baseExpr, fieldName, fieldType, varManager, memberGenerator) + .generate(); + } } private abstract static class BaseGenerator { diff --git a/java/src/com/google/template/soy/jbcsrc/runtime/JbcSrcExternRuntime.java b/java/src/com/google/template/soy/jbcsrc/runtime/JbcSrcExternRuntime.java index 9c8d1b4a5b..d4b0648bd5 100644 --- a/java/src/com/google/template/soy/jbcsrc/runtime/JbcSrcExternRuntime.java +++ b/java/src/com/google/template/soy/jbcsrc/runtime/JbcSrcExternRuntime.java @@ -495,4 +495,43 @@ private static T getEnumValue(Class clazz, int enumValue) { throw new RuntimeException(e); } } + + @Keep + @Nullable + public static Object getProtoUnionField(SoyValue base, String fieldName) { + if (base == null || base.isNullish()) { + return null; + } + com.google.protobuf.Message proto = base.getProto(); + com.google.protobuf.Descriptors.Descriptor descriptor = proto.getDescriptorForType(); + com.google.protobuf.Descriptors.FieldDescriptor field = descriptor.findFieldByName(fieldName); + + if (field == null) { + for (com.google.protobuf.Descriptors.FieldDescriptor f : descriptor.getFields()) { + if (f.getJsonName().equals(fieldName)) { + field = f; + break; + } + } + } + + if (field == null && fieldName.endsWith("List")) { + String baseName = fieldName.substring(0, fieldName.length() - 4); + field = descriptor.findFieldByName(baseName); + if (field == null) { + for (com.google.protobuf.Descriptors.FieldDescriptor f : descriptor.getFields()) { + if (f.getJsonName().equals(baseName)) { + field = f; + break; + } + } + } + } + + if (field == null) { + throw new IllegalArgumentException( + "Field " + fieldName + " not found in " + descriptor.getFullName()); + } + return proto.getField(field); + } }